Exemple #1
0
    def test_submitsmresp(self):
        # Successful submit_sm_resp
        c_success = DLR(pdu_type=CommandId.submit_sm_resp,
                        msgid=1,
                        status=CommandStatus.ESME_ROK,
                        smpp_msgid=b'2')
        # Errored submit_sm_resp
        c_errored = DLR(pdu_type=CommandId.submit_sm_resp,
                        msgid=3,
                        status=CommandStatus.ESME_RINVPARLEN)

        self.assertEqual('ESME_ROK', c_success.body)
        self.assertEqual('1', c_success.properties['message-id'])
        self.assertEqual(CommandId.submit_sm_resp.name,
                         c_success.properties['headers']['type'])
        self.assertEqual('2', c_success.properties['headers']['smpp_msgid'])
        self.assertEqual('ESME_RINVPARLEN', c_errored.body)
        self.assertEqual('3', c_errored.properties['message-id'])
        self.assertEqual(CommandId.submit_sm_resp.name,
                         c_errored.properties['headers']['type'])

        # Exceptions:
        self.assertRaises(InvalidParameterError,
                          DLR,
                          pdu_type=CommandId.submit_sm_resp,
                          msgid=1,
                          status=CommandStatus.ESME_ROK)
Exemple #2
0
    def test_deliversm_and_datasm(self):
        for pdu_type in [CommandId.deliver_sm, CommandId.data_sm]:
            c = DLR(pdu_type=pdu_type,
                    msgid=1,
                    status=CommandStatus.ESME_ROK,
                    cid='test',
                    dlr_details={'some': 'detail'})

            self.assertEqual(CommandStatus.ESME_ROK.name, c.body)
            self.assertEqual('1', c.properties['message-id'])
            self.assertEqual(pdu_type.name, c.properties['headers']['type'])
            self.assertEqual('test', c.properties['headers']['cid'])
            self.assertEqual('detail', c.properties['headers']['dlr_some'])

            # Exceptions:
            self.assertRaises(InvalidParameterError,
                              DLR,
                              pdu_type=pdu_type,
                              msgid=1,
                              status=CommandStatus.ESME_ROK)
            self.assertRaises(InvalidParameterError,
                              DLR,
                              pdu_type=pdu_type,
                              msgid=1,
                              status=CommandStatus.ESME_ROK,
                              dlr_details={'some': 'detail'})
            self.assertRaises(InvalidParameterError,
                              DLR,
                              pdu_type=pdu_type,
                              msgid=1,
                              status=CommandStatus.ESME_ROK,
                              cid='test')
Exemple #3
0
    def deliver_sm_event_post_interception(self, *args, **kw):
        """This event is called whenever a deliver_sm pdu is received through a SMPPc
        It will hand the pdu to the router or a dlr thrower (depending if its a DLR or not).

        Note: this event will catch data_sm pdus as well
        """

        try:
            # Control args
            if 'smpp' not in kw or 'routable' not in kw:
                self.log.error(
                    'deliver_sm_event_post_interception missing arguments after interception: %s',
                    kw)
                raise InterceptorRunError(
                    'deliver_sm_event_post_interception missing arguments after interception'
                )

            # Set defaults
            smpp = kw['smpp']
            routable = kw['routable']

            if 'concatenated' in kw:
                concatenated = kw['concatenated']
            else:
                concatenated = False

            # Get message_content
            if 'short_message' in routable.pdu.params and len(
                    routable.pdu.params['short_message']) > 0:
                message_content = routable.pdu.params['short_message']
            elif 'message_payload' in routable.pdu.params:
                message_content = routable.pdu.params['message_payload']
            elif 'short_message' in routable.pdu.params:
                message_content = routable.pdu.params['short_message']
            else:
                message_content = None

            # Post interception:
            if len(args) == 1:
                if isinstance(args[0], bool) and not args[0]:
                    smpp.factory.stats.inc('interceptor_error_count')
                    self.log.error(
                        'Failed running interception script, got a False return.'
                    )
                    raise InterceptorRunError(
                        'Failed running interception script, check log for details'
                    )
                elif isinstance(args[0], dict) and args[0]['smpp_status'] > 0:
                    smpp.factory.stats.inc('interceptor_error_count')
                    self.log.info(
                        'Interceptor script returned %s smpp_status error.',
                        args[0]['smpp_status'])
                    raise DeliverSmInterceptionError(
                        code=args[0]['smpp_status'])
                elif isinstance(args[0], str):
                    smpp.factory.stats.inc('interceptor_count')
                    routable = pickle.loads(args[0])
                else:
                    smpp.factory.stats.inc('interceptor_error_count')
                    self.log.error(
                        'Failed running interception script, got the following return: %s',
                        args[0])
                    raise InterceptorRunError(
                        'Failed running interception script, got the following return: %s'
                        % args[0])

            self.log.debug(
                'Handling deliver_sm_event_post_interception event for smppc: %s',
                self.SMPPClientFactory.config.id)

            routable.pdu.dlr = self.SMPPOperationFactory.isDeliveryReceipt(
                routable.pdu)
            content = DeliverSmContent(routable,
                                       self.SMPPClientFactory.config.id,
                                       pickleProtocol=self.pickleProtocol,
                                       concatenated=concatenated)
            msgid = content.properties['message-id']

            if routable.pdu.dlr is None:
                # We have a SMS-MO

                # UDH is set ?
                UDHI_INDICATOR_SET = False
                if 'esm_class' in routable.pdu.params and hasattr(
                        routable.pdu.params['esm_class'], 'gsmFeatures'):
                    for gsmFeature in routable.pdu.params[
                            'esm_class'].gsmFeatures:
                        if str(gsmFeature) == 'UDHI_INDICATOR_SET':
                            UDHI_INDICATOR_SET = True
                            break

                not_class2 = True
                if 'data_coding' in routable.pdu.params:
                    dcs = routable.pdu.params['data_coding']
                    if (str(dcs.scheme)
                            == 'GSM_MESSAGE_CLASS') and (dcs.schemeData
                                                         is not None):
                        not_class2 = (str(dcs.schemeData.msgClass) !=
                                      'CLASS_2')

                splitMethod = None
                # Is it a part of a long message ?
                if 'sar_msg_ref_num' in routable.pdu.params:
                    splitMethod = 'sar'
                    total_segments = routable.pdu.params['sar_total_segments']
                    segment_seqnum = routable.pdu.params['sar_segment_seqnum']
                    msg_ref_num = routable.pdu.params['sar_msg_ref_num']
                    self.log.debug(
                        'Received SMS-MO part [queue-msgid:%s] using SAR: ttl_segments=%s, segment_sn=%s, msgref=%s',
                        msgid, total_segments, segment_seqnum, msg_ref_num)
                elif UDHI_INDICATOR_SET and not_class2 and message_content[:
                                                                           3] == '\x05\x00\x03':
                    splitMethod = 'udh'
                    total_segments = struct.unpack('!B', message_content[4])[0]
                    segment_seqnum = struct.unpack('!B', message_content[5])[0]
                    msg_ref_num = struct.unpack('!B', message_content[3])[0]
                    self.log.debug(
                        'Received SMS-MO part [queue-msgid:%s] using UDH: ttl_segments=%s, segment_sn=%s, msgref=%s',
                        msgid, total_segments, segment_seqnum, msg_ref_num)

                if splitMethod is None:
                    # It's a simple short message or a part of a concatenated message
                    routing_key = 'deliver.sm.%s' % self.SMPPClientFactory.config.id
                    self.log.debug(
                        "Publishing DeliverSmContent[%s] with routing_key[%s]",
                        msgid, routing_key)
                    yield self.amqpBroker.publish(exchange='messaging',
                                                  routing_key=routing_key,
                                                  content=content)

                    # Get values from data_sm or deliver_sm
                    priority_flag = None
                    if 'priority_flag' in routable.pdu.params:
                        priority_flag = routable.pdu.params['priority_flag']
                    validity_period = None
                    if 'validity_period' in routable.pdu.params:
                        validity_period = routable.pdu.params[
                            'validity_period']

                    # Do not log text for privacy reasons
                    # Added in #691
                    if self.config.log_privacy:
                        logged_content = '** %s byte content **' % len(
                            message_content)
                    else:
                        logged_content = '%r' % message_content

                    self.log.info(
                        "SMS-MO [cid:%s] [queue-msgid:%s] [status:%s] [prio:%s] [validity:%s] [from:%s] [to:%s] \
[content:%s]", self.SMPPClientFactory.config.id, msgid, routable.pdu.status,
                        priority_flag, validity_period,
                        routable.pdu.params['source_addr'],
                        routable.pdu.params['destination_addr'],
                        logged_content)
                else:
                    # Long message part received
                    if self.redisClient is None:
                        self.log.critical(
                            'Invalid RC found while receiving part of long DeliverSm [queue-msgid:%s], MSG IS LOST !',
                            msgid)
                    else:
                        # Save it to redis
                        hashKey = "longDeliverSm:%s:%s:%s" % (
                            self.SMPPClientFactory.config.id, msg_ref_num,
                            routable.pdu.params['destination_addr'])
                        hashValues = {
                            'pdu': routable.pdu,
                            'total_segments': total_segments,
                            'msg_ref_num': msg_ref_num,
                            'segment_seqnum': segment_seqnum
                        }
                        yield self.redisClient.hset(
                            hashKey, segment_seqnum,
                            pickle.dumps(hashValues,
                                         self.pickleProtocol)).addCallback(
                                             self.concatDeliverSMs, hashKey,
                                             splitMethod, total_segments,
                                             msg_ref_num, segment_seqnum)

                        self.log.info(
                            "DeliverSmContent[%s] is part of long msg of (%s), will be enqueued after concatenation.",
                            msgid, total_segments)

                        # Flag it as "will_be_concatenated" and publish it to router
                        routing_key = 'deliver.sm.%s' % self.SMPPClientFactory.config.id
                        self.log.debug(
                            "Publishing DeliverSmContent[%s](flagged:wbc) with routing_key[%s]",
                            msgid, routing_key)
                        content.properties['headers'][
                            'will_be_concatenated'] = True
                        yield self.amqpBroker.publish(exchange='messaging',
                                                      routing_key=routing_key,
                                                      content=content)
            else:
                # This is a DLR !
                # Send DLR to DLRLookup
                yield self.amqpBroker.publish(
                    exchange='messaging',
                    routing_key='dlr.deliver_sm',
                    content=DLR(pdu_type=routable.pdu.id,
                                msgid=self.code_dlr_msgid(routable.pdu),
                                status=routable.pdu.dlr['stat'],
                                cid=self.SMPPClientFactory.config.id,
                                dlr_details=routable.pdu.dlr))
        except (InterceptorRunError, DeliverSmInterceptionError) as e:
            # Do not log text for privacy reasons
            # Added in #691
            if self.config.log_privacy:
                logged_content = '** %s byte content **' % len(message_content)
            else:
                logged_content = '%r' % message_content

            self.log.info(
                "SMS-MO [cid:%s] [i-status:%s] [from:%s] [to:%s] [content:%s]",
                self.SMPPClientFactory.config.id, e.status,
                routable.pdu.params['source_addr'],
                routable.pdu.params['destination_addr'], logged_content)

            # Known exception handling
            defer.returnValue(DataHandlerResponse(status=e.status))
        except Exception as e:
            # Unknown exception handling
            self.log.critical('Got an unknown exception (%s): %s', type(e), e)
            defer.returnValue(
                DataHandlerResponse(status=CommandStatus.ESME_RUNKNOWNERR))
Exemple #4
0
    def submit_sm_resp_event(self, r, amqpMessage):
        msgid = amqpMessage.content.properties['message-id']
        total_bill_amount = None
        will_be_retried = False

        try:
            submit_sm_resp_bill = pickle.loads(
                amqpMessage.content.properties['headers']
                ['submit_sm_bill']).getSubmitSmRespBill()

            if r.response.status == CommandStatus.ESME_ROK:
                # No more retrials !
                del self.submit_retrials[msgid]

                # Get bill information
                total_bill_amount = 0.0
                if submit_sm_resp_bill is not None and submit_sm_resp_bill.getTotalAmounts(
                ) > 0:
                    total_bill_amount = submit_sm_resp_bill.getTotalAmounts()

                # UDH is set ?
                UDHI_INDICATOR_SET = False
                if hasattr(r.request.params['esm_class'], 'gsmFeatures'):
                    for gsmFeature in r.request.params[
                            'esm_class'].gsmFeatures:
                        if str(gsmFeature) == 'UDHI_INDICATOR_SET':
                            UDHI_INDICATOR_SET = True
                            break

                # What type of splitting ?
                splitMethod = None
                if 'sar_msg_ref_num' in r.request.params:
                    splitMethod = 'sar'
                elif UDHI_INDICATOR_SET and r.request.params[
                        'short_message'][:3] == '\x05\x00\x03':
                    splitMethod = 'udh'

                # Concatenate short_message
                if splitMethod is not None:
                    _pdu = r.request
                    if splitMethod == 'sar':
                        short_message = _pdu.params['short_message']
                    else:
                        short_message = _pdu.params['short_message'][6:]

                    while hasattr(_pdu, 'nextPdu'):
                        _pdu = _pdu.nextPdu
                        if splitMethod == 'sar':
                            short_message += _pdu.params['short_message']
                        else:
                            short_message += _pdu.params['short_message'][6:]

                        # Increase bill amount for each submit_sm_resp
                        if submit_sm_resp_bill is not None and submit_sm_resp_bill.getTotalAmounts(
                        ) > 0:
                            total_bill_amount += submit_sm_resp_bill.getTotalAmounts(
                            )
                else:
                    short_message = r.request.params['short_message']

                # Do not log text for privacy reasons
                # Added in #691
                if self.config.log_privacy:
                    logged_content = '** %s byte content **' % len(
                        short_message)
                else:
                    logged_content = '%r' % short_message

                self.log.info(
                    "SMS-MT [cid:%s] [queue-msgid:%s] [smpp-msgid:%s] [status:%s] [prio:%s] [dlr:%s] [validity:%s] \
[from:%s] [to:%s] [content:%s]", self.SMPPClientFactory.config.id, msgid,
                    r.response.params['message_id'], r.response.status,
                    amqpMessage.content.properties['priority'],
                    r.request.params['registered_delivery'].receipt, 'none' if
                    ('headers' not in amqpMessage.content.properties
                     or 'expiration'
                     not in amqpMessage.content.properties['headers']) else
                    amqpMessage.content.properties['headers']['expiration'],
                    r.request.params['source_addr'],
                    r.request.params['destination_addr'], logged_content)
            else:
                # Message must be retried ?
                if str(r.response.status) in self.config.submit_error_retrial:
                    retrial = self.config.submit_error_retrial[str(
                        r.response.status)]

                    # Still have some retries to go ?
                    if self.submit_retrials[msgid] < retrial['count']:
                        # Requeue the message for later redelivery
                        yield self.rejectAndRequeueMessage(
                            amqpMessage, delay=retrial['delay'])
                        will_be_retried = True
                    else:
                        # Prevent this list from over-growing
                        del self.submit_retrials[msgid]

                # Do not log text for privacy reasons
                # Added in #691
                if self.config.log_privacy:
                    logged_content = '** %s byte content **' % len(
                        r.request.params['short_message'])
                else:
                    logged_content = '%r' % r.request.params['short_message']

                # Log the message
                self.log.info(
                    "SMS-MT [cid:%s] [queue-msgid:%s] [status:ERROR/%s] [retry:%s] [prio:%s] [dlr:%s] [validity:%s] \
[from:%s] [to:%s] [content:%s]", self.SMPPClientFactory.config.id, msgid,
                    r.response.status, will_be_retried,
                    amqpMessage.content.properties['priority'],
                    r.request.params['registered_delivery'].receipt, 'none' if
                    ('headers' not in amqpMessage.content.properties
                     or 'expiration'
                     not in amqpMessage.content.properties['headers']) else
                    amqpMessage.content.properties['headers']['expiration'],
                    r.request.params['source_addr'],
                    r.request.params['destination_addr'], logged_content)

            # It is a final submit_sm_resp !
            if not will_be_retried:
                # Cancel any mapped rejectTimer to this message
                # (in case this message was rejected in the past)
                self.clearRejectTimer(msgid)
                self.log.debug(
                    "ACKing amqpMessage [%s] having routing_key [%s]", msgid,
                    amqpMessage.routing_key)
                # ACK the message in queue, this will remove it from the queue
                yield self.ackMessage(amqpMessage)

            # Send DLR to DLRLookup
            if r.response.status == CommandStatus.ESME_ROK:
                dlr = DLR(pdu_type=r.response.id,
                          msgid=msgid,
                          status=r.response.status,
                          smpp_msgid=r.response.params['message_id'])
            else:
                dlr = DLR(pdu_type=r.response.id,
                          msgid=msgid,
                          status=r.response.status)
            yield self.amqpBroker.publish(exchange='messaging',
                                          routing_key='dlr.submit_sm_resp',
                                          content=dlr)

            # Bill will be charged by bill_request.submit_sm_resp.UID queue consumer
            if total_bill_amount > 0:
                pubQueueName = 'bill_request.submit_sm_resp.%s' % submit_sm_resp_bill.user.uid
                content = SubmitSmRespBillContent(submit_sm_resp_bill.bid,
                                                  submit_sm_resp_bill.user.uid,
                                                  total_bill_amount)
                self.log.debug(
                    "Requesting a SubmitSmRespBillContent from a bill [bid:%s] with routing_key[%s]: %s",
                    submit_sm_resp_bill.bid, pubQueueName, total_bill_amount)
                yield self.amqpBroker.publish(exchange='billing',
                                              routing_key=pubQueueName,
                                              content=content)

            if self.config.publish_submit_sm_resp:
                # Send back submit_sm_resp to submit.sm.resp.CID queue
                # There's no actual listeners on this queue, it can be used to
                # track submit_sm_resp messages from a 3rd party app
                content = SubmitSmRespContent(
                    r.response, msgid, pickleProtocol=self.pickleProtocol)
                self.log.debug(
                    "Sending back SubmitSmRespContent[%s] with routing_key[%s]",
                    msgid, amqpMessage.content.properties['reply-to'])
                yield self.amqpBroker.publish(
                    exchange='messaging',
                    routing_key=amqpMessage.content.properties['reply-to'],
                    content=content)
        except Exception as e:
            self.log.error(
                '(%s) while handling submit_sm_resp pdu for msgid:%s: %s',
                type(e), msgid, e)
        else:
            if will_be_retried:
                defer.returnValue(False)