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)
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)
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()
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
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()
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')
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])
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)
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)
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')
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, }})
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
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()
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()
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, } } )
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)
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()
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()
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()
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')))
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))
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)
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)
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)
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)
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)
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)
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"))
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)
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)
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')
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)
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
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)
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
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
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')
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)
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)
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)
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)
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)
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()
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()
def clientConnectionFailed(self, connector, reason): log.err(reason, 'Connection failed') ClientFactory.clientConnectionFailed(self, connector, reason)