ruby-smpp 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CONTRIBUTORS.txt +9 -0
- data/History.txt +5 -0
- data/Manifest.txt +11 -2
- data/NEWS.txt +44 -0
- data/README.txt +2 -2
- data/examples/PDU1.example +26 -0
- data/examples/PDU2.example +26 -0
- data/examples/sample_gateway.rb +113 -99
- data/examples/sample_smsc.rb +103 -0
- data/lib/smpp/base.rb +39 -16
- data/lib/smpp/pdu/base.rb +23 -24
- data/lib/smpp/pdu/bind_base.rb +25 -0
- data/lib/smpp/pdu/bind_resp_base.rb +17 -0
- data/lib/smpp/pdu/bind_transceiver.rb +3 -5
- data/lib/smpp/pdu/bind_transceiver_response.rb +3 -6
- data/lib/smpp/pdu/deliver_sm.rb +72 -29
- data/lib/smpp/pdu/deliver_sm_response.rb +7 -0
- data/lib/smpp/pdu/enquire_link.rb +6 -0
- data/lib/smpp/pdu/enquire_link_response.rb +7 -1
- data/lib/smpp/pdu/generic_nack.rb +14 -2
- data/lib/smpp/pdu/submit_multi.rb +13 -12
- data/lib/smpp/pdu/submit_multi_response.rb +43 -4
- data/lib/smpp/pdu/submit_sm.rb +61 -22
- data/lib/smpp/pdu/submit_sm_response.rb +11 -2
- data/lib/smpp/pdu/unbind.rb +6 -0
- data/lib/smpp/pdu/unbind_response.rb +7 -0
- data/lib/smpp/server.rb +224 -0
- data/lib/smpp/transceiver.rb +87 -30
- data/lib/smpp/version.rb +1 -1
- data/lib/smpp.rb +3 -1
- data/ruby-smpp.gemspec +36 -0
- data/test/smpp_test.rb +218 -12
- metadata +16 -5
data/lib/smpp/pdu/base.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# PDUs are the protcol base units in SMPP
|
2
2
|
module Smpp::Pdu
|
3
3
|
class Base
|
4
|
+
#Protocol Version
|
5
|
+
PROTOCOL_VERSION = 0x34
|
4
6
|
# Error constants
|
5
7
|
ESME_ROK = 0x00000000 # OK!
|
6
8
|
ESME_RINVMSGLEN = 0x00000001 # Message Length is invalid
|
@@ -59,7 +61,7 @@ module Smpp::Pdu
|
|
59
61
|
CANCEL_SM_RESP = 0X80000008
|
60
62
|
ENQUIRE_LINK = 0X00000015
|
61
63
|
ENQUIRE_LINK_RESP = 0X80000015
|
62
|
-
SUBMIT_MULTI
|
64
|
+
SUBMIT_MULTI = 0X00000021
|
63
65
|
SUBMIT_MULTI_RESP = 0X80000021
|
64
66
|
# PDU sequence number.
|
65
67
|
@@seq = [Time.now.to_i]
|
@@ -67,6 +69,9 @@ module Smpp::Pdu
|
|
67
69
|
# Add monitor to sequence counter for thread safety
|
68
70
|
@@seq.extend(MonitorMixin)
|
69
71
|
|
72
|
+
#factory class registry
|
73
|
+
@@cmd_map = {}
|
74
|
+
|
70
75
|
attr_reader :command_id, :command_status, :sequence_number, :body, :data
|
71
76
|
|
72
77
|
def initialize(command_id, command_status, seq, body='')
|
@@ -103,6 +108,12 @@ module Smpp::Pdu
|
|
103
108
|
end
|
104
109
|
end
|
105
110
|
|
111
|
+
#This factory should be implemented in every subclass that can create itself from wire
|
112
|
+
#data. The subclass should also register itself with the 'handles_cmd' class method.
|
113
|
+
def Base.from_wire_data(seq, status, body)
|
114
|
+
raise Exception.new("#{self.class} claimed to handle wire data, but doesn't.")
|
115
|
+
end
|
116
|
+
|
106
117
|
# PDU factory method for common client PDUs (used to create PDUs from wire data)
|
107
118
|
def Base.create(data)
|
108
119
|
header = data[0..15]
|
@@ -111,33 +122,21 @@ module Smpp::Pdu
|
|
111
122
|
end
|
112
123
|
len, cmd, status, seq = header.unpack('N4')
|
113
124
|
body = data[16..-1]
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
when GENERIC_NACK:
|
120
|
-
GenericNack.new(seq, status, body)
|
121
|
-
when UNBIND:
|
122
|
-
Unbind.new(seq)
|
123
|
-
when UNBIND_RESP:
|
124
|
-
UnbindResponse.new(seq, status)
|
125
|
-
when BIND_TRANSMITTER_RESP:
|
126
|
-
BindTransmitterResponse.new(seq, status, body) # could be opt'l params too
|
127
|
-
when BIND_RECEIVER_RESP:
|
128
|
-
BindReceiverResponse.new(seq, status, body)
|
129
|
-
when BIND_TRANSCEIVER_RESP:
|
130
|
-
BindTransceiverResponse.new(seq, status, body)
|
131
|
-
when SUBMIT_SM_RESP:
|
132
|
-
SubmitSmResponse.new(seq, status, body)
|
133
|
-
when SUBMIT_MULTI_RESP:
|
134
|
-
SubmitMultiResponse.new(seq, status, body)
|
135
|
-
when DELIVER_SM:
|
136
|
-
DeliverSm.new(seq, status, body)
|
125
|
+
|
126
|
+
#if a class has been registered to handle this command_id, try
|
127
|
+
#to create an instance from the wire data
|
128
|
+
if @@cmd_map[cmd]
|
129
|
+
@@cmd_map[cmd].from_wire_data(seq, status, body)
|
137
130
|
else
|
138
131
|
Smpp::Base.logger.error "Unknown PDU: 0x#{cmd.to_s(16)}"
|
139
132
|
return nil
|
140
133
|
end
|
141
134
|
end
|
135
|
+
|
136
|
+
#maps a subclass as the handler for a particulular pdu
|
137
|
+
def Base.handles_cmd(command_id)
|
138
|
+
@@cmd_map[command_id] = self
|
139
|
+
end
|
140
|
+
|
142
141
|
end
|
143
142
|
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
|
@@ -1,6 +1,4 @@
|
|
1
|
-
class Smpp::Pdu::BindTransceiver < Smpp::Pdu::
|
2
|
-
|
3
|
-
|
4
|
-
super(BIND_TRANSCEIVER, 0, next_sequence_number, body)
|
5
|
-
end
|
1
|
+
class Smpp::Pdu::BindTransceiver < Smpp::Pdu::BindBase
|
2
|
+
@command_id = BIND_TRANSCEIVER
|
3
|
+
handles_cmd BIND_TRANSCEIVER
|
6
4
|
end
|
@@ -1,7 +1,4 @@
|
|
1
|
-
class Smpp::Pdu::BindTransceiverResponse < Smpp::Pdu::
|
2
|
-
|
3
|
-
|
4
|
-
super(BIND_TRANSCEIVER_RESP, status, seq, system_id) # pass in system_id as body for simple debugging
|
5
|
-
@system_id = system_id
|
6
|
-
end
|
1
|
+
class Smpp::Pdu::BindTransceiverResponse < Smpp::Pdu::BindRespBase
|
2
|
+
@command_id = BIND_TRANSCEIVER_RESP
|
3
|
+
handles_cmd BIND_TRANSCEIVER_RESP
|
7
4
|
end
|
data/lib/smpp/pdu/deliver_sm.rb
CHANGED
@@ -1,40 +1,83 @@
|
|
1
1
|
# Received for MO message or delivery notification
|
2
|
-
class Smpp::Pdu::DeliverSm < Smpp::Pdu::Base
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
dest_addr_ton,
|
11
|
-
dest_addr_npi,
|
12
|
-
@destination_addr,
|
13
|
-
@esm_class,
|
14
|
-
protocol_id,
|
15
|
-
priority_flag,
|
16
|
-
schedule_delivery_time,
|
17
|
-
validity_period,
|
18
|
-
registered_delivery,
|
19
|
-
replace_if_present_flag,
|
20
|
-
data_coding,
|
21
|
-
sm_default_msg_id,
|
22
|
-
sm_length,
|
23
|
-
@short_message = body.unpack('Z*CCZ*CCZ*CCCZ*Z*CCCCCa*')
|
24
|
-
logger.debug "DeliverSM with source_addr=#{@source_addr}, destination_addr=#{@destination_addr}"
|
2
|
+
class Smpp::Pdu::DeliverSm < Smpp::Pdu::Base
|
3
|
+
handles_cmd DELIVER_SM
|
4
|
+
attr_reader :service_type, :source_addr_ton, :source_addr_npi, :source_addr, :dest_addr_ton, :dest_addr_npi,
|
5
|
+
:destination_addr, :esm_class, :protocol_id, :priority_flag, :schedule_delivery_time,
|
6
|
+
:validity_period, :registered_delivery, :replace_if_present_flag, :data_coding,
|
7
|
+
:sm_default_msg_id, :sm_length, :stat, :msg_reference, :udh, :short_message
|
8
|
+
|
9
|
+
def initialize(source_addr, destination_addr, short_message, options={}, seq=nil)
|
25
10
|
|
26
|
-
|
11
|
+
@udh = options[:udh]
|
12
|
+
@service_type = options[:service_type]? options[:service_type] :''
|
13
|
+
@source_addr_ton = options[:source_addr_ton]?options[:source_addr_ton]:0 # network specific
|
14
|
+
@source_addr_npi = options[:source_addr_npi]?options[:source_addr_npi]:1 # unknown
|
15
|
+
@source_addr = source_addr
|
16
|
+
@dest_addr_ton = options[:dest_addr_ton]?options[:dest_addr_ton]:1 # international
|
17
|
+
@dest_addr_npi = options[:dest_addr_npi]?options[:dest_addr_npi]:1 # unknown
|
18
|
+
@destination_addr = destination_addr
|
19
|
+
@esm_class = options[:esm_class]?options[:esm_class]:0 # default smsc mode
|
20
|
+
@protocol_id = options[:protocol_id]?options[:protocol_id]:0
|
21
|
+
@priority_flag = options[:priority_flag]?options[:priority_flag]:1
|
22
|
+
@schedule_delivery_time = options[:schedule_delivery_time]?options[:schedule_delivery_time]:''
|
23
|
+
@validity_period = options[:validity_period]?options[:validity_period]:''
|
24
|
+
@registered_delivery = options[:registered_delivery]?options[:registered_delivery]:1 # we want delivery notifications
|
25
|
+
@replace_if_present_flag = options[:replace_if_present_flag]?options[:replace_if_present_flag]:0
|
26
|
+
@data_coding = options[:data_coding]?options[:data_coding]:3 # iso-8859-1
|
27
|
+
@sm_default_msg_id = options[:sm_default_msg_id]?options[:sm_default_msg_id]:0
|
28
|
+
@short_message = short_message
|
29
|
+
payload = @udh ? @udh + @short_message : @short_message
|
30
|
+
@sm_length = payload.length
|
31
|
+
|
32
|
+
#fields set for delivery report
|
33
|
+
@stat = options[:stat]
|
34
|
+
@msg_reference = options[:msg_reference]
|
35
|
+
|
36
|
+
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,
|
37
|
+
@dest_addr_ton, @dest_addr_npi, @destination_addr, @esm_class, @protocol_id, @priority_flag, @schedule_delivery_time, @validity_period,
|
38
|
+
@registered_delivery, @replace_if_present_flag, @data_coding, @sm_default_msg_id, @sm_length, payload)
|
39
|
+
|
40
|
+
seq ||= next_sequence_number
|
41
|
+
|
42
|
+
super(DELIVER_SM, 0, seq, pdu_body)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.from_wire_data(seq, status, body)
|
46
|
+
options = {}
|
47
|
+
# brutally unpack it
|
48
|
+
options[:service_type],
|
49
|
+
options[:source_addr_ton],
|
50
|
+
options[:source_addr_npi],
|
51
|
+
source_addr,
|
52
|
+
options[:dest_addr_ton],
|
53
|
+
options[:dest_addr_npi],
|
54
|
+
destination_addr,
|
55
|
+
options[:esm_class],
|
56
|
+
options[:protocol_id],
|
57
|
+
options[:priority_flag],
|
58
|
+
options[:schedule_delivery_time],
|
59
|
+
options[:validity_period],
|
60
|
+
options[:registered_delivery],
|
61
|
+
options[:replace_if_present_flag],
|
62
|
+
options[:data_coding],
|
63
|
+
options[:sm_default_msg_id],
|
64
|
+
options[:sm_length],
|
65
|
+
short_message = body.unpack('Z*CCZ*CCZ*CCCZ*Z*CCCCCa*')
|
66
|
+
Smpp::Base.logger.debug "DeliverSM with source_addr=#{source_addr}, destination_addr=#{destination_addr}"
|
67
|
+
|
68
|
+
#Note: if the SM is a delivery receipt (esm_class=4) then the short_message _may_ be in this format:
|
27
69
|
# "id:Smsc2013 sub:1 dlvrd:1 submit date:0610171515 done date:0610171515 stat:0 err:0 text:blah"
|
28
70
|
# or this format:
|
29
71
|
# "4790000000SMSAlert^id:1054BC63 sub:0 dlvrd:1 submit date:0610231217 done date:0610231217 stat:DELIVRD err: text:"
|
30
72
|
# (according to the SMPP spec, the format is vendor specific)
|
31
73
|
# For example, Tele2 (Norway):
|
32
74
|
# "<msisdn><shortcode>?id:10ea34755d3d4f7a20900cdb3349e549 sub:001 dlvrd:001 submit date:0611011228 done date:0611011230 stat:DELIVRD err:000 Text:abc'!10ea34755d3d4f7a20900cdb3349e549"
|
33
|
-
if
|
34
|
-
|
75
|
+
if options[:esm_class] == 4
|
76
|
+
options[:msg_reference] = short_message.scanf('id:%s').to_s
|
35
77
|
# @stat must be parsed according to the SMSC vendor's specifications (see comment above)
|
36
|
-
|
78
|
+
options[:stat] = 0
|
37
79
|
end
|
38
|
-
|
80
|
+
|
81
|
+
new(source_addr, destination_addr, short_message, options, seq)
|
39
82
|
end
|
40
|
-
end
|
83
|
+
end
|
@@ -1,5 +1,12 @@
|
|
1
1
|
class Smpp::Pdu::DeliverSmResponse < Smpp::Pdu::Base
|
2
|
+
handles_cmd DELIVER_SM_RESP
|
3
|
+
|
2
4
|
def initialize(seq, status=ESME_ROK)
|
5
|
+
seq ||= next_sequence_number
|
3
6
|
super(DELIVER_SM_RESP, status, seq, "\000") # body must be NULL..!
|
4
7
|
end
|
8
|
+
|
9
|
+
def self.from_wire_data(seq, status, body)
|
10
|
+
new(seq, status)
|
11
|
+
end
|
5
12
|
end
|
@@ -1,5 +1,11 @@
|
|
1
1
|
class Smpp::Pdu::EnquireLinkResponse < Smpp::Pdu::Base
|
2
|
-
|
2
|
+
handles_cmd ENQUIRE_LINK_RESP
|
3
|
+
|
4
|
+
def initialize(seq = next_sequence_number)
|
3
5
|
super(ENQUIRE_LINK_RESP, ESME_ROK, seq)
|
4
6
|
end
|
7
|
+
|
8
|
+
def self.from_wire_data(seq, status, body)
|
9
|
+
new(seq)
|
10
|
+
end
|
5
11
|
end
|
@@ -1,8 +1,20 @@
|
|
1
1
|
# signals invalid message header
|
2
2
|
class Smpp::Pdu::GenericNack < Smpp::Pdu::Base
|
3
|
+
handles_cmd GENERIC_NACK
|
4
|
+
|
3
5
|
attr_accessor :error_code
|
4
|
-
|
5
|
-
|
6
|
+
|
7
|
+
def initialize(seq, error_code, original_sequence_code = nil)
|
8
|
+
#TODO: original_sequence_code used to be passed to this function
|
9
|
+
#however, a GENERIC_NACK has only one sequence number and no body
|
10
|
+
#so this is a useless variable. I leave it here only to preserve
|
11
|
+
#the API, but it has no practical use.
|
12
|
+
seq ||= next_sequence_number
|
13
|
+
super(GENERIC_NACK, error_code, seq)
|
6
14
|
@error_code = error_code
|
7
15
|
end
|
16
|
+
|
17
|
+
def self.from_wire_data(seq, status, body)
|
18
|
+
new(seq,status,body)
|
19
|
+
end
|
8
20
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# Sending an MT message to multiple addresses
|
2
2
|
# Author: Abhishek Parolkar, (abhishek[at]parolkar.com)
|
3
|
+
#TODO: Implement from_wire_data for this pdu class.
|
3
4
|
class Smpp::Pdu::SubmitMulti < Smpp::Pdu::Base
|
4
5
|
IS_SMEADDR = 1 # type of dest_flag
|
5
6
|
IS_DISTLISTNAME = 2 #type of dest_flag
|
@@ -45,23 +46,23 @@ class Smpp::Pdu::SubmitMulti < Smpp::Pdu::Base
|
|
45
46
|
a = @data.to_s.unpack('N4')
|
46
47
|
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], @msg_body[0..30])
|
47
48
|
end
|
49
|
+
|
48
50
|
def build_destination_addresses(dest_array,dest_addr_ton,dest_addr_npi, dest_flag = IS_SMEADDR)
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
formatted_array.push(packet_str)
|
51
|
+
formatted_array = Array.new
|
52
|
+
dest_array.each { |dest_elem|
|
53
|
+
if dest_flag == IS_SMEADDR
|
54
|
+
packet_str = sprintf("%c%c%c%s",IS_SMEADDR,dest_addr_ton,dest_addr_npi,dest_elem)
|
55
|
+
formatted_array.push(packet_str)
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
57
|
+
elsif dest_flag == IS_DISTLISTNAME
|
58
|
+
packet_str = sprintf("%c%s",IS_SMEADDR,dest_elem)
|
59
|
+
formatted_array.push(packet_str)
|
59
60
|
|
60
|
-
|
61
|
+
end
|
61
62
|
|
62
|
-
|
63
|
+
}
|
63
64
|
|
64
|
-
|
65
|
+
formatted_array.join('\0');
|
65
66
|
end
|
66
67
|
|
67
68
|
end
|
@@ -1,10 +1,49 @@
|
|
1
1
|
# Recieving response for an MT message sent to multiple addresses
|
2
2
|
# Author: Abhishek Parolkar, (abhishek[at]parolkar.com)
|
3
3
|
class Smpp::Pdu::SubmitMultiResponse < Smpp::Pdu::Base
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
class UnsuccessfulSme
|
5
|
+
Struct.new(:dest_addr_ton, :dest_addr_npi, :destination_addr, :error_status_code)
|
6
|
+
end
|
7
|
+
|
8
|
+
handles_cmd SUBMIT_MULTI_RESP
|
9
|
+
|
10
|
+
attr_accessor :message_id, :unsuccess_smes
|
11
|
+
|
12
|
+
def initialize(seq, status, message_id, unsuccess_smes = [])
|
13
|
+
@unsuccess_smes = unsuccess_smes
|
14
|
+
seq ||= next_sequence_number
|
15
|
+
|
16
|
+
packed_smes = ""
|
17
|
+
unsuccess_smes.each do |sme|
|
18
|
+
packed_smes << [
|
19
|
+
sme.dest_addr_ton,
|
20
|
+
sme.dest_addr_npi,
|
21
|
+
sme.destination_addr,
|
22
|
+
sme.error_status_code
|
23
|
+
].pack("CCZ*N")
|
24
|
+
end
|
25
|
+
body = [message_id, unsuccess_smes.size, packed_smes].pack("Z*Ca*")
|
26
|
+
|
27
|
+
super(SUBMIT_MULTI_RESP, status, seq, body)
|
8
28
|
@message_id = message_id
|
9
29
|
end
|
30
|
+
|
31
|
+
def self.from_wire_data(seq, status, body)
|
32
|
+
message_id, no_unsuccess, rest = body.unpack("Z*Ca*")
|
33
|
+
unsuccess_smes = []
|
34
|
+
|
35
|
+
no_unsuccess.times do |i|
|
36
|
+
#unpack the next sme
|
37
|
+
dest_addr_ton, dest_addr_npi, destination_addr, error_status_code =
|
38
|
+
rest.unpack("CCZ*N")
|
39
|
+
#remove the SME from rest
|
40
|
+
rest.slice!(0,7 + destination_addr.length)
|
41
|
+
unsuccess_smes << UnsuccessfulSme.new(dest_addr_ton, dest_addr_npi, destination_addr, error_status_code)
|
42
|
+
end
|
43
|
+
|
44
|
+
new(seq, status, message_id, unsuccess_smes)
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
10
49
|
end
|
data/lib/smpp/pdu/submit_sm.rb
CHANGED
@@ -1,34 +1,46 @@
|
|
1
1
|
# Sending an MT message
|
2
2
|
class Smpp::Pdu::SubmitSm < Smpp::Pdu::Base
|
3
|
+
handles_cmd SUBMIT_SM
|
4
|
+
attr_reader :service_type, :source_addr_ton, :source_addr_npi, :source_addr, :dest_addr_ton, :dest_addr_npi,
|
5
|
+
:destination_addr, :esm_class, :protocol_id, :priority_flag, :schedule_delivery_time,
|
6
|
+
:validity_period, :registered_delivery, :replace_if_present_flag, :data_coding,
|
7
|
+
:sm_default_msg_id, :sm_length, :udh, :short_message
|
8
|
+
|
3
9
|
|
4
10
|
# Note: short_message (the SMS body) must be in iso-8859-1 format
|
5
|
-
def initialize(source_addr, destination_addr, short_message, options={})
|
11
|
+
def initialize(source_addr, destination_addr, short_message, options={}, seq = nil)
|
6
12
|
|
7
13
|
@msg_body = short_message
|
8
14
|
|
9
|
-
udh = options[:udh]
|
10
|
-
service_type = options[:service_type]? options[:service_type] :''
|
11
|
-
source_addr_ton = options[:source_addr_ton]?options[:source_addr_ton]:0 # network specific
|
12
|
-
source_addr_npi = options[:source_addr_npi]?options[:source_addr_npi]:1 # unknown
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
15
|
+
@udh = options[:udh]
|
16
|
+
@service_type = options[:service_type]? options[:service_type] :''
|
17
|
+
@source_addr_ton = options[:source_addr_ton]?options[:source_addr_ton]:0 # network specific
|
18
|
+
@source_addr_npi = options[:source_addr_npi]?options[:source_addr_npi]:1 # unknown
|
19
|
+
@source_addr = source_addr
|
20
|
+
@dest_addr_ton = options[:dest_addr_ton]?options[:dest_addr_ton]:1 # international
|
21
|
+
@dest_addr_npi = options[:dest_addr_npi]?options[:dest_addr_npi]:1 # unknown
|
22
|
+
@destination_addr = destination_addr
|
23
|
+
@esm_class = options[:esm_class]?options[:esm_class]:0 # default smsc mode
|
24
|
+
@protocol_id = options[:protocol_id]?options[:protocol_id]:0
|
25
|
+
@priority_flag = options[:priority_flag]?options[:priority_flag]:1
|
26
|
+
@schedule_delivery_time = options[:schedule_delivery_time]?options[:schedule_delivery_time]:''
|
27
|
+
@validity_period = options[:validity_period]?options[:validity_period]:''
|
28
|
+
@registered_delivery = options[:registered_delivery]?options[:registered_delivery]:1 # we want delivery notifications
|
29
|
+
@replace_if_present_flag = options[:replace_if_present_flag]?options[:replace_if_present_flag]:0
|
30
|
+
@data_coding = options[:data_coding]?options[:data_coding]:3 # iso-8859-1
|
31
|
+
@sm_default_msg_id = options[:sm_default_msg_id]?options[:sm_default_msg_id]:0
|
32
|
+
@short_message = short_message
|
33
|
+
payload = @udh ? @udh + @short_message : @short_message
|
34
|
+
@sm_length = payload.length
|
26
35
|
|
27
36
|
# craft the string/byte buffer
|
28
|
-
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,
|
29
|
-
dest_addr_ton, dest_addr_npi, destination_addr, esm_class, protocol_id, priority_flag, schedule_delivery_time, validity_period,
|
30
|
-
registered_delivery, replace_if_present_flag, data_coding, sm_default_msg_id, sm_length, payload)
|
31
|
-
|
37
|
+
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,
|
38
|
+
@dest_addr_ton, @dest_addr_npi, @destination_addr, @esm_class, @protocol_id, @priority_flag, @schedule_delivery_time, @validity_period,
|
39
|
+
@registered_delivery, @replace_if_present_flag, @data_coding, @sm_default_msg_id, @sm_length, payload)
|
40
|
+
|
41
|
+
seq ||= next_sequence_number
|
42
|
+
|
43
|
+
super(SUBMIT_SM, 0, seq, pdu_body)
|
32
44
|
end
|
33
45
|
|
34
46
|
# some special formatting is needed for SubmitSm PDUs to show the actual message content
|
@@ -37,4 +49,31 @@ class Smpp::Pdu::SubmitSm < Smpp::Pdu::Base
|
|
37
49
|
a = @data.to_s.unpack('N4')
|
38
50
|
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], @msg_body[0..30])
|
39
51
|
end
|
52
|
+
|
53
|
+
def self.from_wire_data(seq, status, body)
|
54
|
+
options = {}
|
55
|
+
|
56
|
+
options[:service_type],
|
57
|
+
options[:source_addr_ton],
|
58
|
+
options[:source_addr_npi],
|
59
|
+
source_addr,
|
60
|
+
options[:dest_addr_ton],
|
61
|
+
options[:dest_addr_npi],
|
62
|
+
destination_addr,
|
63
|
+
options[:esm_class],
|
64
|
+
options[:protocol_id],
|
65
|
+
options[:priority_flag],
|
66
|
+
options[:schedule_delivery_time],
|
67
|
+
options[:validity_period],
|
68
|
+
options[:registered_delivery],
|
69
|
+
options[:replace_if_present_flag],
|
70
|
+
options[:data_coding],
|
71
|
+
options[:sm_default_msg_id],
|
72
|
+
options[:sm_length],
|
73
|
+
short_message = body.unpack('Z*CCZ*CCZ*CCCZ*Z*CCCCCa*')
|
74
|
+
Smpp::Base.logger.debug "DeliverSM with source_addr=#{source_addr}, destination_addr=#{destination_addr}"
|
75
|
+
|
76
|
+
new(source_addr, destination_addr, short_message, options, seq)
|
77
|
+
end
|
78
|
+
|
40
79
|
end
|
@@ -1,8 +1,17 @@
|
|
1
1
|
class Smpp::Pdu::SubmitSmResponse < Smpp::Pdu::Base
|
2
|
+
handles_cmd SUBMIT_SM_RESP
|
3
|
+
|
2
4
|
attr_accessor :message_id
|
5
|
+
|
3
6
|
def initialize(seq, status, message_id)
|
4
|
-
|
5
|
-
|
7
|
+
seq ||= next_sequence_number
|
8
|
+
body = message_id.to_s + "\000"
|
9
|
+
super(SUBMIT_SM_RESP, status, seq, body)
|
6
10
|
@message_id = message_id
|
7
11
|
end
|
12
|
+
|
13
|
+
def self.from_wire_data(seq, status, body)
|
14
|
+
message_id = body.chomp("\000")
|
15
|
+
new(seq, status, message_id)
|
16
|
+
end
|
8
17
|
end
|
data/lib/smpp/pdu/unbind.rb
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
class Smpp::Pdu::UnbindResponse < Smpp::Pdu::Base
|
2
|
+
handles_cmd UNBIND_RESP
|
3
|
+
|
2
4
|
def initialize(seq, status)
|
5
|
+
seq ||= next_sequence_number
|
3
6
|
super(UNBIND_RESP, status, seq)
|
4
7
|
end
|
8
|
+
|
9
|
+
def self.from_wire_data(seq, status, body)
|
10
|
+
new(seq, status)
|
11
|
+
end
|
5
12
|
end
|