# # smpplib -- SMPP Library for Python # Copyright (c) 2005 Martynas Jocius # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """SMPP Commands module""" import logging import struct import six from smpplib import consts, exceptions, pdu from smpplib.ptypes import flag, ostr logger = logging.getLogger('smpplib.command') def factory(command_name, **kwargs): """Return instance of a specific command class""" try: return { 'bind_transmitter': BindTransmitter, 'bind_transmitter_resp': BindTransmitterResp, 'bind_receiver': BindReceiver, 'bind_receiver_resp': BindReceiverResp, 'bind_transceiver': BindTransceiver, 'bind_transceiver_resp': BindTransceiverResp, 'data_sm': DataSM, 'data_sm_resp': DataSMResp, 'generic_nack': GenericNAck, 'submit_sm': SubmitSM, 'submit_sm_resp': SubmitSMResp, 'deliver_sm': DeliverSM, 'deliver_sm_resp': DeliverSMResp, 'query_sm': QuerySM, 'query_sm_resp': QuerySMResp, 'unbind': Unbind, 'unbind_resp': UnbindResp, 'enquire_link': EnquireLink, 'enquire_link_resp': EnquireLinkResp, 'alert_notification': AlertNotification, }[command_name](command_name, **kwargs) except KeyError: raise exceptions.UnknownCommandError('Command "%s" is not supported' % command_name) def get_optional_name(code): """Return optional_params name by given code. If code is unknown, raise UnkownCommandError exception""" for key, value in six.iteritems(consts.OPTIONAL_PARAMS): if value == code: return key raise exceptions.UnknownCommandError('Unknown SMPP command code "0x%x"' % code) def get_optional_code(name): """Return optional_params code by given command name. If name is unknown, raise UnknownCommandError exception""" try: return consts.OPTIONAL_PARAMS[name] except KeyError: raise exceptions.UnknownCommandError('Unknown SMPP command name "%s"' % name) def unpack_short(data, pos): return struct.unpack('>H', data[pos:pos+2])[0], pos + 2 class Command(pdu.PDU): """SMPP PDU Command class""" params = {} def __init__(self, command, need_sequence=True, allow_unknown_opt_params=False, **kwargs): super(Command, self).__init__(**kwargs) self.allow_unknown_opt_params = allow_unknown_opt_params self.command = command if need_sequence and (kwargs.get('sequence') is None): self.sequence = self._next_seq() if kwargs.get('status') is None: self.status = consts.SMPP_ESME_ROK self._set_vars(**kwargs) def _set_vars(self, **kwargs): """set attributes accordingly to kwargs""" for key, value in six.iteritems(kwargs): if not hasattr(self, key) or getattr(self, key) is None: setattr(self, key, value) def generate_params(self): """Generate binary data from the object""" if hasattr(self, 'prep') and callable(self.prep): self.prep() body = consts.EMPTY_STRING for field in self.params_order: param = self.params[field] if self.field_is_optional(field): if param.type is int: value = self._generate_int_tlv(field) if value: body += value elif param.type is str: value = self._generate_string_tlv(field) if value: body += value elif param.type is ostr: value = self._generate_ostring_tlv(field) if value: body += value else: if param.type is int: value = self._generate_int(field) body += value elif param.type is str: value = self._generate_string(field) body += value elif param.type is ostr: value = self._generate_ostring(field) if value: body += value return body def _generate_opt_header(self, field): """Generate a header for an optional parameter""" raise NotImplementedError('Vendors not supported') def _generate_int(self, field): """Generate integer value""" fmt = self._int_pack_format(field) data = getattr(self, field) if data: return struct.pack(">" + fmt, data) else: return consts.NULL_STRING def _generate_string(self, field): """Generate string value""" field_value = getattr(self, field) if hasattr(self.params[field], 'size'): size = self.params[field].size value = field_value.ljust(size, chr(0)) elif hasattr(self.params[field], 'max'): if len(field_value or '') >= self.params[field].max: field_value = field_value[0:self.params[field].max - 1] if field_value: value = field_value + chr(0) else: value = chr(0) setattr(self, field, field_value) return six.b(value) def _generate_ostring(self, field): """Generate octet string value (no null terminator)""" value = getattr(self, field) if value: return value else: return None # chr(0) def _generate_int_tlv(self, field): """Generate integer value""" fmt = self._int_pack_format(field) data = getattr(self, field) field_code = get_optional_code(field) field_length = self.params[field].size value = None if data is not None: value = struct.pack(">HH" + fmt, field_code, field_length, data) return value def _generate_string_tlv(self, field): """Generate string value""" field_value = getattr(self, field) field_code = get_optional_code(field) if hasattr(self.params[field], 'size'): size = self.params[field].size fvalue = field_value.ljust(size, chr(0)) value = struct.pack(">HH", field_code, size) + fvalue elif hasattr(self.params[field], 'max'): if len(field_value or '') > self.params[field].max: field_value = field_value[0:self.params[field].max - 1] if field_value: fvalue = field_value + chr(0) field_length = len(fvalue) value = struct.pack(">HH", field_code, field_length) + fvalue.encode() else: value = None # chr(0) return value def _generate_ostring_tlv(self, field): """Generate octet string value (no null terminator)""" try: field_value = getattr(self, field) except: return None field_code = get_optional_code(field) value = None if field_value: field_length = len(field_value) value = struct.pack(">HH", field_code, field_length) + field_value return value def _int_pack_format(self, field): """Return format type""" return consts.INT_PACK_FORMATS[self.params[field].size] def _parse_int(self, field, data, pos): """ Parse fixed-length chunk from a PDU. Return (data, pos) tuple. """ size = self.params[field].size fmt = self._int_pack_format(field) field_value, = struct.unpack(">" + fmt, data[pos:pos + size]) setattr(self, field, field_value) pos += size return data, pos def _parse_string(self, field, data, pos, length=None): """ Parse variable-length string from a PDU. Return (data, pos) tuple. """ if length is None: end = data.find(consts.NULL_STRING, pos) length = end - pos else: length -= 1 # length includes trailing NULL character setattr(self, field, data[pos:pos + length]) pos += length + 1 return data, pos def _parse_ostring(self, field, data, pos, length=None): """ Parse an octet string from a PDU. Return (data, pos) tuple. """ if length is None: length_field = self.params[field].len_field length = int(getattr(self, length_field)) setattr(self, field, data[pos:pos + length]) pos += length return data, pos def is_fixed(self, field): """Return True if field has fixed length, False otherwise""" if hasattr(self.params[field], 'size'): return True return False def parse_params(self, data): """Parse data into the object structure""" pos = 0 dlen = len(data) for field in self.params_order: param = self.params[field] if pos == dlen or self.field_is_optional(field): break if param.type is int: data, pos = self._parse_int(field, data, pos) elif param.type is str: data, pos = self._parse_string(field, data, pos) elif param.type is ostr: data, pos = self._parse_ostring(field, data, pos) if pos < dlen: self.parse_optional_params(data[pos:]) def parse_optional_params(self, data): """Parse optional parameters. Optional parameters have the following format: * type (2 bytes) * length (2 bytes) * value (variable, bytes) """ dlen = len(data) pos = 0 while pos < dlen: type_code, pos = unpack_short(data, pos) length, pos = unpack_short(data, pos) try: field = get_optional_name(type_code) except exceptions.UnknownCommandError as e: if self.allow_unknown_opt_params: logger.warning("Unknown optional parameter type 0x%x, skipping", type_code) pos += length continue raise param = self.params[field] if param.type is int: data, pos = self._parse_int(field, data, pos) elif param.type is str: data, pos = self._parse_string(field, data, pos, length) elif param.type is ostr: data, pos = self._parse_ostring(field, data, pos, length) def field_exists(self, field): """Return True if field exists, False otherwise""" return hasattr(self.params, field) def field_is_optional(self, field): """Return True if field is optional, False otherwise""" if hasattr(self, 'mandatory_fields') and field in self.mandatory_fields: return False elif field in consts.OPTIONAL_PARAMS: return True elif self.is_vendor(): # FIXME: No vendor support yet return False return False class Param(object): """Command parameter info class""" def __init__(self, **kwargs): if 'type' not in kwargs: raise KeyError('Parameter Type not defined') if kwargs.get('type') not in (int, str, ostr, flag): raise ValueError("Invalid parameter type: %s" % kwargs.get('type')) valid_keys = ('type', 'size', 'min', 'max', 'len_field') for k in kwargs: if k not in valid_keys: raise KeyError("Key '%s' not allowed here" % k) self.type = kwargs.get('type') for param in ('size', 'min', 'max', 'len_field'): if param in kwargs: setattr(self, param, kwargs[param]) def __repr__(self): """Shows type of Param in console""" return ''.join(('')) class BindTransmitter(Command): """Bind as a transmitter command""" params = { 'system_id': Param(type=str, max=16), 'password': Param(type=str, max=9), 'system_type': Param(type=str, max=13), 'interface_version': Param(type=int, size=1), 'addr_ton': Param(type=int, size=1), 'addr_npi': Param(type=int, size=1), 'address_range': Param(type=str, max=41), } # Order is important, but params dictionary is unordered params_order = ( 'system_id', 'password', 'system_type', 'interface_version', 'addr_ton', 'addr_npi', 'address_range', ) def __init__(self, command, **kwargs): super(BindTransmitter, self).__init__(command, **kwargs) self._set_vars(**(dict.fromkeys(self.params))) self.interface_version = consts.SMPP_VERSION_34 class BindReceiver(BindTransmitter): """Bind as a receiver command""" def __init__(self, command, **kwargs): super(BindReceiver, self).__init__(command, **kwargs) class BindTransceiver(BindTransmitter): """Bind as receiver and transmitter command""" def __init__(self, command, **kwargs): super(BindTransceiver, self).__init__(command, **kwargs) class BindTransmitterResp(Command): """Response for bind as a transmitter command""" params = { 'system_id': Param(type=str, max=16), 'sc_interface_version': Param(type=int, size=1), } params_order = ('system_id', 'sc_interface_version') def __init__(self, command, **kwargs): super(BindTransmitterResp, self).__init__(command, need_sequence=False, **kwargs) self._set_vars(**(dict.fromkeys(self.params))) class BindReceiverResp(BindTransmitterResp): """Response for bind as a reciever command""" def __init__(self, command, **kwargs): super(BindReceiverResp, self).__init__(command, **kwargs) class BindTransceiverResp(BindTransmitterResp): """Response for bind as a transceiver command""" def __init__(self, command, **kwargs): super(BindTransceiverResp, self).__init__(command, **kwargs) class DataSM(Command): """data_sm command is used to transfer data between SMSC and the ESME""" params = { 'service_type': Param(type=str, max=6), 'source_addr_ton': Param(type=int, size=1), 'source_addr_npi': Param(type=int, size=1), 'source_addr': Param(type=str, max=21), 'dest_addr_ton': Param(type=int, size=1), 'dest_addr_npi': Param(type=int, size=1), 'destination_addr': Param(type=str, max=21), 'esm_class': Param(type=int, size=1), 'registered_delivery': Param(type=int, size=1), 'data_coding': Param(type=int, size=1), # Optional params: 'source_port': Param(type=int, size=2), 'source_addr_subunit': Param(type=int, size=1), 'source_network_type': Param(type=int, size=1), 'source_bearer_type': Param(type=int, size=1), 'source_telematics_id': Param(type=int, size=2), 'destination_port': Param(type=int, size=2), 'dest_addr_subunit': Param(type=int, size=1), 'dest_network_type': Param(type=int, size=1), 'dest_bearer_type': Param(type=int, size=1), 'dest_telematics_id': Param(type=int, size=2), 'sar_msg_ref_num': Param(type=int, size=2), 'sar_total_segments': Param(type=int, size=1), 'sar_segment_seqnum': Param(type=int, size=1), 'more_messages_to_send': Param(type=int, size=1), 'qos_time_to_live': Param(type=int, size=4), 'payload_type': Param(type=int, size=1), 'message_payload': Param(type=ostr, max=260), 'receipted_message_id': Param(type=str, max=65), 'message_state': Param(type=int, size=1), 'network_error_code': Param(type=ostr, size=3), 'user_message_reference': Param(type=int, size=2), 'privacy_indicator': Param(type=int, size=1), 'callback_num': Param(type=ostr, min=4, max=19), 'callback_num_pres_ind': Param(type=int, size=1), 'callback_num_atag': Param(type=str, max=65), 'source_subaddress': Param(type=str, min=2, max=23), 'dest_subaddress': Param(type=str, min=2, max=23), 'user_response_code': Param(type=int, size=1), 'display_time': Param(type=int, size=1), 'sms_signal': Param(type=int, size=2), 'ms_validity': Param(type=int, size=1), 'ms_msg_wait_facilities': Param(type=int, size=1), 'number_of_messages': Param(type=int, size=1), 'alert_on_message_delivery': Param(type=flag), 'language_indicator': Param(type=int, size=1), 'its_reply_type': Param(type=int, size=1), 'its_session_info': Param(type=int, size=2), } params_order = ( 'service_type', 'source_addr_ton', 'source_addr_npi', 'source_addr', 'dest_addr_ton', 'dest_addr_npi', 'destination_addr', 'esm_class', 'registered_delivery', 'data_coding', # Optional params: 'source_port', 'source_addr_subunit', 'source_network_type', 'source_bearer_type', 'source_telematics_id', 'destination_port', 'dest_addr_subunit', 'dest_network_type', 'dest_bearer_type', 'dest_telematics_id', 'sar_msg_ref_num', 'sar_total_segments', 'sar_segment_seqnum', 'more_messages_to_send', 'qos_time_to_live', 'payload_type', 'message_payload', 'receipted_message_id', 'message_state', 'network_error_code', 'user_message_reference', 'privacy_indicator', 'callback_num', 'callback_num_pres_ind', 'callback_num_atag', 'source_subaddress', 'dest_subaddress', 'user_response_code', 'display_time', 'sms_signal', 'ms_validity', 'ms_msg_wait_facilities', 'number_of_messages', 'alert_on_message_delivery', 'language_indicator', 'its_reply_type', 'its_session_info', ) def __init__(self, command, **kwargs): super(DataSM, self).__init__(command, **kwargs) self._set_vars(**(dict.fromkeys(self.params))) class DataSMResp(Command): """Reponse command for data_sm""" params = { 'message_id': Param(type=str, max=65), # Optional params: #type size is implementation specific. 'delivery_failure_reason': Param(type=str, max=256), 'network_error_code': Param(type=str, max=3), 'additional_status_info_text': Param(type=str, max=256), 'dpf_result': Param(type=int, size=1), } params_order = ( 'message_id', # Optional params: 'delivery_failure_reason', 'network_error_code', 'additional_status_info_text', 'dpf_result', ) def __init__(self, command, **kwargs): super(DataSMResp, self).__init__(command, **kwargs) self._set_vars(**(dict.fromkeys(self.params))) class GenericNAck(Command): """General Negative Acknowledgement class""" params = {} params_order = () _defs = [] def __init__(self, command, **kwargs): super(GenericNAck, self).__init__(command, need_sequence=False, **kwargs) class SubmitSM(Command): """submit_sm command class This command is used by an ESME to submit short message to the SMSC. submit_sm PDU does not support the transaction mode.""" # # Service type # The following generic service types are defined: # '' -- default # 'CMT' -- Cellural Messaging # 'CPT' -- Cellural Paging # 'VMN' -- Voice Mail Notification # 'VMA' -- Voice Mail Alerting # 'WAP' -- Wireless Application Protocol # 'USSD' -- Unstructured Supplementary Services Data service_type = None # Type of Number for source address source_addr_ton = None # Numbering Plan Indicator for source address source_addr_npi = None # Address of SME which originated this message source_addr = None # TON for destination dest_addr_ton = None # NPI for destination dest_addr_npi = None # Destination address for this message destination_addr = None # Message mode and message type esm_class = None # SMPP_MSGMODE_DEFAULT # Protocol Identifier protocol_id = None # Priority level of this message priority_flag = None # Message is to be scheduled by the SMSC for delivery schedule_delivery_time = None # Validity period of this message validity_period = None # Indicator to signify if an SMSC delivery receipt or and SME # acknowledgement is required. registered_delivery = None # This flag indicates if submitted message should replace an existing # message replace_if_present_flag = None # Encoding scheme of the short messaege data data_coding = None # SMPP_ENCODING_DEFAULT#ISO10646 # Indicates the short message to send from a list of predefined # ('canned') short messages stored on the SMSC sm_default_msg_id = None # Message length in octets sm_length = 0 # Up to 254 octets of short message user data short_message = None # Optional are taken from params list and are set dynamically when # __init__ is called. params = { 'service_type': Param(type=str, max=6), 'source_addr_ton': Param(type=int, size=1), 'source_addr_npi': Param(type=int, size=1), 'source_addr': Param(type=str, max=21), 'dest_addr_ton': Param(type=int, size=1), 'dest_addr_npi': Param(type=int, size=1), 'destination_addr': Param(type=str, max=21), 'esm_class': Param(type=int, size=1), 'protocol_id': Param(type=int, size=1), 'priority_flag': Param(type=int, size=1), 'schedule_delivery_time': Param(type=str, max=17), 'validity_period': Param(type=str, max=17), 'registered_delivery': Param(type=int, size=1), 'replace_if_present_flag': Param(type=int, size=1), 'data_coding': Param(type=int, size=1), 'sm_default_msg_id': Param(type=int, size=1), 'sm_length': Param(type=int, size=1), 'short_message': Param(type=ostr, max=254, len_field='sm_length'), # Optional params 'user_message_reference': Param(type=int, size=2), 'source_port': Param(type=int, size=2), 'source_addr_subunit': Param(type=int, size=2), 'destination_port': Param(type=int, size=2), 'dest_addr_subunit': Param(type=int, size=1), 'sar_msg_ref_num': Param(type=int, size=2), 'sar_total_segments': Param(type=int, size=1), 'sar_segment_seqnum': Param(type=int, size=1), 'more_messages_to_send': Param(type=int, size=1), 'payload_type': Param(type=int, size=1), 'message_payload': Param(type=ostr, max=260), 'privacy_indicator': Param(type=int, size=1), 'callback_num': Param(type=ostr, min=4, max=19), 'callback_num_pres_ind': Param(type=int, size=1), 'source_subaddress': Param(type=str, min=2, max=23), 'dest_subaddress': Param(type=str, min=2, max=23), 'user_response_code': Param(type=int, size=1), 'display_time': Param(type=int, size=1), 'sms_signal': Param(type=int, size=2), 'ms_validity': Param(type=int, size=1), 'ms_msg_wait_facilities': Param(type=int, size=1), 'number_of_messages': Param(type=int, size=1), 'alert_on_message_delivery': Param(type=flag), 'language_indicator': Param(type=int, size=1), 'its_reply_type': Param(type=int, size=1), 'its_session_info': Param(type=int, size=2), 'ussd_service_op': Param(type=int, size=1), } params_order = ( 'service_type', 'source_addr_ton', 'source_addr_npi', 'source_addr', 'dest_addr_ton', 'dest_addr_npi', 'destination_addr', 'esm_class', 'protocol_id', 'priority_flag', 'schedule_delivery_time', 'validity_period', 'registered_delivery', 'replace_if_present_flag', 'data_coding', 'sm_default_msg_id', 'sm_length', 'short_message', # Optional params 'user_message_reference', 'source_port', 'source_addr_subunit', 'destination_port', 'dest_addr_subunit', 'sar_msg_ref_num', 'sar_total_segments', 'sar_segment_seqnum', 'more_messages_to_send', 'payload_type', 'message_payload', 'privacy_indicator', 'callback_num', 'callback_num_pres_ind', 'source_subaddress', 'dest_subaddress', 'user_response_code', 'display_time', 'sms_signal', 'ms_validity', 'ms_msg_wait_facilities', 'number_of_messages', 'alert_on_message_delivery', 'language_indicator', 'its_reply_type', 'its_session_info', 'ussd_service_op', ) def __init__(self, command, **kwargs): super(SubmitSM, self).__init__(command, **kwargs) self._set_vars(**(dict.fromkeys(self.params))) def prep(self): """Prepare to generate binary data""" if self.short_message: if getattr(self, 'message_payload', None): raise ValueError('`message_payload` can not be used with `short_message`') self.sm_length = len(self.short_message) else: self.sm_length = 0 class SubmitSMResp(Command): """Response command for submit_sm""" params = { 'message_id': Param(type=str, max=65), } params_order = ('message_id',) def __init__(self, command, **kwargs): super(SubmitSMResp, self).__init__(command, need_sequence=False, **kwargs) self._set_vars(**(dict.fromkeys(self.params))) class DeliverSM(SubmitSM): """deliver_sm command class, similar to submit_sm but has different optional params""" params = { 'service_type': Param(type=str, max=6), 'source_addr_ton': Param(type=int, size=1), 'source_addr_npi': Param(type=int, size=1), 'source_addr': Param(type=str, max=21), 'dest_addr_ton': Param(type=int, size=1), 'dest_addr_npi': Param(type=int, size=1), 'destination_addr': Param(type=str, max=21), 'esm_class': Param(type=int, size=1), 'protocol_id': Param(type=int, size=1), 'priority_flag': Param(type=int, size=1), 'schedule_delivery_time': Param(type=str, max=17), 'validity_period': Param(type=str, max=17), 'registered_delivery': Param(type=int, size=1), 'replace_if_present_flag': Param(type=int, size=1), 'data_coding': Param(type=int, size=1), 'sm_default_msg_id': Param(type=int, size=1), 'sm_length': Param(type=int, size=1), 'short_message': Param(type=ostr, max=254, len_field='sm_length'), # Optional params 'user_message_reference': Param(type=int, size=2), 'source_port': Param(type=int, size=2), 'destination_port': Param(type=int, size=2), 'sar_msg_ref_num': Param(type=int, size=2), 'sar_total_segments': Param(type=int, size=1), 'sar_segment_seqnum': Param(type=int, size=1), 'user_response_code': Param(type=int, size=1), 'privacy_indicator': Param(type=int, size=1), 'payload_type': Param(type=int, size=1), 'message_payload': Param(type=ostr, max=260), 'callback_num': Param(type=ostr, min=4, max=19), 'source_subaddress': Param(type=str, min=2, max=23), 'dest_subaddress': Param(type=str, min=2, max=23), 'language_indicator': Param(type=int, size=1), 'its_session_info': Param(type=int, size=2), 'network_error_code': Param(type=ostr, size=3), 'message_state': Param(type=int, size=1), 'receipted_message_id': Param(type=str, max=65), 'source_network_type': Param(type=int, size=1), 'dest_network_type': Param(type=int, size=1), 'more_messages_to_send': Param(type=int, size=1), } params_order = ( 'service_type', 'source_addr_ton', 'source_addr_npi', 'source_addr', 'dest_addr_ton', 'dest_addr_npi', 'destination_addr', 'esm_class', 'protocol_id', 'priority_flag', 'schedule_delivery_time', 'validity_period', 'registered_delivery', 'replace_if_present_flag', 'data_coding', 'sm_default_msg_id', 'sm_length', 'short_message', # Optional params 'user_message_reference', 'source_port', 'destination_port', 'sar_msg_ref_num', 'sar_total_segments', 'sar_segment_seqnum', 'user_response_code', 'privacy_indicator', 'payload_type', 'message_payload', 'callback_num', 'source_subaddress', 'dest_subaddress', 'language_indicator', 'its_session_info', 'network_error_code', 'message_state', 'receipted_message_id', 'source_network_type', 'dest_network_type', 'more_messages_to_send', ) def __init__(self, command, **kwargs): super(DeliverSM, self).__init__(command, **kwargs) self._set_vars(**(dict.fromkeys(self.params))) class DeliverSMResp(SubmitSMResp): """deliver_sm_response response class, same as submit_sm""" message_id = None def __init__(self, command, **kwargs): super(DeliverSMResp, self).__init__(command, **kwargs) class QuerySM(Command): """query_sm command class This command is used by an ESME to query the state of a short message to the SMSC. source_addr* values must match those supplied when the message was submitted.""" # Message ID of the message whose state is to be queried. message_id = None # Type of Number for source address source_addr_ton = None # Numbering Plan Indicator for source address source_addr_npi = None # Address of SME which originated this message source_addr = None # Optional are taken from params list and are set dynamically when # __init__ is called. params = { 'message_id': Param(type=str, max=65), 'source_addr_ton': Param(type=int, size=1), 'source_addr_npi': Param(type=int, size=1), 'source_addr': Param(type=str, max=21), } params_order = ( 'message_id', 'source_addr_ton', 'source_addr_npi', 'source_addr', ) def __init__(self, command, **kwargs): super(QuerySM, self).__init__(command, **kwargs) self._set_vars(**(dict.fromkeys(self.params))) def prep(self): """Prepare to generate binary data""" if not self.message_id: raise ValueError('`message_id` is mandatory') class QuerySMResp(Command): """Response command for query_sm""" mandatory_fields = ('message_state') params = { 'message_id': Param(type=str, max=65), 'final_date': Param(type=str, max=17), 'message_state': Param(type=int, size=1), 'error_code': Param(type=int, size=1), } params_order = ( 'message_id', 'final_date', 'message_state', 'error_code', ) def __init__(self, command, **kwargs): super(QuerySMResp, self).__init__(command, need_sequence=False, **kwargs) self._set_vars(**(dict.fromkeys(self.params))) class Unbind(Command): """Unbind command""" params = {} params_order = () def __init__(self, command, **kwargs): super(Unbind, self).__init__(command, **kwargs) class UnbindResp(Command): """Unbind response command""" params = {} params_order = () def __init__(self, command, **kwargs): super(UnbindResp, self).__init__(command, need_sequence=False, **kwargs) class EnquireLink(Command): """Enquire link command""" params = {} params_order = () def __init__(self, command, **kwargs): super(EnquireLink, self).__init__(command, **kwargs) class EnquireLinkResp(Command): """Enquire link command response""" params = {} params_order = () def __init__(self, command, **kwargs): super(EnquireLinkResp, self).__init__(command, need_sequence=False, **kwargs) class AlertNotification(Command): """`alert_notification` command class""" # Type of Number for source address source_addr_ton = None # Numbering Plan Indicator for source address source_addr_npi = None # Address of SME which originated this message source_addr = None # TON for destination esme_addr_ton = None # NPI for destination esme_addr_npi = None # Destination address for this message esme_addr = None # Optional are taken from params list and are set dynamically when # __init__ is called. params = { 'source_addr_ton': Param(type=int, size=1), 'source_addr_npi': Param(type=int, size=1), 'source_addr': Param(type=str, max=21), 'esme_addr_ton': Param(type=int, size=1), 'esme_addr_npi': Param(type=int, size=1), 'esme_addr': Param(type=str, max=21), # Optional params 'ms_availability_status' : Param(type=int, size=1), } params_order = ( 'source_addr_ton', 'source_addr_npi', 'source_addr', 'esme_addr_ton', 'esme_addr_npi', 'esme_addr', # Optional params 'ms_availability_status', ) def __init__(self, command, **kwargs): super(AlertNotification, self).__init__(command, **kwargs) self._set_vars(**(dict.fromkeys(self.params)))