File Coverage

blib/lib/Net/SMPP.pm
Criterion Covered Total %
statement 1443 1570 91.9
branch 787 948 83.0
condition 5 25 20.0
subroutine 206 222 92.7
pod 38 94 40.4
total 2479 2859 86.7


line stmt bran cond sub pod time code
1             # Net::SMPP.pm - SMPP over TCP, pure perl implementation
2             # Copyright (c) 2001-2011 Sampo Kellomaki , All rights reserved.
3             # Portions Copyright (c) 2001-2005 Symlabs, All rights reserved.
4             # This code may be distributed under same terms as perl. NO WARRANTY.
5             # Work sponsored by Symlabs, the LDAP and directory experts (www.symlabs.com)
6             # 12.3.2001, Sampo Kellomaki
7             # 7.7.2001, added SMPP 4.0 support --Sampo #4
8             # 9.7.2001, continued 4.0 hacking --Sampo #4
9             # 11.7.2001, added J-Phone specific extended options --Sampo #4
10             # 12.7.2001, fixed eating options off @_ --Sampo
11             # 1.8.2001, merged in fixes from Felix Gaehtgens , bumped
12             # version to 0.90 to reflect successfully conducted tests --Sampo
13             # 25.9.2001, tagged all 4.0 specifics so that 3.4-only version can be #4
14             # extracted for public distribution --Sampo #4
15             # 11.12.2001, fixed encode_deliver_v4 to encode_deliver_sm_v4, bug reported
16             # by Cristina Del Amo (Cristina.delAmo@vodafone-us.com), --Sampo
17             # 4.1.2002, Fixed enquiry_link to enquire_link --Sampo
18             # 10.1.2002, applied big patch by Lars Thegler to
19             # make pack and unpack templates perl5.005_03 compatible. --Sampo
20             # Caught bugs in decode_outbind_v34(), encode_query_sm(),
21             # encode_query_sm_resp() and replace_sm() --Sampo
22             # 11.1.2002, 7bit pack and unpack --Sampo
23             # 3.4.2002, command length check from Cris, rolled out 1.01 --Sampo
24             # 7.12.2002, applied some patched by Luis Munoz --Sampo
25             # 8.12.2002, more patched from Luis, --Sampo
26             # 23.9.2004, applied bind ip patch from Igor Ivoilov --Sampo
27             # 29.4.2005, applied patch from Kristian Nielsen --Sampo
28             # 21.4.2006, applied sysread patch from Dziugas.Baltrunas@bite..lt. Similar
29             # patch was also proposed by Felix Gaehtgens --Sampo
30             # 20.7.2007, patch from Matthias Meyser to fix enquiry_link, document 7bit (1.11) --Sampo
31             # 14.12.2008, adapted to SMPPv50, thanks to Gema niskazhu (and curse to
32             # the spec authors for not letting me know about new version) --Sampo
33             # 24.6.2010, tweaked for perl 5.8.8 --Sampo
34             # 29.5.2011, improved signal handling in read_hard(), patch from Clemens Dorner --Sampo
35             #
36             # Why ${*$me}{async} vs. $me->async ?
37             #
38             # $Id: SMPP.pm,v 1.31 2008-12-02 16:41:30 sampo Exp $
39              
40             ### The comments often refer to sections of the following document
41             ### Short Message Peer to Peer Protocol Specification v3.4,
42             ### 12-Oct-1999, Issue 1.2 (from www.smpp.org)
43             ###
44             ### Reference document for version 4.0 support was #4
45             ### Short Message Peer to Peer (SMPP) V4 Protocol Specification, #4
46             ### 29-Apr-1997, Version 1.1 (from Aldiscon/Logica) #4
47              
48             package Net::SMPP;
49              
50             require 5.008;
51 1     1   14039 use strict;
  1         3  
  1         41  
52 1     1   983 use Socket;
  1         4248  
  1         514  
53 1     1   783 use Symbol;
  1         801  
  1         60  
54 1     1   5 use Carp;
  1         1  
  1         48  
55 1     1   787 use IO::Socket;
  1         23927  
  1         6  
56 1     1   1471 use Data::Dumper; # for debugging
  1         2  
  1         66  
57              
58 1     1   16 use vars qw(@ISA $VERSION %default %param_by_name $trace);
  1         1  
  1         132  
59             @ISA = qw(IO::Socket::INET);
60             $VERSION = '1.19';
61             $trace = 0;
62              
63 1     1   6 use constant Transmitter => 1; # SMPP transmitter mode of operation
  1         2  
  1         66  
64 1     1   5 use constant Receiver => 2; # receiver mode of operation
  1         2  
  1         46  
65 1     1   6 use constant Transceiver => 3; # both
  1         2  
  1         573  
66              
67             ### command_status code (Error Codes) from sec 5.1.3, table 5-2, pp.112-114
68              
69 1         166 use constant status_code => {
70             0x00000000 => { code => 'ESME_ROK', msg => 'No error', },
71             0x00000001 => { code => 'ESME_RINVMSGLEN', msg => 'Message Length is invalid', },
72             0x00000002 => { code => 'ESME_RINVCMDLEN', msg => 'Command Length is invalid', },
73             0x00000003 => { code => 'ESME_RINVCMDID', msg => 'Invalid Command ID', },
74             0x00000004 => { code => 'ESME_RINVBNDSTS', msg => 'Incorrect BIND Status for given command', },
75             0x00000005 => { code => 'ESME_RALYBND', msg => 'ESME Already in bound state', },
76             0x00000006 => { code => 'ESME_RINVPRTFLG', msg => 'Invalid priority flag', },
77             0x00000007 => { code => 'ESME_RINVREGDLVFLG', msg => 'Invalid registered delivery flag', },
78             0x00000008 => { code => 'ESME_RSYSERR', msg => 'System Error', },
79             # 0x00000009 => { code => 'ESME_', msg => '', },
80             0x0000000a => { code => 'ESME_RINVSRCADR', msg => 'Invalid source address', },
81             0x0000000b => { code => 'ESME_RINVDSTADR', msg => 'Invalid destination address', },
82             0x0000000c => { code => 'ESME_RINVMSGID', msg => 'Message ID is invalid', },
83             0x0000000d => { code => 'ESME_RBINDFAIL', msg => 'Bind failed', },
84             0x0000000e => { code => 'ESME_RINVPASWD', msg => 'Invalid password', },
85             0x0000000f => { code => 'ESME_RINVSYSID', msg => 'Invalid System ID', },
86             # 0x00000010 => { code => 'ESME_', msg => '', },
87             0x00000011 => { code => 'ESME_RCANCELFAIL', msg => 'Cancel SM Failed', },
88             # 0x00000012 => { code => 'ESME_', msg => '', },
89             0x00000013 => { code => 'ESME_RREPLACEFAIL', msg => 'Replace SM Failed', },
90             0x00000014 => { code => 'ESME_RMSGQFUL', msg => 'Message queue full', },
91             0x00000015 => { code => 'ESME_RINVSERTYP', msg => 'Invalid service type', },
92             # 0x00000016 - 0x00000032 reserved
93             0x00000033 => { code => 'ESME_RINVNUMDESTS', msg => 'Invalid number of destinations', },
94             0x00000034 => { code => 'ESME_RINVDLNAME', msg => 'Invalid distribution list name', },
95             # 0x00000035 - 0x0000003f reserved
96             0x00000040 => { code => 'ESME_RINVDESTFLAG', msg => 'Destination flag is invalid (submit_multi)', },
97             # 0x00000041 => { code => 'ESME_', msg => '', },
98             0x00000042 => { code => 'ESME_RINVSUBREP', msg => "Invalid `submit with replace' request (i.e. submit_sm with replace_if_present_flag set)", },
99             0x00000043 => { code => 'ESME_RINVESMCLASS', msg => 'Invalid esm_class field data', },
100             0x00000044 => { code => 'ESME_RCNTSUBDL', msg => 'Cannot submit to distribution list', },
101             0x00000045 => { code => 'ESME_RSUBMITFAIL', msg => 'submit_sm or submit_multi failed', },
102             # 0x00000046 => { code => 'ESME_', msg => '', },
103             # 0x00000047 => { code => 'ESME_', msg => '', },
104             0x00000048 => { code => 'ESME_RINVSRCTON', msg => 'Invalid source address TON', },
105             0x00000049 => { code => 'ESME_RINVSRCNPI', msg => 'Invalid source address NPI', },
106             # 0x0000004a - 0x0000004f undocumented
107             0x00000050 => { code => 'ESME_RINVDSTTON', msg => 'Invalid destination address TON', },
108             0x00000051 => { code => 'ESME_RINVDSTNPI', msg => 'Invalid destination address NPI', },
109             # 0x00000052 => { code => 'ESME_', msg => '', },
110             0x00000053 => { code => 'ESME_RINVSYSTYP', msg => 'Invalid system_type field', },
111             0x00000054 => { code => 'ESME_RINVREPFLAG', msg => 'Invalid replace_if_present flag', },
112             0x00000055 => { code => 'ESME_RINVNUMMSGS', msg => 'Invalid number of messages', },
113             # 0x00000056 => { code => 'ESME_', msg => '', },
114             # 0x00000057 => { code => 'ESME_', msg => '', },
115             0x00000058 => { code => 'ESME_RTHROTTLED', msg => 'Throttling error (ESME has exceeded allowed message limits)', },
116             # 0x00000059 - 0x00000060 reserved
117             0x00000061 => { code => 'ESME_RINVSCHED', msg => 'Invalid scheduled delivery time', },
118             0x00000062 => { code => 'ESME_RINVEXPIRY', msg => 'Invalid message validity period (expiry time)', },
119             0x00000063 => { code => 'ESME_RINVDFTMSGID', msg => 'Predefined message invalid or not found', },
120             0x00000064 => { code => 'ESME_RX_T_APPN', msg => 'ESME Receiver Temporary App Error Code', },
121             0x00000065 => { code => 'ESME_RX_P_APPN', msg => 'ESME Receiver Permanent App Error Code', },
122             0x00000066 => { code => 'ESME_RX_R_APPN', msg => 'ESME Receiver Reject Message Error Code', },
123             0x00000067 => { code => 'ESME_RQUERYFAIL', msg => 'query_sm request failed', },
124             # 0x00000068 - 0x000000bf reserved
125             0x000000c0 => { code => 'ESME_RINVOPTPARSTREAM', msg => 'Error in the optional part of the PDU Body', },
126             0x000000c1 => { code => 'ESME_ROPTPARNOTALLWD', msg => 'Optional paramenter not allowed', },
127             0x000000c2 => { code => 'ESME_RINVPARLEN', msg => 'Invalid parameter length', },
128             0x000000c3 => { code => 'ESME_RMISSINGOPTPARAM', msg => 'Expected optional parameter missing', },
129             0x000000c4 => { code => 'ESME_RINVOPTPARAMVAL', msg => 'Invalid optional parameter value', },
130             # 0x000000c5 - 0x000000fd reserved
131             0x000000fe => { code => 'ESME_RDELIVERYFAILURE', msg => 'Delivery Failure (used for data_sm_resp)', },
132             0x000000ff => { code => 'ESME_RUNKNOWNERR', msg => 'Unknown error', },
133             # 0x00000100 - 0x000003ff reserved for SMPP extension
134             # 0x00000400 - 0x000004ff reserved for SMSC vendor specific errors
135             # 0x00000500 - 0xffffffff reserved
136              
137             ### *** Dear reader: if you know more error codes, e.g. in the
138             ### vendor specific range, please let me know so we can teach
139             ### this module about them.
140              
141 1     1   8 };
  1         3  
142              
143             ### Convert the status code table into constants
144              
145             do {
146 1     1   5 no strict "refs";
  1         2  
  1         250  
147             for my $k (keys(%{&status_code}))
148             {
149 0     0   0 eval { *{status_code->{$k}->{code}} = sub { return $k; } };
150 0     0   0 eval { *{status_code->{$k}->{code}.'_msg'} = sub { return *{status_code->{$k}->{msg}}; } };
  0         0  
151             }
152             };
153              
154             ### Command IDs, sec 5.1.2.1, table 5-1, pp. 110-111
155              
156 1     1   6 use constant CMD_generic_nack => 0x80000000;
  1         2  
  1         70  
157 1     1   26 use constant CMD_bind_receiver => 0x00000001;
  1         1  
  1         44  
158 1     1   4 use constant CMD_bind_receiver_resp => 0x80000001;
  1         1  
  1         61  
159 1     1   5 use constant CMD_bind_transmitter => 0x00000002;
  1         2  
  1         42  
160 1     1   5 use constant CMD_bind_transmitter_resp => 0x80000002;
  1         2  
  1         48  
161 1     1   4 use constant CMD_query_sm => 0x00000003;
  1         2  
  1         41  
162 1     1   6 use constant CMD_query_sm_resp => 0x80000003;
  1         1  
  1         50  
163 1     1   5 use constant CMD_submit_sm => 0x00000004;
  1         2  
  1         52  
164 1     1   5 use constant CMD_submit_sm_resp => 0x80000004;
  1         3  
  1         60  
165 1     1   6 use constant CMD_deliver_sm => 0x00000005;
  1         2  
  1         53  
166 1     1   5 use constant CMD_deliver_sm_resp => 0x80000005;
  1         2  
  1         42  
167 1     1   4 use constant CMD_unbind => 0x00000006;
  1         2  
  1         48  
168 1     1   10 use constant CMD_unbind_resp => 0x80000006;
  1         2  
  1         35  
169 1     1   5 use constant CMD_replace_sm => 0x00000007;
  1         1  
  1         47  
170 1     1   5 use constant CMD_replace_sm_resp => 0x80000007;
  1         1  
  1         42  
171 1     1   6 use constant CMD_cancel_sm => 0x00000008;
  1         2  
  1         37  
172 1     1   5 use constant CMD_cancel_sm_resp => 0x80000008;
  1         2  
  1         44  
173 1     1   5 use constant CMD_bind_transceiver => 0x00000009; # v3.4
  1         1  
  1         40  
174 1     1   4 use constant CMD_bind_transceiver_resp => 0x80000009; # v3.4
  1         2  
  1         42  
175 1     1   4 use constant CMD_delivery_receipt => 0x00000009; # v4 #4
  1         2  
  1         48  
176 1     1   5 use constant CMD_delivery_receipt_resp => 0x80000009; # v4 #4
  1         2  
  1         36  
177 1     1   6 use constant CMD_enquire_link_v4 => 0x0000000a; #4
  1         1  
  1         51  
178 1     1   5 use constant CMD_enquire_link_resp_v4 => 0x8000000a; #4
  1         19  
  1         45  
179 1     1   5 use constant CMD_outbind => 0x0000000b;
  1         2  
  1         55  
180 1     1   5 use constant CMD_enquire_link => 0x00000015;
  1         2  
  1         42  
181 1     1   5 use constant CMD_enquire_link_resp => 0x80000015;
  1         1  
  1         47  
182 1     1   6 use constant CMD_submit_multi => 0x00000021;
  1         1  
  1         50  
183 1     1   4 use constant CMD_submit_multi_resp => 0x80000021;
  1         2  
  1         49  
184 1     1   4 use constant CMD_alert_notification => 0x00000102;
  1         2  
  1         38  
185 1     1   4 use constant CMD_data_sm => 0x00000103;
  1         2  
  1         34  
186 1     1   4 use constant CMD_data_sm_resp => 0x80000103;
  1         2  
  1         43  
187              
188             ### Type of Number constants, see section 5.2.5, p. 117
189              
190 1     1   4 use constant TON_unknown => 0x00;
  1         1  
  1         38  
191 1     1   5 use constant TON_international => 0x01;
  1         2  
  1         39  
192 1     1   5 use constant TON_national => 0x02;
  1         2  
  1         39  
193 1     1   4 use constant TON_network_specific => 0x03;
  1         1  
  1         31  
194 1     1   11 use constant TON_subscriber_number => 0x04;
  1         2  
  1         41  
195 1     1   5 use constant TON_alphanumeric => 0x05;
  1         2  
  1         40  
196 1     1   5 use constant TON_abbreviated => 0x06;
  1         1  
  1         53  
197              
198             ### Number plan indicators, sec 5.2.6, p. 118
199              
200 1     1   5 use constant NPI_unknown => 0x00;
  1         1  
  1         133  
201 1     1   5 use constant NPI_isdn => 0x01; # E163/E164
  1         2  
  1         41  
202 1     1   6 use constant NPI_data => 0x03; # X.121
  1         1  
  1         57  
203 1     1   5 use constant NPI_telex => 0x04; # F.69
  1         1  
  1         37  
204 1     1   5 use constant NPI_land_mobile => 0x06; # E.212
  1         2  
  1         50  
205 1     1   5 use constant NPI_national => 0x08;
  1         1  
  1         36  
206 1     1   5 use constant NPI_private => 0x09;
  1         2  
  1         49  
207 1     1   5 use constant NPI_ERMES => 0x0a;
  1         2  
  1         54  
208 1     1   5 use constant NPI_internet => 0x0e; # IP
  1         2  
  1         1673  
209 1     1   8 use constant NPI_wap => 0x12; # WAP client id
  1         1  
  1         42  
210              
211             ### ESM class constants, these are additive, use or (|) to combine them (5.2.12, p.121)
212              
213 1     1   5 use constant ESM_mode_mask => 0x03;
  1         2  
  1         41  
214 1     1   4 use constant ESM_type_mask => 0x3c;
  1         1  
  1         37  
215 1     1   4 use constant ESM_feature_mask => 0xc0;
  1         2  
  1         40  
216              
217 1     1   4 use constant ESM_mode_default => 0x00; # usually store and forward
  1         2  
  1         36  
218 1     1   5 use constant ESM_mode_datagram => 0x01;
  1         1  
  1         40  
219 1     1   5 use constant ESM_mode_forward => 0x02; # i.e. transaction mode
  1         1  
  1         36  
220 1     1   4 use constant ESM_mode_store_and_forward => 0x03; # store and forward mode (even if not default)
  1         2  
  1         37  
221              
222 1     1   5 use constant ESM_type_default => 0x00; # default message type (i.e. normal message)
  1         1  
  1         36  
223 1     1   4 use constant ESM_type_delivery_receipt => 0x04; # SMSC Delivery receipt (SMSC->ESME only)
  1         1  
  1         38  
224 1     1   5 use constant ESM_type_delivery_ack => 0x08; # ESME delivery acknowledgement
  1         2  
  1         48  
225 1     1   12 use constant ESM_type_0011 => 0x0a;
  1         2  
  1         41  
226 1     1   5 use constant ESM_type_user_ack => 0x10; # ESME manual/user acknowledgement
  1         1  
  1         35  
227 1     1   5 use constant ESM_type_0101 => 0x14;
  1         1  
  1         41  
228 1     1   5 use constant ESM_type_conversation_abort => 0x18; # Korean CDMA (SMSC->ESME only)
  1         1  
  1         38  
229 1     1   4 use constant ESM_type_0111 => 0x1a;
  1         1  
  1         47  
230 1     1   4 use constant ESM_type_intermed_deliv_notif => 0x20; # Intermediate delivery notification (SMSC->ESME)
  1         2  
  1         40  
231 1     1   4 use constant ESM_type_1001 => 0x24;
  1         1  
  1         34  
232 1     1   38 use constant ESM_type_1010 => 0x28;
  1         3  
  1         41  
233 1     1   4 use constant ESM_type_1011 => 0x2a;
  1         2  
  1         39  
234 1     1   4 use constant ESM_type_1100 => 0x30;
  1         2  
  1         58  
235 1     1   5 use constant ESM_type_1101 => 0x34;
  1         1  
  1         32  
236 1     1   4 use constant ESM_type_1110 => 0x38;
  1         2  
  1         39  
237 1     1   4 use constant ESM_type_1111 => 0x3a;
  1         1  
  1         48  
238              
239 1     1   4 use constant ESM_feature_none => 0x00;
  1         2  
  1         42  
240 1     1   5 use constant ESM_feature_UDHI => 0x40; # User Data Header Ind, only relevant for MT short messages
  1         1  
  1         42  
241 1     1   5 use constant ESM_feature_reply_path => 0x80; # only relevant for GSM networks
  1         2  
  1         42  
242 1     1   3 use constant ESM_feature_UDHI_and_reply_path => 0xc0; # only relevant for GSM networks
  1         2  
  1         44  
243              
244             ### Registered delivery bits (5.2.17, p. 124)
245              
246 1     1   4 use constant REG_receipt_mask => 0x03;
  1         8  
  1         35  
247 1     1   5 use constant REG_ack_mask => 0x0c;
  1         1  
  1         40  
248 1     1   5 use constant REG_intermed_notif_mask => 0x80;
  1         1  
  1         47  
249            
250 1     1   4 use constant REG_receipt_none => 0x00;
  1         2  
  1         35  
251 1     1   5 use constant REG_receipt_always => 0x01; # receipt is returned for both success and failure
  1         1  
  1         42  
252 1     1   4 use constant REG_receipt_on_fail => 0x02;
  1         2  
  1         34  
253 1     1   5 use constant REG_receipt_res => 0x03;
  1         7  
  1         42  
254            
255 1     1   4 use constant REG_ack_none => 0x00;
  1         2  
  1         33  
256 1     1   4 use constant REG_ack_delivery => 0x04;
  1         1  
  1         40  
257 1     1   4 use constant REG_ack_user => 0x08;
  1         1  
  1         44  
258 1     1   5 use constant REG_ack_delivery_and_user => 0x0c;
  1         1  
  1         67  
259            
260 1     1   5 use constant REG_intermed_notif_none => 0x00;
  1         1  
  1         41  
261 1     1   4 use constant REG_intermed_notif => 0x10;
  1         1  
  1         34  
262              
263             ### submit_multi dest_flag constants (5.2.25, p. 129)
264              
265 1     1   5 use constant MULTIDESTFLAG_SME_Address => 1;
  1         1  
  1         44  
266 1     1   5 use constant MULTIDESTFLAG_dist_list => 2;
  1         1  
  1         40  
267              
268             ### message_state codes returned in query_sm_resp (5.2.28, table 5-6, p. 130)
269              
270 1     1   5 use constant MSGSTATE_enroute => 1;
  1         1  
  1         34  
271 1     1   4 use constant MSGSTATE_delivered => 2;
  1         2  
  1         38  
272 1     1   5 use constant MSGSTATE_expired => 3; # message validity period has expired
  1         1  
  1         35  
273 1     1   4 use constant MSGSTATE_deleted => 4;
  1         2  
  1         49  
274 1     1   4 use constant MSGSTATE_undeliverable => 5;
  1         9  
  1         40  
275 1     1   5 use constant MSGSTATE_accepted => 6; # i.e. message has been manually read on behalf of
  1         2  
  1         35  
276             # the subscriber by customer service
277 1     1   5 use constant MSGSTATE_unknown => 7; # message is in invalid state
  1         1  
  1         46  
278 1     1   4 use constant MSGSTATE_rejected => 8;
  1         2  
  1         35  
279              
280             ### Facility codes for V4 (used as arguments to bind, or the bits together) #4
281              
282 1     1   4 use constant GF_PVCY => 0x00000001; # V4 extended p.58 Privacy #4
  1         1  
  1         40  
283 1     1   4 use constant GF_SUBADDR => 0x00000002; # V4 extended p.64 #4
  1         1  
  1         35  
284 1     1   4 use constant NF_CC => 0x00080000; # V4 extended p.69 Call Control *** N.B: Spec has bug *** #4
  1         9  
  1         42  
285 1     1   4 use constant NF_PDC => 0x00010000; # V4 extended p.74 #4
  1         2  
  1         36  
286 1     1   4 use constant NF_IS136 => 0x00020000; # V4 extended p.80 (TDMA) #4
  1         2  
  1         35  
287 1     1   4 use constant NF_IS95A => 0x00040000; # V4 extended p.84 (CDMA) (TIA/EIA IS-637) #4
  1         2  
  1         248  
288              
289             ### Default value table that gets incorporated into smpp object unless
290             ### overridden in the constructor
291              
292 1         623 use constant Default => {
293              
294             async => 0,
295             port => 2255, # TCP port
296             timeout => 5, # Connection establishment timeout
297             listen => 120, # size of listen queue for new_listen()
298             mode => Transceiver, # Chooses type of bind #4> (Transceiver is illegal for v4) <4#
299              
300             enquire_interval => 0, # How often enquire PDU is sent during read_hard(). 0 == off
301              
302             ### Version dependent defaults. Mainly these are used to handle different #4
303             ### message header formats between v34 and v4 in a consistent way. Generally #4
304             ### these are set in the constructor based on the smpp_version field. #4
305              
306             smpp_version => 0x34, # Supported versions are 0x34 == 3.4 #4> and 0x40 == 4.0 <4#
307             head_templ => 'NNNN', # v3.4 'NNNN', #4> v4.0 'NNNNxxxx', must change in tandem with above <4#
308             head_len => 16, # v3.4 16, #4> v4.0 20, must change in tandem with smpp_version <4#
309             cmd_version => 0x00000000, # v3.4 0x00000000, #4> v4 0x00010000; to be or'd with cmd <4#
310              
311             ### Default values for bind parameters
312             ### For interpretation of these parameters refer to
313             ### sections 4.1 (p.51) and 5.2 (p. 116).
314              
315             system_id => '', # 5.2.1, usually needs to be supplied
316             password => '', # 5.2.2
317             system_type => '', # 5.2.3, often optional, leave empty
318             interface_version => 0x34, # 5.2.4
319             addr_ton => 0x00, # 5.2.5 type of number
320             addr_npi => 0x00, # 5.2.6 numbering plan indicator
321             address_range => '', # 5.2.7 regular expression matching numbers
322             facilities_mask => 0x00000000, # SMPP v4.0 extension #4
323              
324             ### Default values for submit_sm and deliver_sm
325              
326             service_type => '', # NULL: SMSC defaults, #4> on v4 this is message_class <4#
327             message_class => 0xffff, # v4: 0xffff = not required, 0-0x0fff = non replace, #4
328             # 0x8000-0x8fff = replace types, others reserved (v4 p.32) #4
329             source_addr_ton => 0x00, #? not known, see sec 5.2.5
330             source_addr_npi => 0x00, #? not known, see sec 5.2.6
331             source_addr => '', ## NULL: not known. You should set this for reply to work.
332             dest_addr_ton => 0x00, #??
333             dest_addr_npi => 0x00, #??
334             destination_addr => '', ### Destination address must be supplied
335             esm_class => 0x00, # Default mode (store and forward) and type (5.2.12, p.121)
336             messaging_mode => 0x00, # v4 Default mode (store and forward) (v4, table 6-8, p.33) #4
337             msg_reference => '', # v4, either empty or 9 digits. For user messages 4 first digits must be 0 #4
338             protocol_id => 0x00, ### 0 works for TDMA & CDMA, for GSM set according to GSM 03.40
339             telematic_interworking => 0xff, # v4 name for v34 protocol_id (SMPP V4 Telematic Interworking Identifiers, sec 7.11, p.68) #4
340             priority_flag => 0, # non-priority/bulk/normal
341             priority_level => 0xff, # v4: 0=lowest, 1=lowmid, 2=himid, 3=highest, 4-254 reserved, 255 default #4
342             schedule_delivery_time => '', # NULL: immediate delivery
343             validity_period => '', # NULL: SMSC default validity period
344             registered_delivery => 0x00, # no receipt, no ack, no intermed notif
345             registered_delivery_mode => 0x00, # v4: 0=no receipt, 1=receipt required, 2=nondelivery receipt confirmation #4
346             replace_if_present_flag => 0, # no replacement
347             data_coding => 0, # SMSC default alphabet
348             sm_default_msg_id => 0, # Do not use canned message
349              
350             ### default values for query_sm_resp
351             final_date => '', # NULL: message has not yet reached final state
352             error_code => 0, # no error
353             network_error_code => 0, # v4 no error? #4
354             ### default values for alert_notification
355             esme_addr_ton => 0x00,
356             esme_addr_npi => 0x00,
357              
358             ### default values used by cancel_sm
359             message_id => '', # NULL: other parameters specify message to be cancelled
360              
361             ### Table of PDU handlers. These PDUs are automatically
362             ### handled during wait_pdu() (as opposed to being discarded).
363             ### they are called as
364             ### $smpp->handler($pdu);
365             ### N.B. because the command number is constant, a comma must be used as separator
366             ### to prevent interpretation as string. (Thanks Matthias Meyser for pointing this out.)
367              
368             handlers => {
369             CMD_enquire_link, \&handle_enquire_link,
370             CMD_enquire_link_v4, \&handle_enquire_link, #4
371             },
372 1     1   5 };
  1         1  
373              
374             ### Optional parameter tags, see sec 5.3.2, Table 5-7, pp.132-133
375             ### See also Sec 4.8.1 "TLV Tag", Table 4-60 "TLV Tag Definitions", pp. 135-137
376              
377 1         18618 use constant param_tab => {
378             0x0005 => { name => 'dest_addr_subunit', technology => 'GSM', },
379             0x0006 => { name => 'dest_network_type', technology => 'Generic', },
380             0x0007 => { name => 'dest_bearer_type', technology => 'Generic', },
381             0x0008 => { name => 'dest_telematics_id', technology => 'GSM', },
382              
383             0x000d => { name => 'source_addr_subunit', technology => 'GSM', },
384             0x000e => { name => 'source_network_type', technology => 'Generic', },
385             0x000f => { name => 'source_bearer_type', technology => 'Generic', },
386             0x0010 => { name => 'source_telematics_id', technology => 'GSM', },
387              
388             0x0017 => { name => 'qos_time_to_live', technology => 'Generic', },
389             0x0019 => { name => 'payload_type', technology => 'Generic', },
390             0x001d => { name => 'additional_status_info_text', technology => 'Generic', },
391             0x001e => { name => 'receipted_message_id', technology => 'Generic', },
392             0x0030 => { name => 'ms_msg_wait_facilities', technology => 'GSM', },
393              
394             0x0101 => { name => 'PVCY_AuthenticationStr', technology => '? (J-Phone)', }, # V4ext pp.58-62 #4
395             # "\x01\x00\x00" 0x010000 no privacy option
396              
397             0x0201 => { name => 'privacy_indicator', technology => 'CDMA,TDMA', },
398             0x0202 => { name => 'source_subaddress', technology => 'CDMA,TDMA', }, # V4ext pp. 65-67 #4
399             # Aka PDC_Originator_Subaddr, "\x01\x00\x00" 0x010000 undefined #4> (J-Phone) <4#
400             0x0203 => { name => 'dest_subaddress', technology => 'CDMA,TDMA', }, # V4ext pp. 65-67 #4
401             # Aka PDC_Destination_Subaddr, "\x01\x00\x00" 0x010000 undefined #4> (J-Phone) <4#
402             0x0204 => { name => 'user_message_reference', technology => 'Generic', },
403             0x0205 => { name => 'user_response_code', technology => 'CDMA,TDMA', },
404             0x020a => { name => 'source_port', technology => 'WAP', },
405             0x020b => { name => 'destination_port', technology => 'WAP', },
406             0x020c => { name => 'sar_msg_ref_num', technology => 'Generic', },
407             0x020d => { name => 'language_indicator', technology => 'CDMA,TDMA', },
408             0x020e => { name => 'sar_total_segments', technology => 'Generic', },
409             0x020f => { name => 'sar_segment_seqnum', technology => 'Generic', },
410             0x0210 => { name => 'sc_interface_version', technology => 'Generic', }, # bind_*_resp
411              
412             0x0301 => { name => 'CC_CBN', technology => 'V4', }, # V4ext p.70 Call Back Number #4
413             0x0302 => { name => 'callback_num_pres_ind', technology => 'TDMA', }, # V4ext p.71 CC_CBNPresentation #4
414             0x0303 => { name => 'callback_num_atag', technology => 'TDMA', }, # V4ext p.71 CC_CBNAlphaTag #4
415             0x0304 => { name => 'number_of_messages', technology => 'CDMA', }, # V4ext p.72 CC_NumberOfMessages #4
416             0x0381 => { name => 'callback_num', technology => 'CDMA,TDMA,GSM,iDEN', },
417              
418             0x0420 => { name => 'dpf_result', technology => 'Generic', },
419             0x0421 => { name => 'set_dpf', technology => 'Generic', },
420             0x0422 => { name => 'ms_availability_status', technology => 'Generic', },
421             0x0423 => { name => 'network_error_code', technology => 'Generic', },
422             0x0424 => { name => 'message_payload', technology => 'Generic', },
423             0x0425 => { name => 'delivery_failure_reason', technology => 'Generic', },
424             0x0426 => { name => 'more_messages_to_send', technology => 'GSM', },
425             0x0427 => { name => 'message_state', technology => 'Generic', },
426             0x0428 => { name => 'congestion_state', technology => 'Generic', },
427              
428             0x0501 => { name => 'ussd_service_op', technology => 'GSM (USSD)', },
429              
430             0x0600 => { name => 'broadcast_channel_indicator', technology => 'GSM', },
431             0x0601 => { name => 'broadcast_content_type', technology => 'CDMA, TDMA, GSM', },
432             0x0602 => { name => 'broadcast_content_type_info', technology => 'CDMA, TDMA', },
433             0x0603 => { name => 'broadcast_message_class', technology => 'GSM', },
434             0x0604 => { name => 'broadcast_rep_num', technology => 'GSM', },
435             0x0605 => { name => 'broadcast_frequency_interval', technology => 'CDMA, TDMA, GSM', },
436             0x0606 => { name => 'broadcast_area_identifier', technology => 'CDMA, TDMA, GSM', },
437             0x0607 => { name => 'broadcast_error_status', technology => 'CDMA, TDMA, GSM', },
438             0x0608 => { name => 'broadcast_area_success', technology => 'GSM', },
439             0x0609 => { name => 'broadcast_end_time', technology => 'CDMA, TDMA, GSM', },
440             0x060a => { name => 'broadcast_service_group', technology => 'CDMA, TDMA', },
441             0x060b => { name => 'billing_identification', technology => 'Generic', },
442             0x060d => { name => 'source_network_id', technology => 'Generic', },
443             0x060e => { name => 'dest_network_id', technology => 'Generic', },
444             0x060f => { name => 'source_node_id', technology => 'Generic', },
445             0x0610 => { name => 'dest_node_id', technology => 'Generic', },
446             0x0611 => { name => 'dest_addr_np_resolution', technology => 'CDMA, TDMA (US Only)', },
447             0x0612 => { name => 'dest_addr_np_information', technology => 'CDMA, TDMA (US Only)', },
448             0x0613 => { name => 'dest_addr_np_country', technology => 'CDMA, TDMA (US Only)', },
449              
450             0x1201 => { name => 'display_time', technology => 'CDMA,TDMA', }, # IS136_DisplayTime
451             0x1203 => { name => 'sms_signal', technology => 'TDMA', },
452             0x1204 => { name => 'ms_validity', technology => 'CDMA,TDMA', },
453              
454             0x1304 => { name => 'IS95A_AlertOnDelivery', technology => 'CDMA', }, # V4ext p.85 #4
455             0x1306 => { name => 'IS95A_LanguageIndicator', technology => 'CDMA', }, # V4ext p.86 #4
456             # "\x00" 0x00 = Unknown, 0x01 = english, 0x02 = french, 0x03 = spanish
457             0x130c => { name => 'alert_on_message_delivery', technology => 'CDMA', },
458             0x1380 => { name => 'its_reply_type', technology => 'CDMA', },
459             0x1383 => { name => 'its_session_info', technology => 'CDMA Korean [KORITS]', },
460              
461             # from http://docs.roottori.fi/display/MSGAPI/SMPP+commands
462             # On the other hand, http://sms-clearing.com/downloads/gateway/7_SMPP.pdf
463             # lists tag 0x1403 as holding both MCC and MNC in format "MCC/MNC"
464             0x1402 => { name => 'operator_id', technology => 'vendor extension', },
465             0x1403 => { name => 'tariff', technology => 'Mobile Network Code vendor extension', },
466             # valyakol@gmail.com reports that these should be
467             #0x1402 => { name => 'mBlox_operator', technology => 'Generic', },
468             #0x1403 => { name => 'mBlox_rate', technology => 'Generic', },
469             0x1450 => { name => 'mcc', technology => 'Mobile Country Code vendor extension', },
470             0x1451 => { name => 'mnc', technology => 'Mobile Network Code vendor extension', },
471              
472             0x1101 => { name => 'PDC_MessageClass', technology => '? (J-Phone)', }, # V4ext p.75 #4
473             # "\x20\x00" 0x2000 Sky Mail (service name of J-Phone SMS) #4
474             # 0x2033 - 0x20fe Vendor defined
475             # 0x1001 Coordinator (sender is able to send msg to more than two users)
476             # 0x1002 Hotline (two users communicate using private line)
477             # 0x1003 Relay Mail (Message relays user to user in turn by sender is specified)
478             # 0x1004 Greeting service (J-Phone original) (sender can spec. deiv. date and time) #4
479              
480             0x1102 => { name => 'PDC_PresentationOption', technology => '? (J-Phone)', }, # V4ext p.76 #4
481             # "\x00\xff\xff\xff" 0x00ffffff Receiver defined option
482             # "\x01\xff\xff\xff" 0x01ffffff MS
483              
484             0x1103 => { name => 'PDC_AlertMechanism', technology => '? (J-Phone)', }, # V4ext p.76 #4
485             # "\x01" 0x01 Alert tones level 1, 0x00 = no detectable alert, 0x0f = emergency, 0xff = default
486              
487             0x1104 => { name => 'PDC_Teleservice', technology => '? (J-Phone)', }, # V4 p.77 #4
488             # "\x01" 0x01 Generalized message, 0x00 reserved, 0x02 two way, 0x03 concateneated
489              
490             0x1105 => { name => 'PDC_MultiPartMessage', technology => '? (J-Phone)', # V4 p.77 #4
491             format => 'nCC', # MessageNumber, current_Sequence_Number, Maximum_Sequence_Number
492             },
493             # "\0\0\0\0" 0x00000000 undefined, i.e. no multipart
494              
495             0x1106 => { name => 'PDC_PredefinedMsg', technology => '? (J-Phone)', }, # V4 p.78 #4
496             # "\x00" 0x00 Undefined. This can be used to indicate preformatted messages, possibly with Kanji
497             #0x0101 => { name => '', technology => '? (J-Phone)', },
498              
499             ### Tags not specified in v3.4 specification
500             # *** dear reader, please add here any old or nonstandard tags
501             # that you know to exist so that this module becomes more
502             # useful
503 1     1   5 };
  1         1  
504              
505             ### invert the param_tab so we can get from name to code
506              
507             for my $tag (keys %{¶m_tab}) {
508             $param_by_name{param_tab->{$tag}->{name}} = $tag;
509             }
510              
511             sub format_a_line {
512 0     0 0 0 my ($tt, $prefix) = shift;
513 0         0 my $t=$tt;
514 0         0 $t=~tr[\x20-\x7e][]c;
515             # sprintf("$prefix%04x: " . '%02x ' x length($1) . "\t$t\n", $n+=16, map {ord} split('', $1));
516             }
517              
518             sub hexdump {
519 22     22 0 508 my ($data, $prefix) = @_;
520 22         30 my $n = -16;
521 22         95 $data =~ s/(.{1,16})/
522 22         513 sprintf("$prefix%04x: " . '%02x ' x length($1) . "\n", $n+=16, map {ord} split('', $1))/ge;
  137         642  
523             # sprintf("$prefix%04x: " . '%02x ' x length($1) . "\t$1\n", $n+=16, map {ord} split('', $1))/ge;
524             # format_a_line($1, $prefix)/gsex;
525 22         5560 return $data;
526             }
527              
528             ### The optional values are encoded as TLV (tag, len, value) triplets where
529             ### tag and length are 16 bit network byteorder and value is as much as
530             ### the length says (length does not include tag or length of the length
531             ### field itself).
532              
533             sub decode_optional_params {
534 7     7 0 18 my ($pdu, $offset) = @_;
535 7         26 while ($offset < length($pdu->{data})) {
536 112         281 my ($tag, $len) = unpack 'nn', substr($pdu->{data}, $offset);
537 112         345 my ($val) = unpack "a$len", substr($pdu->{data}, $offset+4);
538 112         299 $pdu->{$tag} = $val; # value is always accessible via numeric tag
539 112 50       240 if (defined param_tab->{$tag}) {
540 112         348 $pdu->{param_tab->{$tag}->{name}} = $val; # assign symbolic name
541             } else {
542 0         0 warn "Unknown tag (offset $offset): $tag, len=".length($val).", val=`$val'";
543             }
544 112         325 $offset += 4 + length($val);
545             }
546             }
547              
548             sub encode_optional_params {
549 86     86 0 115 my $data = '';
550 86         184 while (@_) { # N.B. by using array instead of hash we can control order of items
551 464         507 my $opt_param = shift;
552 464         521 my $val = shift;
553 464 100       1183 next if !defined $opt_param; # skip mandatory parameters that were taken
554 112 50       250 if ($param_by_name{$opt_param}) {
    0          
555 112         409 $data .= pack 'nna*', $param_by_name{$opt_param}, length($val), $val;
556             } elsif ($opt_param =~ /^\d+$/) { # specification by numeric tag
557 0 0 0     0 if ($val > -128 && $val < 127) {
    0 0        
558 0         0 $data .= pack 'nnc', $opt_param, 1, $val;
559             } elsif ($val > -32768 && $val < 32767) {
560 0         0 $data .= pack 'nnn!', $opt_param, 2, $val;
561             } else {
562 0         0 $data .= pack 'nnN!', $opt_param, 4, $val;
563             }
564             } else {
565 0         0 warn "Unknown optional parameter `$opt_param', skipping";
566             }
567             }
568 86         191 return $data;
569             }
570              
571             ### return $_[0]->req_backend($op, &encode, @_);
572              
573             sub req_backend {
574 41     41 0 71 my $me = shift;
575 41         61 my $op = shift;
576 41         63 my $data = shift;
577 41         57 my ($async, $seq);
578 41         43 shift; # skip over second copy of $me
579              
580             ### Extract operational parameters that should not make part of PDU
581            
582 41         193 for (my $i=0; $i <= $#_; $i+=2) {
583 363 100       1117 next if !defined $_[$i];
584 107 50       372 if ($_[$i] eq 'async') { $async = splice @_,$i,2,undef,undef; }
  0 50       0  
585 0         0 elsif ($_[$i] eq 'seq') { $seq = splice @_,$i,2,undef,undef; }
586             }
587 41 50       88 $async = ${*$me}{async} if !defined $async;
  41         124  
588 41 50       102 if (!defined $seq) {
589 41         47 $seq = ++(${*$me}{seq});
  41         98  
590             }
591              
592 41         177 $data .= &encode_optional_params; # will process remaining @_
593              
594 41         105 my $header = pack(${*$me}{head_templ}, ${*$me}{head_len}+length($data),
  41         104  
  41         153  
595 41         57 $op|${*$me}{cmd_version}, 0, $seq);
596              
597 41 50       105 warn "req Header:\n".hexdump($header,"\t") if $trace;
598 41 50       84 warn "req Body:\n".hexdump($data,"\t") if $trace;
599 41         199 $me->syswrite($header.$data);
600 41 50       1650 return $seq if $async;
601            
602             # Synchronous operation: wait for response
603              
604 0 0       0 warn "req sent, waiting response" if $trace;
605 0         0 return $me->wait_pdu($op | ${*$me}{cmd_version} | 0x80000000, $seq);
  0         0  
606             }
607              
608             ### return $_[0]->resp_backend($op, &encode, @_);
609              
610             sub resp_backend {
611 45     45 0 79 my $me = shift;
612 45         53 my $op = shift;
613 45         72 my $data = shift;
614 45         57 my ($async, $seq, $status);
615 45         52 shift; # skip over second copy of $me
616              
617             ### Extract operational parameters that should not make part of PDU
618            
619 45         141 for (my $i=0; $i <= $#_; $i+=2) {
620 101 100       269 next if !defined $_[$i];
621 52 50       184 if ($_[$i] eq 'async') { $async = splice @_,$i,2,undef,undef; }
  0 100       0  
    100          
622 45         204 elsif ($_[$i] eq 'seq') { $seq = splice @_,$i,2,undef,undef; }
623 2         8 elsif ($_[$i] eq 'status') { $status = splice @_,$i,2,undef,undef; }
624             }
625 45 50       95 croak "seq must be supplied" if !defined $seq;
626 45 100       139 $status = 0 if !defined $status;
627              
628 45         90 $data .= &encode_optional_params; # will process remaining @_
629              
630 45         125 my $header = pack(${*$me}{head_templ}, ${*$me}{head_len}+length($data),
  45         114  
  45         174  
631 45         69 $op|${*$me}{cmd_version}, $status, $seq);
632             #warn "$op,$seq==>".join(':',@_);
633              
634 45 50       127 warn "resp Header:\n".hexdump($header, "\t") if $trace;
635 45 50       84 warn "resp Body:\n".hexdump($data, "\t") if $trace;
636 45         194 $me->syswrite($header.$data);
637 45         1458 return $seq;
638             }
639              
640             ### These triplets occur often enough to warrant common function
641              
642             sub decode_source_addr {
643 31     31 0 69 my ($pdu, $data) = @_;
644 31         226 ($pdu->{source_addr_ton}, # 2 C
645             $pdu->{source_addr_npi}, # 3 C
646             $pdu->{source_addr}) = unpack 'CCZ*', $data;
647 31         87 return 1 + 1 + length($pdu->{source_addr}) + 1;
648             }
649              
650             sub decode_destination_addr {
651 21     21 0 81 my ($pdu, $data) = @_;
652 21         113 ($pdu->{dest_addr_ton}, # 2 C
653             $pdu->{dest_addr_npi}, # 3 C
654             $pdu->{destination_addr}) = unpack 'CCZ*', $data;
655 21         57 return 1 + 1 + length($pdu->{destination_addr}) + 1;
656             }
657              
658             sub decode_source_and_destination {
659 21     21 0 56 my ($pdu, $data) = @_;
660 21         54 my $len = decode_source_addr($pdu, $data);
661 21         65 $len += decode_destination_addr($pdu, substr($data, $len));
662 21         69 return $len;
663             }
664              
665             ### Some PDUs do not have any body (mandatory parameters)
666              
667             sub decode_empty {
668             #my $pdu = shift;
669 22     22 0 37 return 0;
670             }
671              
672             ###
673             ### Public API functions for emitting trivial empty PDUs
674             ###
675              
676 2     2 1 113 sub unbind { $_[0]->req_backend(CMD_unbind, '', @_) }
677              
678             sub enquire_link {
679 2     2 1 94 my $me = $_[0];
680 2 100       7 return $me->req_backend(${*$me}{smpp_version}==0x40?CMD_enquire_link_v4:CMD_enquire_link, '', @_); #4
  2         16  
681 0         0 $me->req_backend(CMD_enquire_link, '', @_);
682             }
683              
684             sub enquire_link_resp {
685 2     2 1 88 my $me = $_[0];
686 2 100       4 return $me->resp_backend(${*$me}{smpp_version}==0x40?CMD_enquire_link_resp_v4:CMD_enquire_link_resp, '', @_); #4
  2         15  
687 0         0 $me->resp_backend(CMD_enquire_link_resp, '', @_);
688             }
689              
690 2     2 1 88 sub generic_nack { $_[0]->resp_backend(CMD_generic_nack, '', @_) }
691 2     2 1 104 sub unbind_resp { $_[0]->resp_backend(CMD_unbind_resp, '', @_) }
692 4     4 1 235 sub replace_sm_resp { $_[0]->resp_backend(CMD_replace_sm_resp, '', @_) }
693 4     4 1 237 sub cancel_sm_resp { $_[0]->resp_backend(CMD_cancel_sm_resp, '', @_) }
694 2     2 0 178 sub delivery_receipt_resp { $_[0]->resp_backend(CMD_delivery_receipt_resp, '', @_) }
695              
696             ###
697             ### All bind operations have same PDU format (4.1.1, p.46)
698             ###
699              
700             sub decode_bind {
701 4     4 0 5 my $pdu = shift;
702 4         7 my $me = shift;
703 4         17 ($pdu->{system_id}) = unpack 'Z*', $pdu->{data}; # 1 Z
704 4         10 my $len = length($pdu->{system_id}) + 1;
705 4         20 ($pdu->{password}) = unpack 'Z*', substr($pdu->{data}, $len); # 2 Z
706 4         9 $len += length($pdu->{password}) + 1;
707 4         11 ($pdu->{system_type}) = unpack 'Z*', substr($pdu->{data}, $len); # 3 Z
708 4         9 $len += length($pdu->{system_type}) + 1;
709 4         25 ($pdu->{interface_version}, # 4
710             $pdu->{addr_ton}, # 5
711             $pdu->{addr_npi}, # 6
712             $pdu->{address_range}) = unpack 'CCCZ*', substr($pdu->{data}, $len);
713 4         10 $len += 3 + length($pdu->{address_range}) + 1;
714 4 100       11 if (${*$me}{smpp_version}==0x40) { #4
  4         14  
715 2         7 ($pdu->{facilities_mask}) = unpack 'N', substr($pdu->{data}, $len); #4
716 2         4 $len += 4; #4
717             } #4
718 4         17 return $len;
719             }
720              
721             sub encode_bind {
722 4     4 0 9 my $me = $_[0];
723 4         6 my ($system_id, $password, $system_type, $interface_version,
724             $addr_ton, $addr_npi, $address_range, $facilities_mask);
725              
726             ### Extract mandatory parameters from argument stream
727            
728 4         15 for (my $i=1; $i <= $#_; $i+=2) {
729 16 50       37 next if !defined $_[$i];
730 16 100       75 if ($_[$i] eq 'system_id') { $system_id = splice @_,$i,2,undef,undef; }
  2 100       10  
    100          
    100          
    100          
    100          
    100          
    100          
    50          
731 2         8 elsif ($_[$i] eq 'password') { $password = splice @_,$i,2,undef,undef; }
732 2         7 elsif ($_[$i] eq 'system_type') { $system_type = splice @_,$i,2,undef,undef; }
733 1         3 elsif ($_[$i] eq 'interface_version') { $interface_version = splice @_,$i,2,undef,undef; }
734 2         8 elsif ($_[$i] eq 'interface_type') { $interface_version = splice @_,$i,2,undef,undef; }
735 2         7 elsif ($_[$i] eq 'addr_ton') { $addr_ton = splice @_,$i,2,undef,undef; }
736 2         7 elsif ($_[$i] eq 'addr_npi') { $addr_npi = splice @_,$i,2,undef,undef; }
737 2         6 elsif ($_[$i] eq 'address_range') { $address_range = splice @_,$i,2,undef,undef; }
738 1         5 elsif ($_[$i] eq 'facilities_mask') { $facilities_mask = splice @_,$i,2,undef,undef; } #4
739             }
740              
741             ### Apply defaults for those mandatory arguments that were not specified
742            
743 4 100       11 $system_id = ${*$me}{system_id} if !defined $system_id;
  2         8  
744 4 100       10 $password = ${*$me}{password} if !defined $password;
  2         5  
745 4 100       49 $system_type = ${*$me}{system_type} if !defined $system_type;
  2         6  
746 4 100       9 $interface_version = ${*$me}{interface_version} if !defined $interface_version;
  1         2  
747 4 100       9 $addr_ton = ${*$me}{addr_ton} if !defined $addr_ton;
  2         5  
748 4 100       10 $addr_npi = ${*$me}{addr_npi} if !defined $addr_npi;
  2         5  
749 4 100       9 $address_range = ${*$me}{address_range} if !defined $address_range;
  2         4  
750 4 100       9 $facilities_mask = ${*$me}{facilities_mask} if !defined $facilities_mask; #4
  3         6  
751              
752             ### N.B. v3.4 last argument, $facilities_mask, will be ignored because #4
753             ### template misses N, v4.0 it will be used because template has N #4
754 4 100       6 return pack(${*$me}{smpp_version}==0x40?'Z*Z*Z*CCCZ*N':'Z*Z*Z*CCCZ*', #4
  4         41  
755             $system_id, $password, $system_type, #4
756             $interface_version, $addr_ton, $addr_npi, #4
757             $address_range, $facilities_mask); #4
758 0         0 return pack('Z*Z*Z*CCCZ*',
759             $system_id, $password, $system_type,
760             $interface_version, $addr_ton, $addr_npi,
761             $address_range);
762             }
763              
764             ### All bind operations have same response format (4.1.2, p.47)
765              
766             sub decode_bind_resp_v34 {
767 3     3 0 8 my $pdu = shift;
768 3         4 my $me = shift;
769 3         16 ($pdu->{system_id}) = unpack 'Z*', $pdu->{data};
770 3         9 return length($pdu->{system_id}) + 1;
771             }
772              
773             #4#cut
774             sub decode_bind_resp_v4 {
775 2     2 0 5 my $pdu = shift;
776 2         4 my $me = shift;
777 2         9 ($pdu->{system_id}) = unpack 'Z*', $pdu->{data};
778 2         6 my $len = length($pdu->{system_id}) + 1;
779 2         8 ($pdu->{facilities_mask}) = unpack 'N', substr($pdu->{data}, $len);
780 2         6 return $len + 4;
781             }
782             #4#end
783              
784             sub encode_bind_resp {
785 5     5 0 8 my $me = $_[0];
786 5         6 my ($system_id, $facilities_mask);
787              
788 5         17 for (my $i=1; $i <= $#_; $i+=2) {
789 12 50       26 next if !defined $_[$i];
790 12 100       42 if ($_[$i] eq 'system_id') { $system_id = splice @_,$i,2,undef,undef; }
  3 100       17  
791 1         4 elsif ($_[$i] eq 'facilities_mask') { $facilities_mask = splice @_,$i,2,undef,undef; } #4
792             }
793 5 100       13 $system_id = ${*$me}{system_id} if !defined $system_id;
  2         7  
794 5 100       11 $facilities_mask = ${*$me}{facilities_mask} if !defined $facilities_mask; #4
  4         13  
795 5 100       9 return pack(${*$me}{smpp_version}==0x40?'Z*N':'Z*', $system_id, $facilities_mask); #4
  5         39  
796 0         0 return pack('Z*', $system_id);
797             }
798              
799             ###
800             ### Public API functions to emit binds and bind_resps.
801             ###
802              
803 2     2 1 190 sub bind_transceiver { $_[0]->req_backend(CMD_bind_transceiver, &encode_bind, @_) }
804 2     2 1 198 sub bind_transmitter { $_[0]->req_backend(CMD_bind_transmitter, &encode_bind, @_) }
805 0     0 1 0 sub bind_receiver { $_[0]->req_backend(CMD_bind_receiver, &encode_bind, @_) }
806              
807 3     3 1 143 sub bind_transceiver_resp { $_[0]->resp_backend(CMD_bind_transceiver_resp, &encode_bind_resp, @_) }
808 2     2 1 105 sub bind_transmitter_resp { $_[0]->resp_backend(CMD_bind_transmitter_resp, &encode_bind_resp, @_) }
809 0     0 1 0 sub bind_receiver_resp { $_[0]->resp_backend(CMD_bind_receiver_resp, &encode_bind_resp, @_) }
810              
811             ### outbind (4.1.7.1)
812              
813             sub decode_outbind_v34 {
814 2     2 0 6 my $pdu = shift;
815 2         4 my $me = shift;
816 2         12 ($pdu->{system_id}) = unpack 'Z*', $pdu->{data};
817 2         7 my $len = length($pdu->{system_id}) + 1;
818 2         12 ($pdu->{password}) = unpack 'Z*', substr($pdu->{data}, $len);
819 2         6 return $len + length($pdu->{password}) + 1;
820             }
821              
822             #4#cut
823             sub decode_outbind_v4 {
824 2     2 0 4 my $pdu = shift;
825 2         4 my $me = shift;
826 2         11 ($pdu->{password}) = unpack 'Z*', $pdu->{data};
827 2         5 return length($pdu->{password}) + 1;
828             }
829             #4#end
830              
831             sub encode_outbind {
832 4     4 0 10 my $me = $_[0];
833 4         5 my ($system_id, $password);
834              
835 4         27 for (my $i=1; $i <= $#_; $i+=2) {
836 7 50       31 next if !defined $_[$i];
837 7 100       41 if ($_[$i] eq 'system_id') { $system_id = splice @_,$i,2,undef,undef; }
  1 100       6  
838 2         11 elsif ($_[$i] eq 'password') { $password = splice @_,$i,2,undef,undef; }
839             }
840            
841 4 100       16 $system_id = ${*$me}{system_id} if !defined $system_id;
  3         13  
842 4 100       13 $password = ${*$me}{password} if !defined $password;
  2         7  
843             ### N.B. v4 does not have system_id. "CX" construct skips over this parameter #4
844 4 100       8 return pack(${*$me}{smpp_version}==0x40?'CXZ*':'Z*Z*', $system_id, $password); #4
  4         74  
845 0         0 return pack('Z*Z*', $system_id, $password);
846             }
847              
848             sub outbind {
849 4     4 1 295 my $me = $_[0];
850 4 50       38 push @_, seq => ++(${*$me}{seq}) unless grep $_ eq 'seq', @_;
  4         64  
851 4         18 return $me->resp_backend(CMD_outbind, &encode_outbind, @_);
852             }
853              
854             ### outbind does not have response
855              
856             ### submit (4.4.1), deliver (4.6.1) (both use same PDU format), p.59
857              
858             sub decode_submit_v34 {
859 6     6 0 12 my $pdu = shift;
860 6         31 ($pdu->{service_type}) = unpack 'Z*', $pdu->{data};
861 6         14 my $len = length($pdu->{service_type}) + 1;
862 6         25 $len += decode_source_and_destination($pdu, substr($pdu->{data}, $len));
863            
864 6         49 ($pdu->{esm_class}, # 8
865             $pdu->{protocol_id}, # 9
866             $pdu->{priority_flag}, # 10
867             $pdu->{schedule_delivery_time}) = unpack 'CCCZ*', substr($pdu->{data}, $len);
868 6         16 $len += 1 + 1 + 1 + length($pdu->{schedule_delivery_time}) + 1;
869              
870 6         24 ($pdu->{validity_period}) = unpack 'Z*', substr($pdu->{data}, $len);
871 6         11 $len += length($pdu->{validity_period}) + 1;
872              
873 6         7 my $sm_length;
874 6         90 ($pdu->{registered_delivery}, # 13
875             $pdu->{replace_if_present_flag}, # 14
876             $pdu->{data_coding}, # 15
877             $pdu->{sm_default_msg_id}, # 16
878             $sm_length, # 17
879             # 1
880             # 12345678901234567 8
881             ) = unpack 'CCCCC', substr($pdu->{data}, $len);
882 6         12 $len += 1 + 1 + 1 + 1 + 1;
883 6         27 ($pdu->{short_message} # 18
884             ) = unpack "a$sm_length", substr($pdu->{data}, $len);
885 6         14 return $len + $sm_length;
886             }
887              
888             sub encode_submit_v34 {
889 6     6 0 12 my $me = $_[0];
890 6         10 my ($service_type, $source_addr_ton, $source_addr_npi, $source_addr,
891             $dest_addr_ton, $dest_addr_npi, $destination_addr,
892             $esm_class, $protocol_id, $priority_flag,
893             $schedule_delivery_time, $validity_period,
894             $registered_delivery, $replace_if_present_flag, $data_coding,
895             $sm_default_msg_id, $short_message);
896              
897             ### Extract mandatory parameters from argument stream
898            
899 6         22 for (my $i=1; $i <= $#_; $i+=2) {
900 114 50       212 next if !defined $_[$i];
901 114 100       938 if ($_[$i] eq 'service_type') { $service_type = splice @_,$i,2,undef,undef; }
  4 100       20  
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
902 4         13 elsif ($_[$i] eq 'source_addr_ton') { $source_addr_ton = splice @_,$i,2,undef,undef; }
903 4         15 elsif ($_[$i] eq 'source_addr_npi') { $source_addr_npi = splice @_,$i,2,undef,undef; }
904 4         17 elsif ($_[$i] eq 'source_addr') { $source_addr = splice @_,$i,2,undef,undef; }
905 4         13 elsif ($_[$i] eq 'dest_addr_ton') { $dest_addr_ton = splice @_,$i,2,undef,undef; }
906 4         14 elsif ($_[$i] eq 'dest_addr_npi') { $dest_addr_npi = splice @_,$i,2,undef,undef; }
907 6         25 elsif ($_[$i] eq 'destination_addr') { $destination_addr = splice @_,$i,2,undef,undef; }
908 4         13 elsif ($_[$i] eq 'esm_class') { $esm_class = splice @_,$i,2,undef,undef; }
909 4         16 elsif ($_[$i] eq 'protocol_id') { $protocol_id = splice @_,$i,2,undef,undef; }
910 4         13 elsif ($_[$i] eq 'priority_flag') { $priority_flag = splice @_,$i,2,undef,undef; }
911 4         13 elsif ($_[$i] eq 'schedule_delivery_time') { $schedule_delivery_time = splice @_,$i,2,undef,undef; }
912 4         12 elsif ($_[$i] eq 'validity_period') { $validity_period = splice @_,$i,2,undef,undef; }
913 4         12 elsif ($_[$i] eq 'registered_delivery') { $registered_delivery = splice @_,$i,2,undef,undef; }
914 4         14 elsif ($_[$i] eq 'replace_if_present_flag') { $replace_if_present_flag = splice @_,$i,2,undef,undef; }
915 4         22 elsif ($_[$i] eq 'data_coding') { $data_coding = splice @_,$i,2,undef,undef; }
916 4         13 elsif ($_[$i] eq 'sm_default_msg_id') { $sm_default_msg_id = splice @_,$i,2,undef,undef; }
917 2         8 elsif ($_[$i] eq 'short_message') { $short_message = splice @_,$i,2,undef,undef; }
918             }
919              
920             ### Apply defaults for those mandatory arguments that were not specified
921            
922 6 100       15 $service_type = ${*$me}{service_type} if !defined $service_type;
  2         8  
923 6 100       15 $source_addr_ton = ${*$me}{source_addr_ton} if !defined $source_addr_ton;
  2         5  
924 6 100       16 $source_addr_npi = ${*$me}{source_addr_npi} if !defined $source_addr_npi;
  2         13  
925 6 100       16 $source_addr = ${*$me}{source_addr} if !defined $source_addr;
  2         4  
926 6 100       14 $dest_addr_ton = ${*$me}{dest_addr_ton} if !defined $dest_addr_ton;
  2         6  
927 6 100       15 $dest_addr_npi = ${*$me}{dest_addr_npi} if !defined $dest_addr_npi;
  2         5  
928 6 50       13 croak "Must supply destination_addr to submit_sm or deliver_sm" if !defined $destination_addr;
929 6 100       16 $esm_class = ${*$me}{esm_class} if !defined $esm_class;
  2         4  
930 6 100       15 $protocol_id = ${*$me}{protocol_id} if !defined $protocol_id;
  2         4  
931 6 100       18 $priority_flag = ${*$me}{priority_flag} if !defined $priority_flag;
  2         11  
932 6 100       20 $schedule_delivery_time = ${*$me}{schedule_delivery_time} if !defined $schedule_delivery_time;
  2         5  
933 6 100       13 $validity_period = ${*$me}{validity_period} if !defined $validity_period;
  2         5  
934 6 100       16 $registered_delivery = ${*$me}{registered_delivery} if !defined $registered_delivery;
  2         5  
935 6 100       10 $replace_if_present_flag = ${*$me}{replace_if_present_flag} if !defined $replace_if_present_flag;
  2         5  
936 6 100       14 $data_coding = ${*$me}{data_coding} if !defined $data_coding;
  2         7  
937 6 100       11 $sm_default_msg_id = ${*$me}{sm_default_msg_id} if !defined $sm_default_msg_id;
  2         4  
938 6 100       18 $short_message = '' if !defined $short_message;
939              
940 6         75 return pack('Z*CCZ*CCZ*CCCZ*Z*CCCCCa*',
941             $service_type, $source_addr_ton, $source_addr_npi, $source_addr,
942             $dest_addr_ton, $dest_addr_npi, $destination_addr,
943             $esm_class, $protocol_id, $priority_flag,
944             $schedule_delivery_time, $validity_period,
945             $registered_delivery, $replace_if_present_flag, $data_coding,
946             $sm_default_msg_id, length($short_message), $short_message, );
947             }
948              
949             #4#cut
950             ### submit_sm_v4 (6.4.4.1), v4 p.32
951              
952             sub decode_submit_v4 {
953 2     2 0 4 my $pdu = shift;
954 2         17 ($pdu->{message_class}, # 1 (2)
955             $pdu->{source_addr_ton}, # 2 (1)
956             $pdu->{source_addr_npi}, # 3 (1)
957             $pdu->{source_addr}, # 4 (n+1)
958             ) = unpack 'nCCZ*', $pdu->{data};
959 2         6 my $len = 2 + 1 + 1 + length($pdu->{source_addr}) + 1;
960              
961 2         10 ($pdu->{number_of_dests}) = unpack 'N', substr($pdu->{data}, $len);
962 2         3 $len += 4;
963             #warn "a decode_submit $len ($pdu->{number_of_dests}): ".hexdump(substr($pdu->{data}, $len));
964            
965             ### Walk down the variable length destination address list
966              
967 2         9 for (my $i = 0; $i < $pdu->{number_of_dests}; $i++) {
968 4         24 ($pdu->{dest_addr_ton}[$i], # SME ton (v4 table 6-9, p. 36)
969             $pdu->{dest_addr_npi}[$i], # SME npi
970             $pdu->{destination_addr}[$i]) # SME address
971             = unpack 'CCZ*', substr($pdu->{data}, $len);
972 4         17 $len += 1 + 1 + length($pdu->{destination_addr}[$i]) + 1;
973             #warn "b decode_submit $len: ".hexdump(substr($pdu->{data}, $len));
974             }
975            
976             ### Now that we skipped over the variable length destinations
977             ### we are ready to decode the rest of the packet.
978              
979 2         12 ($pdu->{messaging_mode}, # 7 C
980             $pdu->{msg_reference}) = unpack 'CZ*', substr($pdu->{data}, $len);
981 2         5 $len += 1 + length($pdu->{msg_reference}) + 1;
982             #warn "c decode_submit $len: ".hexdump(substr($pdu->{data}, $len));
983              
984 2         12 ($pdu->{telematic_interworking}, # 9 C
985             $pdu->{priority_level}, # 10 C
986             $pdu->{schedule_delivery_time}) = unpack 'CCZ*', substr($pdu->{data}, $len);
987 2         4 $len += 1 + 1 + length($pdu->{schedule_delivery_time}) + 1;
988 2 50       7 warn "d decode_submit $len: ".hexdump(substr($pdu->{data}, $len)) if $trace;
989              
990 2         3 my $sm_length;
991 2         14 ($pdu->{validity_period}, # 12 n v4: n.b. this is now short instead of Cstr
992             $pdu->{registered_delivery}, # 13 C
993             $pdu->{data_coding}, # 14 C
994             $pdu->{sm_default_msg_id}, # 15 C
995             $sm_length, # 16 n
996              
997             # 1
998             # 7890123456 7
999             ) = unpack 'nCCCn', substr($pdu->{data}, $len);
1000 2         5 $len += 2 + 1 + 1 + 1 + 2;
1001 2         9 ($pdu->{short_message} # 17 a
1002             ) = unpack "a$sm_length", substr($pdu->{data}, $len);
1003 2         5 $len += $sm_length;
1004 2 50       8 warn "e decode_submit ($pdu->{short_message}) $len: ".hexdump(substr($pdu->{data}, $len)) if $trace;
1005              
1006 2         5 $pdu->{service_type} = $pdu->{message_class}; # compat v34
1007 2         5 $pdu->{esm_class} = $pdu->{messaging_mode}; # compat v34
1008 2         4 $pdu->{protocol_id} = $pdu->{telematic_interworking}; # compat v34
1009 2         6 $pdu->{priority_flag} = $pdu->{priority_level}; # compat v34
1010            
1011 2         5 return $len;
1012             }
1013              
1014             sub encode_submit_v4 {
1015 2     2 0 6 my $me = $_[0];
1016 2         4 my ($message_class, $source_addr_ton, $source_addr_npi, $source_addr,
1017             @dest_addr_ton, @dest_addr_npi, @destination_addr,
1018             $messaging_mode, $msg_reference, $telematic_interworking, $priority_level,
1019             $schedule_delivery_time, $validity_period,
1020             $registered_delivery_mode, $data_coding,
1021             $sm_default_msg_id, $short_message, $addr_data);
1022              
1023             ### Extract mandatory parameters from argument stream
1024            
1025 2         9 for (my $i=1; $i <= $#_; $i+=2) {
1026 18 50       44 next if !defined $_[$i];
1027             #warn "iter $i: >$_[$i]<";
1028 18 100       198 if ($_[$i] eq 'message_class') { $message_class = splice @_,$i,2,undef,undef; }
  1 50       6  
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    50          
    100          
    50          
    100          
    100          
    50          
    100          
    100          
    100          
    50          
1029 0         0 elsif ($_[$i] eq 'service_type') { $message_class = splice @_,$i,2,undef,undef; } # v34
1030 1         4 elsif ($_[$i] eq 'source_addr_ton') { $source_addr_ton = splice @_,$i,2,undef,undef; }
1031 1         5 elsif ($_[$i] eq 'source_addr_npi') { $source_addr_npi = splice @_,$i,2,undef,undef; }
1032 1         4 elsif ($_[$i] eq 'source_addr') { $source_addr = splice @_,$i,2,undef,undef; }
1033             elsif ($_[$i] eq 'dest_addr_ton') {
1034 1 50       5 @dest_addr_ton = ref($_[$i+1]) ? @{scalar(splice @_,$i,2,undef,undef)}
  1         5  
1035             : (scalar(splice @_,$i,2,undef,undef));
1036             }
1037             elsif ($_[$i] eq 'dest_addr_npi') {
1038 1 50       5 @dest_addr_npi = ref($_[$i+1]) ? @{scalar(splice @_,$i,2,undef,undef)}
  1         5  
1039             : (scalar(splice @_,$i,2,undef,undef));
1040             }
1041             elsif ($_[$i] eq 'destination_addr') {
1042 2 50       6 @destination_addr = ref($_[$i+1]) ? @{scalar(splice @_,$i,2,undef,undef)}
  2         14  
1043             : (scalar(splice @_,$i,2,undef,undef));
1044             }
1045 1         6 elsif ($_[$i] eq 'messaging_mode') { $messaging_mode = splice @_,$i,2,undef,undef; }
1046 0         0 elsif ($_[$i] eq 'esm_class') { $messaging_mode = splice @_,$i,2,undef,undef; } # v34
1047 1         6 elsif ($_[$i] eq 'msg_reference') { $msg_reference = splice @_,$i,2,undef,undef; }
1048 1         4 elsif ($_[$i] eq 'telematic_interworking') { $telematic_interworking = splice @_,$i,2,undef,undef; }
1049 0         0 elsif ($_[$i] eq 'protocol_id') { $telematic_interworking = splice @_,$i,2,undef,undef; } # v34
1050 1         11 elsif ($_[$i] eq 'priority_level') { $priority_level = splice @_,$i,2,undef,undef; }
1051 0         0 elsif ($_[$i] eq 'priority_flag') { $priority_level = splice @_,$i,2,undef,undef; } # v34
1052 1         5 elsif ($_[$i] eq 'schedule_delivery_time') { $schedule_delivery_time = splice @_,$i,2,undef,undef; }
1053 1         5 elsif ($_[$i] eq 'validity_period') { $validity_period = splice @_,$i,2,undef,undef; }
1054 0         0 elsif ($_[$i] eq 'registered_delivery_mode') { $registered_delivery_mode = splice @_,$i,2,undef,undef; }
1055 1         4 elsif ($_[$i] eq 'registered_delivery') { $registered_delivery_mode = splice @_,$i,2,undef,undef; } # v34
1056 1         4 elsif ($_[$i] eq 'data_coding') { $data_coding = splice @_,$i,2,undef,undef; }
1057 1         4 elsif ($_[$i] eq 'sm_default_msg_id') { $sm_default_msg_id = splice @_,$i,2,undef,undef; }
1058 1         5 elsif ($_[$i] eq 'short_message') { $short_message = splice @_,$i,2,undef,undef; }
1059              
1060             ### Following kludge was added by Felix as PTF when integrating.
1061             ### Basically this should be handled correctly by the generic
1062             ### optional parameter code but didn't work right for Felix at the
1063             ### time. Lets hope this is fixed now. --Sampo
1064             #elsif ($_[$i] eq 'PDC_MultiPartMessage') { my $tmp_mpm = splice @_,$i,2, undef,undef;
1065             # $pdc_multipartmessage = pack("CCCC",
1066             # 0x11, 0x05, 0x00, 0x04)
1067             # . $tmp_mpm
1068             # unless (length ($tmp_mpm) != 4);
1069             # }
1070              
1071             }
1072              
1073             ### Apply defaults for those mandatory arguments that were not specified
1074              
1075 2 100       7 $message_class = ${*$me}{message_class} if !defined $message_class;
  1         4  
1076 2 100       7 $source_addr_ton = ${*$me}{source_addr_ton} if !defined $source_addr_ton;
  1         3  
1077 2 100       8 $source_addr_npi = ${*$me}{source_addr_npi} if !defined $source_addr_npi;
  1         4  
1078 2 100       7 $source_addr = ${*$me}{source_addr} if !defined $source_addr;
  1         4  
1079              
1080 2 50       13 croak "Must supply destination_addr to submit_sm v4" if !@destination_addr;
1081            
1082 2 100       6 $messaging_mode = ${*$me}{messaging_mode} if !defined $messaging_mode;
  1         5  
1083 2 100       7 $msg_reference = ${*$me}{msg_reference} if !defined $msg_reference;
  1         4  
1084 2 100       7 $telematic_interworking = ${*$me}{telematic_interworking} if !defined $telematic_interworking;
  1         4  
1085 2 100       7 $priority_level = ${*$me}{priority_level} if !defined $priority_level;
  1         4  
1086 2 100       9 $schedule_delivery_time = ${*$me}{schedule_delivery_time} if !defined $schedule_delivery_time;
  1         5  
1087 2 100       6 $validity_period = ${*$me}{validity_period} if !defined $validity_period;
  1         3  
1088 2 100       6 $registered_delivery_mode = ${*$me}{registered_delivery_mode} if !defined $registered_delivery_mode;
  1         3  
1089 2 100       6 $data_coding = ${*$me}{data_coding} if !defined $data_coding;
  1         3  
1090 2 100       8 $sm_default_msg_id = ${*$me}{sm_default_msg_id} if !defined $sm_default_msg_id;
  1         3  
1091 2 100       7 $short_message = '' if !defined $short_message;
1092              
1093             ### destination address encoding is pretty messy with variable
1094             ### number of variable length records.
1095              
1096 2         8 for (my $i = 0; $i <= $#destination_addr; $i++) {
1097 4 100       25 my $ton = !defined($dest_addr_ton[$i]) ? ${*$me}{dest_addr_ton} : $dest_addr_ton[$i];
  3         8  
1098 4 100       12 my $npi = !defined($dest_addr_npi[$i]) ? ${*$me}{dest_addr_npi} : $dest_addr_npi[$i];
  3         7  
1099 4         21 $addr_data .= pack 'CCZ*', $ton, $npi, $destination_addr[$i];
1100             }
1101              
1102 2         26 return pack('nCCZ*N',
1103             $message_class, $source_addr_ton, $source_addr_npi, $source_addr,
1104             scalar(@destination_addr)) . $addr_data
1105             . pack('CZ*CCZ*nCCCna*',
1106             $messaging_mode, $msg_reference, $telematic_interworking,
1107             $priority_level, $schedule_delivery_time, $validity_period,
1108             $registered_delivery_mode, $data_coding,
1109             $sm_default_msg_id, length($short_message), $short_message, )
1110             # . $pdc_multipartmessage # *** Felix
1111             ;
1112             }
1113              
1114             ### v4 submit_sm response encoding and decoding is equal to submit_multi_resp v3.4
1115             #4#end
1116              
1117             sub submit_sm {
1118 5     5 1 272 my $me = $_[0];
1119 5         29 return $me->req_backend(CMD_submit_sm, #4
1120 5 100       9 (${*$me}{smpp_version} == 0x40) #4
1121             ? &encode_submit_v4 : &encode_submit_v34, #4
1122             @_); #4
1123 0         0 return $me->req_backend(CMD_submit_sm, &encode_submit_v34, @_);
1124             }
1125              
1126             #4#cut
1127             ### deliver_sm_v4 (v4 6.4.5.1), p.38
1128             ### N.B v34 deliver is decoded as v34 submit
1129              
1130             sub decode_deliver_sm_v4 {
1131 2     2 0 12 my $pdu = shift;
1132 2         10 my $len = decode_source_and_destination($pdu, $pdu->{data});
1133            
1134             ### *** WARNING: if this section of code bombs you should
1135             ### check carefully that Z9 and Z17 are working correctly.
1136             ### Although the spec says that these are fixed length, one
1137             ### should not blindly take this for granted. If fixed length
1138             ### interpreatation is chosen then the $len has to be updated
1139             ### by the fixed length irrespective of what the C string
1140             ### length is. If however the variable length interpretation
1141             ### is chosen then Z* should be used to decode and C string
1142             ### length should be used to update the length. Using Z9 to
1143             ### decode but C string length to update $len is inconsistent
1144             ### although I believe it amounts to the variable length
1145             ### interpretation in the end. --Sampo
1146            
1147 2         11 ($pdu->{msg_reference}) = unpack 'Z9', substr($pdu->{data}, $len); # Felix: its always fixed len
1148 2         4 $len += 9;
1149             #($pdu->{msg_reference}) = unpack 'Z*', substr($pdu->{data}, $len);
1150             #$len += length($pdu->{msg_reference}) + 1;
1151              
1152 2         29 ($pdu->{message_class}, # 8 n
1153             $pdu->{telematic_interworking}, # 9 C
1154             $pdu->{priority_level}, # 10 C
1155             $pdu->{submit_time_stamp}) = unpack 'nCCZ17', substr($pdu->{data}, $len); # Felix: fixed len
1156 2         4 $len += 2 + 1 + 1 + 17;
1157             # $pdu->{submit_time_stamp}) = unpack 'nCCZ*', substr($pdu->{data}, $len);
1158             #$len += 2 + 1 + 1 + length($pdu->{submit_time_stamp}) + 1;
1159              
1160 2         4 my $sm_length;
1161 2         8 ($pdu->{data_coding}, # 15 C
1162             $sm_length, # 17 n
1163             ) = unpack 'Cn', substr($pdu->{data}, $len);
1164 2         3 $len += 1 + 2;
1165 2         12 ($pdu->{short_message}
1166             ) = unpack "a$sm_length", substr($pdu->{data}, $len);
1167 2         4 $len += $sm_length;
1168              
1169 2         5 $pdu->{esm_class} = $pdu->{message_class};
1170 2         6 $pdu->{protocol_id} = $pdu->{telematic_interworking};
1171 2         5 $pdu->{priority_flag} = $pdu->{priority_level};
1172 2         5 $pdu->{schedule_delivery_time} = $pdu->{submit_time_stamp};
1173            
1174 2         5 return $len;
1175             }
1176              
1177             sub encode_deliver_sm_v4 {
1178 2     2 0 5 my $me = $_[0];
1179 2         4 my ($source_addr_ton, $source_addr_npi, $source_addr,
1180             $dest_addr_ton, $dest_addr_npi, $destination_addr,
1181             $msg_reference, $message_class, $telematic_interworking, $priority_level,
1182             $schedule_delivery_time, $data_coding, $short_message);
1183              
1184             ### Extract mandatory parameters from argument stream
1185            
1186 2         10 for (my $i=1; $i <= $#_; $i+=2) {
1187 14 50       35 next if !defined $_[$i];
1188 14 100       185 if ($_[$i] eq 'source_addr_ton') { $source_addr_ton = splice @_,$i,2,undef,undef; }
  1 100       6  
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    50          
    100          
    50          
    100          
    100          
    100          
    50          
1189 1         6 elsif ($_[$i] eq 'source_addr_npi') { $source_addr_npi = splice @_,$i,2,undef,undef; }
1190 1         4 elsif ($_[$i] eq 'source_addr') { $source_addr = splice @_,$i,2,undef,undef; }
1191 1         4 elsif ($_[$i] eq 'dest_addr_ton') { $dest_addr_ton = splice @_,$i,2,undef,undef; }
1192 1         5 elsif ($_[$i] eq 'dest_addr_npi') { $dest_addr_npi = splice @_,$i,2,undef,undef; }
1193 2         13 elsif ($_[$i] eq 'destination_addr') { $destination_addr = splice @_,$i,2,undef,undef; }
1194 1         5 elsif ($_[$i] eq 'msg_reference') { $msg_reference = splice @_,$i,2,undef,undef; }
1195 0         0 elsif ($_[$i] eq 'message_class') { $message_class = splice @_,$i,2,undef,undef; }
1196 1         5 elsif ($_[$i] eq 'esm_class') { $message_class = splice @_,$i,2,undef,undef; } # v34
1197 0         0 elsif ($_[$i] eq 'telematic_interworking') { $telematic_interworking = splice @_,$i,2,undef,undef; }
1198 1         6 elsif ($_[$i] eq 'protocol_id') { $telematic_interworking = splice @_,$i,2,undef,undef; } # v34
1199 0         0 elsif ($_[$i] eq 'priority_level') { $priority_level = splice @_,$i,2,undef,undef; }
1200 1         5 elsif ($_[$i] eq 'priority_flag') { $priority_level = splice @_,$i,2,undef,undef; } # v34
1201 1         6 elsif ($_[$i] eq 'schedule_delivery_time') { $schedule_delivery_time = splice @_,$i,2,undef,undef; }
1202 1         4 elsif ($_[$i] eq 'data_coding') { $data_coding = splice @_,$i,2,undef,undef; }
1203 1         13 elsif ($_[$i] eq 'short_message') { $short_message = splice @_,$i,2,undef,undef; }
1204             }
1205              
1206             ### Apply defaults for those mandatory arguments that were not specified
1207            
1208 2 100       16 $source_addr_ton = ${*$me}{source_addr_ton} if !defined $source_addr_ton;
  1         4  
1209 2 100       7 $source_addr_npi = ${*$me}{source_addr_npi} if !defined $source_addr_npi;
  1         3  
1210 2 100       13 $source_addr = ${*$me}{source_addr} if !defined $source_addr;
  1         4  
1211 2 100       6 $dest_addr_ton = ${*$me}{dest_addr_ton} if !defined $dest_addr_ton;
  1         5  
1212 2 100       6 $dest_addr_npi = ${*$me}{dest_addr_npi} if !defined $dest_addr_npi;
  1         3  
1213 2 50       7 die "Must supply destination_addr to deliver_sm v4" if !defined $destination_addr;
1214 2 100       6 $msg_reference = ${*$me}{msg_reference} if !defined $msg_reference;
  1         3  
1215 2 100       7 $message_class = ${*$me}{message_class} if !defined $message_class;
  1         3  
1216 2 100       8 $telematic_interworking = ${*$me}{telematic_interworking} if !defined $telematic_interworking;
  1         9  
1217 2 100       8 $priority_level = ${*$me}{priority_level} if !defined $priority_level;
  1         12  
1218 2 100       6 $schedule_delivery_time = ${*$me}{schedule_delivery_time} if !defined $schedule_delivery_time;
  1         6  
1219 2 100       8 $data_coding = ${*$me}{data_coding} if !defined $data_coding;
  1         3  
1220 2 100       7 $short_message = '' if !defined $short_message;
1221            
1222 2         21 return pack('CCZ*CCZ*Z*nCCZ*Cna*',
1223             $source_addr_ton, $source_addr_npi, $source_addr,
1224             $dest_addr_ton, $dest_addr_npi, $destination_addr,
1225             $msg_reference, $message_class, $telematic_interworking, $priority_level,
1226             $schedule_delivery_time, $data_coding, length($short_message),$short_message, );
1227             }
1228             #4#end
1229              
1230             sub deliver_sm {
1231 5     5 1 248 my $me = $_[0];
1232             # N.B. deliver_sm v34 == submit_sm v34
1233 5         31 return $me->req_backend(CMD_deliver_sm, #4
1234 5 100       11 (${*$me}{smpp_version} == 0x40) #4
1235             ? &encode_deliver_sm_v4 : &encode_submit_v34, #4
1236             @_); #4
1237 0         0 return $me->req_backend(CMD_deliver_sm, &encode_submit_v34, @_);
1238             }
1239              
1240             ###
1241              
1242             sub decode_submit_resp_v34 {
1243 9     9 0 12 my $pdu = shift;
1244 9         41 ($pdu->{message_id}) = unpack 'Z*', $pdu->{data};
1245 9         25 return length($pdu->{message_id}) + 1;
1246             }
1247              
1248             sub encode_submit_resp_v34 {
1249 9     9 0 16 my $me = $_[0];
1250 9         12 my ($message_id);
1251              
1252 9         24 for (my $i=1; $i <= $#_; $i+=2) {
1253 22 50       45 next if !defined $_[$i];
1254 22 100       66 if ($_[$i] eq 'message_id') { $message_id = splice @_,$i,2,undef,undef; }
  9         43  
1255             }
1256 9 50       25 warn "message_id=$message_id" if $trace;
1257 9 50       25 croak "message_id must be supplied" if !defined $message_id;
1258 9         74 return pack('Z*', $message_id);
1259             }
1260              
1261             sub submit_sm_resp {
1262 5     5 1 381 my $me = $_[0];
1263              
1264             # N.B. submit_sm_resp v4 == submit_multi_resp v34 #4
1265             # data_sm_resp v34 == submit_sm_resp v34
1266 5         29 return $me->resp_backend(CMD_submit_sm_resp, #4
1267 5 100       10 (${*$me}{smpp_version} == 0x40) #4
1268             ? &encode_submit_sm_resp_v4 #4
1269             : &encode_submit_resp_v34, #4
1270             @_); #4
1271 0         0 return $me->resp_backend(CMD_submit_sm_resp, &encode_submit_resp_v34, @_);
1272             }
1273 3     3 1 254 sub data_sm_resp { $_[0]->resp_backend(CMD_data_sm_resp, &encode_submit_resp_v34, @_) } # pubAPI
1274              
1275             sub deliver_sm_resp { # public API
1276 5     5 1 417 my $me = $_[0];
1277             # N.B. submit_sm_resp v34 == deliver_sm_resp v34
1278 5         33 return $me->resp_backend(CMD_deliver_sm_resp, #4
1279 5 100       12 (${*$me}{smpp_version} == 0x40) #4
1280             ? '' # v4 deliver_resp is empty v4 6.4.5.2, p.40 #4
1281             : &encode_submit_resp_v34, #4
1282             @_); #4
1283 0         0 return $me->resp_backend(CMD_deliver_sm_resp, &encode_submit_resp_v34, @_);
1284             }
1285              
1286             ### submit_multi (4.5.1), p.59
1287              
1288             sub decode_submit_multi {
1289 3     3 0 7 my $pdu = shift;
1290 3         13 ($pdu->{service_type}) = unpack 'Z*', $pdu->{data};
1291 3         7 my $len = length($pdu->{service_type}) + 1;
1292              
1293 3         11 $len += decode_source_addr($pdu, substr($pdu->{data}, $len));
1294              
1295 3         13 ($pdu->{number_of_dests}) = unpack 'C', substr($pdu->{data}, $len);
1296 3         8 $len += 1;
1297              
1298             ### To make life difficult, the multi destination addresses
1299             ### are a hotch potch of variable length, variable type
1300             ### records. Only way to do it is to walk the list.
1301              
1302 3         9 for (my $i = 0; $i < $pdu->{number_of_dests}; $i++) {
1303 6         30 ($pdu->{dest_flag}[$i]) = unpack 'C', substr($pdu->{data}, $len++);
1304 6 100       19 if ($pdu->{dest_flag}[$i] == MULTIDESTFLAG_SME_Address) {
    50          
1305 3         22 ($pdu->{dest_addr_ton}[$i],
1306             $pdu->{dest_addr_npi}[$i],
1307             $pdu->{destination_addr}[$i])
1308             = unpack 'CCZ*', substr($pdu->{data}, $len);
1309 3         12 $len += 1 + 1 + length($pdu->{destination_addr}[$i]) + 1;
1310             } elsif ($pdu->{dest_flag}[$i] == MULTIDESTFLAG_dist_list) {
1311 3         6 $pdu->{dest_addr_ton}[$i] = 0;
1312 3         5 $pdu->{dest_addr_npi}[$i] = 0;
1313 3         12 ($pdu->{destination_addr}[$i])
1314             = unpack 'Z*', substr($pdu->{data}, $len);
1315 3         11 $len += length($pdu->{destination_addr}[$i]) + 1;
1316             } else {
1317 0         0 warn "Unknown multidest flag: $pdu->{dest_flag} (4.5.1.1, p. 75)";
1318             }
1319             }
1320              
1321             ### Now that we skipped over the variable length destinations
1322             ### we are ready to decode the rest of the packet.
1323              
1324 3         24 ($pdu->{esm_class}, # 8
1325             $pdu->{protocol_id}, # 9
1326             $pdu->{priority_flag}, # 10
1327             $pdu->{schedule_delivery_time}) = unpack 'CCCZ*', substr($pdu->{data}, $len);
1328 3         7 $len += 1 + 1 + 1 + length($pdu->{schedule_delivery_time}) + 1;
1329            
1330 3         18 ($pdu->{validity_period}) = unpack 'Z*', substr($pdu->{data}, $len);
1331 3         7 $len += length($pdu->{validity_period}) + 1;
1332              
1333 3         4 my $sm_length;
1334 3         18 ($pdu->{registered_delivery}, # 13
1335             $pdu->{replace_if_present_flag}, # 14
1336             $pdu->{data_coding}, # 15
1337             $pdu->{sm_default_msg_id}, # 16
1338             $sm_length, # 17
1339             # 1
1340             # 8901234567 8
1341             ) = unpack 'CCCCC', substr($pdu->{data}, $len);
1342 3         4 $len += 1 + 1 + 1 + 1 + 1;
1343 3         15 ($pdu->{short_message} # 18
1344             ) = unpack "a$sm_length", substr($pdu->{data}, $len);
1345            
1346 3         10 return $len + $sm_length;
1347             }
1348              
1349             sub encode_submit_multi {
1350 3     3 0 6 my $me = $_[0];
1351 3         6 my ($service_type, $source_addr_ton, $source_addr_npi, $source_addr,
1352             @dest_flag, @dest_addr_ton, @dest_addr_npi, @destination_addr,
1353             $esm_class, $protocol_id, $priority_flag,
1354             $schedule_delivery_time, $validity_period,
1355             $registered_delivery, $replace_if_present_flag, $data_coding,
1356             $sm_default_msg_id, $short_message, $addr_data);
1357              
1358             ### Extract mandatory parameters from argument stream
1359            
1360 3         11 for (my $i=1; $i <= $#_; $i+=2) {
1361 59 50       116 next if !defined $_[$i];
1362 59 100       541 if ($_[$i] eq 'service_type') { $service_type = splice @_,$i,2,undef,undef; }
  2 100       12  
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
1363 2         9 elsif ($_[$i] eq 'source_addr_ton') { $source_addr_ton = splice @_,$i,2,undef,undef; }
1364 2         8 elsif ($_[$i] eq 'source_addr_npi') { $source_addr_npi = splice @_,$i,2,undef,undef; }
1365 2         8 elsif ($_[$i] eq 'source_addr') { $source_addr = splice @_,$i,2,undef,undef; }
1366             elsif ($_[$i] eq 'dest_flag') {
1367 3 50       18 @dest_flag = ref($_[$i+1]) ? @{scalar(splice @_,$i,2,undef,undef)}
  3         21  
1368             : (scalar(splice @_,$i,2,undef,undef));
1369             }
1370             elsif ($_[$i] eq 'dest_addr_ton') {
1371 2 50       9 @dest_addr_ton = ref($_[$i+1]) ? @{scalar(splice @_,$i,2,undef,undef)}
  2         10  
1372             : (scalar(splice @_,$i,2,undef,undef));
1373             }
1374             elsif ($_[$i] eq 'dest_addr_npi') {
1375 2 50       7 @dest_addr_npi = ref($_[$i+1]) ? @{scalar(splice @_,$i,2,undef,undef)}
  2         8  
1376             : (scalar(splice @_,$i,2,undef,undef));
1377             }
1378             elsif ($_[$i] eq 'destination_addr') {
1379 3 50       10 @destination_addr = ref($_[$i+1]) ? @{scalar(splice @_,$i,2,undef,undef)}
  3         14  
1380             : (scalar(splice @_,$i,2,undef,undef));
1381             }
1382 2         8 elsif ($_[$i] eq 'esm_class') { $esm_class = splice @_,$i,2,undef,undef; }
1383 2         8 elsif ($_[$i] eq 'protocol_id') { $protocol_id = splice @_,$i,2,undef,undef; }
1384 2         8 elsif ($_[$i] eq 'priority_flag') { $priority_flag = splice @_,$i,2,undef,undef; }
1385 2         9 elsif ($_[$i] eq 'schedule_delivery_time') { $schedule_delivery_time = splice @_,$i,2,undef,undef; }
1386 2         8 elsif ($_[$i] eq 'validity_period') { $validity_period = splice @_,$i,2,undef,undef; }
1387 2         8 elsif ($_[$i] eq 'registered_delivery') { $registered_delivery = splice @_,$i,2,undef,undef; }
1388 2         7 elsif ($_[$i] eq 'replace_if_present_flag') { $replace_if_present_flag = splice @_,$i,2,undef,undef; }
1389 2         7 elsif ($_[$i] eq 'data_coding') { $data_coding = splice @_,$i,2,undef,undef; }
1390 2         7 elsif ($_[$i] eq 'sm_default_msg_id') { $sm_default_msg_id = splice @_,$i,2,undef,undef; }
1391 1         5 elsif ($_[$i] eq 'short_message') { $short_message = splice @_,$i,2,undef,undef; }
1392             }
1393              
1394             ### Apply defaults for those mandatory arguments that were not specified
1395            
1396 3 100       8 $service_type = ${*$me}{service_type} if !defined $service_type;
  1         5  
1397 3 100       8 $source_addr_ton = ${*$me}{source_addr_ton} if !defined $source_addr_ton;
  1         3  
1398 3 100       8 $source_addr_npi = ${*$me}{source_addr_npi} if !defined $source_addr_npi;
  1         3  
1399 3 100       8 $source_addr = ${*$me}{source_addr} if !defined $source_addr;
  1         4  
1400 3 50       9 croak "Must supply destination_addr to submit_multi" if !@destination_addr;
1401 3 100       9 $esm_class = ${*$me}{esm_class} if !defined $esm_class;
  1         3  
1402 3 100       8 $protocol_id = ${*$me}{protocol_id} if !defined $protocol_id;
  1         4  
1403 3 100       8 $priority_flag = ${*$me}{priority_flag} if !defined $priority_flag;
  1         4  
1404 3 100       9 $schedule_delivery_time = ${*$me}{schedule_delivery_time} if !defined $schedule_delivery_time;
  1         2  
1405 3 100       18 $validity_period = ${*$me}{validity_period} if !defined $validity_period;
  1         2  
1406 3 100       8 $registered_delivery = ${*$me}{registered_delivery} if !defined $registered_delivery;
  1         3  
1407 3 100       8 $replace_if_present_flag = ${*$me}{replace_if_present_flag} if !defined $replace_if_present_flag;
  1         3  
1408 3 100       7 $data_coding = ${*$me}{data_coding} if !defined $data_coding;
  1         3  
1409 3 100       8 $sm_default_msg_id = ${*$me}{sm_default_msg_id} if !defined $sm_default_msg_id;
  1         3  
1410 3 100       12 $short_message = '' if !defined $short_message;
1411              
1412             ### destination address encoding is pretty messy with variable
1413             ### number of variable length variable type records.
1414              
1415 3         10 for (my $i = 0; $i <= $#destination_addr; $i++) {
1416 6 100 66     35 if (!defined($dest_flag[$i])
    50          
1417             || $dest_flag[$i] == MULTIDESTFLAG_SME_Address) {
1418 3 100       15 my $ton = !defined($dest_addr_ton[$i]) ? ${*$me}{dest_addr_ton} : $dest_addr_ton[$i];
  1         3  
1419 3 100       8 my $npi = !defined($dest_addr_npi[$i]) ? ${*$me}{dest_addr_npi} : $dest_addr_npi[$i];
  1         3  
1420 3         19 $addr_data .= pack 'CCCZ*', MULTIDESTFLAG_SME_Address, $ton, $npi, $destination_addr[$i];
1421             } elsif ($dest_flag[$i] == MULTIDESTFLAG_dist_list) {
1422 3         15 $addr_data .= pack 'CZ*', MULTIDESTFLAG_dist_list, $destination_addr[$i];
1423             } else {
1424 0         0 warn "Unknown dest_flag: $dest_flag[$i] (4.5.1, p. 70)";
1425             }
1426             }
1427              
1428 3         31 return pack('Z*CCZ*C',
1429             $service_type, $source_addr_ton, $source_addr_npi, $source_addr,
1430             scalar(@destination_addr)) . $addr_data
1431             . pack('CCCZ*Z*CCCCCa*',
1432             $esm_class, $protocol_id, $priority_flag,
1433             $schedule_delivery_time, $validity_period,
1434             $registered_delivery, $replace_if_present_flag, $data_coding,
1435             $sm_default_msg_id, length($short_message), $short_message, );
1436             }
1437              
1438 3     3 1 218 sub submit_multi { $_[0]->req_backend(CMD_submit_multi, &encode_submit_multi, @_) } # public API
1439              
1440             #4#cut
1441              
1442             sub decode_submit_sm_resp_v4 {
1443 2     2 0 3 my $pdu = shift;
1444 2         10 ($pdu->{message_id}) = unpack 'Z*', $pdu->{data};
1445 2         6 my $len = length($pdu->{message_id}) + 1;
1446 2         11 ($pdu->{no_unsuccess}) = unpack 'n', substr($pdu->{data}, $len);
1447 2         6 $pdu->{num_unsuccess} = $pdu->{no_unsuccess}; # Compat
1448 2         3 $len += 2;
1449              
1450             ### process the unsuccess_sme(s) field into meaningful arrays
1451            
1452 2         8 for (my $i = 0; $i < $pdu->{no_unsuccess}; $i++) {
1453 4         24 ($pdu->{dest_addr_ton}[$i], $pdu->{dest_addr_npi}[$i],
1454             $pdu->{destination_addr}[$i]) = unpack 'CCZ*', substr($pdu->{data}, $len);
1455 4         10 $len += 1 + 1 + length($pdu->{destination_addr}[$i]) + 1;
1456 4         13 ($pdu->{error_status_code}[$i]) = unpack 'N', substr($pdu->{data}, $len);
1457 4         16 $len += 4;
1458             }
1459            
1460 2         4 return $len;
1461             }
1462             #4#end
1463              
1464             sub decode_submit_multi_resp {
1465 3     3 0 5 my $pdu = shift;
1466 3         14 ($pdu->{message_id}) = unpack 'Z*', $pdu->{data};
1467 3         6 my $len = length($pdu->{message_id}) + 1;
1468 3         14 ($pdu->{no_unsuccess}) = unpack 'C', substr($pdu->{data}, $len);
1469 3         8 $pdu->{num_unsuccess} = $pdu->{no_unsuccess}; # Compat
1470 3         5 $len += 1;
1471              
1472             ### process the unsuccess_sme(s) field into meaningful arrays
1473              
1474 3         10 for (my $i = 0; $i < $pdu->{no_unsuccess}; $i++) {
1475 6         1690 ($pdu->{dest_addr_ton}[$i], $pdu->{dest_addr_npi}[$i],
1476             $pdu->{destination_addr}[$i]) = unpack 'CCZ*', substr($pdu->{data}, $len);
1477 6         14 $len += 1 + 1 + length($pdu->{destination_addr}[$i]) + 1;
1478 6         20 ($pdu->{error_status_code}[$i]) = unpack 'N', substr($pdu->{data}, $len);
1479 6         19 $len += 4;
1480             }
1481            
1482 3         7 return $len;
1483             }
1484              
1485             sub encode_submit_multi_resp {
1486 3     3 0 5 my $me = $_[0];
1487 3         6 my ($message_id, @dest_addr_ton, @dest_addr_npi, @destination_addr,
1488             @error_status_code, $addr_data);
1489              
1490 3         10 for (my $i=1; $i <= $#_; $i+=2) {
1491 16 50       51 next if !defined $_[$i];
1492 16 100       77 if ($_[$i] eq 'message_id') { $message_id = splice @_,$i,2,undef,undef; }
  3 100       14  
    100          
    100          
    100          
1493             elsif ($_[$i] eq 'dest_addr_ton') {
1494 2 50       8 @dest_addr_ton = ref($_[$i+1]) ? @{scalar(splice @_,$i,2,undef,undef)}
  2         12  
1495             : (scalar(splice @_,$i,2,undef,undef));
1496             }
1497             elsif ($_[$i] eq 'dest_addr_npi') {
1498 2 50       8 @dest_addr_npi = ref($_[$i+1]) ? @{scalar(splice @_,$i,2,undef,undef)}
  2         9  
1499             : (scalar(splice @_,$i,2,undef,undef));
1500             }
1501             elsif ($_[$i] eq 'destination_addr') {
1502 3 50       10 @destination_addr = ref($_[$i+1]) ? @{scalar(splice @_,$i,2,undef,undef)}
  3         13  
1503             : (scalar(splice @_,$i,2,undef,undef));
1504             }
1505             elsif ($_[$i] eq 'error_status_code') {
1506 3 50       12 @error_status_code = ref($_[$i+1]) ? @{scalar(splice @_,$i,2,undef,undef)}
  3         13  
1507             : (scalar(splice @_,$i,2,undef,undef));
1508             }
1509             }
1510              
1511 3 50       8 croak "message_id must be supplied" if !defined $message_id;
1512             #croak "destination_addr must be supplied" if !@destination_addr;
1513 3 50       15 croak "error_status_code must be supplied" if !@error_status_code;
1514              
1515 3         9 for (my $i = 0; $i <= $#destination_addr; $i++) {
1516 6 100       14 my $ton = !defined($dest_addr_ton[$i]) ? ${*$me}{dest_addr_ton} : $dest_addr_ton[$i];
  4         16  
1517 6 100       21 my $npi = !defined($dest_addr_npi[$i]) ? ${*$me}{dest_addr_npi} : $dest_addr_npi[$i];
  4         10  
1518 6         33 $addr_data .= pack 'CCZ*N', $ton, $npi, $destination_addr[$i], $error_status_code[$i];
1519             }
1520            
1521 3         20 return pack('Z*C', $message_id, scalar(@destination_addr)) . $addr_data;
1522             }
1523              
1524             #4#cut
1525             sub encode_submit_sm_resp_v4 {
1526 2     2 0 9 my $me = $_[0];
1527 2         5 my ($message_id, @dest_addr_ton, @dest_addr_npi, @destination_addr,
1528             @error_status_code);
1529 2         3 my $addr_data = ''; # May be empty if all addresses were successful
1530            
1531 2         10 for (my $i=1; $i <= $#_; $i+=2) {
1532 10 50       24 next if !defined $_[$i];
1533 10 100       59 if ($_[$i] eq 'message_id') { $message_id = splice @_,$i,2,undef,undef; }
  2 50       10  
    100          
    100          
    100          
    100          
1534 0         0 elsif ($_[$i] eq 'sc_msg_reference') { $message_id = splice @_,$i,2,undef,undef; }
1535             elsif ($_[$i] eq 'dest_addr_ton') {
1536 1 50       5 @dest_addr_ton = ref($_[$i+1]) ? @{scalar(splice @_,$i,2,undef,undef)}
  1         5  
1537             : (scalar(splice @_,$i,2,undef,undef));
1538             }
1539             elsif ($_[$i] eq 'dest_addr_npi') {
1540 1 50       5 @dest_addr_npi = ref($_[$i+1]) ? @{scalar(splice @_,$i,2,undef,undef)}
  1         6  
1541             : (scalar(splice @_,$i,2,undef,undef));
1542             }
1543             elsif ($_[$i] eq 'destination_addr') {
1544 2 50       9 @destination_addr = ref($_[$i+1]) ? @{scalar(splice @_,$i,2,undef,undef)}
  2         12  
1545             : (scalar(splice @_,$i,2,undef,undef));
1546             }
1547             elsif ($_[$i] eq 'error_status_code') {
1548 2 50       9 @error_status_code = ref($_[$i+1]) ? @{scalar(splice @_,$i,2,undef,undef)}
  2         11  
1549             : (scalar(splice @_,$i,2,undef,undef));
1550             }
1551             }
1552              
1553 2 50       13 croak "message_id must be supplied" if !defined $message_id;
1554             #croak "destination_addr must be supplied" if !@destination_addr;
1555 2 50       6 croak "error_status_code must be supplied" if !@error_status_code;
1556              
1557 2         9 for (my $i = 0; $i <= $#destination_addr; $i++) {
1558 4 100       12 my $ton = !defined($dest_addr_ton[$i]) ? ${*$me}{dest_addr_ton} : $dest_addr_ton[$i];
  3         10  
1559 4 100       10 my $npi = !defined($dest_addr_npi[$i]) ? ${*$me}{dest_addr_npi} : $dest_addr_npi[$i];
  3         7  
1560 4         27 $addr_data .= pack 'CCZ*N', $ton, $npi, $destination_addr[$i], $error_status_code[$i];
1561             }
1562            
1563 2         14 return pack('Z*n', $message_id, scalar(@destination_addr)) . $addr_data;
1564             }
1565             #4#end
1566              
1567 3     3 1 249 sub submit_multi_resp { $_[0]->resp_backend(CMD_submit_multi_resp, &encode_submit_multi_resp, @_) }
1568              
1569             ### query (4.8.1), p.95
1570              
1571             sub decode_query_v34 {
1572 2     2 0 5 my $pdu = shift;
1573 2         10 ($pdu->{message_id}) = unpack 'Z*', $pdu->{data};
1574 2         5 my $len = length($pdu->{message_id}) + 1;
1575 2         9 $len += decode_source_addr($pdu, substr($pdu->{data}, $len));
1576 2         6 return $len;
1577             }
1578              
1579             #4#cut
1580             sub decode_query_v4 {
1581 2     2 0 3 my $pdu = shift;
1582 2         10 ($pdu->{message_id}) = unpack 'Z*', $pdu->{data};
1583 2         5 my $len = length($pdu->{message_id}) + 1;
1584 2         7 $len += decode_source_and_destination($pdu, substr($pdu->{data}, $len));
1585 2         4 return $len;
1586             }
1587             #4#end
1588              
1589             sub encode_query_sm_v34 {
1590 2     2 0 3 my $me = $_[0];
1591 2         3 my ($message_id, $source_addr_ton, $source_addr_npi, $source_addr);
1592              
1593             ### Extract mandatory parameters from argument stream
1594            
1595 2         8 for (my $i=1; $i <= $#_; $i+=2) {
1596 5 50       13 next if !defined $_[$i];
1597 5 100       21 if ($_[$i] eq 'message_id') { $message_id = splice @_,$i,2,undef,undef; }
  2 100       10  
    100          
    50          
1598 1         5 elsif ($_[$i] eq 'source_addr_ton') { $source_addr_ton = splice @_,$i,2,undef,undef; }
1599 1         4 elsif ($_[$i] eq 'source_addr_npi') { $source_addr_npi = splice @_,$i,2,undef,undef; }
1600 1         4 elsif ($_[$i] eq 'source_addr') { $source_addr = splice @_,$i,2,undef,undef; }
1601             }
1602              
1603             ### Apply defaults for those mandatory arguments that were not specified
1604            
1605 2 50       7 croak "Must supply message_id to query_sm" if !defined $message_id;
1606 2 100       7 $source_addr_ton = ${*$me}{source_addr_ton} if !defined $source_addr_ton;
  1         3  
1607 2 100       7 $source_addr_npi = ${*$me}{source_addr_npi} if !defined $source_addr_npi;
  1         3  
1608 2 100       5 $source_addr = ${*$me}{source_addr} if !defined $source_addr;
  1         3  
1609              
1610 2         13 return pack('Z*CCZ*', $message_id, $source_addr_ton, $source_addr_npi, $source_addr);
1611             }
1612              
1613             #4#cut
1614             sub encode_query_sm_v4 {
1615 2     2 0 4 my $me = $_[0];
1616 2         5 my ($message_id, $source_addr_ton, $source_addr_npi, $source_addr,
1617             $dest_addr_ton, $dest_addr_npi, $destination_addr);
1618              
1619             ### Extract mandatory parameters from argument stream
1620            
1621 2         9 for (my $i=1; $i <= $#_; $i+=2) {
1622 8 50       26 next if !defined $_[$i];
1623 8 100       40 if ($_[$i] eq 'message_id') { $message_id = splice @_,$i,2,undef,undef; }
  2 100       10  
    100          
    100          
    100          
    100          
    50          
1624 1         5 elsif ($_[$i] eq 'source_addr_ton') { $source_addr_ton = splice @_,$i,2,undef,undef; }
1625 1         4 elsif ($_[$i] eq 'source_addr_npi') { $source_addr_npi = splice @_,$i,2,undef,undef; }
1626 1         5 elsif ($_[$i] eq 'source_addr') { $source_addr = splice @_,$i,2,undef,undef; }
1627 1         4 elsif ($_[$i] eq 'dest_addr_ton') { $dest_addr_ton = splice @_,$i,2,undef,undef; }
1628 1         4 elsif ($_[$i] eq 'dest_addr_npi') { $dest_addr_npi = splice @_,$i,2,undef,undef; }
1629 1         5 elsif ($_[$i] eq 'destination_addr') { $destination_addr = splice @_,$i,2,undef,undef; }
1630             }
1631              
1632             ### Apply defaults for those mandatory arguments that were not specified
1633            
1634 2 50       6 croak "Must supply message_id to query_sm" if !defined $message_id;
1635              
1636 2 100       7 $source_addr_ton = ${*$me}{source_addr_ton} if !defined $source_addr_ton;
  1         3  
1637 2 100       6 $source_addr_npi = ${*$me}{source_addr_npi} if !defined $source_addr_npi;
  1         4  
1638 2 100       6 $source_addr = ${*$me}{source_addr} if !defined $source_addr;
  1         3  
1639              
1640 2 100       7 $dest_addr_ton = ${*$me}{dest_addr_ton} if !defined $dest_addr_ton;
  1         2  
1641 2 100       7 $dest_addr_npi = ${*$me}{dest_addr_npi} if !defined $dest_addr_npi;
  1         3  
1642 2 100       7 $destination_addr = ${*$me}{destination_addr} if !defined $destination_addr;
  1         3  
1643              
1644 2         13 return pack('Z*CCZ*CCZ*',
1645             $message_id, $source_addr_ton, $source_addr_npi, $source_addr,
1646             $dest_addr_ton, $dest_addr_npi, $destination_addr);
1647             }
1648             #4#end
1649              
1650             sub query_sm {
1651 4     4 1 209 my $me = $_[0];
1652 4 100       13 return $me->req_backend(CMD_query_sm, ${*$me}{smpp_version} == 0x40 #4
  4         23  
1653             ? &encode_query_sm_v4 : &encode_query_sm_v34, @_); #4
1654 0         0 return $me->req_backend(CMD_query_sm, &encode_query_sm_v34, @_);
1655             }
1656              
1657             sub decode_query_resp_v34 {
1658 2     2 0 4 my $pdu = shift;
1659 2         8 ($pdu->{message_id}) = unpack 'Z*', $pdu->{data};
1660 2         5 my $len = length($pdu->{message_id}) + 1;
1661              
1662 2         10 ($pdu->{final_date}) = unpack 'Z*', substr($pdu->{data}, $len);
1663 2         5 $len += length($pdu->{final_date}) + 1;
1664              
1665 2         9 ($pdu->{message_state}, $pdu->{error_code}) = unpack 'CC', substr($pdu->{data}, $len);
1666 2         4 return $len + 1 + 1;
1667             }
1668              
1669             #4#cut
1670             sub decode_query_resp_v4 {
1671 2     2 0 4 my $pdu = shift;
1672 2         8 ($pdu->{sc_msg_reference}) = unpack 'Z*', $pdu->{data};
1673 2         6 my $len = length($pdu->{sc_msg_reference}) + 1;
1674              
1675 2         11 ($pdu->{final_date}) = unpack 'Z*', substr($pdu->{data}, $len);
1676 2         4 $len += length($pdu->{final_date}) + 1;
1677              
1678 2         11 ($pdu->{message_status}, $pdu->{network_error_code}) = unpack 'CN', substr($pdu->{data}, $len);
1679            
1680 2         6 $pdu->{message_id} = $pdu->{sc_msg_reference}; # v34 compat
1681 2         4 $pdu->{message_state} = $pdu->{message_status}; # v34 compat
1682 2         5 $pdu->{error_code} = $pdu->{network_error_code}; # v34 compat
1683 2         6 return $len + 1 + 4;
1684             }
1685             #4#end
1686              
1687             sub encode_query_sm_resp_v34 {
1688 2     2 0 3 my $me = $_[0];
1689 2         4 my ($message_id, $final_date, $message_state, $error_code);
1690 2         4 $message_id = '2';
1691              
1692 2         7 for (my $i=1; $i < $#_; $i+=2) {
1693 8 50       25 next if !defined $_[$i];
1694 8 100       34 if ($_[$i] eq 'message_id') { $message_id = splice @_,$i,2,undef,undef; }
  2 100       8  
    100          
    100          
1695 1         5 elsif ($_[$i] eq 'final_date') { $final_date = splice @_,$i,2,undef,undef; }
1696 2         8 elsif ($_[$i] eq 'message_state') { $message_state = splice @_,$i,2,undef,undef; }
1697 1         4 elsif ($_[$i] eq 'error_code') { $error_code = splice @_,$i,2,undef,undef; }
1698             }
1699            
1700 2 50       6 croak "message_id must be supplied" if !defined $message_id;
1701 2 100       18 $final_date = ${*$me}{final_date} if !defined $final_date;
  1         4  
1702 2 50       14 croak "message_state must be supplied" if !defined $message_state;
1703 2 100       4 $error_code = ${*$me}{error_code} if !defined $error_code;
  1         3  
1704 2         13 return pack('Z*Z*CC', $message_id, $final_date, $message_state, $error_code);
1705             }
1706              
1707             #4#cut
1708             sub encode_query_sm_resp_v4 {
1709 2     2 0 4 my $me = $_[0];
1710 2         5 my ($sc_msg_reference, $final_date, $message_status, $network_error_code);
1711            
1712 2         7 for (my $i=1; $i <= $#_; $i+=2) {
1713 8 50       28 next if !defined $_[$i];
1714 8 50       75 if ($_[$i] eq 'sc_msg_reference') { $sc_msg_reference = splice @_,$i,2,undef,undef; }
  0 100       0  
    100          
    50          
    100          
    50          
    100          
1715 2         9 elsif ($_[$i] eq 'message_id') { $sc_msg_reference = splice @_,$i,2,undef,undef; } # v34
1716 1         5 elsif ($_[$i] eq 'final_date') { $final_date = splice @_,$i,2,undef,undef; }
1717 0         0 elsif ($_[$i] eq 'message_status') { $message_status = splice @_,$i,2,undef,undef; }
1718 2         7 elsif ($_[$i] eq 'message_state') { $message_status = splice @_,$i,2,undef,undef; } # v34
1719 0         0 elsif ($_[$i] eq 'networkerror_code') { $network_error_code = splice @_,$i,2,undef,undef; }
1720 1         6 elsif ($_[$i] eq 'error_code') { $network_error_code = splice @_,$i,2,undef,undef; } # v34
1721             }
1722            
1723 2 50       7 croak "sc_msg_reference or message_id must be supplied" if !defined $sc_msg_reference;
1724 2 100       8 $final_date = ${*$me}{final_date} if !defined $final_date;
  1         4  
1725 2 50       5 croak "message_status or message_state must be supplied" if !defined $message_status;
1726 2 100       5 $network_error_code = ${*$me}{network_error_code} if !defined $network_error_code;
  1         3  
1727 2         13 return pack('Z*Z*CN', $sc_msg_reference, $final_date, $message_status, $network_error_code);
1728             }
1729             #4#end
1730              
1731             sub query_sm_resp {
1732 4     4 1 196 my $me = $_[0];
1733 4 100       7 return $me->resp_backend(CMD_query_sm_resp, ${*$me}{smpp_version} == 0x40 #4
  4         24  
1734             ? &encode_query_sm_resp_v4 : &encode_query_sm_resp_v34, @_); #4
1735 0         0 return $me->resp_backend(CMD_query_sm_resp, &encode_query_sm_resp_v34, @_);
1736             }
1737              
1738             ### alert_notification (4.12.1), p.108
1739              
1740             sub decode_alert_notification {
1741 3     3 0 6 my $pdu = shift;
1742 3         11 my $len = decode_source_addr($pdu, $pdu->{data});
1743            
1744 3         19 ($pdu->{esme_addr_ton}, # 4
1745             $pdu->{esme_addr_npi}, # 5
1746             $pdu->{esme_addr}) = unpack 'CCZ*', substr($pdu->{data}, $len);
1747            
1748 3         8 return $len + 1 + 1 + length($pdu->{esme_addr}) + 1;
1749             }
1750              
1751             sub encode_alert_notification {
1752 3     3 0 7 my $me = $_[0];
1753 3         6 my ($source_addr_ton, $source_addr_npi, $source_addr,
1754             $esme_addr_ton, $esme_addr_npi, $esme_addr);
1755              
1756             ### Extract mandatory parameters from argument stream
1757            
1758 3         13 for (my $i=1; $i <= $#_; $i+=2) {
1759 14 50       40 next if !defined $_[$i];
1760 14 100       79 if ($_[$i] eq 'source_addr_ton') { $source_addr_ton = splice @_,$i,2,undef,undef; }
  2 100       10  
    100          
    100          
    100          
    100          
1761 2         7 elsif ($_[$i] eq 'source_addr_npi') { $source_addr_npi = splice @_,$i,2,undef,undef; }
1762 2         7 elsif ($_[$i] eq 'source_addr') { $source_addr = splice @_,$i,2,undef,undef; }
1763 2         8 elsif ($_[$i] eq 'esme_addr_ton') { $esme_addr_ton = splice @_,$i,2,undef,undef; }
1764 2         7 elsif ($_[$i] eq 'esme_addr_npi') { $esme_addr_npi = splice @_,$i,2,undef,undef; }
1765 3         12 elsif ($_[$i] eq 'esme_addr') { $esme_addr = splice @_,$i,2,undef,undef; }
1766             }
1767              
1768             ### Apply defaults for those mandatory arguments that were not specified
1769            
1770 3 100       10 $source_addr_ton = ${*$me}{source_addr_ton} if !defined $source_addr_ton;
  1         4  
1771 3 100       10 $source_addr_npi = ${*$me}{source_addr_npi} if !defined $source_addr_npi;
  1         3  
1772 3 100       9 $source_addr = ${*$me}{source_addr} if !defined $source_addr;
  1         4  
1773 3 100       8 $esme_addr_ton = ${*$me}{esme_addr_ton} if !defined $esme_addr_ton;
  1         3  
1774 3 100       10 $esme_addr_npi = ${*$me}{esme_addr_npi} if !defined $esme_addr_npi;
  1         3  
1775 3 50       22 croak "Must supply esme_addr to alert_notification" if !defined $esme_addr;
1776              
1777 3         27 return pack('CCZ*CCZ*',
1778             $source_addr_ton, $source_addr_npi, $source_addr,
1779             $esme_addr_ton, $esme_addr_npi, $esme_addr, );
1780             }
1781              
1782 3     3 1 170 sub alert_notification { $_[0]->req_backend(CMD_alert_notification,
1783             &encode_alert_notification, @_) }
1784              
1785             ### replace (4.10.1), p.102
1786              
1787             sub decode_replace_sm_v34 {
1788 2     2 0 3 my $pdu = shift;
1789 2         10 ($pdu->{message_id}) = unpack 'Z*', $pdu->{data};
1790 2         6 my $len = length($pdu->{message_id}) + 1;
1791 2         8 $len += decode_source_addr($pdu, substr($pdu->{data}, $len));
1792              
1793 2         10 ($pdu->{schedule_delivery_time}) = unpack 'Z*', substr($pdu->{data}, $len);
1794 2         5 $len += length($pdu->{schedule_delivery_time}) + 1;
1795              
1796 2         8 ($pdu->{validity_period}) = unpack 'Z*', substr($pdu->{data}, $len);
1797 2         5 $len += length($pdu->{validity_period}) + 1;
1798              
1799 2         2 my $sm_length;
1800 2         11 ($pdu->{registered_delivery}, # 7
1801             $pdu->{sm_default_msg_id}, # 8
1802             $sm_length, # 9
1803             # 123456789 0
1804             ) = unpack 'CCC', substr($pdu->{data}, $len);
1805 2         3 $len += 1 + 1 + 1;
1806 2         10 ($pdu->{short_message} # 10
1807             ) = unpack "a$sm_length", substr($pdu->{data}, $len);
1808            
1809 2         5 return $len + $sm_length;
1810             }
1811              
1812             #4#cut
1813             sub decode_replace_sm_v4 {
1814 2     2 0 3 my $pdu = shift;
1815 2         9 ($pdu->{msg_reference}) = unpack 'Z*', $pdu->{data};
1816 2         6 my $len = length($pdu->{msg_reference}) + 1;
1817 2         7 $len += decode_source_and_destination($pdu, substr($pdu->{data}, $len));
1818            
1819 2         9 ($pdu->{schedule_delivery_time}, # Z
1820             ) = unpack 'Z*', substr($pdu->{data}, $len);
1821 2         13 $len += length($pdu->{schedule_delivery_time}) + 1;
1822              
1823 2         3 my $sm_length;
1824 2         14 ($pdu->{validity_period}, # 6 n
1825             $pdu->{registered_delivery_mode}, # C
1826             $pdu->{data_coding}, # 8 C
1827             $pdu->{sm_default_msg_id}, # 8 C
1828             $sm_length, # 9 n
1829             ) = unpack 'nCCCn', substr($pdu->{data}, $len);
1830 2         10 $len += 2 + 1 + 1 + 1 + 2;
1831 2         10 ($pdu->{short_message} # 10 a
1832             ) = unpack "a$sm_length", substr($pdu->{data}, $len);
1833            
1834 2         5 $pdu->{message_id} = $pdu->{msg_reference}; # v34 compat
1835 2         5 $pdu->{registered_delivery} = $pdu->{registered_delivery_mode}; # v34 compat
1836            
1837 2         5 return $len + $sm_length;
1838             }
1839             #4#end
1840              
1841             sub encode_replace_sm_v34 {
1842 2     2 0 4 my $me = $_[0];
1843 2         3 my ($message_id, $source_addr_ton, $source_addr_npi, $source_addr,
1844             $schedule_delivery_time, $validity_period,
1845             $registered_delivery, $sm_default_msg_id, $short_message);
1846              
1847             ### Extract mandatory parameters from argument stream
1848            
1849 2         6 for (my $i=1; $i <= $#_; $i+=2) {
1850 10 50       23 next if !defined $_[$i];
1851 10 100       63 if ($_[$i] eq 'message_id') { $message_id = splice @_,$i,2,undef,undef; }
  2 100       10  
    100          
    100          
    100          
    100          
    100          
    100          
    50          
1852 1         5 elsif ($_[$i] eq 'source_addr_ton') { $source_addr_ton = splice @_,$i,2,undef,undef; }
1853 1         5 elsif ($_[$i] eq 'source_addr_npi') { $source_addr_npi = splice @_,$i,2,undef,undef; }
1854 1         5 elsif ($_[$i] eq 'source_addr') { $source_addr = splice @_,$i,2,undef,undef; }
1855 1         4 elsif ($_[$i] eq 'schedule_delivery_time') { $schedule_delivery_time = splice @_,$i,2,undef,undef; }
1856 1         4 elsif ($_[$i] eq 'validity_period') { $validity_period = splice @_,$i,2,undef,undef; }
1857 1         4 elsif ($_[$i] eq 'registered_delivery') { $registered_delivery = splice @_,$i,2,undef,undef; }
1858 1         4 elsif ($_[$i] eq 'sm_default_msg_id') { $sm_default_msg_id = splice @_,$i,2,undef,undef; }
1859 1         4 elsif ($_[$i] eq 'short_message') { $short_message = splice @_,$i,2,undef,undef; }
1860             }
1861              
1862             ### Apply defaults for those mandatory arguments that were not specified
1863            
1864 2 50       7 croak "Must supply message_id to replace_sm" if !defined $message_id;
1865 2 100       6 $source_addr_ton = ${*$me}{source_addr_ton} if !defined $source_addr_ton;
  1         2  
1866 2 100       6 $source_addr_npi = ${*$me}{source_addr_npi} if !defined $source_addr_npi;
  1         3  
1867 2 100       9 $source_addr = ${*$me}{source_addr} if !defined $source_addr;
  1         3  
1868 2 100       4 $schedule_delivery_time = ${*$me}{schedule_delivery_time} if !defined $schedule_delivery_time;
  1         3  
1869 2 100       7 $validity_period = ${*$me}{validity_period} if !defined $validity_period;
  1         3  
1870 2 100       4 $registered_delivery = ${*$me}{registered_delivery} if !defined $registered_delivery;
  1         3  
1871 2 100       7 $sm_default_msg_id = ${*$me}{sm_default_msg_id} if !defined $sm_default_msg_id;
  1         2  
1872 2 100       6 $short_message = ${*$me}{short_message} if !defined $short_message;
  1         3  
1873              
1874 2         17 return pack('Z*CCZ*Z*Z*CCCa*',
1875             $message_id, $source_addr_ton, $source_addr_npi, $source_addr,
1876             $schedule_delivery_time, $validity_period,
1877             $registered_delivery, $sm_default_msg_id, length($short_message), $short_message, );
1878             }
1879              
1880             #4#cut
1881             sub encode_replace_sm_v4 {
1882 2     2 0 4 my $me = $_[0];
1883 2         4 my ($msg_reference, $source_addr_ton, $source_addr_npi, $source_addr,
1884             $dest_addr_ton, $dest_addr_npi, $destination_addr,
1885             $schedule_delivery_time, $validity_period,
1886             $registered_delivery_mode, $data_coding, $sm_default_msg_id, $short_message);
1887              
1888             ### Extract mandatory parameters from argument stream
1889            
1890 2         8 for (my $i=1; $i <= $#_; $i+=2) {
1891 14 50       41 next if !defined $_[$i];
1892 14 50       109 if ($_[$i] eq 'msg_reference') { $msg_reference = splice @_,$i,2,undef,undef; }
  0 100       0  
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    50          
1893 2         10 elsif ($_[$i] eq 'message_id') { $msg_reference = splice @_,$i,2,undef,undef; } # v34
1894 1         4 elsif ($_[$i] eq 'source_addr_ton') { $source_addr_ton = splice @_,$i,2,undef,undef; }
1895 1         4 elsif ($_[$i] eq 'source_addr_npi') { $source_addr_npi = splice @_,$i,2,undef,undef; }
1896 1         3 elsif ($_[$i] eq 'source_addr') { $source_addr = splice @_,$i,2,undef,undef; }
1897 1         9 elsif ($_[$i] eq 'dest_addr_ton') { $dest_addr_ton = splice @_,$i,2,undef,undef; }
1898 1         5 elsif ($_[$i] eq 'dest_addr_npi') { $dest_addr_npi = splice @_,$i,2,undef,undef; }
1899 1         4 elsif ($_[$i] eq 'destination_addr') { $destination_addr = splice @_,$i,2,undef,undef; }
1900 1         5 elsif ($_[$i] eq 'schedule_delivery_time') { $schedule_delivery_time = splice @_,$i,2,undef,undef; }
1901 1         4 elsif ($_[$i] eq 'validity_period') { $validity_period = splice @_,$i,2,undef,undef; }
1902 0         0 elsif ($_[$i] eq 'registered_delivery_mode') { $registered_delivery_mode = splice @_,$i,2,undef,undef; }
1903 1         4 elsif ($_[$i] eq 'registered_delivery') { $registered_delivery_mode = splice @_,$i,2,undef,undef; } # v34
1904 1         5 elsif ($_[$i] eq 'data_coding') { $data_coding = splice @_,$i,2,undef,undef; }
1905 1         4 elsif ($_[$i] eq 'sm_default_msg_id') { $sm_default_msg_id = splice @_,$i,2,undef,undef; }
1906 1         5 elsif ($_[$i] eq 'short_message') { $short_message = splice @_,$i,2,undef,undef; }
1907             }
1908              
1909             ### Apply defaults for those mandatory arguments that were not specified
1910            
1911 2 50       6 croak "Must supply msg_reference or message_id to replace_sm" if !defined $msg_reference;
1912 2 100       6 $source_addr_ton = ${*$me}{source_addr_ton} if !defined $source_addr_ton;
  1         4  
1913 2 100       6 $source_addr_npi = ${*$me}{source_addr_npi} if !defined $source_addr_npi;
  1         4  
1914 2 100       5 $source_addr = ${*$me}{source_addr} if !defined $source_addr;
  1         4  
1915 2 100       8 $dest_addr_ton = ${*$me}{dest_addr_ton} if !defined $dest_addr_ton;
  1         2  
1916 2 100       6 $dest_addr_npi = ${*$me}{dest_addr_npi} if !defined $dest_addr_npi;
  1         4  
1917 2 100       6 $destination_addr = ${*$me}{destination_addr} if !defined $destination_addr;
  1         3  
1918 2 100       7 $schedule_delivery_time = ${*$me}{schedule_delivery_time} if !defined $schedule_delivery_time;
  1         3  
1919 2 100       7 $validity_period = ${*$me}{validity_period} if !defined $validity_period;
  1         4  
1920 2 100       12 $registered_delivery_mode = ${*$me}{registered_delivery_mode} if !defined $registered_delivery_mode;
  1         4  
1921 2 100       6 $data_coding = ${*$me}{data_coding} if !defined $data_coding;
  1         3  
1922 2 100       6 $sm_default_msg_id = ${*$me}{sm_default_msg_id} if !defined $sm_default_msg_id;
  1         3  
1923 2 100       6 $short_message = ${*$me}{short_message} if !defined $short_message;
  1         3  
1924              
1925 2         20 return pack('Z*CCZ*CCZ*Z*nCCCna*',
1926             $msg_reference, $source_addr_ton, $source_addr_npi, $source_addr,
1927             $dest_addr_ton, $dest_addr_npi, $destination_addr,
1928             $schedule_delivery_time, $validity_period,
1929             $registered_delivery_mode, $data_coding, $sm_default_msg_id, length($short_message), $short_message, );
1930             }
1931             #4#end
1932              
1933             sub replace_sm {
1934 4     4 1 173 my $me = $_[0];
1935 4 100       7 return $me->req_backend(CMD_replace_sm, ${*$me}{smpp_version} == 0x40 #4
  4         22  
1936             ? &encode_replace_sm_v4 : &encode_replace_sm_v34, #4
1937             @_); #4
1938 0         0 return $me->req_backend(CMD_replace_sm, &encode_replace_sm_v34, @_);
1939             }
1940              
1941             ### cancel (4.9.1), p.98
1942              
1943             sub decode_cancel {
1944 4     4 0 6 my $pdu = shift;
1945 4         8 my $me = shift;
1946 4         7 my $len = 0;
1947 4 100       10 if (${*$me}{smpp_version}==0x40) { #4
  4         16  
1948 2         7 ($pdu->{service_type}) = unpack 'n', $pdu->{data}; #4
1949 2         5 $len += 2; #4
1950             } else { #4
1951 2         8 ($pdu->{service_type}) = unpack 'Z*', $pdu->{data};
1952 2         5 $len += length($pdu->{service_type}) + 1;
1953             } #4
1954 4         21 ($pdu->{message_id}) = unpack 'Z*', substr($pdu->{data}, $len);
1955 4         8 $len += length($pdu->{message_id}) + 1;
1956              
1957 4         15 $len += decode_source_and_destination($pdu, substr($pdu->{data}, $len));
1958              
1959 4         11 $pdu->{message_class} = $pdu->{service_type}; # v4 #4
1960 4         8 return $len;
1961             }
1962              
1963             sub encode_cancel_sm {
1964 4     4 0 9 my $me = $_[0];
1965 4         6 my ($service_type, $message_id, $source_addr_ton, $source_addr_npi, $source_addr,
1966             $dest_addr_ton, $dest_addr_npi, $destination_addr);
1967              
1968             ### Extract mandatory parameters from argument stream
1969            
1970 4         16 for (my $i=1; $i <= $#_; $i+=2) {
1971 16 50       35 next if !defined $_[$i];
1972 16 100       89 if ($_[$i] eq 'service_type') { $service_type = splice @_,$i,2,undef,undef; }
  1 100       5  
    100          
    100          
    100          
    100          
    100          
    100          
    50          
1973 1         5 elsif ($_[$i] eq 'message_class') { $service_type = splice @_,$i,2,undef,undef; } # v4 #4
1974 2         192 elsif ($_[$i] eq 'message_id') { $message_id = splice @_,$i,2,undef,undef; }
1975 2         7 elsif ($_[$i] eq 'source_addr_ton') { $source_addr_ton = splice @_,$i,2,undef,undef; }
1976 2         8 elsif ($_[$i] eq 'source_addr_npi') { $source_addr_npi = splice @_,$i,2,undef,undef; }
1977 2         7 elsif ($_[$i] eq 'source_addr') { $source_addr = splice @_,$i,2,undef,undef; }
1978 2         7 elsif ($_[$i] eq 'dest_addr_ton') { $dest_addr_ton = splice @_,$i,2,undef,undef; }
1979 2         7 elsif ($_[$i] eq 'dest_addr_npi') { $dest_addr_npi = splice @_,$i,2,undef,undef; }
1980 2         8 elsif ($_[$i] eq 'destination_addr') { $destination_addr = splice @_,$i,2,undef,undef; }
1981             }
1982              
1983             ### Apply defaults for those mandatory arguments that were not specified
1984            
1985 4 100       10 $service_type = ${*$me}{service_type} if !defined $service_type;
  2         7  
1986 4 100       13 $message_id = ${*$me}{message_id} if !defined $message_id;
  2         5  
1987 4 100       9 $source_addr_ton = ${*$me}{source_addr_ton} if !defined $source_addr_ton;
  2         5  
1988 4 100       11 $source_addr_npi = ${*$me}{source_addr_npi} if !defined $source_addr_npi;
  2         4  
1989 4 100       10 $source_addr = ${*$me}{source_addr} if !defined $source_addr;
  2         4  
1990 4 100       9 $dest_addr_ton = ${*$me}{dest_addr_ton} if !defined $dest_addr_ton;
  2         5  
1991 4 100       10 $dest_addr_npi = ${*$me}{dest_addr_npi} if !defined $dest_addr_npi;
  2         5  
1992 4 100       11 $destination_addr = ${*$me}{destination_addr} if !defined $destination_addr;
  2         5  
1993              
1994 4 100       5 return pack(${*$me}{smpp_version}==0x40 ? 'nZ*CCZ*CCZ*' : 'Z*Z*CCZ*CCZ*', #4
  4         37  
1995             $service_type, $message_id, #4
1996             $source_addr_ton, $source_addr_npi, $source_addr, #4
1997             $dest_addr_ton, $dest_addr_npi, $destination_addr, ); #4
1998 0         0 return pack('Z*Z*CCZ*CCZ*',
1999             $service_type, $message_id,
2000             $source_addr_ton, $source_addr_npi, $source_addr,
2001             $dest_addr_ton, $dest_addr_npi, $destination_addr, );
2002             }
2003              
2004 4     4 1 176 sub cancel_sm { $_[0]->req_backend(CMD_cancel_sm, &encode_cancel_sm, @_) } # public API
2005              
2006             ### data_sm (4.7.1), p.87
2007              
2008             sub decode_data_sm {
2009 3     3 0 7 my $pdu = shift;
2010            
2011 3         14 ($pdu->{service_type}) = unpack 'Z*', $pdu->{data};
2012 3         8 my $len = length($pdu->{service_type}) + 1;
2013            
2014 3         13 $len += decode_source_and_destination($pdu, substr($pdu->{data}, $len));
2015              
2016 3         22 ($pdu->{esm_class}, # 8
2017             $pdu->{registered_delivery}, # 9
2018             $pdu->{data_coding}, # 10
2019             # 890
2020             ) = unpack 'CCC', substr($pdu->{data}, $len);
2021            
2022 3         10 return $len + 1 + 1 + 1;
2023             }
2024              
2025             sub encode_data_sm {
2026 3     3 0 9 my $me = $_[0];
2027 3         5 my ($service_type, $source_addr_ton, $source_addr_npi, $source_addr,
2028             $dest_addr_ton, $dest_addr_npi, $destination_addr,
2029             $esm_class, $registered_delivery, $data_coding);
2030              
2031             ### Extract mandatory parameters from argument stream
2032            
2033 3         18 for (my $i=1; $i <= $#_; $i+=2) {
2034 59 50       118 next if !defined $_[$i];
2035 59 100       402 if ($_[$i] eq 'service_type') { $service_type = splice @_,$i,2,undef,undef; }
  2 100       11  
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
2036 2         9 elsif ($_[$i] eq 'source_addr_ton') { $source_addr_ton = splice @_,$i,2,undef,undef; }
2037 2         8 elsif ($_[$i] eq 'source_addr_npi') { $source_addr_npi = splice @_,$i,2,undef,undef; }
2038 2         7 elsif ($_[$i] eq 'source_addr') { $source_addr = splice @_,$i,2,undef,undef; }
2039 2         7 elsif ($_[$i] eq 'dest_addr_ton') { $dest_addr_ton = splice @_,$i,2,undef,undef; }
2040 2         8 elsif ($_[$i] eq 'dest_addr_npi') { $dest_addr_npi = splice @_,$i,2,undef,undef; }
2041 3         14 elsif ($_[$i] eq 'destination_addr') { $destination_addr = splice @_,$i,2,undef,undef; }
2042 2         8 elsif ($_[$i] eq 'esm_class') { $esm_class = splice @_,$i,2,undef,undef; }
2043 2         8 elsif ($_[$i] eq 'registered_delivery') { $registered_delivery = splice @_,$i,2,undef,undef; }
2044 2         7 elsif ($_[$i] eq 'data_coding') { $data_coding = splice @_,$i,2,undef,undef; }
2045             }
2046              
2047             ### Apply defaults for those mandatory arguments that were not specified
2048            
2049 3 100       10 $service_type = ${*$me}{service_type} if !defined $service_type;
  1         145  
2050 3 100       13 $source_addr_ton = ${*$me}{source_addr_ton} if !defined $source_addr_ton;
  1         5  
2051 3 100       10 $source_addr_npi = ${*$me}{source_addr_npi} if !defined $source_addr_npi;
  1         4  
2052 3 100       6 $source_addr = ${*$me}{source_addr} if !defined $source_addr;
  1         5  
2053 3 100       10 $dest_addr_ton = ${*$me}{dest_addr_ton} if !defined $dest_addr_ton;
  1         4  
2054 3 100       10 $dest_addr_npi = ${*$me}{dest_addr_npi} if !defined $dest_addr_npi;
  1         4  
2055              
2056 3 50       8 croak "Must supply destination_addr to data_sm" if !defined $destination_addr;
2057              
2058 3 100       8 $esm_class = ${*$me}{esm_class} if !defined $esm_class;
  1         4  
2059 3 100       9 $registered_delivery = ${*$me}{registered_delivery} if !defined $registered_delivery;
  1         3  
2060 3 100       8 $data_coding = ${*$me}{data_coding} if !defined $data_coding;
  1         2  
2061              
2062 3         28 return pack('Z*CCZ*CCZ*CCC',
2063             $service_type, $source_addr_ton, $source_addr_npi, $source_addr,
2064             $dest_addr_ton, $dest_addr_npi, $destination_addr,
2065             $esm_class, $registered_delivery, $data_coding, );
2066             }
2067              
2068 3     3 1 167 sub data_sm { $_[0]->req_backend(CMD_data_sm, &encode_data_sm, @_) }
2069              
2070             #4#cut
2071             ### delivery_receipt: v4 6.4.6.1, p.41
2072              
2073             sub decode_delivery_receipt {
2074 2     2 0 4 my $pdu = shift;
2075 2         10 my $len = decode_source_and_destination($pdu, $pdu->{data});
2076              
2077 2         11 ($pdu->{msg_reference}) = unpack 'Z*', substr($pdu->{data}, $len);
2078 2         12 $len += length($pdu->{msg_reference}) + 1;
2079              
2080 2         15 ($pdu->{num_msgs_submitted}, # 9 N
2081             $pdu->{num_msgs_delivered}, # 10 N
2082             $pdu->{submit_date}, # 11 Z
2083             ) = unpack 'NNZ*', substr($pdu->{data}, $len);
2084 2         6 $len += 4 + 4 + length($pdu->{submit_date}) + 1;
2085              
2086 2         8 ($pdu->{done_date}) = unpack 'Z*', substr($pdu->{data}, $len);
2087 2         5 $len += length($pdu->{done_date}) + 1;
2088            
2089 2         3 my $sm_length;
2090 2         15 ($pdu->{message_state}, # 13 N
2091             $pdu->{network_error_code}, # 14 N
2092             $pdu->{data_coding}, # 15 C
2093             $sm_length, # 16 n
2094             # 234567890123456 7
2095             ) = unpack 'NNCn', substr($pdu->{data}, $len);
2096 2         4 $len += 4 + 4 + 1 + 2;
2097 2         12 ($pdu->{short_message}, # 17 a
2098             ) = unpack "a$sm_length", substr($pdu->{data}, $len);
2099 2         5 return $len + $sm_length;
2100             }
2101              
2102             sub encode_delivery_receipt {
2103 2     2 0 6 my $me = $_[0];
2104 2         6 my ($source_addr_ton, $source_addr_npi, $source_addr,
2105             $dest_addr_ton, $dest_addr_npi, $destination_addr,
2106             $msg_reference, $num_msgs_submitted, $num_msgs_delivered,
2107             $submit_date, $done_date, $message_state, $network_error_code,
2108             $data_coding, $short_message);
2109              
2110             ### Extract mandatory parameters from argument stream
2111            
2112 2         10 for (my $i=1; $i <= $#_; $i+=2) {
2113 16 50       35 next if !defined $_[$i];
2114 16 100       136 if ($_[$i] eq 'source_addr_ton') { $source_addr_ton = splice @_,$i,2,undef,undef; }
  1 100       7  
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
2115 1         5 elsif ($_[$i] eq 'source_addr_npi') { $source_addr_npi = splice @_,$i,2,undef,undef; }
2116 1         5 elsif ($_[$i] eq 'source_addr') { $source_addr = splice @_,$i,2,undef,undef; }
2117 1         5 elsif ($_[$i] eq 'dest_addr_ton') { $dest_addr_ton = splice @_,$i,2,undef,undef; }
2118 1         4 elsif ($_[$i] eq 'dest_addr_npi') { $dest_addr_npi = splice @_,$i,2,undef,undef; }
2119 2         11 elsif ($_[$i] eq 'destination_addr') { $destination_addr = splice @_,$i,2,undef,undef; }
2120 1         4 elsif ($_[$i] eq 'msg_reference') { $msg_reference = splice @_,$i,2,undef,undef; }
2121 1         5 elsif ($_[$i] eq 'num_msgs_submitted') { $num_msgs_submitted = splice @_,$i,2,undef,undef; }
2122 1         4 elsif ($_[$i] eq 'num_msgs_delivered') { $num_msgs_delivered = splice @_,$i,2,undef,undef; }
2123 1         5 elsif ($_[$i] eq 'submit_date') { $submit_date = splice @_,$i,2,undef,undef; }
2124 1         4 elsif ($_[$i] eq 'done_date') { $done_date = splice @_,$i,2,undef,undef; }
2125 1         5 elsif ($_[$i] eq 'message_state') { $message_state = splice @_,$i,2,undef,undef; }
2126 1         6 elsif ($_[$i] eq 'network_error_code') { $network_error_code = splice @_,$i,2,undef,undef; }
2127 1         4 elsif ($_[$i] eq 'data_coding') { $data_coding = splice @_,$i,2,undef,undef; }
2128 1         6 elsif ($_[$i] eq 'short_message') { $short_message = splice @_,$i,2,undef,undef; }
2129             }
2130              
2131             ### Apply defaults for those mandatory arguments that were not specified
2132            
2133 2 100       8 $source_addr_ton = ${*$me}{source_addr_ton} if !defined $source_addr_ton;
  1         5  
2134 2 100       9 $source_addr_npi = ${*$me}{source_addr_npi} if !defined $source_addr_npi;
  1         4  
2135 2 100       9 $source_addr = ${*$me}{source_addr} if !defined $source_addr;
  1         4  
2136 2 100       7 $dest_addr_ton = ${*$me}{dest_addr_ton} if !defined $dest_addr_ton;
  1         4  
2137 2 100       8 $dest_addr_npi = ${*$me}{dest_addr_npi} if !defined $dest_addr_npi;
  1         4  
2138            
2139 2 50       7 croak "Must supply destination_addr to delivery_receipt" if !defined $destination_addr;
2140              
2141 2 100       13 $msg_reference = ${*$me}{msg_reference} if !defined $msg_reference;
  1         4  
2142 2 100       9 $num_msgs_submitted = ${*$me}{num_msgs_submitted} if !defined $num_msgs_submitted;
  1         3  
2143 2 100       7 $num_msgs_delivered = ${*$me}{num_msgs_delivered} if !defined $num_msgs_delivered;
  1         4  
2144 2 100       7 $submit_date = ${*$me}{submit_date} if !defined $submit_date;
  1         3  
2145 2 100       8 $done_date = ${*$me}{done_date} if !defined $done_date;
  1         3  
2146 2 100       8 $message_state = ${*$me}{message_state} if !defined $message_state;
  1         3  
2147 2 100       8 $network_error_code = ${*$me}{network_error_code} if !defined $network_error_code;
  1         3  
2148 2 100       6 $data_coding = ${*$me}{data_coding} if !defined $data_coding;
  1         3  
2149 2 100       70 $short_message = ${*$me}{short_message} if !defined $short_message;
  1         4  
2150              
2151 2         25 return pack('CCZ*CCZ*Z*NNZ*Z*NNCna*',
2152             $source_addr_ton, $source_addr_npi, $source_addr,
2153             $dest_addr_ton, $dest_addr_npi, $destination_addr,
2154             $msg_reference, $num_msgs_submitted, $num_msgs_delivered,
2155             $submit_date, $done_date, $message_state,
2156             $network_error_code, $data_coding, length($short_message), $short_message);
2157             }
2158              
2159 2     2 0 100 sub delivery_receipt { $_[0]->req_backend(CMD_delivery_receipt, &encode_delivery_receipt, @_) }
2160             #4#end
2161              
2162             ###
2163              
2164             sub set_version {
2165 4     4 1 12 my $me = shift;
2166 4         7 my $version = shift;
2167              
2168 4 100       12 if ($version == 0x40) { #4
2169 2         4 ${*$me}{smpp_version} = 0x40; #4
  2         4  
2170 2         5 ${*$me}{head_templ} = 'NNNNxxxx'; #4
  2         4  
2171 2         4 ${*$me}{head_len} = 20; #4
  2         4  
2172 2         4 ${*$me}{cmd_version} = 0x00010000; #4
  2         5  
2173             } else { #4
2174 2         4 ${*$me}{smpp_version} = $version;
  2         12  
2175 2         4 ${*$me}{head_templ} = 'NNNN';
  2         4  
2176 2         5 ${*$me}{head_len} = 16;
  2         4  
2177 2         4 ${*$me}{cmd_version} = 0x00000000;
  2         6  
2178             } #4
2179             }
2180              
2181             ### Accept a new server child, i.e. accepted socket. This
2182             ### constructor gets called internally just after accept system
2183             ### call when listening socket does accept. See also "new_listen"
2184             ### which gets called when socket is created and put listening.
2185             ### DO NOT USE THIS CONSTRUCTOR FOR CLIENT SIDE CONNECTIONS.
2186             ###
2187             ### The way this code works is that somewhere deep in guts of
2188             ### IO::Socket module the constructor name is hardwired to
2189             ### "new" and there is no way to pass any arguments to this
2190             ### constructor, hence I have to copy the arguments from
2191             ### the parent when constructing. Let's hope this aspect
2192             ### of IO::Socket does not change.
2193              
2194             sub new {
2195 2     2 1 132 my $accept = shift;
2196 2   33     9 my $type = ref($accept) || $accept;
2197 2         9 my $me = gensym;
2198 2         22 for my $k (keys %{*$accept}) {
  2         19  
2199 102         86 ${*$me}{$k} = ${*$accept}{$k};
  102         219  
  102         146  
2200             }
2201 2         15 return bless $me, $type;
2202             }
2203              
2204             ### Create client connection (do not use "new")
2205              
2206             sub new_connect {
2207 2     2 1 89 my $me = shift;
2208 2   33     15 my $type = ref($me) || $me;
2209 2 50       9 my $host = shift if @_ % 2; # host need not be tagged
2210 2         16 my %arg = @_;
2211              
2212 2 50       27 my $s = $type->SUPER::new(
    50          
    50          
    50          
2213             PeerAddr => $host,
2214             PeerPort => exists $arg{port} ? $arg{port} : Default->{port},
2215             LocalAddr => exists $arg{local_ip} ? $arg{local_ip} : Default->{local_ip},
2216             Proto => 'tcp',
2217             Timeout => exists $arg{timeout} ? $arg{timeout} : Default->{timeout},
2218             @_) # pass any extra args to constructor
2219             or return undef;
2220            
2221 2         3816 for my $a (keys %{&Default}) {
  2         20  
2222 94 100       162 ${*$s}{$a} = exists $arg{$a} ? $arg{$a} : Default->{$a};
  94         194  
2223             }
2224 2         8 $s->set_version(${*$s}{smpp_version});
  2         10  
2225             #warn Dumper $s;
2226            
2227 2         10 $s->autoflush(1);
2228             #$s->debug(exists $arg{debug} ? $arg{debug} : undef);
2229 2         79 return $s;
2230             }
2231              
2232             sub new_transceiver {
2233 0     0 1 0 my $type = shift;
2234 0         0 my $me = $type->new_connect(@_);
2235 0 0       0 return undef if !defined $me;
2236 0 0       0 warn "Connected, sending bind: ".Dumper($me) if $trace;
2237 0         0 my $resp = $me->bind_transceiver();
2238 0 0       0 warn "Bound: ".Dumper($resp) if $trace;
2239 0 0       0 return ($me, $resp) if wantarray;
2240 0         0 return $me;
2241             }
2242              
2243             sub new_transmitter {
2244 0     0 1 0 my $type = shift;
2245 0         0 my $me = $type->new_connect(@_);
2246 0 0       0 return undef if !defined $me;
2247 0 0       0 warn "Connected, sending bind: ".Dumper($me) if $trace;
2248 0         0 my $resp = $me->bind_transmitter();
2249 0 0       0 warn "Bound: ".Dumper($resp) if $trace;
2250 0 0       0 return ($me, $resp) if wantarray;
2251 0         0 return $me;
2252             }
2253              
2254             sub new_receiver {
2255 0     0 1 0 my $type = shift;
2256 0         0 my $me = $type->new_connect(@_);
2257 0 0       0 return undef if !defined $me;
2258 0 0       0 warn "Connected, sending bind: ".Dumper($me) if $trace;
2259 0         0 my $resp = $me->bind_receiver();
2260 0 0       0 warn "Bound: ".Dumper($resp) if $trace;
2261 0 0       0 return ($me, $resp) if wantarray;
2262 0         0 return $me;
2263             }
2264              
2265             ### Create new server connection, i.e. listening socket. See
2266             ### also "new" which gets called when connection is accepted
2267             ### from the listening socket.
2268              
2269             sub new_listen {
2270 2     2 1 309 my $me = shift;
2271 2   33     15 my $type = ref($me) || $me;
2272 2 50       13 my $host = shift if @_ % 2; # host need not be tagged
2273 2         11 my %arg = @_;
2274              
2275 2 50       84 my $s = $type->SUPER::new(
    50          
    50          
    50          
2276             LocalAddr => $host,
2277             LocalPort => exists $arg{port} ? $arg{port} : Default->{port},
2278             Proto => 'tcp',
2279             ReuseAddr => 'true',
2280             Listen => exists $arg{listen} ? $arg{listen} : Default->{listen},
2281             Timeout => exists $arg{timeout} ? $arg{timeout} : Default->{timeout})
2282             or return undef;
2283 2         1689 for my $a (keys %{&Default}) {
  2         31  
2284 94 100       155 ${*$s}{$a} = exists $arg{$a} ? $arg{$a} : Default->{$a};
  94         203  
2285             }
2286 2         10 $s->set_version(${*$s}{smpp_version});
  2         15  
2287 2         10 $s->sockopt(SO_REUSEADDR => 1);
2288 2         27 $s->autoflush(1);
2289             #$s->debug(exists $arg{debug} ? $arg{debug} : undef);
2290 2         76 return $s;
2291             }
2292              
2293             ### This table drives the decoding process
2294              
2295 1         1695 use constant pdu_tab => {
2296             0x80000000 => { cmd => 'generic_nack', decode => \&decode_empty, }, # i
2297             0x00000001 => { cmd => 'bind_receiver', decode => \&decode_bind, }, # i
2298             0x80000001 => { cmd => 'bind_receiver_resp', decode => \&decode_bind_resp_v34, }, # i
2299             0x00000002 => { cmd => 'bind_transmitter', decode => \&decode_bind, }, # i
2300             0x80000002 => { cmd => 'bind_transmitter_resp', decode => \&decode_bind_resp_v34, }, # i
2301             0x00000003 => { cmd => 'query_sm', decode => \&decode_query_v34, }, # i
2302             0x80000003 => { cmd => 'query_sm_resp', decode => \&decode_query_resp_v34, }, # i
2303             0x00000004 => { cmd => 'submit_sm', decode => \&decode_submit_v34, }, # i
2304             0x80000004 => { cmd => 'submit_sm_resp', decode => \&decode_submit_resp_v34, }, # i
2305             0x00000005 => { cmd => 'deliver_sm', decode => \&decode_submit_v34, }, # i
2306             0x80000005 => { cmd => 'deliver_sm_resp', decode => \&decode_submit_resp_v34, }, # i
2307             0x00000006 => { cmd => 'unbind', decode => \&decode_empty, }, # i
2308             0x80000006 => { cmd => 'unbind_resp', decode => \&decode_empty, }, # i
2309             0x00000007 => { cmd => 'replace_sm', decode => \&decode_replace_sm_v34, }, # i
2310             0x80000007 => { cmd => 'replace_sm_resp', decode => \&decode_empty, }, # i
2311             0x00000008 => { cmd => 'cancel_sm', decode => \&decode_cancel, }, # i
2312             0x80000008 => { cmd => 'cancel_sm_resp', decode => \&decode_empty, }, # i
2313             0x00000009 => { cmd => 'bind_transceiver', decode => \&decode_bind, }, # i
2314             0x80000009 => { cmd => 'bind_transceiver_resp', decode => \&decode_bind_resp_v34, }, # i
2315             0x0000000b => { cmd => 'outbind', decode => \&decode_outbind_v34, }, # i
2316             0x00000015 => { cmd => 'enquire_link', decode => \&decode_empty, }, # i
2317             0x80000015 => { cmd => 'enquire_link_resp', decode => \&decode_empty, }, # i
2318             0x00000021 => { cmd => 'submit_multi', decode => \&decode_submit_multi, }, # i
2319             0x80000021 => { cmd => 'submit_multi_resp', decode => \&decode_submit_multi_resp, }, # i
2320             0x00000102 => { cmd => 'alert_notification', decode => \&decode_alert_notification, }, # i
2321             0x00000103 => { cmd => 'data_sm', decode => \&decode_data_sm, }, # i
2322             0x80000103 => { cmd => 'data_sm_resp', decode => \&decode_submit_resp_v34, }, # i
2323              
2324             #4#cut
2325             # v4 codes
2326              
2327             0x80010000 => { cmd => 'generic_nack_v4', decode => \&decode_empty, }, # i
2328             0x00010001 => { cmd => 'bind_receiver_v4', decode => \&decode_bind, }, # i
2329             0x80010001 => { cmd => 'bind_receiver_resp_v4', decode => \&decode_bind_resp_v4, }, # i
2330             0x00010002 => { cmd => 'bind_transmitter_v4', decode => \&decode_bind, }, # i
2331             0x80010002 => { cmd => 'bind_transmitter_resp_v4', decode => \&decode_bind_resp_v4, }, # i
2332             0x00010003 => { cmd => 'query_sm_v4', decode => \&decode_query_v4, }, # i
2333             0x80010003 => { cmd => 'query_sm_resp_v4', decode => \&decode_query_resp_v4, }, # i
2334             0x00010004 => { cmd => 'submit_sm_v4', decode => \&decode_submit_v4, }, # i
2335             0x80010004 => { cmd => 'submit_sm_resp_v4', decode => \&decode_submit_sm_resp_v4, }, # i
2336             0x00010005 => { cmd => 'deliver_sm_v4', decode => \&decode_deliver_sm_v4, }, # i
2337             0x80010005 => { cmd => 'deliver_sm_resp_v4', decode => \&decode_empty, }, # i
2338             0x00010006 => { cmd => 'unbind_v4', decode => \&decode_empty, }, # i
2339             0x80010006 => { cmd => 'unbind_resp_v4', decode => \&decode_empty, }, # i
2340             0x00010007 => { cmd => 'replace_sm_v4', decode => \&decode_replace_sm_v4, }, # i
2341             0x80010007 => { cmd => 'replace_sm_resp_v4', decode => \&decode_empty, }, # i
2342             0x00010008 => { cmd => 'cancel_sm_v4', decode => \&decode_cancel, }, # i
2343             0x80010008 => { cmd => 'cancel_sm_resp_v4', decode => \&decode_empty, }, # i
2344             0x00010009 => { cmd => 'delivery_receipt_v4', decode => \&decode_delivery_receipt, }, # ***
2345             0x80010009 => { cmd => 'delivery_receipt_resp_v4', decode => \&decode_empty, }, # i
2346             0x0001000a => { cmd => 'enquire_link_v4', decode => \&decode_empty, }, # i v4
2347             0x8001000a => { cmd => 'enquire_link_resp_v4', decode => \&decode_empty, }, # i v4
2348             0x0001000b => { cmd => 'outbind_v4', decode => \&decode_outbind_v4, }, # i
2349             #4#end
2350 1     1   17 };
  1         2  
2351              
2352             package Net::SMPP::PDU;
2353              
2354             sub message_id {
2355 0     0   0 my $me = shift;
2356 0         0 return $me->{message_id};
2357             }
2358              
2359             sub status {
2360 1     1   15 my $me = shift;
2361 1         4 return $me->{status};
2362             #return ${$me}{status};
2363             #return ${*$me}{status};
2364             }
2365              
2366             sub seq {
2367 0     0   0 my $me = shift;
2368 0         0 return $me->{seq};
2369             }
2370              
2371             sub explain_status {
2372 0     0   0 my $me = shift;
2373 0         0 return sprintf("%s (%s=0x%08X)",
2374             Net::SMPP::status_code->{$me->{status}}->{msg},
2375             Net::SMPP::status_code->{$me->{status}}->{code},
2376             $me->{status});
2377             }
2378              
2379             sub cmd {
2380 0     0   0 my $me = shift;
2381 0         0 return $me->{cmd};
2382             }
2383              
2384             sub explain_cmd {
2385 0     0   0 my $me = shift;
2386 0   0     0 my $cmd = Net::SMPP::pdu_tab->{$me->{cmd}}
2387             || { cmd => sprintf(q{Unknown(0x%08X)}, $me->{cmd}) };
2388 0         0 return $cmd->{cmd};
2389             }
2390              
2391             package Net::SMPP;
2392              
2393             ### Try real hard to read something, i.e. block until the thing has
2394             ### been entirely read.
2395              
2396             sub read_hard {
2397 172     172 0 334 my ($me, $len, $dr, $offset) = @_;
2398 172         420 while (length($$dr) < $len+$offset) {
2399 150         222 my $n = length($$dr) - $offset;
2400 150         192 eval {
2401 150     0   2088 local $SIG{ALRM} = sub { die "alarm\n" }; # NB: \n required
  0         0  
2402 150 50       208 alarm ${*$me}{enquire_interval} if ${*$me}{enquire_interval};
  0         0  
  150         508  
2403 150 50       333 warn "read $n/$len enqint(${*$me}{enquire_interval})" if $trace>1;
  0         0  
2404 150         167 while (1) {
2405 150         553 $n = $me->sysread($$dr, $len-$n, $n+$offset);
2406 150 50       266836 next if $! =~ /^Interrupted/;
2407 150         216 last;
2408             }
2409 150         1968 alarm 0;
2410             };
2411 150 50       275 if ($@) {
2412 0 0       0 warn "ENQUIRE $@" if $trace;
2413 0 0       0 die unless $@ eq "alarm\n"; # propagate unexpected errors
2414 0         0 $me->enquire_link(); # Send a periodic ping
2415             } else {
2416 150 50       352 if (!defined($n)) {
2417 0         0 warn "error reading header from socket: $!";
2418 0         0 ${*$me}{smpperror} = "read_hard I/O error: $!";
  0         0  
2419 0         0 ${*$me}{smpperrorcode} = 1;
  0         0  
2420 0         0 return undef;
2421             }
2422 150 50       539 if (!$n) {
2423 0         0 warn "premature eof reading from socket";
2424 0         0 ${*$me}{smpperror} = "read_hard premature eof";
  0         0  
2425 0         0 ${*$me}{smpperrorcode} = 2;
  0         0  
2426 0         0 return undef;
2427             }
2428             }
2429             }
2430             #warn "read complete";
2431 172         449 return 1;
2432             }
2433              
2434             ### read pdu from wire and decode it, if PDU is understood
2435              
2436             sub read_pdu {
2437 86     86 1 516 my $me = shift;
2438 86         120 my $header = '';
2439 86         105 my $len;
2440 86         96 my $head_len = ${*$me}{head_len};
  86         215  
2441 86 50       235 $me->read_hard($head_len, \$header, 0) or return undef;
2442 86         442 my $pdu = { cmd => 0, status => 0, seq => 0, data => '', };
2443 86         565 ($len,
2444             $pdu->{cmd},
2445             $pdu->{status},
2446             $pdu->{seq},
2447 86         129 $pdu->{reserved}) = unpack ${*$me}{head_templ}, $header;
2448 86 50       220 if ($len < $head_len) {
2449 0         0 warn "Too short length $len < ${*$me}{head_len}, cmd=$pdu->{cmd}, status=$pdu->{status}, seq=$pdu->{seq}";
  0         0  
2450 0         0 ${*$me}{smpperror} = "read_pdu: Too short length $len < ${*$me}{head_len}, cmd=$pdu->{cmd}, status=$pdu->{status}, seq=$pdu->{seq}";
  0         0  
  0         0  
2451 0         0 ${*$me}{smpperrorcode} = 3;
  0         0  
2452 0         0 return undef;
2453             }
2454 86 50       168 warn "read Header:\n".hexdump($header, "\t") if $trace;
2455            
2456 86         115 $len -= $head_len;
2457 86 50       245 $me->read_hard($len, \$pdu->{data}, 0) or do {
2458 0         0 ${*$me}{smpperror} = "read_pdu: invalid length cmd=$pdu->{cmd},status=$pdu->{status}, seq=$pdu->{seq}";
  0         0  
2459 0         0 ${*$me}{smpperrorcode} = 3;
  0         0  
2460 0         0 return undef;
2461             };
2462 86 50       195 warn "read Body:\n".hexdump($pdu->{data}, "\t") if $trace;
2463            
2464             ### Check if we know this PDU and decode it
2465            
2466 86 50       330 if (defined pdu_tab->{$pdu->{cmd}}) {
2467 86         156 $pdu->{known_pdu} = 1;
2468 86         169 my $pdu_templ = pdu_tab->{$pdu->{cmd}};
2469 86         98 my $mandat_len = &{$pdu_templ->{decode}}($pdu, $me);
  86         275  
2470 86 100       245 decode_optional_params($pdu, $mandat_len) if $mandat_len < $len;
2471             }
2472              
2473 86         434 return bless $pdu => 'Net::SMPP::PDU';
2474             }
2475              
2476             sub wait_pdu {
2477 0     0 1 0 my ($me, $look_for_me, $seq) = @_;
2478 0         0 while (1) {
2479 0   0     0 my $pdu = $me->read_pdu() || return undef;
2480 0 0 0     0 return $pdu if $pdu->{cmd} == $look_for_me && $pdu->{seq} == $seq;
2481              
2482             ### Check if PDU has a handlers (e.g. its enquire_link)
2483              
2484 0 0       0 if (exists ${*$me}{handlers}->{$pdu->{cmd}}) {
  0         0  
2485 0         0 &{${*$me}{handlers}->{$pdu->{cmd}}}($me, $pdu);
  0         0  
  0         0  
2486             }
2487            
2488             ### *** effectively all other PDUs get ignored
2489 0 0       0 warn "looking for $look_for_me seq=$seq, skipping cmd=$pdu->{cmd} seq=$pdu->{seq}" if $trace;
2490             }
2491             }
2492              
2493             ### Send a response to enquire_link
2494              
2495             sub handle_enquire_link {
2496 0     0 0 0 my ($me, $pdu) = @_;
2497 0         0 $me->enquire_link_resp(seq => $pdu->{seq});
2498             }
2499              
2500             ### GSM often uses 7bit encoding to squeeze 160 7bit characters
2501             ### in 140 octets. This encoding is not automatically done by
2502             ### this module, but following routines allow one to do it
2503             ### manually.
2504             ###
2505             ### In general we can fit 8 7bit characters in 7 octets.
2506             ###
2507             ### Packing method:
2508             ###
2509             ### BIT: 76543210 76543210 76543210 76543210 76543210 76543210 76543210
2510             ### BYTE: 0 1 2 3 4 5 6
2511             ### CHAR: BAAAAAAA CCBBBBBB DDDCCCCC EEEEDDDD FFFFFEEE GGGGGGFF HHHHHHHG
2512             ###
2513             ### So as can be seen, the characters are encoded lowest bit to lowest
2514             ### available bit position, just wrapping around. Another possiblity
2515             ### would be as follows
2516             ###
2517             ### BIT: 76543210 76543210 76543210 76543210 76543210 76543210 76543210
2518             ### BYTE: 0 1 2 3 4 5 6
2519             ### CHAR: HAAAAAAA HBBBBBBB HCCCCCCC HDDDDDDD HEEEEEEE HFFFFFFF HGGGGGGG
2520             ###
2521             ### In this scheme the last character is distributed over the high bits
2522             ### of the other bytes. while bytes A-G would just be normal.
2523             ###
2524             ### These routines still have some issues in handling the padding. Especially
2525             ### unpack_7bit may leave some artifacts in the end.
2526              
2527             sub pack_7bit {
2528 11     11 1 696 my ($s) = @_;
2529 11         36 $s = unpack 'b*', $s;
2530 11         116 $s =~ s/(.{7})./$1/g; # Zap the high order (8th) bits
2531 11         48 return pack 'b*', $s;
2532             }
2533              
2534             sub unpack_7bit {
2535 11     11 1 36 my ($s) = @_;
2536 11         25 $s = unpack 'b*', $s;
2537 11         127 $s =~ s/(.{7})/${1}0/g; # Stuff in high order (8th) bits
2538 11         31 $s = pack 'b*', $s;
2539 11 100       38 chop $s if substr($s, -1, 1) eq "\x00";
2540 11         34 return $s;
2541             # return pack 'b*', $s;
2542             }
2543              
2544             # "Gema niskazhu"
2545              
2546             1;
2547             __END__