Exemple #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)
Exemple #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)                
Exemple #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))
Exemple #4
0
 def test_parse_relative_mins_only(self):
     str = '000000001000000R'
     rel = smpp_time.parse(str)
     self.assertEqual(smpp_time.SMPPRelativeTime, rel.__class__)
     self.assertEqual(0, rel.years)
     self.assertEqual(0, rel.months)
     self.assertEqual(0, rel.days)
     self.assertEqual(0, rel.hours)
     self.assertEqual(10, rel.minutes)
     self.assertEqual(0, rel.seconds)
     self.assertEqual(str, smpp_time.unparse(rel))
Exemple #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))
Exemple #6
0
 def test_parse_relative(self):
     str = '020610233429000R'
     rel = smpp_time.parse(str)
     self.assertEqual(smpp_time.SMPPRelativeTime, rel.__class__)
     self.assertEqual(2, rel.years)
     self.assertEqual(6, rel.months)
     self.assertEqual(10, rel.days)
     self.assertEqual(23, rel.hours)
     self.assertEqual(34, rel.minutes)
     self.assertEqual(29, rel.seconds)
     self.assertEqual(str, smpp_time.unparse(rel))
Exemple #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))
Exemple #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))
Exemple #9
0
 def test_parse_absolute_negative_offset(self):
     str = '070927233429848-'
     dt = smpp_time.parse(str)
     self.assertEqual(2007, dt.year)
     self.assertEqual(9, dt.month)
     self.assertEqual(27, dt.day)
     self.assertEqual(23, dt.hour)
     self.assertEqual(34, dt.minute)
     self.assertEqual(29, dt.second)
     self.assertEqual(800000, dt.microsecond)
     self.assertEqual(timedelta(hours=-12), dt.tzinfo.utcoffset(None))
     self.assertEqual(str, smpp_time.unparse(dt))
Exemple #10
0
 def test_parse_absolute_no_offset(self):
     str = '070927233429800+'
     dt = smpp_time.parse(str)
     self.assertEqual(2007, dt.year)
     self.assertEqual(9, dt.month)
     self.assertEqual(27, dt.day)
     self.assertEqual(23, dt.hour)
     self.assertEqual(34, dt.minute)
     self.assertEqual(29, dt.second)
     self.assertEqual(800000, dt.microsecond)
     self.assertEqual(None, dt.tzinfo)
     self.assertEqual(str, smpp_time.unparse(dt))
Exemple #11
0
 def _decode(self, bytes_: bytes):
     timeStr = self.encoder._decode(bytes_)
     try:
         return smpp_time.parse(timeStr)
     except Exception as e:
         raise self.decodeErrorClass(str(e), self.decode_error_status)
Exemple #12
0
    def route_routable(self, updated_request):
        try:
            # Do we have a hex-content ?
            if b'hex-content' not in updated_request.args:
                # Convert utf8 to GSM 03.38
                if updated_request.args[b'coding'][0] == b'0':
                    if isinstance(updated_request.args[b'content'][0], bytes):
                        short_message = updated_request.args[b'content'][
                            0].decode().encode('gsm0338', 'replace')
                    else:
                        short_message = updated_request.args[b'content'][
                            0].encode('gsm0338', 'replace')
                    updated_request.args[b'content'][0] = short_message
                else:
                    # Otherwise forward it as is
                    short_message = updated_request.args[b'content'][0]
            else:
                # Otherwise convert hex to bin
                short_message = hex2bin(
                    updated_request.args[b'hex-content'][0])

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            updated_request.write(_return.encode())
            updated_request.finish()