Exemple #1
0
 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)
Exemple #2
0
 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')
Exemple #3
0
 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)
Exemple #4
0
 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'
     )
Exemple #5
0
 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'
     )
Exemple #6
0
 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'
     )
Exemple #7
0
 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'
     )
Exemple #8
0
 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'
     )
Exemple #9
0
 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'
     )
Exemple #10
0
    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)
Exemple #11
0
    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
Exemple #12
0
    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
Exemple #13
0
    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()
Exemple #14
0
    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)
Exemple #15
0
    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)