anjlab-ruby-smpp 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +52 -0
- data/CONTRIBUTORS.txt +11 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +18 -0
- data/LICENSE +20 -0
- data/README.rdoc +89 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/config/environment.rb +2 -0
- data/examples/PDU1.example +26 -0
- data/examples/PDU2.example +26 -0
- data/examples/sample_gateway.rb +137 -0
- data/examples/sample_smsc.rb +102 -0
- data/lib/smpp.rb +25 -0
- data/lib/smpp/base.rb +308 -0
- data/lib/smpp/encoding/utf8_encoder.rb +37 -0
- data/lib/smpp/optional_parameter.rb +35 -0
- data/lib/smpp/pdu/base.rb +183 -0
- data/lib/smpp/pdu/bind_base.rb +25 -0
- data/lib/smpp/pdu/bind_receiver.rb +4 -0
- data/lib/smpp/pdu/bind_receiver_response.rb +4 -0
- data/lib/smpp/pdu/bind_resp_base.rb +17 -0
- data/lib/smpp/pdu/bind_transceiver.rb +4 -0
- data/lib/smpp/pdu/bind_transceiver_response.rb +4 -0
- data/lib/smpp/pdu/deliver_sm.rb +142 -0
- data/lib/smpp/pdu/deliver_sm_response.rb +12 -0
- data/lib/smpp/pdu/enquire_link.rb +11 -0
- data/lib/smpp/pdu/enquire_link_response.rb +11 -0
- data/lib/smpp/pdu/generic_nack.rb +20 -0
- data/lib/smpp/pdu/submit_multi.rb +68 -0
- data/lib/smpp/pdu/submit_multi_response.rb +49 -0
- data/lib/smpp/pdu/submit_sm.rb +91 -0
- data/lib/smpp/pdu/submit_sm_response.rb +31 -0
- data/lib/smpp/pdu/unbind.rb +11 -0
- data/lib/smpp/pdu/unbind_response.rb +12 -0
- data/lib/smpp/receiver.rb +27 -0
- data/lib/smpp/server.rb +223 -0
- data/lib/smpp/transceiver.rb +109 -0
- data/lib/sms.rb +9 -0
- data/ruby-smpp.gemspec +96 -0
- data/test/delegate.rb +28 -0
- data/test/encoding_test.rb +231 -0
- data/test/optional_parameter_test.rb +30 -0
- data/test/pdu_parsing_test.rb +111 -0
- data/test/receiver_test.rb +232 -0
- data/test/responsive_delegate.rb +53 -0
- data/test/server.rb +56 -0
- data/test/smpp_test.rb +239 -0
- data/test/submit_sm_test.rb +40 -0
- data/test/transceiver_test.rb +35 -0
- metadata +133 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
class Smpp::OptionalParameter
|
2
|
+
|
3
|
+
attr_reader :tag, :value
|
4
|
+
|
5
|
+
def initialize(tag, value)
|
6
|
+
@tag = tag
|
7
|
+
@value = value
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](symbol)
|
11
|
+
self.send symbol
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
self.inspect
|
16
|
+
end
|
17
|
+
|
18
|
+
#class methods
|
19
|
+
class << self
|
20
|
+
def from_wire_data(data)
|
21
|
+
|
22
|
+
return nil if data.nil?
|
23
|
+
tag, length, remaining_bytes = data.unpack('H4na*')
|
24
|
+
tag = tag.hex
|
25
|
+
|
26
|
+
raise "invalid data, cannot parse optional parameters" if tag == 0 or length.nil?
|
27
|
+
|
28
|
+
value = remaining_bytes.slice!(0...length)
|
29
|
+
|
30
|
+
return new(tag, value), remaining_bytes
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
# PDUs are the protcol base units in SMPP
|
2
|
+
module Smpp::Pdu
|
3
|
+
class Base
|
4
|
+
#Protocol Version
|
5
|
+
PROTOCOL_VERSION = 0x34
|
6
|
+
# Error constants
|
7
|
+
ESME_ROK = 0x00000000 # OK!
|
8
|
+
ESME_RINVMSGLEN = 0x00000001 # Message Length is invalid
|
9
|
+
ESME_RINVCMDLEN = 0x00000002 # Command Length is invalid
|
10
|
+
ESME_RINVCMDID = 0x00000003 # Invalid Command ID
|
11
|
+
ESME_RINVBNDSTS = 0x00000004 # Incorrect BIND Status for given com-
|
12
|
+
ESME_RALYBND = 0x00000005 # ESME Already in Bound State
|
13
|
+
ESME_RINVPRTFLG = 0x00000006 # Invalid Priority Flag
|
14
|
+
ESME_RINVREGDLVFLG = 0x00000007 # Invalid Registered Delivery Flag
|
15
|
+
ESME_RSYSERR = 0x00000008 # System Error
|
16
|
+
ESME_RINVSRCADR = 0x0000000A # Invalid Source Address
|
17
|
+
ESME_RINVDSTADR = 0x0000000B # Invalid Dest Addr
|
18
|
+
ESME_RINVMSGID = 0x0000000C # Message ID is invalid
|
19
|
+
ESME_RBINDFAIL = 0x0000000D # Bind Failed
|
20
|
+
ESME_RINVPASWD = 0x0000000E # Invalid Password
|
21
|
+
ESME_RINVSYSID = 0x0000000F # Invalid System ID
|
22
|
+
ESME_RCANCELFAIL = 0x00000011 # Cancel SM Failed
|
23
|
+
ESME_RREPLACEFAIL = 0x00000013 # Replace SM Failed
|
24
|
+
ESME_RMSGQFUL = 0x00000014 # Message Queue Full
|
25
|
+
ESME_RINVSERTYP = 0x00000015 # Invalid Service Type
|
26
|
+
ESME_RINVNUMDESTS = 0x00000033 # Invalid number of destinations
|
27
|
+
ESME_RINVDLNAME = 0x00000034 # Invalid Distribution List name
|
28
|
+
ESME_RINVDESTFLAG = 0x00000040 # Destination flag is invalid
|
29
|
+
ESME_RINVSUBREP = 0x00000042 # Invalid ‘submit with replace’ request
|
30
|
+
ESME_RINVESMCLASS = 0x00000043 # Invalid esm_class field data
|
31
|
+
ESME_RCNTSUBDL = 0x00000044 # Cannot Submit to Distribution List
|
32
|
+
ESME_RSUBMITFAIL = 0x00000045 # submit_sm or submit_multi failed
|
33
|
+
ESME_RINVSRCTON = 0x00000048 # Invalid Source address TON
|
34
|
+
ESME_RINVSRCNPI = 0x00000049 # Invalid Source address NPI
|
35
|
+
ESME_RINVDSTTON = 0x00000050 # Invalid Destination address TON
|
36
|
+
ESME_RINVDSTNPI = 0x00000051 # Invalid Destination address NPI
|
37
|
+
ESME_RINVSYSTYP = 0x00000053 # Invalid system_type field
|
38
|
+
ESME_RINVREPFLAG = 0x00000054 # Invalid replace_if_present flag
|
39
|
+
ESME_RINVNUMMSGS = 0x00000055 # Invalid number of messages
|
40
|
+
ESME_RTHROTTLED = 0x00000058 # Throttling error (ESME has exceeded allowed message limits)
|
41
|
+
|
42
|
+
ESME_RX_T_APPN = 0x00000064 # ESME Receiver Temporary App Error Code
|
43
|
+
|
44
|
+
# PDU types
|
45
|
+
GENERIC_NACK = 0X80000000
|
46
|
+
BIND_RECEIVER = 0X00000001
|
47
|
+
BIND_RECEIVER_RESP = 0X80000001
|
48
|
+
BIND_TRANSMITTER = 0X00000002
|
49
|
+
BIND_TRANSMITTER_RESP = 0X80000002
|
50
|
+
BIND_TRANSCEIVER = 0X00000009
|
51
|
+
BIND_TRANSCEIVER_RESP = 0X80000009
|
52
|
+
QUERY_SM = 0X00000003
|
53
|
+
QUERY_SM_RESP = 0X80000003
|
54
|
+
SUBMIT_SM = 0X00000004
|
55
|
+
SUBMIT_SM_RESP = 0X80000004
|
56
|
+
DELIVER_SM = 0X00000005
|
57
|
+
DELIVER_SM_RESP = 0X80000005
|
58
|
+
UNBIND = 0X00000006
|
59
|
+
UNBIND_RESP = 0X80000006
|
60
|
+
REPLACE_SM = 0X00000007
|
61
|
+
REPLACE_SM_RESP = 0X80000007
|
62
|
+
CANCEL_SM = 0X00000008
|
63
|
+
CANCEL_SM_RESP = 0X80000008
|
64
|
+
ENQUIRE_LINK = 0X00000015
|
65
|
+
ENQUIRE_LINK_RESP = 0X80000015
|
66
|
+
SUBMIT_MULTI = 0X00000021
|
67
|
+
SUBMIT_MULTI_RESP = 0X80000021
|
68
|
+
|
69
|
+
OPTIONAL_RECEIPTED_MESSAGE_ID = 0x001E
|
70
|
+
OPTIONAL_MESSAGE_STATE = 0x0427
|
71
|
+
|
72
|
+
SEQUENCE_MAX = 0x7FFFFFFF
|
73
|
+
|
74
|
+
# PDU sequence number.
|
75
|
+
@@seq = [Time.now.to_i]
|
76
|
+
|
77
|
+
# Add monitor to sequence counter for thread safety
|
78
|
+
@@seq.extend(MonitorMixin)
|
79
|
+
|
80
|
+
#factory class registry
|
81
|
+
@@cmd_map = {}
|
82
|
+
|
83
|
+
attr_reader :command_id, :command_status, :sequence_number, :body, :data
|
84
|
+
|
85
|
+
def initialize(command_id, command_status, seq, body='')
|
86
|
+
length = 16 + body.length
|
87
|
+
@command_id = command_id
|
88
|
+
@command_status = command_status
|
89
|
+
@body = body
|
90
|
+
@sequence_number = seq
|
91
|
+
@data = fixed_int(length) + fixed_int(command_id) + fixed_int(command_status) + fixed_int(seq) + body
|
92
|
+
end
|
93
|
+
|
94
|
+
def logger
|
95
|
+
Smpp::Base.logger
|
96
|
+
end
|
97
|
+
|
98
|
+
def to_human
|
99
|
+
# convert header (4 bytes) to array of 4-byte ints
|
100
|
+
a = @data.to_s.unpack('N4')
|
101
|
+
sprintf("(%22s) len=%3d cmd=%8s status=%1d seq=%03d (%s)", self.class.to_s[11..-1], a[0], a[1].to_s(16), a[2], a[3], @body)
|
102
|
+
end
|
103
|
+
|
104
|
+
# return int as binary string of 4 octets
|
105
|
+
def Base.fixed_int(value)
|
106
|
+
arr = [value >> 24, value >> 16, value >> 8, value & 0xff]
|
107
|
+
arr.pack("cccc")
|
108
|
+
end
|
109
|
+
|
110
|
+
def fixed_int(value)
|
111
|
+
Base.fixed_int(value)
|
112
|
+
end
|
113
|
+
|
114
|
+
#expects a hash like {tag => Smpp::OptionalParameter}
|
115
|
+
def Base.optional_parameters_to_buffer(optionals)
|
116
|
+
output = ""
|
117
|
+
optionals.each do |tag, optional_param|
|
118
|
+
length = optional_param.value.length
|
119
|
+
buffer = []
|
120
|
+
buffer += [tag >> 8, tag & 0xff]
|
121
|
+
buffer += [length >> 8, length & 0xff]
|
122
|
+
output << buffer.pack('cccc') << optional_param.value
|
123
|
+
end
|
124
|
+
output
|
125
|
+
end
|
126
|
+
|
127
|
+
def optional_parameters_to_buffer(optionals)
|
128
|
+
Base.optional_parameters_to_buffer(optionals)
|
129
|
+
end
|
130
|
+
|
131
|
+
def next_sequence_number
|
132
|
+
Base.next_sequence_number
|
133
|
+
end
|
134
|
+
|
135
|
+
def Base.next_sequence_number
|
136
|
+
@@seq.synchronize do
|
137
|
+
(@@seq[0] += 1) % SEQUENCE_MAX
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
#This factory should be implemented in every subclass that can create itself from wire
|
142
|
+
#data. The subclass should also register itself with the 'handles_cmd' class method.
|
143
|
+
def Base.from_wire_data(seq, status, body)
|
144
|
+
raise Exception.new("#{self.class} claimed to handle wire data, but doesn't.")
|
145
|
+
end
|
146
|
+
|
147
|
+
# PDU factory method for common client PDUs (used to create PDUs from wire data)
|
148
|
+
def Base.create(data)
|
149
|
+
header = data[0..15]
|
150
|
+
if !header
|
151
|
+
return nil
|
152
|
+
end
|
153
|
+
len, cmd, status, seq = header.unpack('N4')
|
154
|
+
body = data[16..-1]
|
155
|
+
|
156
|
+
#if a class has been registered to handle this command_id, try
|
157
|
+
#to create an instance from the wire data
|
158
|
+
if @@cmd_map[cmd]
|
159
|
+
@@cmd_map[cmd].from_wire_data(seq, status, body)
|
160
|
+
else
|
161
|
+
Smpp::Base.logger.error "Unknown PDU: #{"0x%08x" % cmd}"
|
162
|
+
return nil
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
#maps a subclass as the handler for a particulular pdu
|
167
|
+
def Base.handles_cmd(command_id)
|
168
|
+
@@cmd_map[command_id] = self
|
169
|
+
end
|
170
|
+
|
171
|
+
def Base.parse_optional_parameters(remaining_bytes)
|
172
|
+
optionals = {}
|
173
|
+
while not remaining_bytes.empty?
|
174
|
+
optional = {}
|
175
|
+
optional_parameter, remaining_bytes = Smpp::OptionalParameter.from_wire_data(remaining_bytes)
|
176
|
+
optionals[optional_parameter.tag] = optional_parameter
|
177
|
+
end
|
178
|
+
|
179
|
+
return optionals
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# this class serves as the base for all the bind* commands.
|
2
|
+
# since the command format remains the same for all bind commands,
|
3
|
+
# sub classes just change the @@command_id
|
4
|
+
class Smpp::Pdu::BindBase < Smpp::Pdu::Base
|
5
|
+
class << self; attr_accessor :command_id ; end
|
6
|
+
|
7
|
+
attr_reader :system_id, :password, :system_type, :addr_ton, :addr_npi, :address_range
|
8
|
+
|
9
|
+
def initialize(system_id, password, system_type, addr_ton, addr_npi, address_range, seq = nil)
|
10
|
+
@system_id, @password, @system_type, @addr_ton, @addr_npi, @address_range =
|
11
|
+
system_id, password, system_type, addr_ton, addr_npi, address_range
|
12
|
+
|
13
|
+
seq ||= next_sequence_number
|
14
|
+
body = sprintf("%s\0%s\0%s\0%c%c%c%s\0", system_id, password,system_type, PROTOCOL_VERSION, addr_ton, addr_npi, address_range)
|
15
|
+
super(self.class.command_id, 0, seq, body)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.from_wire_data(seq, status, body)
|
19
|
+
#unpack the body
|
20
|
+
system_id, password, system_type, interface_version, addr_ton,
|
21
|
+
addr_npi, address_range = body.unpack("Z*Z*Z*CCCZ*")
|
22
|
+
|
23
|
+
self.new(system_id, password, system_type, addr_ton, addr_npi, address_range, seq)
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Smpp::Pdu::BindRespBase < Smpp::Pdu::Base
|
2
|
+
class << self; attr_accessor :command_id ; end
|
3
|
+
attr_accessor :system_id
|
4
|
+
|
5
|
+
def initialize(seq, status, system_id)
|
6
|
+
seq ||= next_sequence_number
|
7
|
+
system_id = system_id.to_s + "\000"
|
8
|
+
super(self.class.command_id, status, seq, system_id) # pass in system_id as body for simple debugging
|
9
|
+
@system_id = system_id
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.from_wire_data(seq, status, body)
|
13
|
+
system_id = body.chomp("\000")
|
14
|
+
new(seq, status, system_id)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
|
2
|
+
# Received for MO message or delivery notification
|
3
|
+
class Smpp::Pdu::DeliverSm < Smpp::Pdu::Base
|
4
|
+
handles_cmd DELIVER_SM
|
5
|
+
|
6
|
+
attr_reader :service_type, :source_addr_ton, :source_addr_npi, :source_addr, :dest_addr_ton, :dest_addr_npi,
|
7
|
+
:destination_addr, :esm_class, :protocol_id, :priority_flag, :schedule_delivery_time,
|
8
|
+
:validity_period, :registered_delivery, :replace_if_present_flag, :data_coding,
|
9
|
+
:sm_default_msg_id, :sm_length, :stat, :msg_reference, :udh, :short_message,
|
10
|
+
:message_state, :receipted_message_id, :optional_parameters
|
11
|
+
|
12
|
+
@@encoder = nil
|
13
|
+
|
14
|
+
def initialize(source_addr, destination_addr, short_message, options={}, seq=nil)
|
15
|
+
|
16
|
+
@udh = options[:udh]
|
17
|
+
@service_type = options[:service_type]? options[:service_type] :''
|
18
|
+
@source_addr_ton = options[:source_addr_ton]?options[:source_addr_ton]:0 # network specific
|
19
|
+
@source_addr_npi = options[:source_addr_npi]?options[:source_addr_npi]:1 # unknown
|
20
|
+
@source_addr = source_addr
|
21
|
+
@dest_addr_ton = options[:dest_addr_ton]?options[:dest_addr_ton]:1 # international
|
22
|
+
@dest_addr_npi = options[:dest_addr_npi]?options[:dest_addr_npi]:1 # unknown
|
23
|
+
@destination_addr = destination_addr
|
24
|
+
@esm_class = options[:esm_class]?options[:esm_class]:0 # default smsc mode
|
25
|
+
@protocol_id = options[:protocol_id]?options[:protocol_id]:0
|
26
|
+
@priority_flag = options[:priority_flag]?options[:priority_flag]:0
|
27
|
+
@schedule_delivery_time = options[:schedule_delivery_time]?options[:schedule_delivery_time]:''
|
28
|
+
@validity_period = options[:validity_period]?options[:validity_period]:''
|
29
|
+
@registered_delivery = options[:registered_delivery]?options[:registered_delivery]:1 # we want delivery notifications
|
30
|
+
@replace_if_present_flag = options[:replace_if_present_flag]?options[:replace_if_present_flag]:0
|
31
|
+
@data_coding = options[:data_coding]?options[:data_coding]:3 # iso-8859-1
|
32
|
+
@sm_default_msg_id = options[:sm_default_msg_id]?options[:sm_default_msg_id]:0
|
33
|
+
@short_message = short_message
|
34
|
+
payload = @udh ? @udh.to_s + @short_message : @short_message
|
35
|
+
@sm_length = payload.length
|
36
|
+
|
37
|
+
#fields set for delivery report
|
38
|
+
@stat = options[:stat]
|
39
|
+
@msg_reference = options[:msg_reference]
|
40
|
+
@receipted_message_id = options[:receipted_message_id]
|
41
|
+
@message_state = options[:message_state]
|
42
|
+
@optional_parameters = options[:optional_parameters]
|
43
|
+
|
44
|
+
pdu_body = sprintf("%s\0%c%c%s\0%c%c%s\0%c%c%c%s\0%s\0%c%c%c%c%c%s", @service_type, @source_addr_ton, @source_addr_npi, @source_addr,
|
45
|
+
@dest_addr_ton, @dest_addr_npi, @destination_addr, @esm_class, @protocol_id, @priority_flag, @schedule_delivery_time, @validity_period,
|
46
|
+
@registered_delivery, @replace_if_present_flag, @data_coding, @sm_default_msg_id, @sm_length, payload)
|
47
|
+
|
48
|
+
seq ||= next_sequence_number
|
49
|
+
|
50
|
+
super(DELIVER_SM, 0, seq, pdu_body)
|
51
|
+
end
|
52
|
+
|
53
|
+
def total_parts
|
54
|
+
@udh ? @udh[4] : 0
|
55
|
+
end
|
56
|
+
|
57
|
+
def part
|
58
|
+
@udh ? @udh[5] : 0
|
59
|
+
end
|
60
|
+
|
61
|
+
def message_id
|
62
|
+
@udh ? @udh[3] : 0
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.from_wire_data(seq, status, body)
|
66
|
+
options = {}
|
67
|
+
# brutally unpack it
|
68
|
+
options[:service_type],
|
69
|
+
options[:source_addr_ton],
|
70
|
+
options[:source_addr_npi],
|
71
|
+
source_addr,
|
72
|
+
options[:dest_addr_ton],
|
73
|
+
options[:dest_addr_npi],
|
74
|
+
destination_addr,
|
75
|
+
options[:esm_class],
|
76
|
+
options[:protocol_id],
|
77
|
+
options[:priority_flag],
|
78
|
+
options[:schedule_delivery_time],
|
79
|
+
options[:validity_period],
|
80
|
+
options[:registered_delivery],
|
81
|
+
options[:replace_if_present_flag],
|
82
|
+
options[:data_coding],
|
83
|
+
options[:sm_default_msg_id],
|
84
|
+
options[:sm_length],
|
85
|
+
remaining_bytes = body.unpack('Z*CCZ*CCZ*CCCZ*Z*CCCCCa*')
|
86
|
+
|
87
|
+
short_message = remaining_bytes.slice!(0...options[:sm_length])
|
88
|
+
|
89
|
+
#everything left in remaining_bytes is 3.4 optional parameters
|
90
|
+
options[:optional_parameters] = parse_optional_parameters(remaining_bytes)
|
91
|
+
|
92
|
+
#parse the 'standard' optional parameters for delivery receipts
|
93
|
+
options[:optional_parameters].each do |tag, tlv|
|
94
|
+
if OPTIONAL_MESSAGE_STATE == tag
|
95
|
+
value = tlv[:value].unpack('C')
|
96
|
+
options[:message_state] = value[0] if value
|
97
|
+
|
98
|
+
elsif OPTIONAL_RECEIPTED_MESSAGE_ID == tag
|
99
|
+
value = tlv[:value].unpack('A*')
|
100
|
+
options[:receipted_message_id] = value[0] if value
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Check to see if body has a 5 bit header
|
105
|
+
if short_message.unpack("c")[0] == 5
|
106
|
+
options[:udh] = short_message.slice!(0..5).unpack("CCCCCC")
|
107
|
+
end
|
108
|
+
|
109
|
+
#Note: if the SM is a delivery receipt (esm_class=4) then the short_message _may_ be in this format:
|
110
|
+
# "id:Smsc2013 sub:1 dlvrd:1 submit date:0610171515 done date:0610171515 stat:0 err:0 text:blah"
|
111
|
+
# or this format:
|
112
|
+
# "4790000000SMSAlert^id:1054BC63 sub:0 dlvrd:1 submit date:0610231217 done date:0610231217 stat:DELIVRD err: text:"
|
113
|
+
# (according to the SMPP spec, the format is vendor specific)
|
114
|
+
# For example, Tele2 (Norway):
|
115
|
+
# "<msisdn><shortcode>?id:10ea34755d3d4f7a20900cdb3349e549 sub:001 dlvrd:001 submit date:0611011228 done date:0611011230 stat:DELIVRD err:000 Text:abc'!10ea34755d3d4f7a20900cdb3349e549"
|
116
|
+
if options[:esm_class] == 4
|
117
|
+
msg_ref_match = short_message.match(/id:([^ ]*)/)
|
118
|
+
if msg_ref_match
|
119
|
+
options[:msg_reference] = msg_ref_match[1]
|
120
|
+
end
|
121
|
+
|
122
|
+
stat_match = short_message.match(/stat:([^ ]*)/)
|
123
|
+
if stat_match
|
124
|
+
options[:stat] = stat_match[1]
|
125
|
+
end
|
126
|
+
|
127
|
+
Smpp::Base.logger.debug "DeliverSM with source_addr=#{source_addr}, destination_addr=#{destination_addr}, msg_reference=#{options[:msg_reference]}, stat=#{options[:stat]}"
|
128
|
+
else
|
129
|
+
Smpp::Base.logger.debug "DeliverSM with source_addr=#{source_addr}, destination_addr=#{destination_addr}"
|
130
|
+
end
|
131
|
+
|
132
|
+
#yield the data_coding and short_message to the encoder if one is set
|
133
|
+
short_message = @@encoder.encode(options[:data_coding], short_message) if @@encoder.respond_to?(:encode)
|
134
|
+
|
135
|
+
new(source_addr, destination_addr, short_message, options, seq)
|
136
|
+
end
|
137
|
+
|
138
|
+
#set an encoder that can be called to yield the data_coding and short_message
|
139
|
+
def self.data_encoder=(encoder)
|
140
|
+
@@encoder = encoder
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Smpp::Pdu::DeliverSmResponse < Smpp::Pdu::Base
|
2
|
+
handles_cmd DELIVER_SM_RESP
|
3
|
+
|
4
|
+
def initialize(seq, status=ESME_ROK)
|
5
|
+
seq ||= next_sequence_number
|
6
|
+
super(DELIVER_SM_RESP, status, seq, "\000") # body must be NULL..!
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.from_wire_data(seq, status, body)
|
10
|
+
new(seq, status)
|
11
|
+
end
|
12
|
+
end
|