Exemplo n.º 1
0
 def test_equality_with_array_and_set(self):
     r1 = RegisteredDelivery(
         RegisteredDeliveryReceipt.SMSC_DELIVERY_RECEIPT_REQUESTED,
         {RegisteredDeliverySmeOriginatedAcks.SME_DELIVERY_ACK_REQUESTED})
     r2 = RegisteredDelivery(
         RegisteredDeliveryReceipt.SMSC_DELIVERY_RECEIPT_REQUESTED,
         [RegisteredDeliverySmeOriginatedAcks.SME_DELIVERY_ACK_REQUESTED])
     self.assertEqual(r1, r2)
Exemplo n.º 2
0
 def test_equality_with_array_duplicates(self):
     r1 = RegisteredDelivery(
         RegisteredDeliveryReceipt.SMSC_DELIVERY_RECEIPT_REQUESTED, [
             RegisteredDeliverySmeOriginatedAcks.SME_MANUAL_ACK_REQUESTED,
             RegisteredDeliverySmeOriginatedAcks.SME_MANUAL_ACK_REQUESTED
         ])
     r2 = RegisteredDelivery(
         RegisteredDeliveryReceipt.SMSC_DELIVERY_RECEIPT_REQUESTED,
         [RegisteredDeliverySmeOriginatedAcks.SME_MANUAL_ACK_REQUESTED])
     self.assertEqual(r1, r2)
Exemplo n.º 3
0
 def test_conversion(self):
     value = RegisteredDelivery(
         RegisteredDeliveryReceipt.SMSC_DELIVERY_RECEIPT_REQUESTED, [
             RegisteredDeliverySmeOriginatedAcks.SME_DELIVERY_ACK_REQUESTED,
             RegisteredDeliverySmeOriginatedAcks.SME_MANUAL_ACK_REQUESTED
         ], True)
     self.do_conversion_test(RegisteredDeliveryEncoder(), value, b'1d')
     self.do_null_encode_test(
         RegisteredDeliveryEncoder(),
         RegisteredDelivery(
             RegisteredDeliveryReceipt.NO_SMSC_DELIVERY_RECEIPT_REQUESTED,
             [], False), b'00')
Exemplo n.º 4
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'
     )
Exemplo n.º 5
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'
     )
Exemplo n.º 6
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'
     )
Exemplo n.º 7
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'
     )
Exemplo n.º 8
0
    def _checkSendAuthorizations(self):
        """MT Authorizations check"""

        if not self.user.mt_credential.getAuthorization('smpps_send'):
            raise AuthorizationError(
                'Authorization failed for username [%s] (Can not send MT messages).'
                % self.user)
        if (not self.user.mt_credential.getAuthorization('set_dlr_level')
                and self.submit_sm.params['registered_delivery'] !=
                RegisteredDelivery(RegisteredDeliveryReceipt.
                                   NO_SMSC_DELIVERY_RECEIPT_REQUESTED)):
            raise AuthorizationError(
                'Authorization failed for username [%s] (Setting dlr level is not authorized).'
                % self.user)
        if (not self.user.mt_credential.getAuthorization('set_source_address')
                and len(self.submit_sm.params['source_addr']) > 0):
            raise AuthorizationError(
                'Authorization failed for username [%s] (Setting source address is not authorized).'
                % self.user)
        if (not self.user.mt_credential.getAuthorization('set_priority')
                and self._convert_to_string('priority_flag') !=
                priority_flag_value_map[0]):
            raise AuthorizationError(
                'Authorization failed for username [%s] (Setting priority is not authorized).'
                % self.user)
Exemplo n.º 9
0
    def test_authorized_set_dlr_level(self):
        user = self.routerpb_factory.getUser('u1')
        user.mt_credential.setAuthorization('set_dlr_level', True)

        # Connect and bind
        yield self.smppc_factory.connectAndBind()
        self.assertEqual(self.smppc_factory.smpp.sessionState,
                         SMPPSessionStates.BOUND_TRX)

        # Install mockers
        self.smppc_factory.lastProto.PDUReceived = Mock(
            wraps=self.smppc_factory.lastProto.PDUReceived)

        # SMPPClient > SMPPServer
        SubmitSmPDU = copy.deepcopy(self.SubmitSmPDU)
        SubmitSmPDU.params['registered_delivery'] = RegisteredDelivery(
            RegisteredDeliveryReceipt.
            SMSC_DELIVERY_RECEIPT_REQUESTED_FOR_FAILURE)
        yield self.smppc_factory.lastProto.sendDataRequest(SubmitSmPDU)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        self.assertEqual(self.smppc_factory.smpp.sessionState,
                         SMPPSessionStates.UNBOUND)

        # Asserts SMPPClient side
        self.assertEqual(self.smppc_factory.lastProto.PDUReceived.call_count,
                         2)
        self.assertEqual(
            self.smppc_factory.lastProto.PDUReceived.call_args_list[0][0]
            [0].id, CommandId.submit_sm_resp)
        self.assertEqual(
            self.smppc_factory.lastProto.PDUReceived.call_args_list[0][0]
            [0].status, CommandStatus.ESME_ROK)
Exemplo n.º 10
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'
     )
Exemplo n.º 11
0
    def test_receive_NACK_deliver_sm_on_delivery_error(self):
        yield self.connect('127.0.0.1', self.pbPort)
        yield self.prepareRoutingsAndStartConnector(
            port=self.ErrorOnSubmitSMSCPort.getHost().port)

        # Bind
        yield self.smppc_factory.connectAndBind()

        # Install mocks
        self.smpps_factory.lastProto.sendPDU = Mock(
            wraps=self.smpps_factory.lastProto.sendPDU)

        # Send a SMS MT through smpps interface
        SubmitSmPDU = copy.deepcopy(self.SubmitSmPDU)
        SubmitSmPDU.params['registered_delivery'] = RegisteredDelivery(
            RegisteredDeliveryReceipt.
            SMSC_DELIVERY_RECEIPT_REQUESTED_FOR_FAILURE)
        yield self.smppc_factory.lastProto.sendDataRequest(SubmitSmPDU)

        # Wait 1 seconds for submit_sm_resp
        yield waitFor(1)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        yield self.stopSmppClientConnectors()

        # Run tests
        self.assertEqual(self.smpps_factory.lastProto.sendPDU.call_count, 3)
        # smpps response was (1) a submit_sm_resp with ESME_ROK
        response_pdu_1 = self.smpps_factory.lastProto.sendPDU.call_args_list[
            0][0][0]
        self.assertEqual(response_pdu_1.id, pdu_types.CommandId.submit_sm_resp)
        self.assertEqual(response_pdu_1.seqNum, 2)
        self.assertEqual(response_pdu_1.status,
                         pdu_types.CommandStatus.ESME_ROK)
        self.assertTrue(response_pdu_1.params['message_id'] is not None)
        # (2) a deliver_sm
        response_pdu_2 = self.smpps_factory.lastProto.sendPDU.call_args_list[
            1][0][0]
        self.assertEqual(response_pdu_2.id, pdu_types.CommandId.deliver_sm)
        self.assertEqual(response_pdu_2.seqNum, 1)
        self.assertEqual(response_pdu_2.status,
                         pdu_types.CommandStatus.ESME_ROK)
        self.assertEqual(response_pdu_2.params['source_addr'],
                         SubmitSmPDU.params['destination_addr'])
        self.assertEqual(response_pdu_2.params['destination_addr'],
                         SubmitSmPDU.params['source_addr'])
        self.assertEqual(response_pdu_2.params['receipted_message_id'],
                         response_pdu_1.params['message_id'])
        self.assertEqual(response_pdu_2.params['message_state'],
                         MessageState.UNDELIVERABLE)
Exemplo n.º 12
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'
     )
Exemplo n.º 13
0
    def test_no_charging_on_delivery_error(self):
        """This test case we:
         - User have early_decrement_balance_percent set to 10 (%)
         - Submit a message to SMPPs
         - Message is routed to a SMPPc
         - Delivery is errored (submit_sm_resp received in SMPPc and transmitted as
           a deliver_sm through SMPPs)

        The user will be charged for 10% of the rate, the rest is not charged because
        message were not delivered to destination.
        """

        yield self.connect('127.0.0.1', self.pbPort)
        mt_c = MtMessagingCredential()
        mt_c.setQuota('balance', 2.0)
        mt_c.setQuota('submit_sm_count', 10)
        mt_c.setQuota('early_decrement_balance_percent', 10)
        user = User(1, Group(1), 'username', 'password', mt_c)
        yield self.prepareRoutingsAndStartConnector(
            route_rate=1.0,
            user=user,
            port=self.ErrorOnSubmitSMSCPort.getHost().port)

        # Bind
        yield self.smppc_factory.connectAndBind()

        # Install mocks
        self.smpps_factory.lastProto.sendPDU = Mock(
            wraps=self.smpps_factory.lastProto.sendPDU)

        # Send a SMS MT through smpps interface
        SubmitSmPDU = copy.deepcopy(self.SubmitSmPDU)
        SubmitSmPDU.params['registered_delivery'] = RegisteredDelivery(
            RegisteredDeliveryReceipt.
            SMSC_DELIVERY_RECEIPT_REQUESTED_FOR_FAILURE)
        yield self.smppc_factory.lastProto.sendDataRequest(SubmitSmPDU)

        # Wait 1 seconds for submit_sm_resp
        yield waitFor(1)

        # Unbind & Disconnect
        yield self.smppc_factory.smpp.unbindAndDisconnect()
        yield self.stopSmppClientConnectors()

        # Run tests
        self.assertEqual(self.smpps_factory.lastProto.sendPDU.call_count, 3)
        # smpps response was (1) a submit_sm_resp with ESME_ROK and
        response_pdu_1 = self.smpps_factory.lastProto.sendPDU.call_args_list[
            0][0][0]
        self.assertEqual(response_pdu_1.id, pdu_types.CommandId.submit_sm_resp)
        self.assertEqual(response_pdu_1.seqNum, 2)
        self.assertEqual(response_pdu_1.status,
                         pdu_types.CommandStatus.ESME_ROK)
        self.assertTrue(response_pdu_1.params['message_id'] is not None)
        # (2) a deliver_sm
        response_pdu_2 = self.smpps_factory.lastProto.sendPDU.call_args_list[
            1][0][0]
        self.assertEqual(response_pdu_2.id, pdu_types.CommandId.deliver_sm)
        self.assertEqual(response_pdu_2.params['message_state'],
                         MessageState.UNDELIVERABLE)
        # (3) an unbind_resp
        response_pdu_3 = self.smpps_factory.lastProto.sendPDU.call_args_list[
            2][0][0]
        self.assertEqual(response_pdu_3.id, pdu_types.CommandId.unbind_resp)
        # Assert quotas after SMS is sent (only 10% were charged)
        assertionUser = self.pbRoot_f.getUser(user.uid)
        self.assertAlmostEqual(assertionUser.mt_credential.getQuota('balance'),
                               1.9)
        self.assertAlmostEqual(
            assertionUser.mt_credential.getQuota('submit_sm_count'), 9)
Exemplo n.º 14
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)
Exemplo n.º 15
0
    def route_routable(self, updated_request):
        try:
            # Do we have a hex-content ?
            if b'hex-content' not in updated_request.args:
                # Convert utf8 to GSM 03.38
                if updated_request.args[b'coding'][0] == b'0':
                    if isinstance(updated_request.args[b'content'][0], bytes):
                        short_message = updated_request.args[b'content'][
                            0].decode().encode('gsm0338', 'replace')
                    else:
                        short_message = updated_request.args[b'content'][
                            0].encode('gsm0338', 'replace')
                    updated_request.args[b'content'][0] = short_message
                else:
                    # Otherwise forward it as is
                    short_message = updated_request.args[b'content'][0]
            else:
                # Otherwise convert hex to bin
                short_message = hex2bin(
                    updated_request.args[b'hex-content'][0])

            # Authentication
            user = authenticate_user(updated_request.args[b'username'][0],
                                     updated_request.args[b'password'][0],
                                     self.RouterPB, self.stats, self.log)

            # Update CnxStatus
            user.getCnxStatus().httpapi['connects_count'] += 1
            user.getCnxStatus().httpapi['submit_sm_request_count'] += 1
            user.getCnxStatus().httpapi['last_activity_at'] = datetime.now()

            # Build SubmitSmPDU
            SubmitSmPDU = self.opFactory.SubmitSM(
                source_addr=None if b'from' not in updated_request.args else
                updated_request.args[b'from'][0],
                destination_addr=updated_request.args[b'to'][0],
                short_message=short_message,
                data_coding=int(updated_request.args[b'coding'][0]),
                custom_tlvs=updated_request.args[b'custom_tlvs'][0])
            self.log.debug("Built base SubmitSmPDU: %s", SubmitSmPDU)

            # Make Credential validation
            v = HttpAPICredentialValidator('Send',
                                           user,
                                           updated_request,
                                           submit_sm=SubmitSmPDU)
            v.validate()

            # Update SubmitSmPDU by default values from user MtMessagingCredential
            SubmitSmPDU = v.updatePDUWithUserDefaults(SubmitSmPDU)

            # Prepare for interception then routing
            routedConnector = None  # init
            routable = RoutableSubmitSm(SubmitSmPDU, user)
            self.log.debug("Built Routable %s for SubmitSmPDU: %s", routable,
                           SubmitSmPDU)

            # Should we tag the routable ?
            tags = []
            if b'tags' in updated_request.args:
                tags = updated_request.args[b'tags'][0].split(b',')
                for tag in tags:
                    if isinstance(tag, bytes):
                        routable.addTag(tag.decode())
                    else:
                        routable.addTag(tag)
                    self.log.debug('Tagged routable %s: +%s', routable, tag)

            # Intercept
            interceptor = self.RouterPB.getMTInterceptionTable(
            ).getInterceptorFor(routable)
            if interceptor is not None:
                self.log.debug(
                    "RouterPB selected %s interceptor for this SubmitSmPDU",
                    interceptor)
                if self.interceptorpb_client is None:
                    self.stats.inc('interceptor_error_count')
                    self.log.error("InterceptorPB not set !")
                    raise InterceptorNotSetError('InterceptorPB not set !')
                if not self.interceptorpb_client.isConnected:
                    self.stats.inc('interceptor_error_count')
                    self.log.error("InterceptorPB not connected !")
                    raise InterceptorNotConnectedError(
                        'InterceptorPB not connected !')

                script = interceptor.getScript()
                self.log.debug("Interceptor script loaded: %s", script)

                # Run !
                r = yield self.interceptorpb_client.run_script(
                    script, routable)
                if isinstance(r, dict) and r['http_status'] != 200:
                    self.stats.inc('interceptor_error_count')
                    self.log.error(
                        'Interceptor script returned %s http_status error.',
                        r['http_status'])
                    raise InterceptorRunError(
                        code=r['http_status'],
                        message='Interception specific error code %s' %
                        r['http_status'])
                elif isinstance(r, (str, bytes)):
                    self.stats.inc('interceptor_count')
                    routable = pickle.loads(r)
                else:
                    self.stats.inc('interceptor_error_count')
                    self.log.error(
                        'Failed running interception script, got the following return: %s',
                        r)
                    raise InterceptorRunError(
                        message=
                        'Failed running interception script, check log for details'
                    )

            # Get the route
            route = self.RouterPB.getMTRoutingTable().getRouteFor(routable)
            if route is None:
                self.stats.inc('route_error_count')
                self.log.error(
                    "No route matched from user %s for SubmitSmPDU: %s", user,
                    routable.pdu)
                raise RouteNotFoundError("No route found")

            # Get connector from selected route
            self.log.debug("RouterPB selected %s route for this SubmitSmPDU",
                           route)
            routedConnector = route.getConnector()
            # Is it a failover route ? then check for a bound connector, otherwise don't route
            # The failover route requires at least one connector to be up, no message enqueuing will
            # occur otherwise.
            if repr(route) == 'FailoverMTRoute':
                self.log.debug(
                    'Selected route is a failover, will ensure connector is bound:'
                )
                while True:
                    c = self.SMPPClientManagerPB.perspective_connector_details(
                        routedConnector.cid)
                    if c:
                        self.log.debug('Connector [%s] is: %s',
                                       routedConnector.cid, c['session_state'])
                    else:
                        self.log.debug('Connector [%s] is not found',
                                       routedConnector.cid)

                    if c and c['session_state'][:6] == 'BOUND_':
                        # Choose this connector
                        break
                    else:
                        # Check next connector, None if no more connectors are available
                        routedConnector = route.getConnector()
                        if routedConnector is None:
                            break

            if routedConnector is None:
                self.stats.inc('route_error_count')
                self.log.error(
                    "Failover route has no bound connector to handle SubmitSmPDU: %s",
                    routable.pdu)
                raise ConnectorNotFoundError(
                    "Failover route has no bound connectors")

            # Re-update SubmitSmPDU with parameters from the route's connector
            connector_config = self.SMPPClientManagerPB.perspective_connector_config(
                routedConnector.cid)
            if connector_config:
                connector_config = pickle.loads(connector_config)
                routable = update_submit_sm_pdu(routable=routable,
                                                config=connector_config)

            # Set a placeholder for any parameter update to be applied on the pdu(s)
            param_updates = {}

            # Set priority
            priority = 0
            if b'priority' in updated_request.args:
                priority = int(updated_request.args[b'priority'][0])
                param_updates['priority_flag'] = priority_flag_value_map[
                    priority]
            self.log.debug("SubmitSmPDU priority is set to %s", priority)

            # Set schedule_delivery_time
            if b'sdt' in updated_request.args:
                param_updates['schedule_delivery_time'] = parse(
                    updated_request.args[b'sdt'][0])
                self.log.debug(
                    "SubmitSmPDU schedule_delivery_time is set to %s (%s)",
                    routable.pdu.params['schedule_delivery_time'],
                    updated_request.args[b'sdt'][0])

            # Set validity_period
            if b'validity-period' in updated_request.args:
                delta = timedelta(
                    minutes=int(updated_request.args[b'validity-period'][0]))
                param_updates['validity_period'] = datetime.today() + delta
                self.log.debug(
                    "SubmitSmPDU validity_period is set to %s (+%s minutes)",
                    routable.pdu.params['validity_period'],
                    updated_request.args[b'validity-period'][0])

            # Got any updates to apply on pdu(s) ?
            if len(param_updates) > 0:
                routable = update_submit_sm_pdu(
                    routable=routable,
                    config=param_updates,
                    config_update_params=list(param_updates))

            # Set DLR bit mask on the last pdu
            _last_pdu = routable.pdu
            while True:
                if hasattr(_last_pdu, 'nextPdu'):
                    _last_pdu = _last_pdu.nextPdu
                else:
                    break
            # DLR setting is clearly described in #107
            _last_pdu.params['registered_delivery'] = RegisteredDelivery(
                RegisteredDeliveryReceipt.NO_SMSC_DELIVERY_RECEIPT_REQUESTED)
            if updated_request.args[b'dlr'][0] == b'yes':
                _last_pdu.params['registered_delivery'] = RegisteredDelivery(
                    RegisteredDeliveryReceipt.SMSC_DELIVERY_RECEIPT_REQUESTED)
                self.log.debug("SubmitSmPDU registered_delivery is set to %s",
                               str(_last_pdu.params['registered_delivery']))

                dlr_level = int(updated_request.args[b'dlr-level'][0])
                if b'dlr-url' in updated_request.args:
                    dlr_url = updated_request.args[b'dlr-url'][0]
                else:
                    dlr_url = None
                if updated_request.args[b'dlr-level'][0] == b'1':
                    dlr_level_text = 'SMS-C'
                elif updated_request.args[b'dlr-level'][0] == b'2':
                    dlr_level_text = 'Terminal'
                else:
                    dlr_level_text = 'All'
                dlr_method = updated_request.args[b'dlr-method'][0]
            else:
                dlr_url = None
                dlr_level = 0
                dlr_level_text = 'No'
                dlr_method = None

            # QoS throttling
            if (
                    user.mt_credential.getQuota('http_throughput')
                    and user.mt_credential.getQuota('http_throughput') >= 0
            ) and user.getCnxStatus().httpapi['qos_last_submit_sm_at'] != 0:
                qos_throughput_second = 1 / float(
                    user.mt_credential.getQuota('http_throughput'))
                qos_throughput_ysecond_td = timedelta(
                    microseconds=qos_throughput_second * 1000000)
                qos_delay = datetime.now() - user.getCnxStatus(
                ).httpapi['qos_last_submit_sm_at']
                if qos_delay < qos_throughput_ysecond_td:
                    self.stats.inc('throughput_error_count')
                    self.log.error(
                        "QoS: submit_sm_event is faster (%s) than fixed throughput (%s), user:%s, rejecting message.",
                        qos_delay, qos_throughput_ysecond_td, user)

                    raise ThroughputExceededError("User throughput exceeded")
            user.getCnxStatus(
            ).httpapi['qos_last_submit_sm_at'] = datetime.now()

            # Get number of PDUs to be sent (for billing purpose)
            _pdu = routable.pdu
            submit_sm_count = 1
            while hasattr(_pdu, 'nextPdu'):
                _pdu = _pdu.nextPdu
                submit_sm_count += 1

            # Pre-sending submit_sm: Billing processing
            bill = route.getBillFor(user)
            self.log.debug(
                "SubmitSmBill [bid:%s] [ttlamounts:%s] generated for this SubmitSmPDU (x%s)",
                bill.bid, bill.getTotalAmounts(), submit_sm_count)
            charging_requirements = []
            u_balance = user.mt_credential.getQuota('balance')
            u_subsm_count = user.mt_credential.getQuota('submit_sm_count')
            if u_balance is not None and bill.getTotalAmounts() > 0:
                # Ensure user have enough balance to pay submit_sm and submit_sm_resp
                charging_requirements.append({
                    'condition':
                    bill.getTotalAmounts() * submit_sm_count <= u_balance,
                    'error_message':
                    'Not enough balance (%s) for charging: %s' %
                    (u_balance, bill.getTotalAmounts())
                })
            if u_subsm_count is not None:
                # Ensure user have enough submit_sm_count to to cover
                # the bill action (decrement_submit_sm_count)
                charging_requirements.append({
                    'condition':
                    bill.getAction('decrement_submit_sm_count') *
                    submit_sm_count <= u_subsm_count,
                    'error_message':
                    'Not enough submit_sm_count (%s) for charging: %s' %
                    (u_subsm_count,
                     bill.getAction('decrement_submit_sm_count'))
                })

            if self.RouterPB.chargeUserForSubmitSms(
                    user, bill, submit_sm_count,
                    charging_requirements) is None:
                self.stats.inc('charging_error_count')
                self.log.error(
                    'Charging user %s failed, [bid:%s] [ttlamounts:%s] SubmitSmPDU (x%s)',
                    user, bill.bid, bill.getTotalAmounts(), submit_sm_count)
                raise ChargingError(
                    'Cannot charge submit_sm, check RouterPB log file for details'
                )

            ########################################################
            # Send SubmitSmPDU through smpp client manager PB server
            self.log.debug(
                "Connector '%s' is set to be a route for this SubmitSmPDU",
                routedConnector.cid)
            c = self.SMPPClientManagerPB.perspective_submit_sm(
                uid=user.uid,
                cid=routedConnector.cid,
                SubmitSmPDU=routable.pdu,
                submit_sm_bill=bill,
                priority=priority,
                pickled=False,
                dlr_url=dlr_url,
                dlr_level=dlr_level,
                dlr_method=dlr_method,
                dlr_connector=routedConnector.cid)

            # Build final response
            if not c.result:
                self.stats.inc('server_error_count')
                self.log.error('Failed to send SubmitSmPDU to [cid:%s]',
                               routedConnector.cid)
                raise ServerError(
                    'Cannot send submit_sm, check SMPPClientManagerPB log file for details'
                )
            else:
                self.stats.inc('success_count')
                self.stats.set('last_success_at', datetime.now())
                self.log.debug('SubmitSmPDU sent to [cid:%s], result = %s',
                               routedConnector.cid, c.result)
                response = {'return': c.result, 'status': 200}
        except HttpApiError as e:
            self.log.error("Error: %s", e)
            response = {'return': e.message, 'status': e.code}
        except Exception as e:
            self.log.error("Error: %s", e)
            response = {'return': "Unknown error: %s" % e, 'status': 500}
            raise
        finally:
            self.log.debug("Returning %s to %s.", response,
                           updated_request.getClientIP())
            updated_request.setResponseCode(response['status'])

            # Default return
            _return = 'Error "%s"' % response['return']

            # Success return
            if response['status'] == 200 and routedConnector is not None:
                # Do not log text for privacy reasons
                # Added in #691
                if self.config.log_privacy:
                    logged_content = '** %s byte content **' % len(
                        short_message)
                else:
                    if isinstance(short_message, str):
                        short_message = short_message.encode()
                    logged_content = '%r' % re.sub(rb'[^\x20-\x7E]+', b'.',
                                                   short_message)

                self.log.info(
                    'SMS-MT [uid:%s] [cid:%s] [msgid:%s] [prio:%s] [dlr:%s] [from:%s] [to:%s] [content:%s]',
                    user.uid, routedConnector.cid, response['return'],
                    priority, dlr_level_text,
                    routable.pdu.params['source_addr'],
                    updated_request.args[b'to'][0], logged_content)

                _return = 'Success "%s"' % response['return']

            updated_request.write(_return.encode())
            updated_request.finish()
Exemplo n.º 16
0
"This script will enforce sending message while asking for DLR"

from smpp.pdu.pdu_types import RegisteredDeliveryReceipt, RegisteredDelivery

routable.pdu.params['registered_delivery'] = RegisteredDelivery(
    RegisteredDeliveryReceipt.SMSC_DELIVERY_RECEIPT_REQUESTED)
Exemplo n.º 17
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)