| Server IP : 209.209.40.120 / Your IP : 216.73.217.112 Web Server : Microsoft-IIS/10.0 System : Windows NT NEWWWW 10.0 build 17763 (Windows Server 2019) i586 User : NEWWWW$ ( 0) PHP Version : 8.3.30 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /Python312/Lib/site-packages/smpplib/ |
Upload File : |
#
# smpplib -- SMPP Library for Python
# Copyright (c) 2005 Martynas Jocius <mjoc@akl.lt>
#
# 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 client module"""
import binascii
import logging
import select
import socket
import struct
import warnings
from smpplib import consts, exceptions, smpp
class SimpleSequenceGenerator(object):
MIN_SEQUENCE = 0x00000001
MAX_SEQUENCE = 0x7FFFFFFF
def __init__(self):
self._sequence = self.MIN_SEQUENCE
@property
def sequence(self):
return self._sequence
def next_sequence(self):
if self._sequence == self.MAX_SEQUENCE:
self._sequence = self.MIN_SEQUENCE
else:
self._sequence += 1
return self._sequence
class Client(object):
"""SMPP client class"""
state = consts.SMPP_CLIENT_STATE_CLOSED
host = None
port = None
vendor = None
_socket = None
_ssl_context = None
sequence_generator = None
def __init__(
self,
host,
port,
timeout=5,
sequence_generator=None,
logger_name=None,
ssl_context=None,
allow_unknown_opt_params=None,
):
self.host = host
self.port = int(port)
self._ssl_context = ssl_context
self.timeout = timeout
self.logger = logging.getLogger(logger_name or 'smpp.Client.{}'.format(id(self)))
if sequence_generator is None:
sequence_generator = SimpleSequenceGenerator()
self.sequence_generator = sequence_generator
if allow_unknown_opt_params is None:
warnings.warn(
"Unknown optional parameters during PDU parsing will stop "
"causing an exception in a future smpplib version "
"(in order to comply with the SMPP spec). To switch behavior "
"now set allow_unknown_opt_params to True.",
DeprecationWarning,
)
self.allow_unknown_opt_params = False
else:
self.allow_unknown_opt_params = allow_unknown_opt_params
self._socket = self._create_socket()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if self._socket is not None:
try:
self.unbind()
except (exceptions.PDUError, exceptions.ConnectionError) as e:
if len(getattr(e, 'args', tuple())) > 1:
self.logger.warning('(%d) %s. Ignored', e.args[1], e.args[0])
else:
self.logger.warning('%s. Ignored', e)
self.disconnect()
def __del__(self):
if self._socket is not None:
self.logger.warning('%s was not closed', self)
@property
def sequence(self):
return self.sequence_generator.sequence
def next_sequence(self):
return self.sequence_generator.next_sequence()
def _create_socket(self):
raw_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
raw_socket.settimeout(self.timeout)
if self._ssl_context is None:
return raw_socket
return self._ssl_context.wrap_socket(raw_socket)
def connect(self):
"""Connect to SMSC"""
self.logger.info('Connecting to %s:%s...', self.host, self.port)
try:
if self._socket is None:
self._socket = self._create_socket()
self._socket.connect((self.host, self.port))
self.state = consts.SMPP_CLIENT_STATE_OPEN
except socket.error:
raise exceptions.ConnectionError("Connection refused")
def disconnect(self):
"""Disconnect from the SMSC"""
self.logger.info('Disconnecting...')
if self.state != consts.SMPP_CLIENT_STATE_OPEN:
self.logger.warning('%s is disconnecting in the bound state', self)
if self._socket is not None:
self._socket.close()
self._socket = None
self.state = consts.SMPP_CLIENT_STATE_CLOSED
def _bind(self, command_name, **kwargs):
"""Send bind_transmitter command to the SMSC"""
if command_name in ('bind_receiver', 'bind_transceiver'):
self.logger.debug('Receiver mode')
p = smpp.make_pdu(command_name, client=self, **kwargs)
self.send_pdu(p)
try:
resp = self.read_pdu()
except socket.timeout:
raise exceptions.ConnectionError()
if resp.is_error():
raise exceptions.PDUError('({}) {}: {}'.format(
resp.status,
resp.command,
consts.DESCRIPTIONS.get(resp.status, 'Unknown code')),
int(resp.status),
)
return resp
def bind_transmitter(self, **kwargs):
"""Bind as a transmitter"""
return self._bind('bind_transmitter', **kwargs)
def bind_receiver(self, **kwargs):
"""Bind as a receiver"""
return self._bind('bind_receiver', **kwargs)
def bind_transceiver(self, **kwargs):
"""Bind as a transmitter and receiver at once"""
return self._bind('bind_transceiver', **kwargs)
def unbind(self):
"""Unbind from the SMSC"""
p = smpp.make_pdu('unbind', client=self)
self.send_pdu(p)
try:
return self.read_pdu()
except socket.timeout:
raise exceptions.ConnectionError()
def send_pdu(self, p):
"""Send PDU to the SMSC"""
if self.state not in consts.COMMAND_STATES[p.command]:
raise exceptions.PDUError("Command %s failed: %s" % (
p.command,
consts.DESCRIPTIONS[consts.SMPP_ESME_RINVBNDSTS],
))
self.logger.debug('Sending %s PDU', p.command)
generated = p.generate()
self.logger.debug('>>%s (%d bytes)', binascii.b2a_hex(generated), len(generated))
try:
self._socket.sendall(generated)
except socket.error as e:
self.logger.warning(e)
raise exceptions.ConnectionError()
return True
def _recv_exact(self, exact_size):
"""
Keep reading from self._socket until exact_size bytes have been read
"""
parts = []
received = 0
while received < exact_size:
try:
part = self._socket.recv(exact_size - received)
except socket.timeout:
raise
except socket.error as e:
self.logger.warning(e)
raise exceptions.ConnectionError()
if not part:
raise exceptions.ConnectionError()
received += len(part)
parts.append(part)
return b"".join(parts)
def read_pdu(self):
"""Read PDU from the SMSC"""
self.logger.debug('Waiting for PDU...')
raw_len = self._recv_exact(4)
try:
length = struct.unpack('>L', raw_len)[0]
except struct.error:
self.logger.warning('Receive broken pdu... %s', repr(raw_len))
raise exceptions.PDUError('Broken PDU')
raw_pdu = raw_len + self._recv_exact(length - 4)
self.logger.debug('<<%s (%d bytes)', binascii.b2a_hex(raw_pdu), len(raw_pdu))
pdu = smpp.parse_pdu(
raw_pdu,
client=self,
allow_unknown_opt_params=self.allow_unknown_opt_params,
)
self.logger.debug('Read %s PDU', pdu.command)
if pdu.is_error():
return pdu
elif pdu.command in consts.STATE_SETTERS:
self.state = consts.STATE_SETTERS[pdu.command]
return pdu
def accept(self, obj):
"""Accept an object"""
raise NotImplementedError('not implemented')
def _message_received(self, pdu):
"""Handler for received message event"""
status = self.message_received_handler(pdu=pdu)
if status is None:
status = consts.SMPP_ESME_ROK
dsmr = smpp.make_pdu('deliver_sm_resp', client=self, status=status)
dsmr.sequence = pdu.sequence
self.send_pdu(dsmr)
def _enquire_link_received(self, pdu):
"""Response to enquire_link"""
ler = smpp.make_pdu('enquire_link_resp', client=self)
ler.sequence = pdu.sequence
self.send_pdu(ler)
def _alert_notification(self, pdu):
"""Handler for alert notification event"""
self.message_received_handler(pdu=pdu)
def set_message_received_handler(self, func):
"""Set new function to handle message receive event"""
self.message_received_handler = func
def set_message_sent_handler(self, func):
"""Set new function to handle message sent event"""
self.message_sent_handler = func
def set_query_resp_handler(self, func):
"""Set new function to handle query resp event"""
self.query_resp_handler = func
def set_error_pdu_handler(self, func):
"""Set new function to handle PDUs with an error status"""
self.error_pdu_handler = func
def message_received_handler(self, pdu, **kwargs):
"""Custom handler to process received message. May be overridden"""
self.logger.warning('Message received handler (Override me)')
def message_sent_handler(self, pdu, **kwargs):
"""
Called when SMPP server accept message (SUBMIT_SM_RESP).
May be overridden
"""
self.logger.warning('Message sent handler (Override me)')
def query_resp_handler(self, pdu, **kwargs):
"""Custom handler to process response to queries. May be overridden"""
self.logger.warning('Query resp handler (Override me)')
def error_pdu_handler(self, pdu):
raise exceptions.PDUError('({}) {}: {}'.format(
pdu.status,
pdu.command,
consts.DESCRIPTIONS.get(pdu.status, 'Unknown status')),
int(pdu.status),
)
def read_once(self, ignore_error_codes=None, auto_send_enquire_link=True):
"""Read a PDU and act"""
if ignore_error_codes is not None:
warnings.warn(
"ignore_error_codes is deprecated, use set_error_pdu_handler to "
"configure a custom error PDU handler instead.",
DeprecationWarning,
)
try:
try:
pdu = self.read_pdu()
except socket.timeout:
if not auto_send_enquire_link:
raise
self.logger.debug('Socket timeout, listening again')
pdu = smpp.make_pdu('enquire_link', client=self)
self.send_pdu(pdu)
return
if pdu.is_error():
self.error_pdu_handler(pdu)
if pdu.command == 'unbind': # unbind_res
self.logger.info('Unbind command received')
return
elif pdu.command == 'submit_sm_resp':
self.message_sent_handler(pdu=pdu)
elif pdu.command == 'deliver_sm':
self._message_received(pdu)
elif pdu.command == 'query_sm_resp':
self.query_resp_handler(pdu)
elif pdu.command == 'enquire_link':
self._enquire_link_received(pdu)
elif pdu.command == 'enquire_link_resp':
pass
elif pdu.command == 'alert_notification':
self._alert_notification(pdu)
else:
self.logger.warning('Unhandled SMPP command "%s"', pdu.command)
except exceptions.PDUError as e:
if ignore_error_codes and len(e.args) > 1 and e.args[1] in ignore_error_codes:
self.logger.warning('(%d) %s. Ignored.', e.args[1], e.args[0])
else:
raise
def poll(self, ignore_error_codes=None, auto_send_enquire_link=True):
"""Act on available PDUs and return"""
while True:
readable, _writable, _exceptional = select.select([self._socket], [], [], 0)
if not readable:
break
self.read_once(ignore_error_codes, auto_send_enquire_link)
def listen(self, ignore_error_codes=None, auto_send_enquire_link=True):
"""Listen for PDUs and act"""
while True:
self.read_once(ignore_error_codes, auto_send_enquire_link)
def send_message(self, **kwargs):
"""Send message
Required Arguments:
source_addr_ton -- Source address TON
source_addr -- Source address (string)
dest_addr_ton -- Destination address TON
destination_addr -- Destination address (string)
short_message -- Message text (string)
"""
ssm = smpp.make_pdu('submit_sm', client=self, **kwargs)
self.send_pdu(ssm)
return ssm
def query_message(self, **kwargs):
"""Query message state
Required Arguments:
message_id -- SMSC assigned Message ID
source_addr_ton -- Original source address TON
source_addr_npi -- Original source address NPI
source_addr -- Original source address (string)
"""
qsm = smpp.make_pdu('query_sm', client=self, **kwargs)
self.send_pdu(qsm)
return qsm