Пример #1
0
    def dataReceived(self, data):
        self._buffer += data

        while self._buffer:
            header = self.peak_buffer(self.HEADER_SIZE)

            if not header:
                return

            session_id, length = self.deserialize_header(header)
            packet = self.pop_buffer(length)

            if not packet:
                return

            body = packet[self.HEADER_SIZE:]

            try:
                packet_type, params = self.deserialize_body(body)
            except Exception, e:
                log.err("Error parsing packet (%s): %r" % (e, packet))
                self.disconnect()
                return

            self.packet_received(session_id, packet_type, params)
Пример #2
0
 def _make_call(self, params):
     params.setdefault('format', 'json')
     url = '%s?%s' % (self.url, urlencode(params))
     if isinstance(url, unicode):
         url = url.encode('utf-8')
     headers = {'User-Agent': self.user_agent}
     if self.gzip:
         headers['Accept-Encoding'] = 'gzip'
     if self.PRINT_DEBUG:
         print "\n=====\n\n%s /?%s\n" % ('GET', url.split('?', 1)[1])
     response = yield http_request_full(url,
                                        '',
                                        headers,
                                        method='GET',
                                        timeout=self.api_timeout,
                                        agent_class=redirect_agent_builder)
     if self.PRINT_DEBUG:
         print response.delivered_body
         print "\n====="
     try:
         returnValue(json.loads(response.delivered_body))
     except Exception, e:
         log.msg("Error reading API response: %s %r" %
                 (response.code, response.delivered_body))
         log.err()
         raise APIError(e)
Пример #3
0
    def reconcile_outbound_cache(self, batch_id, start_timestamp):
        """
        Rebuild the outbound message cache.
        """
        key_manager = ReconKeyManager(
            start_timestamp, self.cache.TRUNCATE_MESSAGE_KEY_COUNT_AT)
        key_count = 0
        status_counts = defaultdict(int)

        index_page = yield self.batch_outbound_keys_with_addresses(batch_id)
        while index_page is not None:
            for key, timestamp, addr in index_page:
                yield self.cache.add_to_addr(batch_id, addr)
                old_key = key_manager.add_key(key, timestamp)
                if old_key is not None:
                    key_count += 1
                    sc = yield self.get_event_counts(old_key[0])
                    for status, count in sc.iteritems():
                        status_counts[status] += count
            index_page = yield index_page.next_page()

        yield self.cache.add_outbound_message_count(batch_id, key_count)
        for status, count in status_counts.iteritems():
            yield self.cache.add_event_count(batch_id, status, count)
        for key, timestamp in key_manager:
            try:
                yield self.cache.add_outbound_message_key(
                    batch_id, key, self.cache.get_timestamp(timestamp))
                yield self.reconcile_event_cache(batch_id, key)
            except:
                log.err()
Пример #4
0
 def get_jsbox_js_config(self, conv):
     try:
         return jsbox_js_config(conv.config)
     except Exception:
         log.err("Bad jsbox js config: %s" %
                 (jsbox_config_value(conv.config, 'config'), ))
         return
Пример #5
0
    def reconcile_outbound_cache(self, batch_id, start_timestamp):
        """
        Rebuild the outbound message cache.
        """
        key_manager = ReconKeyManager(start_timestamp,
                                      self.cache.TRUNCATE_MESSAGE_KEY_COUNT_AT)
        key_count = 0
        status_counts = defaultdict(int)

        index_page = yield self.batch_outbound_keys_with_addresses(batch_id)
        while index_page is not None:
            for key, timestamp, addr in index_page:
                yield self.cache.add_to_addr(batch_id, addr)
                old_key = key_manager.add_key(key, timestamp)
                if old_key is not None:
                    key_count += 1
                    sc = yield self.get_event_counts(old_key[0])
                    for status, count in sc.iteritems():
                        status_counts[status] += count
            index_page = yield index_page.next_page()

        yield self.cache.add_outbound_message_count(batch_id, key_count)
        for status, count in status_counts.iteritems():
            yield self.cache.add_event_count(batch_id, status, count)
        for key, timestamp in key_manager:
            try:
                yield self.cache.add_outbound_message_key(
                    batch_id, key, self.cache.get_timestamp(timestamp))
                yield self.reconcile_event_cache(batch_id, key)
            except:
                log.err()
Пример #6
0
 def handle_raw_inbound_event(self, request):
     try:
         data = json.loads(request.content.read())
         msg = TransportEvent(_process_fields=True, **to_kwargs(data))
         yield self.handle_inbound_event(msg)
         request.finish()
         if msg.payload["event_type"] == "ack":
             self.update_status(status='ok',
                                component='sent-by-vumi-go',
                                type='vumi_go_sent',
                                message='Sent by Vumi Go')
         elif msg.payload["event_type"] == "nack":
             self.update_status(status='down',
                                component='sent-by-vumi-go',
                                type='vumi_go_failed',
                                message='Vumi Go failed to send')
         self.update_status(status='ok',
                            component='vumi-go-event',
                            type='good_request',
                            message='Good event received from Vumi Go')
     except Exception as e:
         log.err(e)
         request.setResponseCode(400)
         request.finish()
         self.update_status(status='down',
                            component='vumi-go-event',
                            type='bad_request',
                            message='Bad event received from Vumi Go')
Пример #7
0
    def submit_sm(self, **kwargs):
        if self.state not in ['BOUND_TX', 'BOUND_TRX']:
            log.err(('WARNING: submit_sm in wrong state: %s, '
                     'dropping message: %s' % (self.state, kwargs)))
            returnValue(0)

        pdu_params = self.bind_params.copy()
        pdu_params.update(kwargs)
        message = pdu_params['short_message']

        # We use GSM_MAX_SMS_BYTES here because we may have already-encoded
        # UCS-2 data to send and therefore can't use the 160 (7-bit) character
        # limit everyone knows and loves. If we have some other encoding
        # instead, this may result in unnecessarily short message parts. The
        # SMSC is probably going to treat whatever we send it as whatever
        # encoding it likes best and then encode (or mangle) it into a form it
        # thinks should be in the GSM message payload. Basically, when we have
        # to split messages up ourselves here we've already lost and the best
        # we can hope for is not getting hurt too badly by the inevitable
        # breakages.
        if len(message) > GSM_MAX_SMS_BYTES:
            if self.config.send_multipart_sar:
                sequence_numbers = yield self._submit_multipart_sar(
                    **pdu_params)
                returnValue(sequence_numbers)
            elif self.config.send_multipart_udh:
                sequence_numbers = yield self._submit_multipart_udh(
                    **pdu_params)
                returnValue(sequence_numbers)

        sequence_number = yield self._submit_sm(**pdu_params)
        returnValue([sequence_number])
Пример #8
0
    def submit_sm(self, **kwargs):
        if self.state not in ['BOUND_TX', 'BOUND_TRX']:
            log.err(('WARNING: submit_sm in wrong state: %s, '
                     'dropping message: %s' % (self.state, kwargs)))
            returnValue(0)

        pdu_params = self.bind_params.copy()
        pdu_params.update(kwargs)
        message = pdu_params['short_message']

        # We use GSM_MAX_SMS_BYTES here because we may have already-encoded
        # UCS-2 data to send and therefore can't use the 160 (7-bit) character
        # limit everyone knows and loves. If we have some other encoding
        # instead, this may result in unnecessarily short message parts. The
        # SMSC is probably going to treat whatever we send it as whatever
        # encoding it likes best and then encode (or mangle) it into a form it
        # thinks should be in the GSM message payload. Basically, when we have
        # to split messages up ourselves here we've already lost and the best
        # we can hope for is not getting hurt too badly by the inevitable
        # breakages.
        if len(message) > GSM_MAX_SMS_BYTES:
            if self.config.send_multipart_sar:
                sequence_numbers = yield self._submit_multipart_sar(
                    **pdu_params)
                returnValue(sequence_numbers)
            elif self.config.send_multipart_udh:
                sequence_numbers = yield self._submit_multipart_udh(
                    **pdu_params)
                returnValue(sequence_numbers)

        sequence_number = yield self._submit_sm(**pdu_params)
        returnValue([sequence_number])
Пример #9
0
    def handle_data_sm(self, pdu):
        if self.state not in ['BOUND_RX', 'BOUND_TRX']:
            log.err('WARNING: Received deliver_sm in wrong state: %s' %
                    (self.state))
            return

        if pdu['header']['command_status'] != 'ESME_ROK':
            return

        # TODO: Only ACK messages once we've processed them?
        sequence_number = pdu['header']['sequence_number']
        pdu_resp = DataSMResp(sequence_number, **self.defaults)
        yield self.send_pdu(pdu_resp)

        pdu_params = pdu['body']['mandatory_parameters']
        pdu_opts = unpacked_pdu_opts(pdu)

        # We might have a `message_payload` optional field to worry about.
        message_payload = pdu_opts.get('message_payload', None)
        if message_payload is not None:
            pdu_params['short_message'] = message_payload.decode('hex')

        if 'short_message' not in pdu_params:
            pdu_params['short_message'] = ''

        esm_class = pdu['body']['mandatory_parameters']['esm_class']
        if not esm_class is 0:
            log.err('WARNING: Received data_sm with esm_class: %s' %
                    (esm_class))
            return

        yield self._handle_deliver_sm_sms(pdu_params)
Пример #10
0
    def get_shortened_url(self, msg, config, url):
        if isinstance(url, unicode):
            url = url.encode('utf-8')

        user_token = self.hash_user(msg.user())

        headers = {
            'User-Agent': 'vumi-wikipedia-http-request',
            'content-type': 'application/json'
        }

        shortening_api_url = config.shortening_api_url.geturl()
        auth_header, clean_api_url = self.get_basic_auth_header(
            shortening_api_url
        )
        if auth_header:
            headers.update(auth_header)

        payload = {'long_url': url, 'user_token': user_token}
        api_url = urljoin(clean_api_url, 'create')
        response = yield http_request_full(
            api_url, json.dumps(payload), headers, method='PUT')
        try:
            result = json.loads(response.delivered_body)
            returnValue(result['short_url'])
        except Exception, e:
            log.msg("Error reading API response: %s %r" % (
                    response.code, response.delivered_body))
            log.err()
            raise APIError(e)
Пример #11
0
 def handle_raw_inbound_event(self, request):
     try:
         data = json.loads(request.content.read())
         msg = TransportEvent(_process_fields=True, **to_kwargs(data))
         yield self.handle_inbound_event(msg)
         request.finish()
         if msg.payload["event_type"] == "ack":
             self.update_status(
                 status='ok', component='sent-by-vumi-go',
                 type='vumi_go_sent', message='Sent by Vumi Go')
         elif msg.payload["event_type"] == "nack":
             self.update_status(
                 status='down', component='sent-by-vumi-go',
                 type='vumi_go_failed', message='Vumi Go failed to send')
         self.update_status(
             status='ok', component='vumi-go-event',
             type='good_request',
             message='Good event received from Vumi Go')
     except Exception as e:
         log.err(e)
         request.setResponseCode(400)
         request.finish()
         self.update_status(
             status='down', component='vumi-go-event',
             type='bad_request', message='Bad event received from Vumi Go')
Пример #12
0
    def handle_raw_inbound_message(self, message_id, request):
        values, errors = self.get_field_values(request, self.EXPECTED_FIELDS)
        if errors:
            log.err('Unhappy incoming message: %s' % (errors, ))
            yield self.finish_request(message_id, json.dumps(errors), code=400)
            return
        self.emit(('SafaricomTransport sending from %s to %s '
                   'for %s message "%s" (%s still pending)') % (
                       values['ORIG'],
                       values['DEST'],
                       values['SESSION_ID'],
                       values['USSD_PARAMS'],
                       len(self._requests),
                   ))
        session_id = values['SESSION_ID']
        from_addr = values['ORIG']
        dest = values['DEST']
        ussd_params = values['USSD_PARAMS']

        session = yield self.session_manager.load_session(session_id)
        if session:
            to_addr = session['to_addr']
            last_ussd_params = session['last_ussd_params']
            new_params = ussd_params[len(last_ussd_params):]
            if new_params:
                if last_ussd_params:
                    content = new_params[1:]
                else:
                    content = new_params
            else:
                content = ''

            session['last_ussd_params'] = ussd_params
            yield self.session_manager.save_session(session_id, session)
            session_event = TransportUserMessage.SESSION_RESUME
        else:
            if ussd_params:
                to_addr = '*%s*%s#' % (dest, ussd_params)
            else:
                to_addr = '*%s#' % (dest, )
            yield self.session_manager.create_session(
                session_id,
                from_addr=from_addr,
                to_addr=to_addr,
                last_ussd_params=ussd_params)
            session_event = TransportUserMessage.SESSION_NEW
            content = ''

        yield self.publish_message(
            message_id=message_id,
            content=content,
            to_addr=to_addr,
            from_addr=from_addr,
            provider='safaricom',
            session_event=session_event,
            transport_type=self.transport_type,
            transport_metadata={'safaricom': {
                'session_id': session_id,
            }})
Пример #13
0
 def get_jsbox_js_config(self, conv):
     try:
         return jsbox_js_config(conv.config)
     except Exception:
         log.err(
             "Bad jsbox js config: %s"
             % (jsbox_config_value(conv.config, 'config'),))
         return
Пример #14
0
 def reconcile_inbound_cache(self, batch_id):
     inbound_keys = yield self.batch_inbound_keys(batch_id)
     for key in inbound_keys:
         try:
             msg = yield self.get_inbound_message(key)
             yield self.cache.add_inbound_message(batch_id, msg)
         except Exception:
             log.err()
Пример #15
0
 def reconcile_inbound_cache(self, batch_id):
     inbound_keys = yield self.batch_inbound_keys(batch_id)
     for key in inbound_keys:
         try:
             msg = yield self.get_inbound_message(key)
             yield self.cache.add_inbound_message(batch_id, msg)
         except Exception:
             log.err()
Пример #16
0
 def reconcile_outbound_cache(self, batch_id):
     outbound_keys = yield self.batch_outbound_keys(batch_id)
     for key in outbound_keys:
         try:
             msg = yield self.get_outbound_message(key)
             yield self.cache.add_outbound_message(batch_id, msg)
             yield self.reconcile_event_cache(batch_id, key)
         except Exception:
             log.err()
Пример #17
0
    def handle_raw_inbound_message(self, message_id, request):
        values, errors = self.get_field_values(request, self.EXPECTED_FIELDS)
        if errors:
            log.err('Unhappy incoming message: %s' % (errors,))
            yield self.finish_request(message_id, json.dumps(errors), code=400)
            return
        self.emit(('SafaricomTransport sending from %s to %s '
                    'for %s message "%s" (%s still pending)') % (
                        values['ORIG'], values['DEST'], values['SESSION_ID'],
                        values['USSD_PARAMS'], len(self._requests),
                    ))
        session_id = values['SESSION_ID']
        from_addr = values['ORIG']
        dest = values['DEST']
        ussd_params = values['USSD_PARAMS']

        session = yield self.session_manager.load_session(session_id)
        if session:
            to_addr = session['to_addr']
            last_ussd_params = session['last_ussd_params']
            new_params = ussd_params[len(last_ussd_params):]
            if new_params:
                if last_ussd_params:
                    content = new_params[1:]
                else:
                    content = new_params
            else:
                content = ''

            session['last_ussd_params'] = ussd_params
            yield self.session_manager.save_session(session_id, session)
            session_event = TransportUserMessage.SESSION_RESUME
        else:
            if ussd_params:
                to_addr = '*%s*%s#' % (dest, ussd_params)
            else:
                to_addr = '*%s#' % (dest,)
            yield self.session_manager.create_session(session_id,
                from_addr=from_addr, to_addr=to_addr,
                last_ussd_params=ussd_params)
            session_event = TransportUserMessage.SESSION_NEW
            content = ''

        yield self.publish_message(
            message_id=message_id,
            content=content,
            to_addr=to_addr,
            from_addr=from_addr,
            provider='safaricom',
            session_event=session_event,
            transport_type=self.transport_type,
            transport_metadata={
                'safaricom': {
                    'session_id': session_id,
                }
            }
        )
Пример #18
0
 def submit_sm_throttled(self, sent_sms_id):
     message = yield self.r_get_message(sent_sms_id)
     if message is None:
         log.err("Could not retrieve throttled message:%s" % (
             sent_sms_id))
     else:
         config = self.get_static_config()
         self.callLater(config.throttle_delay,
                        self._submit_outbound_message, message)
Пример #19
0
 def fail_request(self, request, f):
     if f.check(BadRequestError):
         code = http.BAD_REQUEST
     else:
         code = http.INTERNAL_SERVER_ERROR
     log.err(f)
     request.setResponseCode(code)
     request.write(f.getErrorMessage())
     request.finish()
Пример #20
0
 def reconcile_outbound_cache(self, batch_id):
     outbound_keys = yield self.batch_outbound_keys(batch_id)
     for key in outbound_keys:
         try:
             msg = yield self.get_outbound_message(key)
             yield self.cache.add_outbound_message(batch_id, msg)
             yield self.reconcile_event_cache(batch_id, key)
         except Exception:
             log.err()
Пример #21
0
    def handle_login_error_response(self, session_id, params):
        try:
            self.validate_packet_fields(
                params, self.LOGIN_ERROR_FIELDS, self.OTHER_LOGIN_ERROR_FIELDS)
        except CodedXmlOverTcpError as e:
            self.handle_error(session_id, params.get('requestId'), e)
            return

        log.err("Login failed, disconnecting")
        self.disconnect()
Пример #22
0
 def handle_raw_inbound_message(self, request):
     try:
         data = json.loads(request.content.read())
         msg = TransportUserMessage(_process_fields=True, **to_kwargs(data))
         yield self.handle_inbound_message(msg)
         request.finish()
     except Exception as e:
         log.err(e)
         request.setResponseCode(400)
         request.finish()
Пример #23
0
 def handle_raw_inbound_message(self, request):
     try:
         data = json.loads(request.content.read())
         msg = TransportUserMessage(_process_fields=True, **to_kwargs(data))
         yield self.handle_inbound_message(msg)
         request.finish()
     except Exception as e:
         log.err(e)
         request.setResponseCode(400)
         request.finish()
Пример #24
0
    def handle_error_response(self, session_id, params):
        try:
            self.validate_packet_fields(
                params, self.ERROR_FIELDS, self.OTHER_ERROR_FIELDS)
        except CodedXmlOverTcpError as e:
            self.handle_error(session_id, params.get('requestId'), e)
            return

        log.err(
            "Server sent error message: %s" %
            CodedXmlOverTcpError(params['errorCode'], params.get('errorMsg')))
Пример #25
0
 def submit_sm_failure(self, sent_sms_id, reason, failure_code=None):
     error_message = yield self.r_get_message(sent_sms_id)
     if error_message is None:
         log.err("Could not retrieve failed message:%s" % (
             sent_sms_id))
     else:
         yield self.r_delete_message(sent_sms_id)
         yield self.publish_nack(sent_sms_id, reason)
         yield self.failure_publisher.publish_message(FailureMessage(
                 message=error_message.payload,
                 failure_code=None,
                 reason=reason))
Пример #26
0
    def handle_inbound(self, config, msg, conn_name):
        log.msg("Handling inbound: %s" % (msg, ))

        try:
            contact = yield self.get_contact_for_message(msg, create=False)
        except ContactNotFoundError:
            contact = None
        except ContactError:
            log.err()
            return

        endpoint = self.endpoint_for_contact(config, contact)
        yield self.publish_inbound(msg, endpoint)
Пример #27
0
    def handle_inbound(self, config, msg, conn_name):
        log.msg("Handling inbound: %s" % (msg,))

        try:
            contact = yield self.get_contact_for_message(msg, create=False)
        except ContactNotFoundError:
            contact = None
        except ContactError:
            log.err()
            return

        endpoint = self.endpoint_for_contact(config, contact)
        yield self.publish_inbound(msg, endpoint)
Пример #28
0
    def handle_deliver_sm(self, pdu):
        if self.state not in ['BOUND_RX', 'BOUND_TRX']:
            log.err('WARNING: Received deliver_sm in wrong state: %s' %
                    (self.state))
            return

        if pdu['header']['command_status'] != 'ESME_ROK':
            return

        # TODO: Only ACK messages once we've processed them?
        sequence_number = pdu['header']['sequence_number']
        pdu_resp = DeliverSMResp(sequence_number, **self.bind_params)
        yield self.send_pdu(pdu_resp)

        pdu_params = pdu['body']['mandatory_parameters']
        pdu_opts = unpacked_pdu_opts(pdu)

        # This might be a delivery receipt with PDU parameters. If we get a
        # delivery receipt without these parameters we'll try a regex match
        # later once we've decoded the message properly.
        receipted_message_id = pdu_opts.get('receipted_message_id', None)
        message_state = pdu_opts.get('message_state', None)
        if receipted_message_id is not None and message_state is not None:
            yield self.esme_callbacks.delivery_report(
                message_id=receipted_message_id,
                message_state={
                    1: 'ENROUTE',
                    2: 'DELIVERED',
                    3: 'EXPIRED',
                    4: 'DELETED',
                    5: 'UNDELIVERABLE',
                    6: 'ACCEPTED',
                    7: 'UNKNOWN',
                    8: 'REJECTED',
                }.get(message_state, 'UNKNOWN'),
            )

        # We might have a `message_payload` optional field to worry about.
        message_payload = pdu_opts.get('message_payload', None)
        if message_payload is not None:
            pdu_params['short_message'] = message_payload.decode('hex')

        if detect_ussd(pdu_opts):
            # We have a USSD message.
            yield self._handle_deliver_sm_ussd(pdu, pdu_params, pdu_opts)
        elif detect_multipart(pdu):
            # We have a multipart SMS.
            yield self._handle_deliver_sm_multipart(pdu, pdu_params)
        else:
            # We have a standard SMS.
            yield self._handle_deliver_sm_sms(pdu_params)
Пример #29
0
    def handle_deliver_sm(self, pdu):
        if self.state not in ['BOUND_RX', 'BOUND_TRX']:
            log.err('WARNING: Received deliver_sm in wrong state: %s' % (
                self.state))
            return

        if pdu['header']['command_status'] != 'ESME_ROK':
            return

        # TODO: Only ACK messages once we've processed them?
        sequence_number = pdu['header']['sequence_number']
        pdu_resp = DeliverSMResp(sequence_number, **self.bind_params)
        yield self.send_pdu(pdu_resp)

        pdu_params = pdu['body']['mandatory_parameters']
        pdu_opts = unpacked_pdu_opts(pdu)

        # This might be a delivery receipt with PDU parameters. If we get a
        # delivery receipt without these parameters we'll try a regex match
        # later once we've decoded the message properly.
        receipted_message_id = pdu_opts.get('receipted_message_id', None)
        message_state = pdu_opts.get('message_state', None)
        if receipted_message_id is not None and message_state is not None:
            yield self.esme_callbacks.delivery_report(
                message_id=receipted_message_id,
                message_state={
                    1: 'ENROUTE',
                    2: 'DELIVERED',
                    3: 'EXPIRED',
                    4: 'DELETED',
                    5: 'UNDELIVERABLE',
                    6: 'ACCEPTED',
                    7: 'UNKNOWN',
                    8: 'REJECTED',
                }.get(message_state, 'UNKNOWN'),
            )

        # We might have a `message_payload` optional field to worry about.
        message_payload = pdu_opts.get('message_payload', None)
        if message_payload is not None:
            pdu_params['short_message'] = message_payload.decode('hex')

        if detect_ussd(pdu_opts):
            # We have a USSD message.
            yield self._handle_deliver_sm_ussd(pdu, pdu_params, pdu_opts)
        elif detect_multipart(pdu):
            # We have a multipart SMS.
            yield self._handle_deliver_sm_multipart(pdu, pdu_params)
        else:
            # We have a standard SMS.
            yield self._handle_deliver_sm_sms(pdu_params)
Пример #30
0
    def process_outbound(self, config, msg, connector_name):
        """Process an outbound message.

        Any errors are logged and the message is allowed to continue on its
        path and fulfill its destiny.
        """
        log.debug("Processing outbound: %r" % (msg,))
        msg_mdh = self.get_metadata_helper(msg)
        try:
            yield self.create_transaction_for_outbound(msg)
            msg_mdh.set_paid()
        except BillingError:
            log.err()
        yield self.publish_outbound(msg, self.receive_inbound_connector, None)
Пример #31
0
    def dcs_decode(self, obj, data_coding):
        codec_name = self.data_coding_map.get(data_coding, None)
        if codec_name is None:
            log.msg("WARNING: Not decoding message with data_coding=%s" % (data_coding,))
            return obj
        elif obj is None:
            log.msg("WARNING: Not decoding `None` message with data_coding=%s" % (data_coding,))
            return obj

        try:
            return self.codec.decode(obj, codec_name)
        except UnicodeDecodeError, e:
            log.msg("Error decoding message with data_coding=%s" % (data_coding,))
            log.err(e)
Пример #32
0
 def _start_looping_task(self):
     self._task = LoopingCall(self._beat)
     done = self._task.start(HeartBeatPublisher.HEARTBEAT_PERIOD_SECS,
                             now=False)
     done.addErrback(
         lambda failure: log.err(failure,
                                 "HeartBeatPublisher task died"))
Пример #33
0
 def _start_task(self):
     """Create a timer task to check for missing worker"""
     self._task = LoopingCall(self._periodic_task)
     self._task_done = self._task.start(self.deadline, now=False)
     errfn = lambda failure: log.err(failure,
                                     "Heartbeat verify: timer task died")
     self._task_done.addErrback(errfn)
Пример #34
0
    def consume_user_message(self, msg):
        # log.msg("Received: %s" % (msg.payload,))
        config = yield self.get_config(msg)
        user_id = msg.user()
        session_event = self._message_session_event(msg)
        session_manager = self.get_session_manager(config)
        session = yield self.load_session(session_manager, user_id)

        if session_event == 'close':
            if ((not config.send_sms_content)
                    or (session and session['state'] != 'more')):
                # Session closed, so clean up and don't reply.
                yield session_manager.clear_session(user_id)
            # We never want to respond to close messages, even if we keep the
            # session alive for the "more" handling.
            return

        if session and ('state' not in session):
            # We've seen at least one session that wasn't empty but didn't have
            # the 'state' field. Let's log this and treat it as an empty
            # session instead.
            log.warning("Bad session, resetting: %s" % (session,))
            session = {}

        if (not session) or (session['state'] == 'more'):
            # If we have no session data, treat this as 'new' even if it isn't.
            # Also, new USSD search overrides old "more content" session.
            session_event = 'new'

        if session_event == 'new':
            session = yield session_manager.create_session(user_id)
            session['state'] = 'new'

        pfunc = getattr(self, 'process_message_%s' % (session['state'],))
        try:
            session = yield pfunc(msg, config, session)
            yield self.handle_session_result(session_manager, user_id, session)
        except Exception as err:
            # Uncomment to raise instead of logging (useful for tests)
            # raise
            if isinstance(err, APIError):
                log.warning("API Error: %s" % (err,))
            else:
                log.err()
            self.fire_metric(config, 'ussd_session_error')
            self.reply_to(msg, config.msg_error, False)
            yield session_manager.clear_session(user_id)
Пример #35
0
 def handle_raw_inbound_message(self, request):
     try:
         data = json.loads(request.content.read())
         msg = TransportUserMessage(
             _process_fields=True, **to_kwargs(data))
         yield self.handle_inbound_message(msg)
         request.finish()
         self.update_status(
             status='ok', component='received-from-vumi-go',
             type='good_request', message='Good request received')
     except Exception as e:
         log.err(e)
         request.setResponseCode(400)
         request.finish()
         self.update_status(
             status='down', component='received-from-vumi-go',
             type='bad_request', message='Bad request received')
Пример #36
0
    def consume_content_sms_message(self, msg):
        # log.msg("Received SMS: %s" % (msg.payload,))
        config = yield self.get_config(msg)

        # This is to exclude some spurious messages we might receive.
        if msg['content'] is None:
            self.log_action(msg, 'more-no-content')
            return

        user_id = msg.user()

        session_manager = self.get_session_manager(config)

        session = yield self.load_session(session_manager, user_id)
        self.fire_metric(config, 'sms_more_content_reply')
        if not session:
            self.log_action(msg, 'more-no-session')
            # TODO: Reply with error?
            self.fire_metric(config, 'sms_more_content_reply.no_content')
            return

        if session['state'] != 'more':
            self.log_action(msg, 'more-wrong-session-state')
            return

        # FIXME: This is a stopgap until we can figure out why wy sometimes get
        #        strings instead of integers here.
        raw_more_messages = session.get('more_messages', 0)
        if int(raw_more_messages) != raw_more_messages:
            log.warning("Found non-integer 'more_messages': %r" % (
                raw_more_messages,))
            raw_more_messages = int(raw_more_messages)
        more_messages = raw_more_messages + 1
        session['more_messages'] = more_messages
        if more_messages > 9:
            more_messages = 'extra'
        self.fire_metric(config, 'sms_more_content_reply', more_messages)

        try:
            session = yield self.send_sms_content(msg, config, session)
            yield self.handle_session_result(session_manager, user_id, session)
        except:
            log.err()
            # TODO: Reply with error?
            yield session_manager.clear_session(user_id)
Пример #37
0
 def _call_rapidsms(self, message):
     config = yield self.get_config(message)
     http_method = config.rapidsms_http_method.encode("utf-8")
     headers = self.get_auth_headers(config)
     yield self._store_message(message, config.vumi_reply_timeout)
     response = http_request_full(config.rapidsms_url.geturl(), message.to_json(), headers, http_method)
     response.addCallback(lambda response: log.info(response.code))
     response.addErrback(lambda failure: log.err(failure))
     yield response
Пример #38
0
    def dcs_decode(self, obj, data_coding):
        codec_name = self.data_coding_map.get(data_coding, None)
        if codec_name is None:
            log.msg("WARNING: Not decoding message with data_coding=%s" %
                    (data_coding, ))
            return obj
        elif obj is None:
            log.msg(
                "WARNING: Not decoding `None` message with data_coding=%s" %
                (data_coding, ))
            return obj

        try:
            return self.codec.decode(obj, codec_name)
        except UnicodeDecodeError, e:
            log.msg("Error decoding message with data_coding=%s" %
                    (data_coding, ))
            log.err(e)
Пример #39
0
 def send_failure(self, message, exception, traceback):
     """Send a failure report."""
     try:
         failure_code = getattr(exception, "failure_code",
                                FailureMessage.FC_UNSPECIFIED)
         failure_msg = FailureMessage(
             message=message.payload, failure_code=failure_code,
             reason=traceback)
         connector = self.connectors[self.transport_name]
         d = connector._middlewares.apply_publish(
             "failure", failure_msg, self.transport_name)
         d.addCallback(self.failure_publisher.publish_message)
         d.addCallback(lambda _f: self.failure_published())
     except:
         log.err("Error publishing failure: %s, %s, %s"
                 % (message, exception, traceback))
         raise
     return d
Пример #40
0
 def send_failure(self, message, exception, traceback):
     """Send a failure report."""
     try:
         failure_code = getattr(exception, "failure_code",
                                FailureMessage.FC_UNSPECIFIED)
         failure_msg = FailureMessage(message=message.payload,
                                      failure_code=failure_code,
                                      reason=traceback)
         connector = self.connectors[self.transport_name]
         d = connector._middlewares.apply_publish("failure", failure_msg,
                                                  self.transport_name)
         d.addCallback(self.failure_publisher.publish_message)
         d.addCallback(lambda _f: self.failure_published())
     except:
         log.err("Error publishing failure: %s, %s, %s" %
                 (message, exception, traceback))
         raise
     return d
Пример #41
0
 def handle_raw_inbound_message(self, request):
     try:
         data = json.loads(request.content.read())
         msg = TransportUserMessage(_process_fields=True, **to_kwargs(data))
         yield self.handle_inbound_message(msg)
         request.finish()
         self.update_status(status='ok',
                            component='received-from-vumi-go',
                            type='good_request',
                            message='Good request received')
     except Exception as e:
         log.err(e)
         request.setResponseCode(400)
         request.finish()
         self.update_status(status='down',
                            component='received-from-vumi-go',
                            type='bad_request',
                            message='Bad request received')
Пример #42
0
 def _call_rapidsms(self, message):
     config = yield self.get_config(message)
     http_method = config.rapidsms_http_method.encode("utf-8")
     headers = self.get_auth_headers(config)
     yield self._store_message(message, config.vumi_reply_timeout)
     response = http_request_full(config.rapidsms_url.geturl(),
                                  message.to_json(), headers, http_method)
     response.addCallback(lambda response: log.info(response.code))
     response.addErrback(lambda failure: log.err(failure))
     yield response
Пример #43
0
def decode_message(message, data_coding, data_coding_overrides):
    """
    Messages can arrive with one of a number of specified
    encodings. We only handle a subset of these.

    From the SMPP spec:

    00000000 (0) SMSC Default Alphabet
    00000001 (1) IA5(CCITTT.50)/ASCII(ANSIX3.4)
    00000010 (2) Octet unspecified (8-bit binary)
    00000011 (3) Latin1(ISO-8859-1)
    00000100 (4) Octet unspecified (8-bit binary)
    00000101 (5) JIS(X0208-1990)
    00000110 (6) Cyrllic(ISO-8859-5)
    00000111 (7) Latin/Hebrew (ISO-8859-8)
    00001000 (8) UCS2(ISO/IEC-10646)
    00001001 (9) PictogramEncoding
    00001010 (10) ISO-2022-JP(MusicCodes)
    00001011 (11) reserved
    00001100 (12) reserved
    00001101 (13) Extended Kanji JIS(X 0212-1990)
    00001110 (14) KSC5601
    00001111 (15) reserved

    Particularly problematic are the "Octet unspecified" encodings.
    """
    codecs = {
        1: 'ascii',
        3: 'latin1',
        8: 'utf-16be',  # Actually UCS-2, but close enough.
    }
    codecs.update(data_coding_overrides)
    codec = codecs.get(data_coding, None)
    if codec is None or message is None:
        log.msg("WARNING: Not decoding message with data_coding=%s" %
                (data_coding, ))
    else:
        try:
            return message.decode(codec)
        except Exception, e:
            log.msg("Error decoding message with data_coding=%s" %
                    (data_coding, ))
            log.err(e)
Пример #44
0
    def _decode_message(self, message, data_coding):
        """
        Messages can arrive with one of a number of specified
        encodings. We only handle a subset of these.

        From the SMPP spec:

        00000000 (0) SMSC Default Alphabet
        00000001 (1) IA5(CCITTT.50)/ASCII(ANSIX3.4)
        00000010 (2) Octet unspecified (8-bit binary)
        00000011 (3) Latin1(ISO-8859-1)
        00000100 (4) Octet unspecified (8-bit binary)
        00000101 (5) JIS(X0208-1990)
        00000110 (6) Cyrllic(ISO-8859-5)
        00000111 (7) Latin/Hebrew (ISO-8859-8)
        00001000 (8) UCS2(ISO/IEC-10646)
        00001001 (9) PictogramEncoding
        00001010 (10) ISO-2022-JP(MusicCodes)
        00001011 (11) reserved
        00001100 (12) reserved
        00001101 (13) Extended Kanji JIS(X 0212-1990)
        00001110 (14) KSC5601
        00001111 (15) reserved

        Particularly problematic are the "Octet unspecified" encodings.
        """
        codecs = {
            1: 'ascii',
            3: 'latin1',
            8: 'utf-16be',  # Actually UCS-2, but close enough.
            }
        codecs.update(self.config.data_coding_overrides)
        codec = codecs.get(data_coding, None)
        if codec is None or message is None:
            log.msg("WARNING: Not decoding message with data_coding=%s" % (
                    data_coding,))
        else:
            try:
                return message.decode(codec)
            except Exception, e:
                log.msg("Error decoding message with data_coding=%s" % (
                        data_coding,))
                log.err(e)
Пример #45
0
    def packet_received(self, session_id, packet_type, params):
        log.debug("Packet of type '%s' with session id '%s' received: %s"
                  % (packet_type, session_id, params))

        # dispatch the packet to the appropriate handler
        handler_name = self.PACKET_RECEIVED_HANDLERS.get(packet_type, None)
        if handler_name is None:
            log.err("Packet of an unknown type received: %s" % packet_type)
            return self.send_error_response(
                session_id, params.get('requestId'), '208')

        if (not self.authenticated and
                packet_type not in self.IGNORE_AUTH_PACKETS):
            log.err("'%s' packet received before client authentication "
                    "was completed" % packet_type)
            return self.send_error_response(
                session_id, params.get('requestId'), '207')

        getattr(self, handler_name)(session_id, params)
Пример #46
0
    def handle_inbound(self, config, msg, conn_name):
        """
        Main delegation point for handling inbound messages and
        managing the state machine.
        """

        log.msg("Processing inbound message: %s" % (msg,))

        user_id = msg['from_addr']
        session_manager = yield self.session_manager(config)
        session = yield session_manager.load_session(user_id)
        session_event = msg['session_event']
        if not session or session_event == TransportUserMessage.SESSION_NEW:
            log.msg("Creating session for user %s" % user_id)
            session = {}
            state = self.STATE_START
            yield session_manager.create_session(user_id)
        elif session_event == TransportUserMessage.SESSION_CLOSE:
            yield self.handle_session_close(config, session, msg)
            return
        else:
            log.msg("Loading session for user %s: %s" % (user_id, session,))
            state = session['state']

        try:
            next_state, updated_session = yield self.handlers[state](
                config, session, msg)
            if next_state is None:
                # Session terminated (right now, just in the case of a
                # administrator-initiated configuration change
                yield session_manager.clear_session(user_id)
            else:
                session['state'] = next_state
                session.update(updated_session)
                if state != next_state:
                    log.msg("State transition for user %s: %s => %s" %
                            (user_id, state, next_state))
                yield session_manager.save_session(user_id, session)
        except:
            log.err()
            yield session_manager.clear_session(user_id)
            yield self.publish_error_reply(msg, config)
Пример #47
0
 def _make_call(self, params):
     params.setdefault('format', 'json')
     url = '%s?%s' % (self.url, urlencode(params))
     if isinstance(url, unicode):
         url = url.encode('utf-8')
     headers = {'User-Agent': self.user_agent}
     if self.gzip:
         headers['Accept-Encoding'] = 'gzip'
     if self.PRINT_DEBUG:
         print "\n=====\n\n%s /?%s\n" % ('GET', url.split('?', 1)[1])
     response = yield http_request_full(
         url, '', headers, method='GET', timeout=self.api_timeout,
         agent_class=redirect_agent_builder)
     if self.PRINT_DEBUG:
         print response.delivered_body
         print "\n====="
     try:
         returnValue(json.loads(response.delivered_body))
     except Exception, e:
         log.msg("Error reading API response: %s %r" % (
                 response.code, response.delivered_body))
         log.err()
         raise APIError(e)
Пример #48
0
 def submit_sm_resp(self, *args, **kwargs):
     transport_msg_id = kwargs['message_id']
     sent_sms_id = (
         yield self.r_get_id_for_sequence(kwargs['sequence_number']))
     if sent_sms_id is None:
         log.err("Sequence number lookup failed for:%s" % (
             kwargs['sequence_number'],))
     else:
         yield self.r_set_id_for_third_party_id(
             transport_msg_id, sent_sms_id)
         yield self.r_delete_for_sequence(kwargs['sequence_number'])
         status = kwargs['command_status']
         if status == 'ESME_ROK':
             # The sms was submitted ok
             yield self.submit_sm_success(sent_sms_id, transport_msg_id)
             yield self._stop_throttling()
         elif status in ('ESME_RTHROTTLED', 'ESME_RMSGQFUL'):
             yield self._start_throttling()
             yield self.submit_sm_throttled(sent_sms_id)
         else:
             # We have an error
             yield self.submit_sm_failure(sent_sms_id,
                                          status or 'Unspecified')
             yield self._stop_throttling()
Пример #49
0
    def reconcile_inbound_cache(self, batch_id, start_timestamp):
        """
        Rebuild the inbound message cache.
        """
        key_manager = ReconKeyManager(
            start_timestamp, self.cache.TRUNCATE_MESSAGE_KEY_COUNT_AT)
        key_count = 0

        index_page = yield self.batch_inbound_keys_with_addresses(batch_id)
        while index_page is not None:
            for key, timestamp, addr in index_page:
                yield self.cache.add_from_addr(batch_id, addr)
                old_key = key_manager.add_key(key, timestamp)
                if old_key is not None:
                    key_count += 1
            index_page = yield index_page.next_page()

        yield self.cache.add_inbound_message_count(batch_id, key_count)
        for key, timestamp in key_manager:
            try:
                yield self.cache.add_inbound_message_key(
                    batch_id, key, self.cache.get_timestamp(timestamp))
            except:
                log.err()
Пример #50
0
 def clientConnectionFailed(self, connector, reason):
     log.err(reason, 'Connection failed')
     ClientFactory.clientConnectionFailed(self, connector, reason)