Ejemplo n.º 1
0
 def _decode(self, bytes):
     timeStr = self.encoder._decode(bytes)
     try:
         return smpp_time.parse(timeStr)
     except Exception, e:
         errStr = str(e)
         raise self.decodeErrorClass(errStr, self.decodeErrorStatus)
Ejemplo n.º 2
0
 def _decode(self, bytes):
     timeStr = self.encoder._decode(bytes)
     try:
         return smpp_time.parse(timeStr)
     except Exception, e:
         errStr = str(e)
         raise self.decodeErrorClass(errStr, self.decodeErrorStatus)
Ejemplo n.º 3
0
 def test_parse_relative(self):
     str = "020610233429000R"
     rel = smpp_time.parse(str)
     self.assertEquals(smpp_time.SMPPRelativeTime, rel.__class__)
     self.assertEquals(2, rel.years)
     self.assertEquals(6, rel.months)
     self.assertEquals(10, rel.days)
     self.assertEquals(23, rel.hours)
     self.assertEquals(34, rel.minutes)
     self.assertEquals(29, rel.seconds)
     self.assertEquals(str, smpp_time.unparse(rel))
Ejemplo n.º 4
0
 def test_parse_relative_mins_only(self):
     str = "000000001000000R"
     rel = smpp_time.parse(str)
     self.assertEquals(smpp_time.SMPPRelativeTime, rel.__class__)
     self.assertEquals(0, rel.years)
     self.assertEquals(0, rel.months)
     self.assertEquals(0, rel.days)
     self.assertEquals(0, rel.hours)
     self.assertEquals(10, rel.minutes)
     self.assertEquals(0, rel.seconds)
     self.assertEquals(str, smpp_time.unparse(rel))
Ejemplo n.º 5
0
 def test_parse_relative_mins_only(self):
     str = '000000001000000R'
     rel = smpp_time.parse(str)
     self.assertEquals(smpp_time.SMPPRelativeTime, rel.__class__)
     self.assertEquals(0, rel.years)
     self.assertEquals(0, rel.months)
     self.assertEquals(0, rel.days)
     self.assertEquals(0, rel.hours)
     self.assertEquals(10, rel.minutes)
     self.assertEquals(0, rel.seconds)
     self.assertEquals(str, smpp_time.unparse(rel))
Ejemplo n.º 6
0
 def test_parse_relative(self):
     str = '020610233429000R'
     rel = smpp_time.parse(str)
     self.assertEquals(smpp_time.SMPPRelativeTime, rel.__class__)
     self.assertEquals(2, rel.years)
     self.assertEquals(6, rel.months)
     self.assertEquals(10, rel.days)
     self.assertEquals(23, rel.hours)
     self.assertEquals(34, rel.minutes)
     self.assertEquals(29, rel.seconds)
     self.assertEquals(str, smpp_time.unparse(rel))
Ejemplo n.º 7
0
 def test_parse_absolute_negative_offset(self):
     str = "070927233429848-"
     dt = smpp_time.parse(str)
     self.assertEquals(2007, dt.year)
     self.assertEquals(9, dt.month)
     self.assertEquals(27, dt.day)
     self.assertEquals(23, dt.hour)
     self.assertEquals(34, dt.minute)
     self.assertEquals(29, dt.second)
     self.assertEquals(800000, dt.microsecond)
     self.assertEquals(timedelta(hours=-12), dt.tzinfo.utcoffset(None))
     self.assertEquals(str, smpp_time.unparse(dt))
Ejemplo n.º 8
0
 def test_parse_absolute_no_offset(self):
     str = "070927233429800+"
     dt = smpp_time.parse(str)
     self.assertEquals(2007, dt.year)
     self.assertEquals(9, dt.month)
     self.assertEquals(27, dt.day)
     self.assertEquals(23, dt.hour)
     self.assertEquals(34, dt.minute)
     self.assertEquals(29, dt.second)
     self.assertEquals(800000, dt.microsecond)
     self.assertEquals(None, dt.tzinfo)
     self.assertEquals(str, smpp_time.unparse(dt))
Ejemplo n.º 9
0
 def test_parse_absolute_negative_offset(self):
     str = '070927233429848-'
     dt = smpp_time.parse(str)
     self.assertEquals(2007, dt.year)
     self.assertEquals(9, dt.month)
     self.assertEquals(27, dt.day)
     self.assertEquals(23, dt.hour)
     self.assertEquals(34, dt.minute)
     self.assertEquals(29, dt.second)
     self.assertEquals(800000, dt.microsecond)
     self.assertEquals(timedelta(hours=-12), dt.tzinfo.utcoffset(None))
     self.assertEquals(str, smpp_time.unparse(dt))
Ejemplo n.º 10
0
 def test_parse_absolute_no_offset(self):
     str = '070927233429800+'
     dt = smpp_time.parse(str)
     self.assertEquals(2007, dt.year)
     self.assertEquals(9, dt.month)
     self.assertEquals(27, dt.day)
     self.assertEquals(23, dt.hour)
     self.assertEquals(34, dt.minute)
     self.assertEquals(29, dt.second)
     self.assertEquals(800000, dt.microsecond)
     self.assertEquals(None, dt.tzinfo)
     self.assertEquals(str, smpp_time.unparse(dt))
Ejemplo n.º 11
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()
Ejemplo n.º 12
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()