Esempio n. 1
0
    def test_normal_pickling(self):
        c = SubmitSmRespContent(self.body, 1)

        self.assertNotEquals(c.body, self.body)
        self.assertEquals(c.body, pickle.dumps(self.body, 2))
        self.assertEquals(c['message-id'], 1)
        self.assertTrue('created_at' in c['headers'])
Esempio n. 2
0
    def test_normal_nopickling(self):
        c = SubmitSmRespContent(self.body, 1, prePickle=False)

        self.assertEquals(c.body, self.body)
        self.assertEquals(c['message-id'], 1)
        self.assertTrue('created_at' in c['headers'])
Esempio n. 3
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)
Esempio n. 4
0
    def submit_sm_resp_event(self, r, amqpMessage):
        msgid = amqpMessage.content.properties['message-id']
        total_bill_amount = None
        will_be_retried = False

        if ('headers' not in amqpMessage.content.properties or
                'submit_sm_resp_bill' not in amqpMessage.content.properties['headers']):
            submit_sm_resp_bill = None
        else:
            submit_sm_resp_bill = pickle.loads(
                amqpMessage.content.properties['headers']['submit_sm_resp_bill'])

        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']

            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'],
                          re.sub(r'[^\x20-\x7E]+', '.', short_message))
        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 retrys 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

            # 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'],
                          re.sub(r'[^\x20-\x7E]+', '.', r.request.params['short_message']))

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

        # Redis client is connected ?
        # Check DLR mappings and publish receipt for later throwing
        if self.redisClient is not None:
            # Check for HTTP DLR request from redis 'dlr' key
            # If there's a pending delivery receipt request then serve it
            # back by publishing a DLRContentForHttpapi to the messaging exchange
            pickledDlr = None
            pickledSmppsMap = None
            pickledDlr = yield self.redisClient.get("dlr:%s" % msgid)
            if pickledDlr is None:
                pickledSmppsMap = yield self.redisClient.get("smppsmap:%s" % msgid)

            if pickledDlr is not None:
                self.log.debug('There is a HTTP DLR request for msgid[%s] ...', msgid)

                dlr = pickle.loads(pickledDlr)
                dlr_url = dlr['url']
                dlr_level = dlr['level']
                dlr_method = dlr['method']
                dlr_expiry = dlr['expiry']

                if dlr_level in [1, 3]:
                    self.log.debug('Got DLR information for msgid[%s], url:%s, level:%s',
                                   msgid, dlr_url, dlr_level)

                    # The dlr_url in DLRContentForHttpapi indicates the level
                    # of the actual delivery receipt (1) and not the requested
                    # one (maybe 1 or 3)
                    content = DLRContentForHttpapi(str(r.response.status),
                                                   msgid, dlr_url,
                                                   dlr_level=1, method=dlr_method)
                    routing_key = 'dlr_thrower.http'
                    self.log.debug("Publishing DLRContentForHttpapi[%s] with routing_key[%s]",
                                   msgid, routing_key)
                    yield self.amqpBroker.publish(exchange='messaging',
                                                  routing_key=routing_key,
                                                  content=content)

                    # DLR request is removed if:
                    # - If level 1 is requested (SMSC level only)
                    # - SubmitSmResp returned an error (no more delivery will be tracked)
                    #
                    # When level 3 is requested, the DLR will be removed when
                    # receiving a deliver_sm (terminal receipt)
                    if dlr_level == 1 or r.response.status != CommandStatus.ESME_ROK:
                        self.log.debug('Removing DLR request for msgid[%s]', msgid)
                        yield self.redisClient.delete("dlr:%s" % msgid)
                else:
                    self.log.debug(
                        'Terminal level receipt is requested, will not send any DLR receipt at this level.')

                if dlr_level in [2, 3] and r.response.status == CommandStatus.ESME_ROK:
                    # Map received submit_sm_resp's message_id to the msg for later receipt handling
                    self.log.debug('Mapping smpp msgid: %s to queue msgid: %s, expiring in %s',
                                   r.response.params['message_id'], msgid, dlr_expiry)
                    hashKey = "queue-msgid:%s" % r.response.params['message_id'].upper().lstrip('0')
                    hashValues = {'msgid': msgid, 'connector_type': 'httpapi',}
                    yield self.redisClient.setex(hashKey,
                                                 dlr_expiry,
                                                 pickle.dumps(hashValues, self.pickleProtocol))
            elif pickledSmppsMap is not None:
                self.log.debug('There is a SMPPs mapping for msgid[%s] ...', msgid)

                smpps_map = pickle.loads(pickledSmppsMap)
                system_id = smpps_map['system_id']
                source_addr = smpps_map['source_addr']
                destination_addr = smpps_map['destination_addr']
                sub_date = smpps_map['sub_date']
                registered_delivery = smpps_map['registered_delivery']
                smpps_map_expiry = smpps_map['expiry']

                # Do we need to forward the receipt to the original sender ?
                if ((r.response.status == CommandStatus.ESME_ROK and
                        str(registered_delivery.receipt) in ['SMSC_DELIVERY_RECEIPT_REQUESTED_FOR_FAILURE',
                                                             'SMSC_DELIVERY_RECEIPT_REQUESTED']) or
                        (r.response.status != CommandStatus.ESME_ROK and
                        str(registered_delivery.receipt) == 'SMSC_DELIVERY_RECEIPT_REQUESTED_FOR_FAILURE')):
                    self.log.debug('Got DLR information for msgid[%s], registered_deliver%s, system_id:%s',
                                   msgid, registered_delivery, system_id)

                    if (r.response.status != CommandStatus.ESME_ROK
                            or (r.response.status == CommandStatus.ESME_ROK
                            and self.config.smpp_receipt_on_success_submit_sm_resp)):
                        # Send back a receipt (by throwing deliver_sm or data_sm)
                        content = DLRContentForSmpps(str(r.response.status),
                                                     msgid,
                                                     system_id,
                                                     source_addr,
                                                     destination_addr,
                                                     sub_date)

                        routing_key = 'dlr_thrower.smpps'
                        self.log.debug("Publishing DLRContentForSmpps[%s] with routing_key[%s]",
                                       msgid, routing_key)
                        yield self.amqpBroker.publish(exchange='messaging',
                                                      routing_key=routing_key,
                                                      content=content)

                    if r.response.status == CommandStatus.ESME_ROK:
                        # Map received submit_sm_resp's message_id to the msg for later rceipt handling
                        self.log.debug('Mapping smpp msgid: %s to queue msgid: %s, expiring in %s',
                                       r.response.params['message_id'], msgid, smpps_map_expiry)
                        hashKey = "queue-msgid:%s" % r.response.params['message_id'].upper().lstrip('0')
                        hashValues = {'msgid': msgid, 'connector_type': 'smpps',}
                        yield self.redisClient.setex(hashKey,
                                                     smpps_map_expiry,
                                                     pickle.dumps(hashValues, self.pickleProtocol))
        else:
            self.log.warn('No valid RC were found while checking msg[%s] !', msgid)

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

        if will_be_retried:
            defer.returnValue(False)
Esempio n. 5
0
    def test_normal_pickling(self):
        c = SubmitSmRespContent(self.body, 1)

        self.assertNotEquals(c.body, self.body)
        self.assertEquals(c.body, pickle.dumps(self.body, 2))
        self.assertEquals(c['message-id'], 1)
Esempio n. 6
0
    def test_normal_nopickling(self):
        c = SubmitSmRespContent(self.body, 1, prePickle=False)

        self.assertEquals(c.body, self.body)
        self.assertEquals(c['message-id'], 1)