Skip to content

Commit

Permalink
Initial implementation of SMPP multipart messages using SAR parameters.
Browse files Browse the repository at this point in the history
  • Loading branch information
jerith committed Oct 12, 2013
1 parent 5faa676 commit b601b78
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 2 deletions.
38 changes: 36 additions & 2 deletions vumi/transports/smpp/clientserver/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import json
import uuid
from random import randint

from twisted.internet import reactor
from twisted.internet.protocol import Protocol, ClientFactory
Expand Down Expand Up @@ -457,23 +458,56 @@ def submit_sm(self, **kwargs):
'dropping message: %s' % (self.state, kwargs)))
returnValue(0)

sequence_number = yield self.get_next_seq()
sar_params = kwargs.pop('sar_params', None)

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

if self.config.send_multipart_sar and sar_params is None:
if len(message) > 130:
sequence_number = yield self._submit_multipart_sar(
**pdu_params)
returnValue(sequence_number)

sequence_number = yield self.get_next_seq()

pdu = SubmitSM(sequence_number, **pdu_params)
if kwargs.get('message_type', 'sms') == 'ussd':
update_ussd_pdu(pdu, kwargs.get('continue_session', True),
kwargs.get('session_info', None))

message = pdu_params['short_message']
if self.config.send_long_messages and len(message) > 254:
pdu.add_message_payload(''.join('%02x' % ord(c) for c in message))

if sar_params:
pdu.set_sar_msg_ref_num(sar_params['msg_ref_num'])
pdu.set_sar_total_segments(sar_params['total_segments'])
pdu.set_sar_segment_seqnum(sar_params['segment_seqnum'])

self.send_pdu(pdu)
yield self.push_unacked(sequence_number)
returnValue(sequence_number)

@inlineCallbacks
def _submit_multipart_sar(self, **pdu_params):
message = pdu_params['short_message']
split_msg = []
while message:
split_msg.append(message[:130])
message = message[130:]
ref_num = randint(1, 255)
for i, msg in enumerate(split_msg):
params = pdu_params.copy()
params['short_message'] = msg
params['sar_params'] = {
'msg_ref_num': ref_num,
'total_segments': len(split_msg),
'segment_seqnum': i + 1,
}
sequence_number = yield self.submit_sm(**params)
returnValue(sequence_number)

@inlineCallbacks
def enquire_link(self, **kwargs):
if self.state in ['BOUND_TX', 'BOUND_RX', 'BOUND_TRX']:
Expand Down
26 changes: 26 additions & 0 deletions vumi/transports/smpp/clientserver/tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,32 @@ def test_submit_sm_sms_long(self):
self.assertEqual(''.join('%02x' % ord(c) for c in long_message),
pdu_opts['message_payload'])

@inlineCallbacks
def test_submit_sm_sms_multipart_sar(self):
"""Submit a long SMS message using multipart sar fields."""
esme = yield self.get_esme(config={
'send_multipart_sar': True,
})
long_message = 'This is a long message.' * 20
yield esme.submit_sm(short_message=long_message)
self.assertEqual(4, len(esme.fake_sent_pdus))
msg_parts = []
msg_refs = []

for i, sm_pdu in enumerate(esme.fake_sent_pdus):
sm = unpack_pdu(sm_pdu.get_bin())
pdu_opts = unpacked_pdu_opts(sm)
mandatory_parameters = sm['body']['mandatory_parameters']

self.assertEqual('submit_sm', sm['header']['command_id'])
msg_parts.append(mandatory_parameters['short_message'])
msg_refs.append(pdu_opts['sar_msg_ref_num'])
self.assertEqual(i + 1, pdu_opts['sar_segment_seqnum'])
self.assertEqual(4, pdu_opts['sar_total_segments'])

self.assertEqual(long_message, ''.join(msg_parts))
self.assertEqual(1, len(set(msg_refs)))

@inlineCallbacks
def test_submit_sm_ussd_continue(self):
"""Submit a USSD message with a session continue flag."""
Expand Down
4 changes: 4 additions & 0 deletions vumi/transports/smpp/transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ class SmppTransportConfig(Transport.CONFIG_CLASS):
"`message_payload` optional field instead of the `short_message` "
"field. Default is `False`, simply because that maintains previous "
"behaviour.", default=False, static=True)
send_multipart_sar = ConfigBool(
"If `True`, messages longer than 140 bytes will be sent as a series "
"of smaller messages with the sar_* parameters set. Default is "
"`False`.", default=False, static=True)
split_bind_prefix = ConfigText(
"This is the Redis prefix to use for storing things like sequence "
"numbers and message ids for delivery report handling. It defaults "
Expand Down

0 comments on commit b601b78

Please sign in to comment.