def test_equality_with_array_duplicates(self): e1 = EsmClass(EsmClassMode.DATAGRAM, EsmClassType.DEFAULT, [ EsmClassGsmFeatures.SET_REPLY_PATH, EsmClassGsmFeatures.SET_REPLY_PATH ]) e2 = EsmClass(EsmClassMode.DATAGRAM, EsmClassType.DEFAULT, [EsmClassGsmFeatures.SET_REPLY_PATH]) self.assertEqual(e1, e2)
def test_conversion(self): self.do_conversion_test( EsmClassEncoder(), EsmClass(EsmClassMode.DATAGRAM, EsmClassType.INTERMEDIATE_DELIVERY_NOTIFICATION, [EsmClassGsmFeatures.SET_REPLY_PATH]), b'a1') self.do_null_encode_test( EsmClassEncoder(), EsmClass(EsmClassMode.DEFAULT, EsmClassType.DEFAULT, []), b'00')
def test_equality_with_different_array_order(self): e1 = EsmClass(EsmClassMode.DATAGRAM, EsmClassType.DEFAULT, [ EsmClassGsmFeatures.SET_REPLY_PATH, EsmClassGsmFeatures.UDHI_INDICATOR_SET ]) e2 = EsmClass(EsmClassMode.DATAGRAM, EsmClassType.DEFAULT, [ EsmClassGsmFeatures.UDHI_INDICATOR_SET, EsmClassGsmFeatures.SET_REPLY_PATH ]) self.assertEqual(e1, e2)
def test_SubmitSM_ringtone_conversion(self): pdu = SubmitSM( 455569, service_type='', source_addr_ton=AddrTon.ALPHANUMERIC, source_addr_npi=AddrNpi.UNKNOWN, source_addr='mobileway', dest_addr_ton=AddrTon.INTERNATIONAL, dest_addr_npi=AddrNpi.ISDN, destination_addr='3369809342', esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.DEFAULT, [EsmClassGsmFeatures.UDHI_INDICATOR_SET]), protocol_id=0, priority_flag=PriorityFlag.LEVEL_0, registered_delivery=RegisteredDelivery( RegisteredDeliveryReceipt.SMSC_DELIVERY_RECEIPT_REQUESTED), replace_if_present_flag=ReplaceIfPresentFlag.DO_NOT_REPLACE, data_coding=DataCoding( DataCodingScheme.GSM_MESSAGE_CLASS, DataCodingGsmMsg(DataCodingGsmMsgCoding.DATA_8BIT, DataCodingGsmMsgClass.CLASS_1)), short_message=binascii.a2b_hex( b'06050415811581024a3a5db5a5cdcda5bdb8040084d8c51381481381481381481381481381381481581681781881881061881061b81081181081881061881061681081781081881061881061b81081181081881061881061681081781081b81881321081b81881221081b818811210824dc1446000' )) self.do_conversion_test( PDUEncoder(), pdu, b'000000a900000004000000000006f3910005006d6f62696c65776179000101333336393830393334320040000000000100f5007506050415811581024a3a5db5a5cdcda5bdb8040084d8c51381481381481381481381481381381481581681781881881061881061b81081181081881061881061681081781081881061881061b81081181081881061881061681081781081b81881321081b81881221081b818811210824dc1446000' )
def test_SubmitSM_conversion(self): pdu = SubmitSM( 9284, service_type='', source_addr_ton=AddrTon.ALPHANUMERIC, source_addr_npi=AddrNpi.UNKNOWN, source_addr='mobileway', dest_addr_ton=AddrTon.INTERNATIONAL, dest_addr_npi=AddrNpi.ISDN, destination_addr='1208230', esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.DEFAULT), protocol_id=0, priority_flag=PriorityFlag.LEVEL_0, registered_delivery=RegisteredDelivery( RegisteredDeliveryReceipt.SMSC_DELIVERY_RECEIPT_REQUESTED), replace_if_present_flag=ReplaceIfPresentFlag.DO_NOT_REPLACE, data_coding=DataCoding( DataCodingScheme.GSM_MESSAGE_CLASS, DataCodingGsmMsg(DataCodingGsmMsgCoding.DEFAULT_ALPHABET, DataCodingGsmMsgClass.CLASS_2)), short_message=b'HELLO', ) self.do_conversion_test( PDUEncoder(), pdu, b'000000360000000400000000000024440005006d6f62696c65776179000101313230383233300000000000000100f2000548454c4c4f' )
def test_DeliverSM_with_subaddress(self): pdu = DeliverSM( 1, service_type='BM8', source_addr_ton=AddrTon.INTERNATIONAL, source_addr_npi=AddrNpi.ISDN, source_addr='46123456789', dest_addr_ton=AddrTon.INTERNATIONAL, dest_addr_npi=AddrNpi.ISDN, destination_addr='14046653410', esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.DEFAULT), protocol_id=0, priority_flag=PriorityFlag.LEVEL_0, registered_delivery=RegisteredDelivery( RegisteredDeliveryReceipt.NO_SMSC_DELIVERY_RECEIPT_REQUESTED), replace_if_present_flag=ReplaceIfPresentFlag.DO_NOT_REPLACE, data_coding=DataCoding( DataCodingScheme.GSM_MESSAGE_CLASS, DataCodingGsmMsg(DataCodingGsmMsgCoding.DEFAULT_ALPHABET, DataCodingGsmMsgClass.CLASS_2)), short_message=b"Hello I'm a bigg fan of you", source_subaddress=Subaddress(SubaddressTypeTag.USER_SPECIFIED, b'742'), dest_subaddress=Subaddress(SubaddressTypeTag.USER_SPECIFIED, b'4131'), ) self.do_conversion_test( PDUEncoder(), pdu, b'00000066000000050000000000000001424d38000101343631323334353637383900010131343034363635333431300000000000000000f2001b48656c6c6f2049276d206120626967672066616e206f6620796f7502020004a037343202030005a034313331' )
def test_DeliverSM_sybase_MO_conversion(self): pdu = DeliverSM( 1, service_type='CMT', source_addr_ton=AddrTon.INTERNATIONAL, source_addr_npi=AddrNpi.UNKNOWN, source_addr='3411149500001', dest_addr_ton=AddrTon.INTERNATIONAL, dest_addr_npi=AddrNpi.UNKNOWN, destination_addr='12345455', esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.DEFAULT), protocol_id=0, priority_flag=PriorityFlag.LEVEL_0, registered_delivery=RegisteredDelivery( RegisteredDeliveryReceipt.NO_SMSC_DELIVERY_RECEIPT_REQUESTED), replace_if_present_flag=ReplaceIfPresentFlag.DO_NOT_REPLACE, data_coding=DataCoding( DataCodingScheme.GSM_MESSAGE_CLASS, DataCodingGsmMsg(DataCodingGsmMsgCoding.DEFAULT_ALPHABET, DataCodingGsmMsgClass.CLASS_2)), short_message=b'HELLO\x00', ) self.do_conversion_test( PDUEncoder(), pdu, b'0000003f000000050000000000000001434d540001003334313131343935303030303100010031323334353435350000000000000000f2000648454c4c4f00' )
def test_DeliverSM_handset_ack_conversion(self): pdu = DeliverSM( 10, service_type='CMT', source_addr_ton=AddrTon.INTERNATIONAL, source_addr_npi=AddrNpi.UNKNOWN, source_addr='6515555678', dest_addr_ton=AddrTon.INTERNATIONAL, dest_addr_npi=AddrNpi.UNKNOWN, destination_addr='123', esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.SMSC_DELIVERY_RECEIPT), protocol_id=0, priority_flag=PriorityFlag.LEVEL_0, registered_delivery=RegisteredDelivery( RegisteredDeliveryReceipt.NO_SMSC_DELIVERY_RECEIPT_REQUESTED), replace_if_present_flag=ReplaceIfPresentFlag.DO_NOT_REPLACE, data_coding=DataCoding( scheme_data=DataCodingDefault.SMSC_DEFAULT_ALPHABET), short_message= b'id:1891273321 sub:001 dlvrd:001 submit date:1305050826 done date:1305050826 stat:DELIVRD err:000 Text:DLVRD TO MOBILE\x00', message_state=MessageState.DELIVERED, receipted_message_id='70BA8A69', ) self.do_conversion_test( PDUEncoder(), pdu, b'000000b900000005000000000000000a434d5400010036353135353535363738000100313233000400000000000000007669643a31383931323733333231207375623a30303120646c7672643a303031207375626d697420646174653a3133303530353038323620646f6e6520646174653a3133303530353038323620737461743a44454c49565244206572723a30303020546578743a444c56524420544f204d4f42494c45000427000102001e0009373042413841363900' )
def test_DeliverSM_syniverse_MO_conversion(self): pdu = DeliverSM( 2676551972, service_type='AWSBD', source_addr_ton=AddrTon.INTERNATIONAL, source_addr_npi=AddrNpi.ISDN, source_addr='16505551234', dest_addr_ton=AddrTon.INTERNATIONAL, dest_addr_npi=AddrNpi.ISDN, destination_addr='17735554070', esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.DEFAULT), protocol_id=0, priority_flag=PriorityFlag.LEVEL_0, registered_delivery=RegisteredDelivery( RegisteredDeliveryReceipt.NO_SMSC_DELIVERY_RECEIPT_REQUESTED), replace_if_present_flag=ReplaceIfPresentFlag.DO_NOT_REPLACE, data_coding=DataCoding(scheme_data=DataCodingDefault.LATIN_1), short_message=b'there is no spoon', ) self.do_conversion_test( PDUEncoder(), pdu, b'0000004d00000005000000009f88f12441575342440001013136353035353531323334000101313737333535353430373000000000000000000300117468657265206973206e6f2073706f6f6e' )
def __init__(self, **kwargs): ##################### # Generic configuration block # cid validation if kwargs.get('id', None) == None: raise ConfigUndefinedIdError('SMPPConfig must have an id') idcheck = re.compile(r'^[A-Za-z0-9_-]{3,25}$') if idcheck.match(str(kwargs.get('id'))) == None: raise ConfigInvalidIdError('SMPPConfig id syntax is invalid') self.id = str(kwargs.get('id')) self.port = kwargs.get('port', 2775) if not isinstance(self.port, int): raise TypeMismatch('port must be an integer') # Logging configuration self.log_file = kwargs.get('log_file', '%s/default-%s.log' % (LOG_PATH, self.id)) self.log_rotate = kwargs.get('log_rotate', 'midnight') self.log_level = kwargs.get('log_level', logging.INFO) self.log_format = kwargs.get( 'log_format', '%(asctime)s %(levelname)-8s %(process)d %(message)s') self.log_date_format = kwargs.get('log_dateformat', '%Y-%m-%d %H:%M:%S') self.log_privacy = kwargs.get('log_privacy', False) if not isinstance(self.log_privacy, bool): raise TypeMismatch('log_privacy must be a boolean') # Timeout for response to bind request self.sessionInitTimerSecs = kwargs.get('sessionInitTimerSecs', 30) if (not isinstance(self.sessionInitTimerSecs, int) and not isinstance(self.sessionInitTimerSecs, float)): raise TypeMismatch( 'sessionInitTimerSecs must be an integer or float') # Enquire link interval self.enquireLinkTimerSecs = kwargs.get('enquireLinkTimerSecs', 30) if (not isinstance(self.enquireLinkTimerSecs, int) and not isinstance(self.enquireLinkTimerSecs, float)): raise TypeMismatch( 'enquireLinkTimerSecs must be an integer or float') # Maximum time lapse allowed between transactions, after which, # the connection is considered as inactive and will reconnect self.inactivityTimerSecs = kwargs.get('inactivityTimerSecs', 300) if not isinstance(self.inactivityTimerSecs, int) and not isinstance( self.inactivityTimerSecs, float): raise TypeMismatch( 'inactivityTimerSecs must be an integer or float') # Timeout for responses to any request PDU self.responseTimerSecs = kwargs.get('responseTimerSecs', 120) if not isinstance(self.responseTimerSecs, int) and not isinstance( self.responseTimerSecs, float): raise TypeMismatch('responseTimerSecs must be an integer or float') # Timeout for reading a single PDU, this is the maximum lapse of time between # receiving PDU's header and its complete read, if the PDU reading timed out, # the connection is considered as 'corrupt' and will reconnect self.pduReadTimerSecs = kwargs.get('pduReadTimerSecs', 10) if not isinstance(self.pduReadTimerSecs, int) and not isinstance( self.pduReadTimerSecs, float): raise TypeMismatch('pduReadTimerSecs must be an integer or float') # DLR # How much time a message is kept in redis waiting for receipt self.dlr_expiry = kwargs.get('dlr_expiry', 86400) if not isinstance(self.dlr_expiry, int) and not isinstance( self.dlr_expiry, float): raise TypeMismatch('dlr_expiry must be an integer or float') ##################### # SMPPClient Specific configuration block self.host = kwargs.get('host', '127.0.0.1') if not isinstance(self.host, str): raise TypeMismatch('host must be a string') self.username = kwargs.get('username', 'smppclient') if len(self.username) > 15: raise TypeMismatch('username is longer than allowed size (15)') self.password = kwargs.get('password', 'password') if len(self.password) > 16: raise TypeMismatch('password is longer than allowed size (16)') self.systemType = kwargs.get('systemType', '') # Reconnection self.reconnectOnConnectionLoss = kwargs.get( 'reconnectOnConnectionLoss', True) if not isinstance(self.reconnectOnConnectionLoss, bool): raise TypeMismatch('reconnectOnConnectionLoss must be a boolean') self.reconnectOnConnectionFailure = kwargs.get( 'reconnectOnConnectionFailure', True) if not isinstance(self.reconnectOnConnectionFailure, bool): raise TypeMismatch( 'reconnectOnConnectionFailure must be a boolean') self.reconnectOnConnectionLossDelay = kwargs.get( 'reconnectOnConnectionLossDelay', 10) if (not isinstance(self.reconnectOnConnectionLossDelay, int) and not isinstance(self.reconnectOnConnectionLossDelay, float)): raise TypeMismatch( 'reconnectOnConnectionLossDelay must be an integer or float') self.reconnectOnConnectionFailureDelay = kwargs.get( 'reconnectOnConnectionFailureDelay', 10) if (not isinstance(self.reconnectOnConnectionFailureDelay, int) and not isinstance(self.reconnectOnConnectionFailureDelay, float)): raise TypeMismatch( 'reconnectOnConnectionFailureDelay must be an integer or float' ) self.useSSL = kwargs.get('useSSL', False) self.SSLCertificateFile = kwargs.get('SSLCertificateFile', None) # Type of bind operation, can be one of these: # - transceiver # - transmitter # - receiver self.bindOperation = kwargs.get('bindOperation', 'transceiver') if self.bindOperation not in [ 'transceiver', 'transmitter', 'receiver' ]: raise UnknownValue('Invalid bindOperation: %s' % self.bindOperation) # These are default parameters, c.f. _setConfigParamsInPDU method in SMPPOperationFactory self.service_type = kwargs.get('service_type', None) self.addressTon = kwargs.get('addressTon', AddrTon.UNKNOWN) self.addressNpi = kwargs.get('addressNpi', AddrNpi.UNKNOWN) self.source_addr_ton = kwargs.get('source_addr_ton', AddrTon.NATIONAL) self.source_addr_npi = kwargs.get('source_addr_npi', AddrNpi.ISDN) self.dest_addr_ton = kwargs.get('dest_addr_ton', AddrTon.INTERNATIONAL) self.dest_addr_npi = kwargs.get('dest_addr_npi', AddrNpi.ISDN) self.addressRange = kwargs.get('addressRange', None) self.source_addr = kwargs.get('source_addr', None) self.esm_class = kwargs.get( 'esm_class', EsmClass(EsmClassMode.STORE_AND_FORWARD, EsmClassType.DEFAULT)) self.protocol_id = kwargs.get('protocol_id', None) self.priority_flag = kwargs.get('priority_flag', PriorityFlag.LEVEL_0) self.schedule_delivery_time = kwargs.get('schedule_delivery_time', None) self.validity_period = kwargs.get('validity_period', None) self.registered_delivery = kwargs.get( 'registered_delivery', RegisteredDelivery( RegisteredDeliveryReceipt.NO_SMSC_DELIVERY_RECEIPT_REQUESTED)) self.replace_if_present_flag = kwargs.get( 'replace_if_present_flag', ReplaceIfPresentFlag.DO_NOT_REPLACE) self.sm_default_msg_id = kwargs.get('sm_default_msg_id', 0) # 5.2.19 data_coding / c. There is no default setting for the data_coding parameter. # Possible values: # SMSC_DEFAULT_ALPHABET: 0x00 / 0 # IA5_ASCII: 0x01 / 1 # OCTET_UNSPECIFIED: 0x02 / 2 # LATIN_1: 0x03 / 3 # OCTET_UNSPECIFIED_COMMON: 0x04 / 4 # JIS: 0x05 / 5 # CYRILLIC: 0x06 / 6 # ISO_8859_8: 0x07 / 7 # UCS2: 0x08 / 8 # PICTOGRAM: 0x09 / 9 # ISO_2022_JP: 0x0a / 10 # EXTENDED_KANJI_JIS: 0x0d / 13 # KS_C_5601: 0x0e / 14 self.data_coding = kwargs.get('data_coding', 0) if self.data_coding not in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 13, 14]: raise UnknownValue('Invalid data_coding: %s' % self.data_coding) # QoS # Rejected messages are requeued with a fixed delay self.requeue_delay = kwargs.get('requeue_delay', 120) if not isinstance(self.requeue_delay, int) and not isinstance( self.requeue_delay, float): raise TypeMismatch('requeue_delay must be an integer or float') self.submit_sm_throughput = kwargs.get('submit_sm_throughput', 1) if (not isinstance(self.submit_sm_throughput, int) and not isinstance(self.submit_sm_throughput, float)): raise TypeMismatch( 'submit_sm_throughput must be an integer or float') # DLR Message id bases from submit_sm_resp to deliver_sm, possible values: # [0] (default) : submit_sm_resp and deliver_sm messages IDs are on the same base. # [1] : submit_sm_resp msg-id is in hexadecimal base, deliver_sm msg-id is in # decimal base. # [2] : submit_sm_resp msg-id is in decimal base, deliver_sm msg-id is in # hexadecimal base. self.dlr_msg_id_bases = kwargs.get('dlr_msg_id_bases', 0) if self.dlr_msg_id_bases not in [0, 1, 2]: raise UnknownValue('Invalid dlr_msg_id_bases: %s' % self.dlr_msg_id_bases)
def getReceipt(self, dlr_pdu, msgid, source_addr, destination_addr, message_status, err, sub_date, source_addr_ton, source_addr_npi, dest_addr_ton, dest_addr_npi): """Will build a DataSm or a DeliverSm (depending on dlr_pdu) containing a receipt data""" if isinstance(message_status, bytes): message_status = message_status.decode() if isinstance(msgid, bytes): msgid = msgid.decode() sm_message_stat = message_status # Prepare message_state if message_status[:5] == 'ESME_': if message_status == 'ESME_ROK': message_state = MessageState.ACCEPTED sm_message_stat = 'ACCEPTD' else: message_state = MessageState.UNDELIVERABLE sm_message_stat = 'UNDELIV' elif message_status == 'UNDELIV': message_state = MessageState.UNDELIVERABLE elif message_status == 'REJECTD': message_state = MessageState.REJECTED elif message_status == 'DELIVRD': message_state = MessageState.DELIVERED elif message_status == 'EXPIRED': message_state = MessageState.EXPIRED elif message_status == 'DELETED': message_state = MessageState.DELETED elif message_status == 'ACCEPTD': message_state = MessageState.ACCEPTED elif message_status == 'ENROUTE': message_state = MessageState.ENROUTE elif message_status == 'UNKNOWN': message_state = MessageState.UNKNOWN else: raise UnknownMessageStatusError('Unknown message_status: %s' % message_status) # Build pdu if dlr_pdu == 'deliver_sm': short_message = r"id:%s submit date:%s done date:%s stat:%s err:%s" % ( msgid, parser.parse(sub_date).strftime("%y%m%d%H%M"), datetime.datetime.now().strftime("%y%m%d%H%M"), sm_message_stat, err, ) # Build DeliverSM pdu pdu = DeliverSM( source_addr=destination_addr, destination_addr=source_addr, esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.SMSC_DELIVERY_RECEIPT), receipted_message_id=msgid, short_message=short_message, message_state=message_state, source_addr_ton=self.get_enum(AddrTon, dest_addr_ton), source_addr_npi=self.get_enum(AddrNpi, dest_addr_npi), dest_addr_ton=self.get_enum(AddrTon, source_addr_ton), dest_addr_npi=self.get_enum(AddrNpi, source_addr_npi), ) else: # Build DataSM pdu pdu = DataSM( source_addr=destination_addr, destination_addr=source_addr, esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.SMSC_DELIVERY_RECEIPT), receipted_message_id=msgid, message_state=message_state, source_addr_ton=self.get_enum(AddrTon, dest_addr_ton), source_addr_npi=self.get_enum(AddrNpi, dest_addr_npi), dest_addr_ton=self.get_enum(AddrTon, source_addr_ton), dest_addr_npi=self.get_enum(AddrNpi, source_addr_npi), ) return pdu
def SubmitSM(self, short_message, data_coding=0, **kwargs): """Depending on the short_message length, this method will return a classical SubmitSM or a serie of linked SubmitSMs (parted message) """ kwargs['short_message'] = short_message kwargs['data_coding'] = data_coding # Possible data_coding values : 0,1,2,3,4,5,6,7,8,9,10,13,14 # Set the max short message length depending on the # coding (7, 8 or 16 bits) if kwargs['data_coding'] in [3, 6, 7, 10]: # 8 bit coding bits = 8 maxSmLength = 140 slicedMaxSmLength = maxSmLength - 6 elif kwargs['data_coding'] in [2, 4, 5, 8, 9, 13, 14]: # 16 bit coding bits = 16 maxSmLength = 70 slicedMaxSmLength = maxSmLength - 3 else: # 7 bit coding is the default # for data_coding in [0, 1] or any other invalid value bits = 7 maxSmLength = 160 slicedMaxSmLength = 153 longMessage = kwargs['short_message'] if bits == 16: smLength = len(kwargs['short_message']) / 2 else: smLength = len(kwargs['short_message']) # if SM is longer than maxSmLength, build multiple SubmitSMs # and link them if smLength > maxSmLength: total_segments = int(math.ceil(smLength / float(slicedMaxSmLength))) # Obey to configured longContentMaxParts if total_segments > self.long_content_max_parts: total_segments = self.long_content_max_parts msg_ref_num = self.claimLongMsgRefNum() for i in range(total_segments): segment_seqnum = i + 1 # Keep in memory previous PDU in order to set nextPdu in it later try: tmpPdu previousPdu = tmpPdu except NameError: previousPdu = None if bits == 16: kwargs['short_message'] = longMessage[slicedMaxSmLength * i * 2:slicedMaxSmLength * (i + 1) * 2] else: kwargs['short_message'] = longMessage[slicedMaxSmLength * i:slicedMaxSmLength * (i + 1)] tmpPdu = self._setConfigParamsInPDU(SubmitSM(**kwargs), kwargs) if self.long_content_split == 'sar': # Slice short_message and create the PDU using SAR options tmpPdu.params['sar_total_segments'] = total_segments tmpPdu.params['sar_segment_seqnum'] = segment_seqnum tmpPdu.params['sar_msg_ref_num'] = msg_ref_num elif self.long_content_split == 'udh': # Slice short_message and create the PDU using UDH options tmpPdu.params['esm_class'] = EsmClass( EsmClassMode.DEFAULT, EsmClassType.DEFAULT, [EsmClassGsmFeatures.UDHI_INDICATOR_SET]) if segment_seqnum < total_segments: tmpPdu.params[ 'more_messages_to_send'] = MoreMessagesToSend.MORE_MESSAGES else: tmpPdu.params[ 'more_messages_to_send'] = MoreMessagesToSend.NO_MORE_MESSAGES # UDH composition: udh = [] # Length of User Data Header udh.append(struct.pack('!B', 5)) # Information Element Identifier, equal to 00 # (Concatenated short messages, 8-bit reference number) udh.append(struct.pack('!B', 0)) # Length of the header, excluding the first two fields; equal to 03 udh.append(struct.pack('!B', 3)) udh.append(struct.pack('!B', msg_ref_num)) udh.append(struct.pack('!B', total_segments)) udh.append(struct.pack('!B', segment_seqnum)) if isinstance(kwargs['short_message'], str): tmpPdu.params['short_message'] = b''.join( udh) + kwargs['short_message'].encode() else: tmpPdu.params['short_message'] = b''.join( udh) + kwargs['short_message'] # - The first PDU is the one we return back # - sar_msg_ref_num takes the seqnum of the initial submit_sm if i == 0: pdu = tmpPdu # PDU chaining if previousPdu is not None: previousPdu.nextPdu = tmpPdu else: pdu = self._setConfigParamsInPDU(SubmitSM(**kwargs), kwargs) return pdu
def test_long_content_delivery_UDH_SmppsConnector(self): yield self.connect('127.0.0.1', self.pbPort) yield self.prepareRoutingsAndStartConnector() # Bind yield self.smppc_factory.connectAndBind() # Install mocks self.smppc_factory.lastProto.PDUDataRequestReceived = Mock( wraps=self.smppc_factory.lastProto.PDUDataRequestReceived) # Build a UDH baseUdh = [] baseUdh.append(struct.pack('!B', 5)) # Length of User Data Header baseUdh.append( struct.pack('!B', 0) ) # Information Element Identifier, equal to 00 (Concatenated short messages, 8-bit reference number) baseUdh.append( struct.pack('!B', 3) ) # Length of the header, excluding the first two fields; equal to 03 baseUdh.append( struct.pack('!B', int(id_generator(size=2, chars=string.digits)))) # msg_ref_num baseUdh.append(struct.pack('!B', 3)) # total_segments # Send a data_sm from the SMSC basePdu = DataSM( source_addr='1234', destination_addr='4567', message_payload='', esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.DEFAULT, [EsmClassGsmFeatures.UDHI_INDICATOR_SET]), ) pdu_part1 = copy.deepcopy(basePdu) udh_part1 = copy.deepcopy(baseUdh) pdu_part2 = copy.deepcopy(basePdu) udh_part2 = copy.deepcopy(baseUdh) pdu_part3 = copy.deepcopy(basePdu) udh_part3 = copy.deepcopy(baseUdh) udh_part1.append(struct.pack('!B', 1)) # segment_seqnum pdu_part1.params[ 'more_messages_to_send'] = MoreMessagesToSend.MORE_MESSAGES pdu_part1.params['message_payload'] = b''.join( udh_part1 ) + b'__1st_part_with_153_char________________________________________________________________________________________________________________________________.' udh_part2.append(struct.pack('!B', 2)) # segment_seqnum pdu_part2.params[ 'more_messages_to_send'] = MoreMessagesToSend.MORE_MESSAGES pdu_part2.params['message_payload'] = b''.join( udh_part2 ) + b'__2nd_part_with_153_char________________________________________________________________________________________________________________________________.' udh_part3.append(struct.pack('!B', 3)) # segment_seqnum pdu_part3.params[ 'more_messages_to_send'] = MoreMessagesToSend.NO_MORE_MESSAGES pdu_part3.params['message_payload'] = b''.join( udh_part3) + b'__3rd_part_end.' yield self.triggerDataSmFromSMSC([pdu_part1, pdu_part2, pdu_part3]) # Run tests self.assertEqual( self.smppc_factory.lastProto.PDUDataRequestReceived.call_count, 3) # First received pdu received_pdu_1 = self.smppc_factory.lastProto.PDUDataRequestReceived.call_args_list[ 0][0][0] self.assertEqual(received_pdu_1.seqNum, 1) self.assertEqual(received_pdu_1.id, CommandId.data_sm) self.assertEqual(received_pdu_1.params['source_addr'], basePdu.params['source_addr']) self.assertEqual(received_pdu_1.params['destination_addr'], basePdu.params['destination_addr']) self.assertEqual(received_pdu_1.params['esm_class'], basePdu.params['esm_class']) self.assertEqual(received_pdu_1.params['message_payload'][6:], pdu_part1.params['message_payload'][6:]) # Second received pdu received_pdu_2 = self.smppc_factory.lastProto.PDUDataRequestReceived.call_args_list[ 1][0][0] self.assertEqual(received_pdu_2.seqNum, 2) self.assertEqual(received_pdu_2.id, CommandId.data_sm) self.assertEqual(received_pdu_2.params['source_addr'], basePdu.params['source_addr']) self.assertEqual(received_pdu_2.params['destination_addr'], basePdu.params['destination_addr']) self.assertEqual(received_pdu_2.params['esm_class'], basePdu.params['esm_class']) self.assertEqual(received_pdu_2.params['message_payload'][6:], pdu_part2.params['message_payload'][6:]) # Third received pdu received_pdu_3 = self.smppc_factory.lastProto.PDUDataRequestReceived.call_args_list[ 2][0][0] self.assertEqual(received_pdu_3.seqNum, 3) self.assertEqual(received_pdu_3.id, CommandId.data_sm) self.assertEqual(received_pdu_3.params['source_addr'], basePdu.params['source_addr']) self.assertEqual(received_pdu_3.params['destination_addr'], basePdu.params['destination_addr']) self.assertEqual(received_pdu_3.params['esm_class'], basePdu.params['esm_class']) self.assertEqual(received_pdu_3.params['message_payload'][6:], pdu_part3.params['message_payload'][6:]) # Unbind and disconnect yield self.smppc_factory.smpp.unbindAndDisconnect() yield self.stopSmppClientConnectors()
def test_long_content_delivery_UDH_HttpConnector(self): yield self.connect('127.0.0.1', self.pbPort) # Connect to SMSC source_connector = Connector(id_generator()) yield self.prepareRoutingsAndStartConnector(source_connector) # Build a UDH baseUdh = [] baseUdh.append(struct.pack('!B', 5)) # Length of User Data Header baseUdh.append( struct.pack('!B', 0) ) # Information Element Identifier, equal to 00 (Concatenated short messages, 8-bit reference number) baseUdh.append( struct.pack('!B', 3) ) # Length of the header, excluding the first two fields; equal to 03 baseUdh.append( struct.pack('!B', int(id_generator(size=2, chars=string.digits)))) # msg_ref_num baseUdh.append(struct.pack('!B', 3)) # total_segments # Send a data_sm from the SMSC basePdu = DataSM( source_addr='1234', destination_addr='4567', message_payload='', esm_class=EsmClass(EsmClassMode.DEFAULT, EsmClassType.DEFAULT, [EsmClassGsmFeatures.UDHI_INDICATOR_SET]), ) pdu_part1 = copy.deepcopy(basePdu) udh_part1 = copy.deepcopy(baseUdh) pdu_part2 = copy.deepcopy(basePdu) udh_part2 = copy.deepcopy(baseUdh) pdu_part3 = copy.deepcopy(basePdu) udh_part3 = copy.deepcopy(baseUdh) udh_part1.append(struct.pack('!B', 1)) # segment_seqnum pdu_part1.params[ 'more_messages_to_send'] = MoreMessagesToSend.MORE_MESSAGES pdu_part1.params['message_payload'] = b''.join( udh_part1 ) + b'__1st_part_with_153_char________________________________________________________________________________________________________________________________.' udh_part2.append(struct.pack('!B', 2)) # segment_seqnum pdu_part2.params[ 'more_messages_to_send'] = MoreMessagesToSend.MORE_MESSAGES pdu_part2.params['message_payload'] = b''.join( udh_part2 ) + b'__2nd_part_with_153_char________________________________________________________________________________________________________________________________.' udh_part3.append(struct.pack('!B', 3)) # segment_seqnum pdu_part3.params[ 'more_messages_to_send'] = MoreMessagesToSend.NO_MORE_MESSAGES pdu_part3.params['message_payload'] = b''.join( udh_part3) + b'__3rd_part_end.' yield self.triggerDataSmFromSMSC([pdu_part1, pdu_part2, pdu_part3]) # Run tests # Destination connector must receive the message one time (no retries) self.assertEqual(self.AckServerResource.render_POST.call_count, 1) # Assert received args receivedHttpReq = self.AckServerResource.last_request.args self.assertEqual(len(receivedHttpReq), 7) self.assertEqual(receivedHttpReq[b'from'], [basePdu.params['source_addr']]) self.assertEqual(receivedHttpReq[b'to'], [basePdu.params['destination_addr']]) self.assertEqual(receivedHttpReq[b'content'], [ pdu_part1.params['message_payload'][6:] + pdu_part2.params['message_payload'][6:] + pdu_part3.params['message_payload'][6:] ]) self.assertEqual(receivedHttpReq[b'origin-connector'], [source_connector.cid.encode()]) # Disconnector from SMSC yield self.stopConnector(source_connector)
async def send_deliver_sm( self, source_addr: str, destination_addr: str, text: str, source_addr_npi: AddrNpi, dest_addr_npi: AddrNpi, source_addr_ton: AddrTon, dest_addr_ton: AddrTon, priority_flag: PriorityFlag = PriorityFlag.LEVEL_0, registered_delivery: RegisteredDelivery = None): if registered_delivery is None: registered_delivery = RegisteredDelivery( RegisteredDeliveryReceipt.NO_SMSC_DELIVERY_RECEIPT_REQUESTED) def try_to_encode(text: str, codec: str) -> Optional[bytes]: try: return text.encode(codec) except UnicodeEncodeError: return None codec_data_coding_map = [('ascii', DataCodingDefault.SMSC_DEFAULT_ALPHABET), ('latin-1', DataCodingDefault.LATIN_1), ('cyrillic', DataCodingDefault.CYRILLIC), ('utf-16be', DataCodingDefault.UCS2)] short_message = None data_coding = None for pair in codec_data_coding_map: short_message = try_to_encode(text=text, codec=pair[0]) if short_message: data_coding = pair[1] break if data_coding is None or short_message is None: raise Exception(f'Text {text} could not be encoded') deliver_sm_dict = { 'sequence_number': self.next_sequence_number(), 'service_type': None, 'source_addr_ton': source_addr_ton, 'source_addr_npi': source_addr_npi, 'source_addr': source_addr, 'dest_addr_ton': dest_addr_ton, 'dest_addr_npi': dest_addr_npi, 'destination_addr': destination_addr, 'esm_class': EsmClass(EsmClassMode.DEFAULT, EsmClassType.DEFAULT), 'protocol_id': None, 'priority_flag': priority_flag, 'registered_delivery': registered_delivery, 'replace_if_present_flag': ReplaceIfPresentFlag.DO_NOT_REPLACE, 'data_coding': DataCoding(scheme_data=data_coding), 'short_message': short_message, } max_size = 100 if len(short_message) <= max_size: deliver_sm = DeliverSM(**deliver_sm_dict) requests = [deliver_sm] else: requests = [] short_messages = [ short_message[x:x + max_size] for x in range(0, len(short_message), max_size) ] ref_num = self.next_ref_num() total_segments = len(short_messages) if total_segments > 255: raise Exception('Text is too long and it cannot be sent') for seq_num, trunk_short_message in enumerate(short_messages): deliver_sm_dict['short_message'] = trunk_short_message deliver_sm_dict['sequence_number'] = self.next_sequence_number( ) deliver_sm = DeliverSM(**deliver_sm_dict, sar_msg_ref_num=ref_num, sar_total_segments=total_segments, sar_segment_seqnum=seq_num + 1) requests.append(deliver_sm) self._send_requests(requests=requests, merge=True)