anjlab-ruby-smpp 0.6.0

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.
Files changed (51) hide show
  1. data/CHANGELOG +52 -0
  2. data/CONTRIBUTORS.txt +11 -0
  3. data/Gemfile +8 -0
  4. data/Gemfile.lock +18 -0
  5. data/LICENSE +20 -0
  6. data/README.rdoc +89 -0
  7. data/Rakefile +53 -0
  8. data/VERSION +1 -0
  9. data/config/environment.rb +2 -0
  10. data/examples/PDU1.example +26 -0
  11. data/examples/PDU2.example +26 -0
  12. data/examples/sample_gateway.rb +137 -0
  13. data/examples/sample_smsc.rb +102 -0
  14. data/lib/smpp.rb +25 -0
  15. data/lib/smpp/base.rb +308 -0
  16. data/lib/smpp/encoding/utf8_encoder.rb +37 -0
  17. data/lib/smpp/optional_parameter.rb +35 -0
  18. data/lib/smpp/pdu/base.rb +183 -0
  19. data/lib/smpp/pdu/bind_base.rb +25 -0
  20. data/lib/smpp/pdu/bind_receiver.rb +4 -0
  21. data/lib/smpp/pdu/bind_receiver_response.rb +4 -0
  22. data/lib/smpp/pdu/bind_resp_base.rb +17 -0
  23. data/lib/smpp/pdu/bind_transceiver.rb +4 -0
  24. data/lib/smpp/pdu/bind_transceiver_response.rb +4 -0
  25. data/lib/smpp/pdu/deliver_sm.rb +142 -0
  26. data/lib/smpp/pdu/deliver_sm_response.rb +12 -0
  27. data/lib/smpp/pdu/enquire_link.rb +11 -0
  28. data/lib/smpp/pdu/enquire_link_response.rb +11 -0
  29. data/lib/smpp/pdu/generic_nack.rb +20 -0
  30. data/lib/smpp/pdu/submit_multi.rb +68 -0
  31. data/lib/smpp/pdu/submit_multi_response.rb +49 -0
  32. data/lib/smpp/pdu/submit_sm.rb +91 -0
  33. data/lib/smpp/pdu/submit_sm_response.rb +31 -0
  34. data/lib/smpp/pdu/unbind.rb +11 -0
  35. data/lib/smpp/pdu/unbind_response.rb +12 -0
  36. data/lib/smpp/receiver.rb +27 -0
  37. data/lib/smpp/server.rb +223 -0
  38. data/lib/smpp/transceiver.rb +109 -0
  39. data/lib/sms.rb +9 -0
  40. data/ruby-smpp.gemspec +96 -0
  41. data/test/delegate.rb +28 -0
  42. data/test/encoding_test.rb +231 -0
  43. data/test/optional_parameter_test.rb +30 -0
  44. data/test/pdu_parsing_test.rb +111 -0
  45. data/test/receiver_test.rb +232 -0
  46. data/test/responsive_delegate.rb +53 -0
  47. data/test/server.rb +56 -0
  48. data/test/smpp_test.rb +239 -0
  49. data/test/submit_sm_test.rb +40 -0
  50. data/test/transceiver_test.rb +35 -0
  51. metadata +133 -0
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'smpp'
4
+
5
+ class OptionalParameterTest < Test::Unit::TestCase
6
+ include Smpp
7
+
8
+ def test_symbol_accessor
9
+ op = OptionalParameter.new(0x2150, "abcd")
10
+ assert_equal "abcd", op[:value]
11
+ assert_equal 0x2150, op[:tag]
12
+ end
13
+
14
+ def test_bad_data_does_not_puke
15
+ assert_raise RuntimeError do
16
+ OptionalParameter.from_wire_data("")
17
+ end
18
+ end
19
+
20
+ def test_from_wire_data
21
+ data = "\041\120\000\002\001\177"
22
+ op, remaining_data = OptionalParameter.from_wire_data(data)
23
+ assert_not_nil op, "op should not be nil"
24
+ assert_equal 0x2150, op.tag
25
+ assert_equal "\001\177", op.value
26
+ assert_equal "", remaining_data
27
+ end
28
+
29
+ end
30
+
@@ -0,0 +1,111 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require File.expand_path(File.dirname(__FILE__) + "../../lib/smpp")
4
+
5
+ class PduParsingTest < Test::Unit::TestCase
6
+
7
+ def test_recieve_single_message
8
+ raw_data = <<-EOF
9
+ 0000 003d 0000 0005 0000 0000 0000 0002
10
+ 0001 0134 3437 3830 3330 3239 3833 3700
11
+ 0101 3434 3738 3033 3032 3938 3337 0000
12
+ 0000 0000 0000 0000 0454 6573 74
13
+ EOF
14
+
15
+ pdu = create_pdu(raw_data)
16
+ assert_equal Smpp::Pdu::DeliverSm, pdu.class
17
+ assert_equal "447803029837", pdu.source_addr
18
+ assert_equal "447803029837", pdu.destination_addr
19
+ assert_nil pdu.udh
20
+ assert_equal "Test", pdu.short_message
21
+ end
22
+
23
+ def test_recieve_part_one_of_multi_part_message
24
+ part_one_message = <<-EOF
25
+ 0000 00d8 0000 0005 0000 0000 0000 0001
26
+ 0001 0134 3437 3937 3334 3238 3634 3400
27
+ 0101 3434 3739 3736 3232 3430 3137 0000
28
+ 0000 0000 0000 0000 9f05 0003 b402 0154
29
+ 6869 7320 6973 2061 206c 6f6e 6720 6d65
30
+ 7373 6167 6520 746f 2074 6573 7420 7768
31
+ 6574 6865 7220 6f72 206e 6f74 2077 6520
32
+ 6765 7420 7468 6520 6865 6164 6572 2069
33
+ 6e66 6f20 7669 6120 7468 6520 534d 5343
34
+ 2074 6861 7420 7765 2077 6f75 6c64 2072
35
+ 6571 7569 7265 2074 6f20 6265 2061 626c
36
+ 6520 746f 2072 6563 6f6d 706f 7365 206c
37
+ 6f6e 6720 6d65 7373 6167 6573 2069 6e20
38
+ 6861 7368 626c 7565
39
+ EOF
40
+
41
+ pdu = create_pdu(part_one_message)
42
+ assert_equal Smpp::Pdu::DeliverSm, pdu.class
43
+ assert_equal "447973428644", pdu.source_addr
44
+ assert_equal "447976224017", pdu.destination_addr
45
+ assert_equal [5, 0, 3, 180, 2, 1], pdu.udh
46
+
47
+ assert_equal 2, pdu.total_parts, "Have total parts of the message"
48
+ assert_equal 1, pdu.part, "Correctly show the part"
49
+ assert_equal 180, pdu.message_id
50
+
51
+ assert_equal "This is a long message to test whether or not we get the header info via the SMSC that we would require to be able to recompose long messages in hashblue", pdu.short_message
52
+ end
53
+
54
+ def test_recieve_part_two_of_multi_part_message
55
+ part_one_message = <<-EOF
56
+ 0000 0062 0000 0005 0000 0000 0000 0002
57
+ 0001 0134 3437 3937 3334 3238 3634 3400
58
+ 0101 3434 3739 3736 3232 3430 3137 0000
59
+ 0000 0000 0000 0000 2905 0003 b402 0220
60
+ 616e 6420 7072 6f76 6964 6520 6120 676f
61
+ 6f64 2075 7365 7220 6578 7065 7269 656e
62
+ 6365
63
+ EOF
64
+
65
+ pdu = create_pdu(part_one_message)
66
+ assert_equal Smpp::Pdu::DeliverSm, pdu.class
67
+ assert_equal "447973428644", pdu.source_addr
68
+ assert_equal "447976224017", pdu.destination_addr
69
+ assert_equal [5, 0, 3, 180, 2, 2], pdu.udh
70
+
71
+ assert_equal 2, pdu.total_parts, "Have total parts of the message"
72
+ assert_equal 2, pdu.part, "Correctly show the part"
73
+ assert_equal 180, pdu.message_id
74
+
75
+ assert_equal " and provide a good user experience", pdu.short_message
76
+ end
77
+
78
+ def test_submit_sm_response_clean
79
+ data = <<-EOF
80
+ 0000 0028 8000 0004 0000 0000 4da8 ebed
81
+ 3534 3131 342d 3034 3135 562d 3231 3230
82
+ 452d 3039 4831 5100
83
+ EOF
84
+
85
+ pdu = create_pdu(data)
86
+ assert_equal Smpp::Pdu::SubmitSmResponse, pdu.class
87
+ assert_equal "54114-0415V-2120E-09H1Q", pdu.message_id
88
+ end
89
+
90
+ def test_submit_sm_response_with_optional_params
91
+ data = <<-EOF
92
+ 0000 0031 8000 0004 0000 042e 4da8 e9a1
93
+ 0021 5300 0201 7721 6700 1653 6f75 7263
94
+ 6520 6164 6472 6573 7320 6465 6e69 6564
95
+ 2e
96
+ EOF
97
+
98
+ pdu = create_pdu(data)
99
+ assert_equal Smpp::Pdu::SubmitSmResponse, pdu.class
100
+ assert_equal "", pdu.message_id
101
+ assert pdu.optional_parameters
102
+ assert_equal "Source address denied.", pdu.optional_parameter(0x2167)
103
+ end
104
+
105
+ protected
106
+ def create_pdu(raw_data)
107
+ hex_data = [raw_data.chomp.gsub(/\s/,"")].pack("H*")
108
+ Smpp::Pdu::Base.create(hex_data)
109
+ end
110
+
111
+ end
@@ -0,0 +1,232 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'smpp'
4
+
5
+ class ReceiverTest < Test::Unit::TestCase
6
+
7
+ class RecordingDelegate
8
+ attr_reader :received_pdus, :received_delivery_report_pdus, :states
9
+ def initialize
10
+ @received_pdus, @received_delivery_report_pdus, @states = [], [], []
11
+ end
12
+ def mo_received(receiver, pdu)
13
+ @received_pdus << pdu
14
+ end
15
+ def delivery_report_received(receiver, pdu)
16
+ @received_delivery_report_pdus << pdu
17
+ end
18
+ def bound(receiver)
19
+ @states << :bound
20
+ end
21
+ end
22
+
23
+ class ExceptionRaisingDelegate < RecordingDelegate
24
+ def mo_received(receiver, pdu)
25
+ raise "exception in delegate"
26
+ end
27
+ end
28
+
29
+ def test_receiving_bind_receiver_response_with_ok_status_should_become_bound
30
+ receiver = build_receiver
31
+ bind_receiver_response = Smpp::Pdu::BindReceiverResponse.new(nil, Smpp::Pdu::Base::ESME_ROK, 1)
32
+
33
+ receiver.process_pdu(bind_receiver_response)
34
+
35
+ assert receiver.bound?
36
+ end
37
+
38
+ def test_receiving_bind_receiver_response_with_ok_status_should_invoke_bound_on_delegate
39
+ delegate = RecordingDelegate.new
40
+ receiver = build_receiver(delegate)
41
+ bind_receiver_response = Smpp::Pdu::BindReceiverResponse.new(nil, Smpp::Pdu::Base::ESME_ROK, 1)
42
+
43
+ receiver.process_pdu(bind_receiver_response)
44
+
45
+ assert_equal [:bound], delegate.states
46
+ end
47
+
48
+ def test_receiving_bind_receiver_response_with_ok_status_should_not_error_if_method_doesnt_exist_on_delegate
49
+ delegate = Object.new
50
+ receiver = build_receiver(delegate)
51
+ bind_receiver_response = Smpp::Pdu::BindReceiverResponse.new(nil, Smpp::Pdu::Base::ESME_ROK, 1)
52
+
53
+ assert_nothing_raised { receiver.process_pdu(bind_receiver_response) }
54
+ end
55
+
56
+ def test_receiving_bind_receiver_response_with_error_status_should_not_become_bound
57
+ receiver = build_receiver
58
+ bind_receiver_response = Smpp::Pdu::BindReceiverResponse.new(nil, Smpp::Pdu::Base::ESME_RBINDFAIL, 1)
59
+
60
+ receiver.process_pdu(bind_receiver_response)
61
+
62
+ assert receiver.unbound?
63
+ end
64
+
65
+ def test_receiving_bind_receiver_response_with_error_status_should_not_invoke_bound_on_delegate
66
+ delegate = RecordingDelegate.new
67
+ receiver = build_receiver(delegate)
68
+ bind_receiver_response = Smpp::Pdu::BindReceiverResponse.new(nil, Smpp::Pdu::Base::ESME_RBINDFAIL, 1)
69
+
70
+ receiver.process_pdu(bind_receiver_response)
71
+
72
+ assert_equal [], delegate.states
73
+ end
74
+
75
+ def test_receiving_bind_receiver_response_with_error_status_should_close_connection
76
+ receiver = build_receiver
77
+ bind_receiver_response = Smpp::Pdu::BindReceiverResponse.new(nil, Smpp::Pdu::Base::ESME_RBINDFAIL, 1)
78
+
79
+ receiver.process_pdu(bind_receiver_response)
80
+
81
+ assert_equal 1, receiver.close_connections
82
+ end
83
+
84
+ def test_receiving_deliver_sm_should_send_deliver_sm_response
85
+ delegate = RecordingDelegate.new
86
+ receiver = build_receiver(delegate)
87
+ deliver_sm = Smpp::Pdu::DeliverSm.new("from", "to", "message")
88
+
89
+ receiver.process_pdu(deliver_sm)
90
+
91
+ first_sent_data = receiver.sent_data.first
92
+ assert_not_nil first_sent_data
93
+ actual_response = Smpp::Pdu::Base.create(first_sent_data)
94
+ expected_response = Smpp::Pdu::DeliverSmResponse.new(deliver_sm.sequence_number)
95
+ assert_equal expected_response.to_human, actual_response.to_human
96
+ end
97
+
98
+ def test_receiving_deliver_sm_should_send_error_response_if_delegate_raises_exception
99
+ delegate = ExceptionRaisingDelegate.new
100
+ receiver = build_receiver(delegate)
101
+ deliver_sm = Smpp::Pdu::DeliverSm.new("from", "to", "message")
102
+
103
+ receiver.process_pdu(deliver_sm)
104
+
105
+ first_sent_data = receiver.sent_data.first
106
+ assert_not_nil first_sent_data
107
+ actual_response = Smpp::Pdu::Base.create(first_sent_data)
108
+ expected_response = Smpp::Pdu::DeliverSmResponse.new(deliver_sm.sequence_number, Smpp::Pdu::Base::ESME_RX_T_APPN)
109
+ assert_equal expected_response.to_human, actual_response.to_human
110
+ end
111
+
112
+ def test_receiving_deliver_sm_should_still_send_deliver_sm_response_when_no_delegate_is_provided
113
+ delegate = nil
114
+ receiver = build_receiver(delegate)
115
+ deliver_sm = Smpp::Pdu::DeliverSm.new("from", "to", "message")
116
+
117
+ receiver.process_pdu(deliver_sm)
118
+
119
+ first_sent_data = receiver.sent_data.first
120
+ assert_not_nil first_sent_data
121
+ actual_response = Smpp::Pdu::Base.create(first_sent_data)
122
+ expected_response = Smpp::Pdu::DeliverSmResponse.new(deliver_sm.sequence_number)
123
+ assert_equal expected_response.to_human, actual_response.to_human
124
+ end
125
+
126
+ def test_receiving_deliver_sm_should_invoke_mo_received_on_delegate
127
+ delegate = RecordingDelegate.new
128
+ receiver = build_receiver(delegate)
129
+ deliver_sm = Smpp::Pdu::DeliverSm.new("from", "to", "message")
130
+
131
+ receiver.process_pdu(deliver_sm)
132
+
133
+ first_received_pdu = delegate.received_pdus.first
134
+ assert_not_nil first_received_pdu
135
+ assert_equal deliver_sm.to_human, first_received_pdu.to_human
136
+ end
137
+
138
+ def test_receiving_deliver_sm_should_not_error_if_mo_received_method_doesnt_exist_on_delegate
139
+ delegate = Object.new
140
+ receiver = build_receiver(delegate)
141
+ deliver_sm = Smpp::Pdu::DeliverSm.new("from", "to", "message")
142
+
143
+ assert_nothing_raised { receiver.process_pdu(deliver_sm) }
144
+ end
145
+
146
+ def test_receiving_deliver_sm_for_esm_class_4_should_invoke_delivery_report_received_on_delegate
147
+ delegate = RecordingDelegate.new
148
+ receiver = build_receiver(delegate)
149
+ deliver_sm = Smpp::Pdu::DeliverSm.new("from", "to", "message", :esm_class => 4)
150
+
151
+ receiver.process_pdu(deliver_sm)
152
+
153
+ first_received_delivery_report_pdu = delegate.received_delivery_report_pdus.first
154
+ assert_not_nil first_received_delivery_report_pdu
155
+ assert_equal deliver_sm.to_human, first_received_delivery_report_pdu.to_human
156
+ end
157
+
158
+ def test_receiving_deliver_sm_should_not_error_if_received_delivery_report_method_doesnt_exist_on_delegate
159
+ delegate = Object.new
160
+ receiver = build_receiver(delegate)
161
+ deliver_sm = Smpp::Pdu::DeliverSm.new("from", "to", "message", :esm_class => 4)
162
+
163
+ assert_nothing_raised { receiver.process_pdu(deliver_sm) }
164
+ end
165
+
166
+ private
167
+
168
+ def build_receiver(delegate = nil)
169
+ receiver = Smpp::Receiver.new(1, {}, delegate)
170
+ class << receiver
171
+ attr_reader :sent_data, :close_connections
172
+ def send_data(data)
173
+ @sent_data = (@sent_data || []) + [data]
174
+ end
175
+ def close_connection
176
+ @close_connections = (@close_connections || 0) + 1
177
+ end
178
+ end
179
+ receiver
180
+ end
181
+
182
+ end
183
+
184
+ require 'server'
185
+ require 'delegate'
186
+
187
+ class SmppTest < Test::Unit::TestCase
188
+
189
+ def config
190
+ Server::config
191
+ end
192
+
193
+ def test_transceiver_should_bind_and_unbind_then_stop
194
+ EventMachine.run {
195
+ EventMachine.start_server "localhost", 9000, Server::Unbind
196
+ EventMachine.connect "localhost", 9000, Smpp::Receiver, config, Delegate.new
197
+ }
198
+ # should not hang here: the server's response should have caused the client to terminate
199
+ end
200
+
201
+ def test_bind_receiver
202
+ pdu1 = Smpp::Pdu::BindReceiver.new(
203
+ config[:system_id],
204
+ config[:password],
205
+ config[:system_type],
206
+ config[:source_ton],
207
+ config[:source_npi],
208
+ config[:source_address_range]
209
+ )
210
+
211
+ pdu2 = Smpp::Pdu::Base.create(pdu1.data)
212
+
213
+ assert_instance_of(Smpp::Pdu::BindReceiver, pdu2)
214
+ assert_equal(pdu1.system_id, pdu2.system_id)
215
+ assert_equal(pdu1.password, pdu2.password)
216
+ assert_equal(pdu1.system_type, pdu2.system_type)
217
+ assert_equal(pdu1.addr_ton, pdu2.addr_ton)
218
+ assert_equal(pdu1.addr_npi, pdu2.addr_npi)
219
+ assert_equal(pdu1.address_range, pdu2.address_range)
220
+ assert_equal(pdu1.sequence_number, pdu2.sequence_number)
221
+ assert_equal(pdu1.command_status, pdu2.command_status)
222
+ end
223
+
224
+ def test_bind_receiver_response
225
+ pdu1 = Smpp::Pdu::BindReceiverResponse.new(nil, Smpp::Pdu::Base::ESME_ROK, config[:system_id])
226
+ pdu2 = Smpp::Pdu::Base.create(pdu1.data)
227
+ assert_instance_of(Smpp::Pdu::BindReceiverResponse, pdu2)
228
+ assert_equal(pdu1.system_id, pdu2.system_id)
229
+ assert_equal(pdu1.sequence_number, pdu2.sequence_number)
230
+ assert_equal(pdu1.command_status, pdu2.command_status)
231
+ end
232
+ end
@@ -0,0 +1,53 @@
1
+ #TODO This should be made prettier with mocha
2
+ class ResponsiveDelegate
3
+ attr_reader :seq, :event_counter
4
+
5
+ def initialize
6
+ @seq = 0
7
+ @event_counter = nil
8
+ end
9
+ def seq
10
+ @seq += 1
11
+ end
12
+ def count_function
13
+ func = caller(1)[0].split("`")[1].split("'")[0].to_sym
14
+ @event_counter = {} unless @event_counter.is_a?(Hash)
15
+ @event_counter[func] = 0 if @event_counter[func].nil?
16
+ @event_counter[func]+=1
17
+ end
18
+
19
+ def mo_received(transceiver, pdu)
20
+ count_function
21
+ puts "** mo_received"
22
+ end
23
+
24
+ def delivery_report_received(transceiver, pdu)
25
+ count_function
26
+ puts "** delivery_report_received"
27
+ end
28
+
29
+ def message_accepted(transceiver, mt_message_id, pdu)
30
+ count_function
31
+ puts "** message_sent"
32
+ #sending messages from delegate to escape making a fake message sender - not nice :(
33
+ $tx.send_mt(self.seq, 1, 2, "short_message @ message_accepted")
34
+ end
35
+
36
+ def message_rejected(transceiver, mt_message_id, pdu)
37
+ count_function
38
+ puts "** message_rejected"
39
+ $tx.send_mt(self.seq, 1, 2, "short_message @ message_rejected")
40
+ end
41
+
42
+ def bound(transceiver)
43
+ count_function
44
+ puts "** bound"
45
+ $tx.send_mt(self.seq, 1, 2, "short_message @ bound")
46
+ end
47
+
48
+ def unbound(transceiver)
49
+ count_function
50
+ puts "** unbound"
51
+ EventMachine::stop_event_loop
52
+ end
53
+ end
data/test/server.rb ADDED
@@ -0,0 +1,56 @@
1
+ # a server which immediately requests the client to unbind
2
+ module Server
3
+ def self.config
4
+ {
5
+ :host => 'localhost',
6
+ :port => 2775,
7
+ :system_id => 'foo',
8
+ :password => 'bar',
9
+ :system_type => '',
10
+ :source_ton => 0,
11
+ :source_npi => 1,
12
+ :destination_ton => 1,
13
+ :destination_npi => 1,
14
+ :source_address_range => '',
15
+ :destination_address_range => ''
16
+ }
17
+ end
18
+
19
+ module Unbind
20
+ def receive_data(data)
21
+ send_data Smpp::Pdu::Unbind.new.data
22
+ end
23
+ end
24
+
25
+ module SubmitSmResponse
26
+ def receive_data(data)
27
+ # problem: our Pdu's should have factory methods for "both ways"; ie. when created
28
+ # by client, and when created from wire data.
29
+ send_data Smpp::Pdu::SubmitSmResponse.new(1, 2, "100").data
30
+ end
31
+ end
32
+
33
+ module SubmitSmResponseWithErrorStatus
34
+ attr_reader :state #state=nil => bind => state=bound => send =>state=sent => unbind => state=unbound
35
+ def receive_data(data)
36
+ if @state.nil?
37
+ @state = 'bound'
38
+ pdu = Smpp::Pdu::Base.create(data)
39
+ response_pdu = Smpp::Pdu::BindTransceiverResponse.new(pdu.sequence_number,Smpp::Pdu::Base::ESME_ROK,Server::config[:system_id])
40
+ send_data response_pdu.data
41
+ elsif @state == 'bound'
42
+ @state = 'sent'
43
+ pdu = Smpp::Pdu::Base.create(data)
44
+ pdu.to_human
45
+ send_data Smpp::Pdu::SubmitSmResponse.new(pdu.sequence_number, Smpp::Pdu::Base::ESME_RINVDSTADR, pdu.body).data
46
+ #send_data Smpp::Pdu::SubmitSmResponse.new(1, 2, "100").data
47
+ elsif @state == 'sent'
48
+ @state = 'unbound'
49
+ send_data Smpp::Pdu::Unbind.new.data
50
+ else
51
+ raise "unexpected state"
52
+ end
53
+ end
54
+ end
55
+
56
+ end