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.
- 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,20 @@
|
|
1
|
+
# signals invalid message header
|
2
|
+
class Smpp::Pdu::GenericNack < Smpp::Pdu::Base
|
3
|
+
handles_cmd GENERIC_NACK
|
4
|
+
|
5
|
+
attr_accessor :error_code
|
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)
|
14
|
+
@error_code = error_code
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.from_wire_data(seq, status, body)
|
18
|
+
new(seq,status,body)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# Sending an MT message to multiple addresses
|
2
|
+
# Author: Abhishek Parolkar, (abhishek[at]parolkar.com)
|
3
|
+
#TODO: Implement from_wire_data for this pdu class.
|
4
|
+
class Smpp::Pdu::SubmitMulti < Smpp::Pdu::Base
|
5
|
+
IS_SMEADDR = 1 # type of dest_flag
|
6
|
+
IS_DISTLISTNAME = 2 #type of dest_flag
|
7
|
+
|
8
|
+
# Note: short_message (the SMS body) must be in iso-8859-1 format
|
9
|
+
def initialize(source_addr, destination_addr_array, short_message, options={})
|
10
|
+
options.merge!(
|
11
|
+
:esm_class => 0, # default smsc mode
|
12
|
+
:dcs => 3 # iso-8859-1
|
13
|
+
) { |key, old_val, new_val| old_val }
|
14
|
+
|
15
|
+
@msg_body = short_message
|
16
|
+
|
17
|
+
udh = options[:udh]
|
18
|
+
service_type = ''
|
19
|
+
source_addr_ton = 0 # network specific
|
20
|
+
source_addr_npi = 1 # unknown
|
21
|
+
number_of_dests = destination_addr_array.length # Max value can be 254
|
22
|
+
dest_addr_ton = 1 # international
|
23
|
+
dest_addr_npi = 1 # unknown
|
24
|
+
dest_addresses = build_destination_addresses(destination_addr_array,dest_addr_ton,dest_addr_npi,IS_SMEADDR)
|
25
|
+
esm_class = options[:esm_class]
|
26
|
+
protocol_id = 0
|
27
|
+
priority_flag = 0
|
28
|
+
schedule_delivery_time = ''
|
29
|
+
validity_period = ''
|
30
|
+
registered_delivery = 1 # we want delivery notifications
|
31
|
+
replace_if_present_flag = 0
|
32
|
+
data_coding = options[:dcs]
|
33
|
+
sm_default_msg_id = 0
|
34
|
+
payload = udh ? udh + short_message : short_message # this used to be (short_message + "\0")
|
35
|
+
sm_length = payload.length
|
36
|
+
|
37
|
+
# craft the string/byte buffer
|
38
|
+
pdu_body = sprintf("%s\0%c%c%s\0%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, number_of_dests,dest_addresses, 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
|
+
super(SUBMIT_MULTI, 0, next_sequence_number, pdu_body)
|
41
|
+
end
|
42
|
+
|
43
|
+
# some special formatting is needed for SubmitSm PDUs to show the actual message content
|
44
|
+
def to_human
|
45
|
+
# convert header (4 bytes) to array of 4-byte ints
|
46
|
+
a = @data.to_s.unpack('N4')
|
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])
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_destination_addresses(dest_array,dest_addr_ton,dest_addr_npi, dest_flag = IS_SMEADDR)
|
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)
|
56
|
+
|
57
|
+
elsif dest_flag == IS_DISTLISTNAME
|
58
|
+
packet_str = sprintf("%c%s",IS_SMEADDR,dest_elem)
|
59
|
+
formatted_array.push(packet_str)
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
}
|
64
|
+
|
65
|
+
formatted_array.join("\0");
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Recieving response for an MT message sent to multiple addresses
|
2
|
+
# Author: Abhishek Parolkar, (abhishek[at]parolkar.com)
|
3
|
+
class Smpp::Pdu::SubmitMultiResponse < Smpp::Pdu::Base
|
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)
|
28
|
+
@message_id = message_id
|
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
|
+
|
49
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# Sending an MT message
|
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, :optional_parameters
|
8
|
+
|
9
|
+
|
10
|
+
# Note: short_message (the SMS body) must be in iso-8859-1 format
|
11
|
+
def initialize(source_addr, destination_addr, short_message, options={}, seq = nil)
|
12
|
+
|
13
|
+
@msg_body = short_message
|
14
|
+
|
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]:0
|
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
|
35
|
+
|
36
|
+
@optional_parameters = options[:optional_parameters]
|
37
|
+
|
38
|
+
# craft the string/byte buffer
|
39
|
+
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,
|
40
|
+
@dest_addr_ton, @dest_addr_npi, @destination_addr, @esm_class, @protocol_id, @priority_flag, @schedule_delivery_time, @validity_period,
|
41
|
+
@registered_delivery, @replace_if_present_flag, @data_coding, @sm_default_msg_id, @sm_length, payload)
|
42
|
+
|
43
|
+
if @optional_parameters
|
44
|
+
pdu_body << optional_parameters_to_buffer(@optional_parameters)
|
45
|
+
end
|
46
|
+
|
47
|
+
seq ||= next_sequence_number
|
48
|
+
|
49
|
+
super(SUBMIT_SM, 0, seq, pdu_body)
|
50
|
+
end
|
51
|
+
|
52
|
+
# some special formatting is needed for SubmitSm PDUs to show the actual message content
|
53
|
+
def to_human
|
54
|
+
# convert header (4 bytes) to array of 4-byte ints
|
55
|
+
a = @data.to_s.unpack('N4')
|
56
|
+
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])
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.from_wire_data(seq, status, body)
|
60
|
+
options = {}
|
61
|
+
|
62
|
+
options[:service_type],
|
63
|
+
options[:source_addr_ton],
|
64
|
+
options[:source_addr_npi],
|
65
|
+
source_addr,
|
66
|
+
options[:dest_addr_ton],
|
67
|
+
options[:dest_addr_npi],
|
68
|
+
destination_addr,
|
69
|
+
options[:esm_class],
|
70
|
+
options[:protocol_id],
|
71
|
+
options[:priority_flag],
|
72
|
+
options[:schedule_delivery_time],
|
73
|
+
options[:validity_period],
|
74
|
+
options[:registered_delivery],
|
75
|
+
options[:replace_if_present_flag],
|
76
|
+
options[:data_coding],
|
77
|
+
options[:sm_default_msg_id],
|
78
|
+
options[:sm_length],
|
79
|
+
remaining_bytes = body.unpack('Z*CCZ*CCZ*CCCZ*Z*CCCCCa*')
|
80
|
+
|
81
|
+
short_message = remaining_bytes.slice!(0...options[:sm_length])
|
82
|
+
|
83
|
+
#everything left in remaining_bytes is 3.4 optional parameters
|
84
|
+
options[:optional_parameters] = parse_optional_parameters(remaining_bytes)
|
85
|
+
|
86
|
+
Smpp::Base.logger.debug "SubmitSM with source_addr=#{source_addr}, destination_addr=#{destination_addr}"
|
87
|
+
|
88
|
+
new(source_addr, destination_addr, short_message, options, seq)
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Smpp::Pdu::SubmitSmResponse < Smpp::Pdu::Base
|
2
|
+
handles_cmd SUBMIT_SM_RESP
|
3
|
+
|
4
|
+
attr_accessor :message_id
|
5
|
+
attr_accessor :optional_parameters
|
6
|
+
|
7
|
+
def initialize(seq, status, message_id, optional_parameters=nil)
|
8
|
+
seq ||= next_sequence_number
|
9
|
+
body = message_id.to_s + "\000"
|
10
|
+
super(SUBMIT_SM_RESP, status, seq, body)
|
11
|
+
@message_id = message_id
|
12
|
+
@optional_parameters = optional_parameters
|
13
|
+
end
|
14
|
+
|
15
|
+
def optional_parameter(tag)
|
16
|
+
if optional_parameters
|
17
|
+
if param = optional_parameters[tag]
|
18
|
+
param.value
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.from_wire_data(seq, status, body)
|
24
|
+
message_id, remaining_bytes = body.unpack("Z*a*")
|
25
|
+
optionals = nil
|
26
|
+
if remaining_bytes && !remaining_bytes.empty?
|
27
|
+
optionals = parse_optional_parameters(remaining_bytes)
|
28
|
+
end
|
29
|
+
new(seq, status, message_id, optionals)
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class Smpp::Pdu::UnbindResponse < Smpp::Pdu::Base
|
2
|
+
handles_cmd UNBIND_RESP
|
3
|
+
|
4
|
+
def initialize(seq, status)
|
5
|
+
seq ||= next_sequence_number
|
6
|
+
super(UNBIND_RESP, status, seq)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.from_wire_data(seq, status, body)
|
10
|
+
new(seq, status)
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# The SMPP Receiver maintains a unidirectional connection to an SMSC.
|
2
|
+
# Provide a config hash with connection options to get started.
|
3
|
+
# See the sample_gateway.rb for examples of config values.
|
4
|
+
# The receiver accepts a delegate object that may implement
|
5
|
+
# the following (all optional) methods:
|
6
|
+
#
|
7
|
+
# mo_received(receiver, pdu)
|
8
|
+
# delivery_report_received(receiver, pdu)
|
9
|
+
# bound(receiver)
|
10
|
+
# unbound(receiver)
|
11
|
+
|
12
|
+
class Smpp::Receiver < Smpp::Base
|
13
|
+
|
14
|
+
# Send BindReceiverResponse PDU.
|
15
|
+
def send_bind
|
16
|
+
raise IOError, 'Receiver already bound.' unless unbound?
|
17
|
+
pdu = Pdu::BindReceiver.new(
|
18
|
+
@config[:system_id],
|
19
|
+
@config[:password],
|
20
|
+
@config[:system_type],
|
21
|
+
@config[:source_ton],
|
22
|
+
@config[:source_npi],
|
23
|
+
@config[:source_address_range])
|
24
|
+
write_pdu(pdu)
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/smpp/server.rb
ADDED
@@ -0,0 +1,223 @@
|
|
1
|
+
# --------
|
2
|
+
# This is experimental stuff submitted by taryn@taryneast.org
|
3
|
+
# --------
|
4
|
+
|
5
|
+
# the opposite of a client-based receiver, the server transmitter will send
|
6
|
+
# out MOs to the client when set up
|
7
|
+
class Smpp::Server < Smpp::Base
|
8
|
+
|
9
|
+
attr_accessor :bind_status
|
10
|
+
|
11
|
+
# Expects a config hash,
|
12
|
+
# a proc to invoke for incoming (MO) messages,
|
13
|
+
# a proc to invoke for delivery reports,
|
14
|
+
# and optionally a hash-like storage for pending delivery reports.
|
15
|
+
def initialize(config, received_messages = [], sent_messages = [])
|
16
|
+
super(config)
|
17
|
+
@state = :unbound
|
18
|
+
@received_messages = received_messages
|
19
|
+
@sent_messages = sent_messages
|
20
|
+
|
21
|
+
ed = @config[:enquire_link_delay_secs] || 5
|
22
|
+
comm_inactivity_timeout = [ed - 5, 3].max
|
23
|
+
rescue Exception => ex
|
24
|
+
logger.error "Exception setting up server: #{ex}"
|
25
|
+
raise
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
#######################################################################
|
30
|
+
# Session management functions
|
31
|
+
#######################################################################
|
32
|
+
# Session helpers
|
33
|
+
|
34
|
+
# convenience methods
|
35
|
+
# is this session currently bound?
|
36
|
+
def bound?
|
37
|
+
@state == :bound
|
38
|
+
end
|
39
|
+
# is this session currently unbound?
|
40
|
+
def unbound?
|
41
|
+
@state == :unbound
|
42
|
+
end
|
43
|
+
# set of valid bind statuses
|
44
|
+
BIND_STATUSES = {:transmitter => :bound_tx,
|
45
|
+
:receiver => :bound_rx, :transceiver => :bound_trx}
|
46
|
+
# set the bind status based on the common-name for the bind class
|
47
|
+
def set_bind_status(bind_classname)
|
48
|
+
@bind_status = BIND_STATUSES[bind_classname]
|
49
|
+
end
|
50
|
+
# and kill the bind status when done
|
51
|
+
def unset_bind_status
|
52
|
+
@bind_status = nil
|
53
|
+
end
|
54
|
+
# what is the bind_status?
|
55
|
+
def bind_status
|
56
|
+
@bind_status
|
57
|
+
end
|
58
|
+
# convenience function - are we able to transmit in this bind-Status?
|
59
|
+
def transmitting?
|
60
|
+
# not transmitting if not bound
|
61
|
+
return false if unbound? || bind_status.nil?
|
62
|
+
# receivers can't transmit
|
63
|
+
bind_status != :bound_rx
|
64
|
+
end
|
65
|
+
# convenience function - are we able to receive in this bind-Status?
|
66
|
+
def receiving?
|
67
|
+
# not receiving if not bound
|
68
|
+
return false if unbound? || bind_status.nil?
|
69
|
+
# transmitters can't receive
|
70
|
+
bind_status != :bound_tx
|
71
|
+
end
|
72
|
+
|
73
|
+
def am_server?
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
# REVISIT - not sure if these are using the correct data. Currently just
|
78
|
+
# pulls the data straight out of the given pdu and sends it right back.
|
79
|
+
#
|
80
|
+
def fetch_bind_response_class(bind_classname)
|
81
|
+
# check we have a valid classname - probably overkill as only our code
|
82
|
+
# will send the classnames through
|
83
|
+
raise IOError, "bind class name missing" if bind_classname.nil?
|
84
|
+
raise IOError, "bind class name: #{bind_classname} unknown" unless BIND_STATUSES.has_key?(bind_classname)
|
85
|
+
|
86
|
+
case bind_classname
|
87
|
+
when :transceiver
|
88
|
+
return Smpp::Pdu::BindTransceiverResponse
|
89
|
+
when :transmitter
|
90
|
+
return Smpp::Pdu::BindTransmitterResponse
|
91
|
+
when :receiver
|
92
|
+
return Smpp::Pdu::BindReceiverResponse
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# actually perform the action of binding the session to the given session
|
97
|
+
# type
|
98
|
+
def bind_session(bind_pdu, bind_classname)
|
99
|
+
# TODO: probably should not "raise" here - what's better?
|
100
|
+
raise IOError, "Session already bound." if bound?
|
101
|
+
response_class = fetch_bind_response_class(bind_classname)
|
102
|
+
|
103
|
+
# TODO: look inside the pdu for the password and check it
|
104
|
+
|
105
|
+
send_bind_response(bind_pdu, response_class)
|
106
|
+
|
107
|
+
@state = :bound
|
108
|
+
set_bind_status(bind_classname)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Send BindReceiverResponse PDU - used in response to a "bind_receiver"
|
112
|
+
# pdu.
|
113
|
+
def send_bind_response(bind_pdu, bind_class)
|
114
|
+
resp_pdu = bind_class.new(
|
115
|
+
bind_pdu.sequence_number,
|
116
|
+
# currently assume that it binds ok
|
117
|
+
Pdu::Base::ESME_ROK,
|
118
|
+
# TODO: not sure where we get the system ID
|
119
|
+
# is this the session id?
|
120
|
+
bind_pdu.system_id)
|
121
|
+
write_pdu(resp_pdu)
|
122
|
+
end
|
123
|
+
|
124
|
+
#######################################################################
|
125
|
+
# Message submission (transmitter) functions (used by transmitter and
|
126
|
+
# transceiver-bound system)
|
127
|
+
# Note - we only support submit_sm message type, not submit_multi or
|
128
|
+
# data_sm message types
|
129
|
+
#######################################################################
|
130
|
+
# Receive an incoming message to send to the network and respond
|
131
|
+
# REVISIT = just a stub
|
132
|
+
def receive_sm(pdu)
|
133
|
+
# TODO: probably should not "raise" here - what's better?
|
134
|
+
raise IOError, "Connection not bound." if unbound?
|
135
|
+
# Doesn't matter if it's a TX/RX/TRX, have to send a SubmitSmResponse:
|
136
|
+
# raise IOError, "Connection not set to receive" unless receiving?
|
137
|
+
|
138
|
+
# Must respond to SubmitSm requests with the same sequence number
|
139
|
+
m_seq = pdu.sequence_number
|
140
|
+
# add the id to the list of ids of which we're awaiting acknowledgement
|
141
|
+
@received_messages << m_seq
|
142
|
+
|
143
|
+
# In theory this is where the MC would actually do something useful with
|
144
|
+
# the PDU - eg send it on to the network. We'd check if it worked and
|
145
|
+
# send a failure PDU if it failed.
|
146
|
+
#
|
147
|
+
# Given this is a dummy MC, that's not necessary, so all our responses
|
148
|
+
# will be OK.
|
149
|
+
|
150
|
+
# so respond with a successful response
|
151
|
+
pdu = Pdu::SubmitSmResponse.new(m_seq, Pdu::Base::ESME_ROK, message_id = '' )
|
152
|
+
write_pdu pdu
|
153
|
+
@received_messages.delete m_seq
|
154
|
+
|
155
|
+
logger.info "Received submit sm message: #{m_seq}"
|
156
|
+
end
|
157
|
+
|
158
|
+
#######################################################################
|
159
|
+
# Message delivery (receiver) functions (used by receiver and
|
160
|
+
# transceiver-bound system)
|
161
|
+
#######################################################################
|
162
|
+
# When we get an incoming SMS to send on to the client, we need to
|
163
|
+
# initiate one of these PDUs.
|
164
|
+
# Note - data doesn't have to be valid, as we're not really doing much
|
165
|
+
# useful with it. Only the params that will be pulled out by the test
|
166
|
+
# system need to be valid.
|
167
|
+
def deliver_sm(from, to, message, config = {})
|
168
|
+
# TODO: probably should not "raise" here - what's better?
|
169
|
+
raise IOError, "Connection not bound." if unbound?
|
170
|
+
raise IOError, "Connection not set to receive" unless receiving?
|
171
|
+
|
172
|
+
# submit the given message
|
173
|
+
new_pdu = Pdu::DeliverSm.new(from, to, message, config)
|
174
|
+
write_pdu(new_pdu)
|
175
|
+
# add the id to the list of ids of which we're awaiting acknowledgement
|
176
|
+
@sent_messages << m_seq
|
177
|
+
|
178
|
+
logger.info "Delivered SM message id: #{m_seq}"
|
179
|
+
|
180
|
+
new_pdu
|
181
|
+
end
|
182
|
+
|
183
|
+
# Acknowledge delivery of an outgoing MO message
|
184
|
+
# REVISIT = just a stub
|
185
|
+
def accept_deliver_sm_response(pdu)
|
186
|
+
m_seq = pdu.sequence_number
|
187
|
+
# add the id to the list of ids we're awaiting acknowledgement of
|
188
|
+
# REVISIT - what id do we need to store?
|
189
|
+
unless @sent_messages && @sent_messages.include?(m_seq)
|
190
|
+
logger.error("Received deliver response for message for which we have no saved id: #{m_seq}")
|
191
|
+
else
|
192
|
+
@sent_messages.delete(m_seq)
|
193
|
+
logger.info "Acknowledged receipt of SM delivery message id: #{m_seq}"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
|
198
|
+
# a PDU is received
|
199
|
+
# these pdus are all responses to a message sent by the client and require
|
200
|
+
# their own special response
|
201
|
+
def process_pdu(pdu)
|
202
|
+
case pdu
|
203
|
+
# client has asked to set up a connection
|
204
|
+
when Pdu::BindTransmitter
|
205
|
+
bind_session(pdu, :transmitter)
|
206
|
+
when Pdu::BindReceiver
|
207
|
+
bind_session(pdu, :receiver)
|
208
|
+
when Pdu::BindTransceiver
|
209
|
+
bind_session(pdu, :transceiver)
|
210
|
+
# client has acknowledged receipt of a message we sent to them
|
211
|
+
when Pdu::DeliverSmResponse
|
212
|
+
accept_deliver_sm_response(pdu) # acknowledge its sending
|
213
|
+
|
214
|
+
# client has asked for a message to be sent
|
215
|
+
when Pdu::SubmitSm
|
216
|
+
receive_sm(pdu)
|
217
|
+
else
|
218
|
+
# for generic functions or default fallback
|
219
|
+
super(pdu)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|