def setUp(self):
        self.script1 = MTInterceptorScript('abc')
        self.script2 = MTInterceptorScript('def')
        self.script3 = MTInterceptorScript('ghi')
        self.script4 = MTInterceptorScript('jkl')
        self.group100 = Group(100)
        self.user1 = User(1, self.group100, 'username', 'password')
        self.user2 = User(2, self.group100, 'username', 'password')

        self.mt_filter1 = [UserFilter(self.user1)]
        self.mt_filter2 = [DestinationAddrFilter('^10\d+')]
        self.transparent_filter = [TransparentFilter()]
        self.interceptor1 = StaticMTInterceptor(self.mt_filter1, self.script1)
        self.interceptor2 = StaticMTInterceptor(self.mt_filter2, self.script2)
        self.interceptor3 = StaticMTInterceptor(self.transparent_filter,
                                                self.script3)
        self.interceptor4 = DefaultInterceptor(self.script4)

        self.PDU_dst_1 = SubmitSM(
            source_addr=b'x',
            destination_addr=b'200',
            short_message=b'hello world',
        )
        self.PDU_dst_2 = SubmitSM(
            source_addr=b'x',
            destination_addr=b'100',
            short_message=b'hello world',
        )

        self.routable_matching_interceptor1 = RoutableSubmitSm(
            self.PDU_dst_1, self.user1)
        self.routable_matching_interceptor2 = RoutableSubmitSm(
            self.PDU_dst_2, self.user2)
        self.routable_notmatching_any = RoutableSubmitSm(
            self.PDU_dst_1, self.user2)
Beispiel #2
0
    def setUp(self):
        self.connector1 = SmppClientConnector('abc')
        self.connector2 = SmppClientConnector('def')
        self.connector3 = SmppClientConnector('ghi')
        self.connector4 = SmppClientConnector('jkl')
        self.group100 = Group(100)
        self.user1 = User(1, self.group100, 'username', 'password')
        self.user2 = User(2, self.group100, 'username', 'password')

        self.mt_filter1 = [UserFilter(self.user1)]
        self.mt_filter2 = [DestinationAddrFilter('^10\d+')]
        self.transparent_filter = [TransparentFilter()]
        self.mt_route1 = StaticMTRoute(self.mt_filter1, self.connector1, 0.0)
        self.mt_route2 = StaticMTRoute(self.mt_filter2, self.connector2, 0.0)
        self.mt_route3 = StaticMTRoute(self.transparent_filter,
                                       self.connector3, 0.0)
        self.mt_route4 = DefaultRoute(self.connector4)

        self.PDU_dst_1 = SubmitSM(
            source_addr='x',
            destination_addr='200',
            short_message='hello world',
        )
        self.PDU_dst_2 = SubmitSM(
            source_addr='x',
            destination_addr='100',
            short_message='hello world',
        )

        self.routable_matching_route1 = RoutableSubmitSm(
            self.PDU_dst_1, self.user1)
        self.routable_matching_route2 = RoutableSubmitSm(
            self.PDU_dst_2, self.user2)
        self.routable_notmatching_any = RoutableSubmitSm(
            self.PDU_dst_1, self.user2)
Beispiel #3
0
    def test_locking(self):
        o = RoutableSubmitSm(self.PDU, self.user, datetime.now())

        self.assertRaises(InvalidLockError, o.lockPduParam, 'anything')
        self.assertRaises(InvalidLockError, o.pduParamIsLocked, 'anything')

        o.lockPduParam('service_type')
        self.assertTrue(o.pduParamIsLocked('service_type'))
        self.assertFalse(o.pduParamIsLocked('source_addr_ton'))
Beispiel #4
0
    def setUp(self):
        RouteTestCase.setUp(self)

        self.PDU_dst_1 = SubmitSM(
            source_addr='20203060',
            destination_addr='1',
            short_message='hello world',
        )

        self.routable_user1 = RoutableSubmitSm(self.PDU_dst_1, self.user1)
        self.routable_user2 = RoutableSubmitSm(self.PDU_dst_1, self.user2)
Beispiel #5
0
    def test_tagging(self):
        o = RoutableSubmitSm(self.PDU, self.user, datetime.now())

        self.assertRaises(InvalidTagError, o.addTag, 'anything')
        self.assertRaises(InvalidTagError, o.hasTag, 'anything')
        self.assertRaises(InvalidTagError, o.removeTag, 'anything')

        o.addTag(23)
        self.assertTrue(o.hasTag(23))
        self.assertFalse(o.hasTag(30))
        self.assertRaises(TagNotFoundError, o.removeTag, 30)
        self.assertEqual([23], o.getTags())
        o.flushTags()
        self.assertEqual([], o.getTags())
Beispiel #6
0
    def test_standard(self):
        o = RoutableSubmitSm(self.PDU, self.user, datetime.now())

        self.assertEqual(o.pdu, self.PDU)
        self.assertEqual(o.user.uid, self.user.uid)
        self.assertEqual(o.user.group.gid, self.user.group.gid)
        self.assertNotEqual(o.datetime, None)
Beispiel #7
0
    def test_tagging(self):
        o = RoutableSubmitSm(self.PDU, self.user, datetime.now())

        self.assertRaises(InvalidTagError, o.addTag, "anything")
        self.assertRaises(InvalidTagError, o.hasTag, "anything")
        self.assertRaises(InvalidTagError, o.removeTag, "anything")

        o.addTag(23)
        self.assertTrue(o.hasTag(23))
        self.assertFalse(o.hasTag(30))
        self.assertRaises(TagNotFoundError, o.removeTag, 30)
        self.assertEqual([23], o.getTags())
        o.flushTags()
        self.assertEqual([], o.getTags())
Beispiel #8
0
    def test_locking(self):
        o = RoutableSubmitSm(self.PDU, self.user, datetime.now())

        self.assertRaises(InvalidLockError, o.lockPduParam, 'anything')
        self.assertRaises(InvalidLockError, o.pduParamIsLocked, 'anything')

        o.lockPduParam('service_type')
        self.assertTrue(o.pduParamIsLocked('service_type'))
        self.assertFalse(o.pduParamIsLocked('source_addr_ton'))
Beispiel #9
0
 def test_without_datetime(self):
     o = RoutableSubmitSm(self.PDU, self.user)
     self.assertNotEqual(o.datetime, None)
Beispiel #10
0
    def test_tagging(self):
        o = RoutableSubmitSm(self.PDU, self.user, datetime.now())

        _any_object = object()
        self.assertRaises(InvalidTagError, o.addTag, _any_object)
        self.assertRaises(InvalidTagError, o.hasTag, _any_object)
        self.assertRaises(InvalidTagError, o.removeTag, _any_object)

        # Integer tags
        o.addTag(23)
        self.assertTrue(o.hasTag(23))
        self.assertFalse(o.hasTag(30))
        self.assertRaises(TagNotFoundError, o.removeTag, 30)
        self.assertEqual(['23'], o.getTags())
        o.flushTags()
        self.assertEqual([], o.getTags())

        # String tags
        o.addTag('23')
        self.assertTrue(o.hasTag('23'))
        self.assertFalse(o.hasTag('30'))
        self.assertRaises(TagNotFoundError, o.removeTag, '30')
        self.assertEqual(['23'], o.getTags())
        o.flushTags()
        self.assertEqual([], o.getTags())

        # Mixed tags
        o.addTag('23')
        o.addTag(24)
        self.assertEqual(['23', '24'], o.getTags())
        o.flushTags()
Beispiel #11
0
    def test_tagging(self):
        o = RoutableSubmitSm(self.PDU, self.user, datetime.now())

        _any_object = object()
        self.assertRaises(InvalidTagError, o.addTag, _any_object)
        self.assertRaises(InvalidTagError, o.hasTag, _any_object)
        self.assertRaises(InvalidTagError, o.removeTag, _any_object)

        # Integer tags
        o.addTag(23)
        self.assertTrue(o.hasTag(23))
        self.assertFalse(o.hasTag(30))
        self.assertRaises(TagNotFoundError, o.removeTag, 30)
        self.assertEqual(['23'], o.getTags())
        o.flushTags()
        self.assertEqual([], o.getTags())

        # String tags
        o.addTag('23')
        self.assertTrue(o.hasTag('23'))
        self.assertFalse(o.hasTag('30'))
        self.assertRaises(TagNotFoundError, o.removeTag, '30')
        self.assertEqual(['23'], o.getTags())
        o.flushTags()
        self.assertEqual([], o.getTags())

        # Mixed tags
        o.addTag('23')
        o.addTag(24)
        self.assertEqual(['23', '24'], o.getTags())
        o.flushTags()
Beispiel #12
0
    def route_routable(self, request):
        try:
            # Do we have a hex-content ?
            if 'hex-content' not in request.args:
                # Convert utf8 to GSM 03.38
                if request.args['coding'][0] == '0':
                    short_message = gsm_encode(
                        request.args['content'][0].decode('utf-8'))
                else:
                    # Otherwise forward it as is
                    short_message = request.args['content'][0]
            else:
                # Otherwise convert hex to bin
                short_message = hex2bin(request.args['hex-content'][0])

            # Authentication
            user = self.RouterPB.authenticateUser(
                username=request.args['username'][0],
                password=request.args['password'][0])
            if user is None:
                self.stats.inc('auth_error_count')

                self.log.debug(
                    "Authentication failure for username:%s and password:%s",
                    request.args['username'][0], request.args['password'][0])
                self.log.error("Authentication failure for username:%s",
                               request.args['username'][0])
                raise AuthenticationError(
                    'Authentication failure for username:%s' %
                    request.args['username'][0])

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

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

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

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

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

            # Should we tag the routable ?
            tags = []
            if 'tags' in request.args:
                tags = request.args['tags'][0].split(',')
                for tag in tags:
                    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):
                    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'
                    )

            # Routing
            route = self.RouterPB.getMTRoutingTable().getRouteFor(routable)
            if route is None:
                self.log.error(
                    "No route matched from user %s for SubmitSmPDU: %s", user,
                    SubmitSmPDU)
                raise RouteNotFoundError("No route found")

            # Get connector from selected route
            self.log.debug("RouterPB selected %s for this SubmitSmPDU", route)

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

            # Get the bill
            bill = route.getBillFor(user)

            response = {
                'return': {
                    'unit_rate': bill.getTotalAmounts(),
                    'submit_sm_count': submit_sm_count
                },
                'status': 200
            }
        except Exception as e:
            self.log.error("Error: %s", e)

            if hasattr(e, 'code'):
                response = {'return': e.message, 'status': e.code}
            else:
                response = {'return': "Unknown error: %s" % e, 'status': 500}
        finally:
            self.log.debug("Returning %s to %s.", response,
                           request.getClientIP())

            # Return message
            if response['return'] is None:
                response['return'] = 'System error'
                request.setResponseCode(500)
            else:
                request.setResponseCode(response['status'])

            request.write(json.dumps(response['return']))
            request.finish()
Beispiel #13
0
    def render(self, request):
        """
        /send request processing

        Note: This method MUST behave exactly like jasmin.protocols.smpp.factory.SMPPServerFactory.submit_sm_event
        """

        self.log.debug("Rendering /send response with args: %s from %s" %
                       (request.args, request.getClientIP()))
        response = {'return': None, 'status': 200}

        # updated_request will be filled with default values where request will never get modified
        # updated_request is used for sending the SMS, request is just kept as an original request object
        updated_request = request

        try:
            # Validation
            fields = {
                'to': {
                    'optional': False,
                    'pattern': re.compile(r'^\+{0,1}\d+$')
                },
                'from': {
                    'optional': True
                },
                'coding': {
                    'optional': True,
                    'pattern':
                    re.compile(r'^(0|1|2|3|4|5|6|7|8|9|10|13|14){1}$')
                },
                'username': {
                    'optional': False,
                    'pattern': re.compile(r'^.{1,30}$')
                },
                'password': {
                    'optional': False,
                    'pattern': re.compile(r'^.{1,30}$')
                },
                # Priority validation pattern can be validated/filtered further more through HttpAPICredentialValidator
                'priority': {
                    'optional': True,
                    'pattern': re.compile(r'^[0-3]$')
                },
                'dlr': {
                    'optional': False,
                    'pattern': re.compile(r'^(yes|no)$')
                },
                'dlr-url': {
                    'optional': True,
                    'pattern': re.compile(r'^(http|https)\://.*$')
                },
                # DLR Level validation pattern can be validated/filtered further more through HttpAPICredentialValidator
                'dlr-level': {
                    'optional': True,
                    'pattern': re.compile(r'^[1-3]$')
                },
                'dlr-method': {
                    'optional': True,
                    'pattern': re.compile(r'^(get|post)$', re.IGNORECASE)
                },
                'content': {
                    'optional': False
                },
            }

            # Default coding is 0 when not provided
            if 'coding' not in updated_request.args:
                updated_request.args['coding'] = ['0']

            # Set default for undefined updated_request.arguments
            if 'dlr-url' in updated_request.args or 'dlr-level' in updated_request.args:
                updated_request.args['dlr'] = ['yes']
            if 'dlr' not in updated_request.args:
                # Setting DLR updated_request to 'no'
                updated_request.args['dlr'] = ['no']

            # Set default values
            if updated_request.args['dlr'][0] == 'yes':
                if 'dlr-level' not in updated_request.args:
                    # If DLR is requested and no dlr-level were provided, assume minimum level (1)
                    updated_request.args['dlr-level'] = [1]
                if 'dlr-method' not in updated_request.args:
                    # If DLR is requested and no dlr-method were provided, assume default (POST)
                    updated_request.args['dlr-method'] = ['POST']

            # DLR method must be uppercase
            if 'dlr-method' in updated_request.args:
                updated_request.args['dlr-method'][0] = updated_request.args[
                    'dlr-method'][0].upper()

            # Make validation
            v = UrlArgsValidator(updated_request, fields)
            v.validate()

            # Authentication
            user = self.RouterPB.authenticateUser(
                username=updated_request.args['username'][0],
                password=updated_request.args['password'][0])
            if user is None:
                self.log.debug(
                    "Authentication failure for username:%s and password:%s" %
                    (updated_request.args['username'][0],
                     updated_request.args['password'][0]))
                self.log.error("Authentication failure for username:%s" %
                               updated_request.args['username'][0])
                raise AuthenticationError(
                    'Authentication failure for username:%s' %
                    updated_request.args['username'][0])

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

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

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

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

            # Routing
            routedConnector = None  # init
            routable = RoutableSubmitSm(SubmitSmPDU, user)
            route = self.RouterPB.getMTRoutingTable().getRouteFor(routable)
            if route is None:
                self.log.error(
                    "No route matched from user %s for SubmitSmPDU: %s" %
                    (user, SubmitSmPDU))
                raise RouteNotFoundError("No route found")

            # Get connector from selected route
            self.log.debug("RouterPB selected %s for this SubmitSmPDU" % route)
            routedConnector = route.getConnector()

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

            # Set DLR bit mask
            # c.f. 5.2.17 registered_delivery
            ####################################################################
            # dlr-level # Signification                  # registered_delivery #
            ####################################################################
            # 1         # SMS-C level                    # x x x x x x 1 0     #
            # 2         # Terminal level (only)          # x x x x x x 0 1     #
            # 3         # SMS-C level and Terminal level # x x x x x x 0 1     #
            ####################################################################
            if updated_request.args['dlr'][0] == 'yes':
                if updated_request.args['dlr-level'][0] == '1':
                    SubmitSmPDU.params[
                        'registered_delivery'] = RegisteredDelivery(
                            RegisteredDeliveryReceipt.
                            NO_SMSC_DELIVERY_RECEIPT_REQUESTED)
                elif updated_request.args['dlr-level'][
                        0] == '2' or updated_request.args['dlr-level'][
                            0] == '3':
                    SubmitSmPDU.params[
                        'registered_delivery'] = RegisteredDelivery(
                            RegisteredDeliveryReceipt.
                            SMSC_DELIVERY_RECEIPT_REQUESTED_FOR_FAILURE)
                self.log.debug("SubmitSmPDU registered_delivery is set to %s" %
                               str(SubmitSmPDU.params['registered_delivery']))

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

            # Get number of PDUs to be sent (for billing purpose)
            _pdu = SubmitSmPDU
            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.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(
                routedConnector.cid,
                SubmitSmPDU,
                priority,
                pickled=False,
                dlr_url=dlr_url,
                dlr_level=dlr_level,
                dlr_method=dlr_method,
                submit_sm_resp_bill=bill.getSubmitSmRespBill())

            # Build final response
            if not c.result:
                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.log.debug('SubmitSmPDU sent to [cid:%s], result = %s' %
                               (routedConnector.cid, c.result))
                response = {'return': c.result, 'status': 200}
        except Exception, e:
            self.log.error("Error: %s" % e)

            if hasattr(e, 'code'):
                response = {'return': e.message, 'status': e.code}
            else:
                response = {'return': "Unknown error: %s" % e, 'status': 500}
Beispiel #14
0
    def render(self, request):
        """
        /rate request processing

        Note: This method will indicate the rate of the message once sent
        """

        self.log.debug("Rendering /rate response with args: %s from %s" %
                       (request.args, request.getClientIP()))
        request.responseHeaders.addRawHeader(b"content-type",
                                             b"application/json")
        response = {'return': None, 'status': 200}

        self.stats.inc('request_count')
        self.stats.set('last_request_at', datetime.now())

        try:
            # Validation (must be almost the same params as /send service)
            fields = {
                'to': {
                    'optional': False,
                    'pattern': re.compile(r'^\+{0,1}\d+$')
                },
                'from': {
                    'optional': True
                },
                'coding': {
                    'optional': True,
                    'pattern':
                    re.compile(r'^(0|1|2|3|4|5|6|7|8|9|10|13|14){1}$')
                },
                'username': {
                    'optional': False,
                    'pattern': re.compile(r'^.{1,9}$')
                },
                'password': {
                    'optional': False,
                    'pattern': re.compile(r'^.{1,9}$')
                },
                # Priority validation pattern can be validated/filtered further more through HttpAPICredentialValidator
                'priority': {
                    'optional': True,
                    'pattern': re.compile(r'^[0-3]$')
                },
                # Validity period validation pattern can be validated/filtered further more through HttpAPICredentialValidator
                'validity-period': {
                    'optional': True,
                    'pattern': re.compile(r'^\d+$')
                },
                'content': {
                    'optional': True
                },
            }

            # Default coding is 0 when not provided
            if 'coding' not in request.args:
                request.args['coding'] = ['0']

            # Content is optional, defaults to empty string
            if 'content' not in request.args:
                request.args['content'] = ['']

            # Make validation
            v = UrlArgsValidator(request, fields)
            v.validate()

            # Authentication
            user = self.RouterPB.authenticateUser(
                username=request.args['username'][0],
                password=request.args['password'][0])
            if user is None:
                self.stats.inc('auth_error_count')

                self.log.debug(
                    "Authentication failure for username:%s and password:%s" %
                    (request.args['username'][0], request.args['password'][0]))
                self.log.error("Authentication failure for username:%s" %
                               request.args['username'][0])
                raise AuthenticationError(
                    'Authentication failure for username:%s' %
                    request.args['username'][0])

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

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

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

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

            # Routing
            routedConnector = None  # init
            routable = RoutableSubmitSm(SubmitSmPDU, user)
            route = self.RouterPB.getMTRoutingTable().getRouteFor(routable)
            if route is None:
                self.log.error(
                    "No route matched from user %s for SubmitSmPDU: %s" %
                    (user, SubmitSmPDU))
                raise RouteNotFoundError("No route found")

            # Get connector from selected route
            self.log.debug("RouterPB selected %s for this SubmitSmPDU" % route)
            routedConnector = route.getConnector()

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

            # Get the bill
            bill = route.getBillFor(user)

            response = {
                'return': {
                    'unit_rate': bill.getTotalAmounts(),
                    'submit_sm_count': submit_sm_count
                },
                'status': 200
            }
        except Exception, e:
            self.log.error("Error: %s" % e)

            if hasattr(e, 'code'):
                response = {'return': e.message, 'status': e.code}
            else:
                response = {'return': "Unknown error: %s" % e, 'status': 500}
Beispiel #15
0
    def route_routable(self, updated_request):
        try:
            # Authentication
            user = self.RouterPB.authenticateUser(
                username=updated_request.args['username'][0],
                password=updated_request.args['password'][0])
            if user is None:
                self.stats.inc('auth_error_count')

                self.log.debug(
                    "Authentication failure for username:%s and password:%s",
                    updated_request.args['username'][0],
                    updated_request.args['password'][0])
                self.log.error("Authentication failure for username:%s",
                               updated_request.args['username'][0])
                raise AuthenticationError(
                    'Authentication failure for username:%s' %
                    updated_request.args['username'][0])

            # 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 'from' not in updated_request.args else
                updated_request.args['from'][0],
                destination_addr=updated_request.args['to'][0],
                short_message=updated_request.args['content'][0],
                data_coding=int(updated_request.args['coding'][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)

            # 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):
                    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()

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

            # Set validity_period
            if 'validity-period' in updated_request.args:
                delta = timedelta(
                    minutes=int(updated_request.args['validity-period'][0]))
                routable.pdu.params['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['validity-period'][0])

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

                dlr_level = int(updated_request.args['dlr-level'][0])
                if 'dlr-url' in updated_request.args:
                    dlr_url = updated_request.args['dlr-url'][0]
                else:
                    dlr_url = None
                if updated_request.args['dlr-level'][0] == '1':
                    dlr_level_text = 'SMS-C'
                elif updated_request.args['dlr-level'][0] == '2':
                    dlr_level_text = 'Terminal'
                else:
                    dlr_level_text = 'All'
                dlr_method = updated_request.args['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') >= 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) for 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(
                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)

            # 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 Exception, e:
            self.log.error("Error: %s", e)

            if hasattr(e, 'code'):
                response = {'return': e.message, 'status': e.code}
            else:
                response = {'return': "Unknown error: %s" % e, 'status': 500}
Beispiel #16
0
    def submit_sm_event(self, system_id, *args):
        """This event handler will deliver the submit_sm to the right smppc connector.
        Note that Jasmin deliver submit_sm messages like this:
        - from httpapi to smppc (handled in jasmin.protocols.http.server)
        - from smpps to smppc (this event handler)

        Note: This event handler MUST behave exactly like jasmin.protocols.http.server.Send.render
        """
        self.log.debug('Handling submit_sm event for system_id: %s' %
                       system_id)

        # Args validation
        if len(args) != 2:
            self.log.error('(submit_sm_event/%s) Invalid args: %s' %
                           (system_id, args))
            raise SubmitSmInvalidArgsError()
        if not isinstance(args[1], pdu_types.PDURequest):
            self.log.error(
                '(submit_sm_event/%s) Received an unknown object when waiting for a PDURequest: %s'
                % (system_id, args[1]))
            raise SubmitSmInvalidArgsError()
        if args[1].id != pdu_types.CommandId.submit_sm:
            self.log.error(
                '(submit_sm_event/%s) Received a non submit_sm command id: %s'
                % (system_id, args[1].id))
            raise SubmitSmInvalidArgsError()
        if not isinstance(args[0], SMPPServerProtocol):
            self.log.error(
                '(submit_sm_event/%s) Received an unknown object when waiting for a SMPPServerProtocol: %s'
                % (system_id, args[0]))
            raise SubmitSmInvalidArgsError()

        proto = args[0]
        user = proto.user
        SubmitSmPDU = args[1]

        # Update CnxStatus
        user.getCnxStatus().smpps['submit_sm_request_count'] += 1

        # Basic validation
        if len(SubmitSmPDU.params['destination_addr']
               ) < 1 or SubmitSmPDU.params['destination_addr'] is None:
            self.log.error(
                '(submit_sm_event/%s) SubmitSmPDU have no defined destination_addr'
                % system_id)
            raise SubmitSmWithoutDestinationAddrError()

        # Make Credential validation
        v = SmppsCredentialValidator('Send', user, SubmitSmPDU)
        v.validate()

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

        if self.RouterPB is None:
            self.log.error(
                '(submit_sm_event/%s) RouterPB not set: submit_sm will not be routed'
                % system_id)
            return

        # Routing
        routedConnector = None  # init
        routable = RoutableSubmitSm(SubmitSmPDU, user)
        route = self.RouterPB.getMTRoutingTable().getRouteFor(routable)
        if route is None:
            self.log.error(
                "No route matched from user %s for SubmitSmPDU: %s" %
                (user, SubmitSmPDU))
            raise SubmitSmRouteNotFoundError()

        # Get connector from selected route
        self.log.debug("RouterPB selected %s for this SubmitSmPDU" % route)
        routedConnector = route.getConnector()

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

                raise SubmitSmThroughputExceededError()
        user.getCnxStatus().smpps['qos_last_submit_sm_at'] = datetime.now()

        # Pre-sending submit_sm: Billing processing
        bill = route.getBillFor(user)
        self.log.debug(
            "SubmitSmBill [bid:%s] [ttlamounts:%s] generated for this SubmitSmPDU"
            % (bill.bid, bill.getTotalAmounts()))
        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() <= 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') <= 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, requirements=charging_requirements) is None:
            self.log.error(
                'Charging user %s failed, [bid:%s] [ttlamounts:%s] (check router log)'
                % (user, bill.bid, bill.getTotalAmounts()))
            raise SubmitSmChargingError()

        # Get priority value from SubmitSmPDU to pass to SMPPClientManagerPB.perspective_submit_sm()
        priority = 0
        if SubmitSmPDU.params['priority_flag'] is not None:
            priority = SubmitSmPDU.params['priority_flag'].index

        if self.SMPPClientManagerPB is None:
            self.log.error(
                '(submit_sm_event/%s) SMPPClientManagerPB not set: submit_sm will not be submitted'
                % system_id)
            return

        ########################################################
        # 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(
            routedConnector.cid,
            SubmitSmPDU,
            priority,
            pickled=False,
            submit_sm_resp_bill=bill.getSubmitSmRespBill(),
            source_connector=proto)

        # Build final response
        if not c.result:
            self.log.error('Failed to send SubmitSmPDU to [cid:%s]' %
                           routedConnector.cid)
            raise SubmitSmRoutingError()
        else:
            self.log.debug('SubmitSmPDU sent to [cid:%s], result = %s' %
                           (routedConnector.cid, c.result))
            return DataHandlerResponse(status=pdu_types.CommandStatus.ESME_ROK,
                                       message_id=c.result)
Beispiel #17
0
    def submit_sm_event_interceptor(self, system_id, *args):
        "Intercept submit_sm befor handing it to self.submit_sm_event"
        self.log.debug('Intercepting submit_sm event for system_id: %s',
                       system_id)

        # Args validation
        if len(args) != 2:
            self.log.error('(submit_sm_event/%s) Invalid args: %s', system_id,
                           args)
            raise SubmitSmInvalidArgsError()
        if not isinstance(args[1], pdu_types.PDURequest):
            self.log.error(
                '(submit_sm_event/%s) Received an unknown object when waiting for a PDURequest: %s',
                system_id, args[1])
            raise SubmitSmInvalidArgsError()
        if args[1].id != pdu_types.CommandId.submit_sm:
            self.log.error(
                '(submit_sm_event/%s) Received a non submit_sm command id: %s',
                system_id, args[1].id)
            raise SubmitSmInvalidArgsError()
        if not isinstance(args[0], SMPPServerProtocol):
            self.log.error(
                '(submit_sm_event/%s) Received an unknown object when waiting for a SMPPServerProtocol: %s',
                system_id, args[0])
            raise SubmitSmInvalidArgsError()

        proto = args[0]
        user = proto.user
        SubmitSmPDU = args[1]

        # Update CnxStatus
        user.getCnxStatus().smpps['submit_sm_request_count'] += 1

        # Basic validation
        if len(SubmitSmPDU.params['destination_addr']
               ) < 1 or SubmitSmPDU.params['destination_addr'] is None:
            self.log.error(
                '(submit_sm_event/%s) SubmitSmPDU have no defined destination_addr',
                system_id)
            raise SubmitSmWithoutDestinationAddrError()

        # Make Credential validation
        v = SmppsCredentialValidator('Send', user, SubmitSmPDU)
        v.validate()

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

        if self.RouterPB is None:
            self.log.error(
                '(submit_sm_event_interceptor/%s) RouterPB not set: submit_sm will not be routed',
                system_id)
            return

        # Prepare for interception then routing
        routable = RoutableSubmitSm(SubmitSmPDU, user)

        # Interception inline
        # @TODO: make Interception in a thread, just like httpapi interception
        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 !
            d = self.interceptorpb_client.run_script(script, routable)
            d.addCallback(self.submit_sm_post_interception,
                          system_id=system_id,
                          proto=proto)
            d.addErrback(self.submit_sm_post_interception)
            return d
        else:
            return self.submit_sm_post_interception(routable=routable,
                                                    system_id=system_id,
                                                    proto=proto)
Beispiel #18
0
    def route_routable(self, request):
        try:
            # Do we have a hex-content ?
            if 'hex-content' not in request.args:
                # Convert utf8 to GSM 03.38
                if request.args['coding'][0] == '0':
                    short_message = gsm_encode(request.args['content'][0].decode('utf-8'))
                else:
                    # Otherwise forward it as is
                    short_message = request.args['content'][0]
            else:
                # Otherwise convert hex to bin
                short_message = hex2bin(request.args['hex-content'][0])

            # Authentication
            user = self.RouterPB.authenticateUser(
                username=request.args['username'][0],
                password=request.args['password'][0]
            )
            if user is None:
                self.stats.inc('auth_error_count')

                self.log.debug(
                    "Authentication failure for username:%s and password:%s",
                    request.args['username'][0], request.args['password'][0])
                self.log.error(
                    "Authentication failure for username:%s",
                    request.args['username'][0])
                raise AuthenticationError(
                    'Authentication failure for username:%s' % request.args['username'][0])

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

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

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

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

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

            # Should we tag the routable ?
            tags = []
            if 'tags' in request.args:
                tags = request.args['tags'][0].split(',')
                for tag in tags:
                    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):
                    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')

            # Routing
            route = self.RouterPB.getMTRoutingTable().getRouteFor(routable)
            if route is None:
                self.log.error("No route matched from user %s for SubmitSmPDU: %s", user, SubmitSmPDU)
                raise RouteNotFoundError("No route found")

            # Get connector from selected route
            self.log.debug("RouterPB selected %s for this SubmitSmPDU", route)

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

            # Get the bill
            bill = route.getBillFor(user)

            response = {
                'return': {
                    'unit_rate': bill.getTotalAmounts(),
                    'submit_sm_count': submit_sm_count},
                'status': 200}
        except Exception as e:
            self.log.error("Error: %s", e)

            if hasattr(e, 'code'):
                response = {'return': e.message, 'status': e.code}
            else:
                response = {'return': "Unknown error: %s" % e, 'status': 500}
        finally:
            self.log.debug("Returning %s to %s.", response, request.getClientIP())

            # Return message
            if response['return'] is None:
                response['return'] = 'System error'
                request.setResponseCode(500)
            else:
                request.setResponseCode(response['status'])

            request.write(json.dumps(response['return']))
            request.finish()
Beispiel #19
0
    def route_routable(self, updated_request):
        try:
            # Do we have a hex-content ?
            if 'hex-content' not in updated_request.args:
                # Convert utf8 to GSM 03.38
                if updated_request.args['coding'][0] == '0':
                    short_message = gsm_encode(
                        updated_request.args['content'][0].decode('utf-8'))
                else:
                    # Otherwise forward it as is
                    short_message = updated_request.args['content'][0]
            else:
                # Otherwise convert hex to bin
                short_message = hex2bin(updated_request.args['hex-content'][0])

            # Authentication
            user = self.RouterPB.authenticateUser(
                username=updated_request.args['username'][0],
                password=updated_request.args['password'][0])
            if user is None:
                self.stats.inc('auth_error_count')

                self.log.debug(
                    "Authentication failure for username:%s and password:%s",
                    updated_request.args['username'][0],
                    updated_request.args['password'][0])
                self.log.error("Authentication failure for username:%s",
                               updated_request.args['username'][0])
                raise AuthenticationError(
                    'Authentication failure for username:%s' %
                    updated_request.args['username'][0])

            # 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 'from' not in updated_request.args else
                updated_request.args['from'][0],
                destination_addr=updated_request.args['to'][0],
                short_message=short_message,
                data_coding=int(updated_request.args['coding'][0]),
                custom_tlvs=updated_request.args['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 'tags' in updated_request.args:
                tags = updated_request.args['tags'][0].split(',')
                for tag in tags:
                    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):
                    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 priority
            priority = 0
            if 'priority' in updated_request.args:
                priority = int(updated_request.args['priority'][0])
                routable.pdu.params['priority_flag'] = priority_flag_value_map[
                    priority]
            self.log.debug("SubmitSmPDU priority is set to %s", priority)

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

            # Set validity_period
            if 'validity-period' in updated_request.args:
                delta = timedelta(
                    minutes=int(updated_request.args['validity-period'][0]))
                routable.pdu.params['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['validity-period'][0])

            # 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['dlr'][0] == '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['dlr-level'][0])
                if 'dlr-url' in updated_request.args:
                    dlr_url = updated_request.args['dlr-url'][0]
                else:
                    dlr_url = None
                if updated_request.args['dlr-level'][0] == '1':
                    dlr_level_text = 'SMS-C'
                elif updated_request.args['dlr-level'][0] == '2':
                    dlr_level_text = 'Terminal'
                else:
                    dlr_level_text = 'All'
                dlr_method = updated_request.args['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') >= 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)

            # 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 Exception as e:
            self.log.error("Error: %s", e)

            if hasattr(e, 'code'):
                response = {'return': e.message, 'status': e.code}
            else:
                response = {'return': "Unknown error: %s" % e, 'status': 500}
        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:
                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['to'][0],
                    re.sub(r'[^\x20-\x7E]+', '.', short_message))
                _return = 'Success "%s"' % response['return']

            updated_request.write(_return)
            updated_request.finish()
Beispiel #20
0
    def route_routable(self, updated_request):
        try:
            # Do we have a hex-content ?
            if 'hex-content' not in updated_request.args:
                # Convert utf8 to GSM 03.38
                if updated_request.args['coding'][0] == '0':
                    short_message = gsm_encode(updated_request.args['content'][0].decode('utf-8'))
                else:
                    # Otherwise forward it as is
                    short_message = updated_request.args['content'][0]
            else:
                # Otherwise convert hex to bin
                short_message = hex2bin(updated_request.args['hex-content'][0])

            # Authentication
            user = self.RouterPB.authenticateUser(
                username=updated_request.args['username'][0],
                password=updated_request.args['password'][0])
            if user is None:
                self.stats.inc('auth_error_count')

                self.log.debug(
                    "Authentication failure for username:%s and password:%s",
                    updated_request.args['username'][0], updated_request.args['password'][0])
                self.log.error(
                    "Authentication failure for username:%s",
                    updated_request.args['username'][0])
                raise AuthenticationError(
                    'Authentication failure for username:%s' % updated_request.args['username'][0])

            # 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 'from' not in updated_request.args else updated_request.args['from'][0],
                destination_addr=updated_request.args['to'][0],
                short_message=short_message,
                data_coding=int(updated_request.args['coding'][0]),
                custom_tlvs=updated_request.args['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 'tags' in updated_request.args:
                tags = updated_request.args['tags'][0].split(',')
                for tag in tags:
                    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):
                    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 priority
            priority = 0
            if 'priority' in updated_request.args:
                priority = int(updated_request.args['priority'][0])
                routable.pdu.params['priority_flag'] = priority_flag_value_map[priority]
            self.log.debug("SubmitSmPDU priority is set to %s", priority)

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

            # Set validity_period
            if 'validity-period' in updated_request.args:
                delta = timedelta(minutes=int(updated_request.args['validity-period'][0]))
                routable.pdu.params['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['validity-period'][0])

            # 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['dlr'][0] == '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['dlr-level'][0])
                if 'dlr-url' in updated_request.args:
                    dlr_url = updated_request.args['dlr-url'][0]
                else:
                    dlr_url = None
                if updated_request.args['dlr-level'][0] == '1':
                    dlr_level_text = 'SMS-C'
                elif updated_request.args['dlr-level'][0] == '2':
                    dlr_level_text = 'Terminal'
                else:
                    dlr_level_text = 'All'
                dlr_method = updated_request.args['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') >= 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)

            # 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 Exception as e:
            self.log.error("Error: %s", e)

            if hasattr(e, 'code'):
                response = {'return': e.message, 'status': e.code}
            else:
                response = {'return': "Unknown error: %s" % e, 'status': 500}
        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:
                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['to'][0],
                    re.sub(r'[^\x20-\x7E]+', '.', short_message))
                _return = 'Success "%s"' % response['return']

            updated_request.write(_return)
            updated_request.finish()