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,102 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Sample SMPP SMS Gateway.
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require File.dirname(__FILE__) + '/../lib/smpp'
|
7
|
+
require File.dirname(__FILE__) + '/../lib/smpp/server'
|
8
|
+
|
9
|
+
# set up logger
|
10
|
+
Smpp::Base.logger = Logger.new('smsc.log')
|
11
|
+
|
12
|
+
# the transceiver
|
13
|
+
$tx = nil
|
14
|
+
|
15
|
+
# We use EventMachine to receive keyboard input (which we send as MT messages).
|
16
|
+
# A "real" gateway would probably get its MTs from a message queue instead.
|
17
|
+
module KeyboardHandler
|
18
|
+
include EventMachine::Protocols::LineText2
|
19
|
+
|
20
|
+
def receive_line(data)
|
21
|
+
puts "Sending MO: #{data}"
|
22
|
+
from = '1111111111'
|
23
|
+
to = '1111111112'
|
24
|
+
$tx.send_mo(from, to, data)
|
25
|
+
|
26
|
+
|
27
|
+
# if you want to send messages with custom options, uncomment below code, this configuration allows the sender ID to be alpha numeric
|
28
|
+
# $tx.send_mt(123, "RubySmpp", to, "Testing RubySmpp as sender id",{
|
29
|
+
# :source_addr_ton=> 5,
|
30
|
+
# :service_type => 1,
|
31
|
+
# :source_addr_ton => 5,
|
32
|
+
# :source_addr_npi => 0 ,
|
33
|
+
# :dest_addr_ton => 2,
|
34
|
+
# :dest_addr_npi => 1,
|
35
|
+
# :esm_class => 3 ,
|
36
|
+
# :protocol_id => 0,
|
37
|
+
# :priority_flag => 0,
|
38
|
+
# :schedule_delivery_time => nil,
|
39
|
+
# :validity_period => nil,
|
40
|
+
# :registered_delivery=> 1,
|
41
|
+
# :replace_if_present_flag => 0,
|
42
|
+
# :data_coding => 0,
|
43
|
+
# :sm_default_msg_id => 0
|
44
|
+
# })
|
45
|
+
|
46
|
+
# if you want to send message to multiple destinations , uncomment below code
|
47
|
+
# $tx.send_multi_mt(123, from, ["919900000001","919900000002","919900000003"], "I am echoing that ruby-smpp is great")
|
48
|
+
prompt
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def prompt
|
53
|
+
print "Enter MO body: "
|
54
|
+
$stdout.flush
|
55
|
+
end
|
56
|
+
|
57
|
+
def logger
|
58
|
+
Smpp::Base.logger
|
59
|
+
end
|
60
|
+
|
61
|
+
def start(config)
|
62
|
+
|
63
|
+
# Run EventMachine in loop so we can reconnect when the SMSC drops our connection.
|
64
|
+
loop do
|
65
|
+
EventMachine::run do
|
66
|
+
$tx = EventMachine::start_server(
|
67
|
+
config[:host],
|
68
|
+
config[:port],
|
69
|
+
Smpp::Server,
|
70
|
+
config
|
71
|
+
)
|
72
|
+
end
|
73
|
+
logger.warn "Event loop stopped. Restarting in 5 seconds.."
|
74
|
+
sleep 5
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Start the Gateway
|
79
|
+
begin
|
80
|
+
puts "Starting SMS Gateway"
|
81
|
+
|
82
|
+
# SMPP properties. These parameters the ones provided sample_gateway.rb and
|
83
|
+
# will work with it.
|
84
|
+
config = {
|
85
|
+
:host => 'localhost',
|
86
|
+
:port => 6000,
|
87
|
+
:system_id => 'hugo',
|
88
|
+
:password => 'ggoohu',
|
89
|
+
:system_type => 'vma', # default given according to SMPP 3.4 Spec
|
90
|
+
:interface_version => 52,
|
91
|
+
:source_ton => 0,
|
92
|
+
:source_npi => 1,
|
93
|
+
:destination_ton => 1,
|
94
|
+
:destination_npi => 1,
|
95
|
+
:source_address_range => '',
|
96
|
+
:destination_address_range => '',
|
97
|
+
:enquire_link_delay_secs => 10
|
98
|
+
}
|
99
|
+
start(config)
|
100
|
+
rescue Exception => ex
|
101
|
+
puts "Exception in SMS Gateway: #{ex} at #{ex.backtrace[0]}"
|
102
|
+
end
|
data/lib/smpp.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# SMPP v3.4 subset implementation.
|
2
|
+
# SMPP is a short message peer-to-peer protocol typically used to communicate
|
3
|
+
# with SMS Centers (SMSCs) over TCP/IP.
|
4
|
+
#
|
5
|
+
# August Z. Flatby
|
6
|
+
# august@apparat.no
|
7
|
+
|
8
|
+
require 'logger'
|
9
|
+
|
10
|
+
$:.unshift(File.dirname(__FILE__))
|
11
|
+
require 'smpp/base.rb'
|
12
|
+
require 'smpp/transceiver.rb'
|
13
|
+
require 'smpp/receiver.rb'
|
14
|
+
require 'smpp/optional_parameter'
|
15
|
+
require 'smpp/pdu/base.rb'
|
16
|
+
require 'smpp/pdu/bind_base.rb'
|
17
|
+
require 'smpp/pdu/bind_resp_base.rb'
|
18
|
+
|
19
|
+
# Load all PDUs
|
20
|
+
Dir.glob(File.join(File.dirname(__FILE__), 'smpp', 'pdu', '*.rb')) do |f|
|
21
|
+
require f unless f.match('base.rb$')
|
22
|
+
end
|
23
|
+
|
24
|
+
# Default logger. Invoke this call in your client to use another logger.
|
25
|
+
Smpp::Base.logger = Logger.new(STDOUT)
|
data/lib/smpp/base.rb
ADDED
@@ -0,0 +1,308 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'scanf'
|
3
|
+
require 'monitor'
|
4
|
+
require 'eventmachine'
|
5
|
+
|
6
|
+
module Smpp
|
7
|
+
class InvalidStateException < Exception; end
|
8
|
+
|
9
|
+
class Base < EventMachine::Connection
|
10
|
+
include Smpp
|
11
|
+
|
12
|
+
# :bound or :unbound
|
13
|
+
attr_accessor :state
|
14
|
+
|
15
|
+
def initialize(config, delegate)
|
16
|
+
@state = :unbound
|
17
|
+
@config = config
|
18
|
+
@data = ""
|
19
|
+
@delegate = delegate
|
20
|
+
|
21
|
+
# Array of un-acked MT message IDs indexed by sequence number.
|
22
|
+
# As soon as we receive SubmitSmResponse we will use this to find the
|
23
|
+
# associated message ID, and then create a pending delivery report.
|
24
|
+
@ack_ids = {}
|
25
|
+
|
26
|
+
ed = @config[:enquire_link_delay_secs] || 5
|
27
|
+
comm_inactivity_timeout = 2 * ed
|
28
|
+
end
|
29
|
+
|
30
|
+
# queries the state of the transmitter - is it bound?
|
31
|
+
def unbound?
|
32
|
+
@state == :unbound
|
33
|
+
end
|
34
|
+
|
35
|
+
def bound?
|
36
|
+
@state == :bound
|
37
|
+
end
|
38
|
+
|
39
|
+
def Base.logger
|
40
|
+
@@logger
|
41
|
+
end
|
42
|
+
|
43
|
+
def Base.logger=(logger)
|
44
|
+
@@logger = logger
|
45
|
+
end
|
46
|
+
|
47
|
+
def logger
|
48
|
+
@@logger
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
# invoked by EventMachine when connected
|
53
|
+
def post_init
|
54
|
+
# send Bind PDU if we are a binder (eg
|
55
|
+
# Receiver/Transmitter/Transceiver
|
56
|
+
send_bind unless defined?(am_server?) && am_server?
|
57
|
+
|
58
|
+
# start timer that will periodically send enquire link PDUs
|
59
|
+
start_enquire_link_timer(@config[:enquire_link_delay_secs]) if @config[:enquire_link_delay_secs]
|
60
|
+
rescue Exception => ex
|
61
|
+
logger.error "Error starting RX: #{ex.message} at #{ex.backtrace[0]}"
|
62
|
+
end
|
63
|
+
|
64
|
+
# sets up a periodic timer that will periodically enquire as to the
|
65
|
+
# state of the connection
|
66
|
+
# Note: to add in custom executable code (that only runs on an open
|
67
|
+
# connection), derive from the appropriate Smpp class and overload the
|
68
|
+
# method named: periodic_call_method
|
69
|
+
def start_enquire_link_timer(delay_secs)
|
70
|
+
logger.info "Starting enquire link timer (with #{delay_secs}s interval)"
|
71
|
+
EventMachine::PeriodicTimer.new(delay_secs) do
|
72
|
+
if error?
|
73
|
+
logger.warn "Link timer: Connection is in error state. Disconnecting."
|
74
|
+
close_connection
|
75
|
+
elsif unbound?
|
76
|
+
logger.warn "Link is unbound, waiting until next #{delay_secs} interval before querying again"
|
77
|
+
else
|
78
|
+
|
79
|
+
# if the user has defined a method to be called periodically, do
|
80
|
+
# it now - and continue if it indicates to do so
|
81
|
+
rval = defined?(periodic_call_method) ? periodic_call_method : true
|
82
|
+
|
83
|
+
# only send an OK if this worked
|
84
|
+
write_pdu Pdu::EnquireLink.new if rval
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# EventMachine::Connection#receive_data
|
90
|
+
def receive_data(data)
|
91
|
+
#append data to buffer
|
92
|
+
@data << data
|
93
|
+
|
94
|
+
while (@data.length >=4)
|
95
|
+
cmd_length = @data[0..3].unpack('N').first
|
96
|
+
if(@data.length < cmd_length)
|
97
|
+
#not complete packet ... break
|
98
|
+
break
|
99
|
+
end
|
100
|
+
|
101
|
+
pkt = @data.slice!(0,cmd_length)
|
102
|
+
|
103
|
+
begin
|
104
|
+
# parse incoming PDU
|
105
|
+
pdu = read_pdu(pkt)
|
106
|
+
|
107
|
+
# let subclass process it
|
108
|
+
process_pdu(pdu) if pdu
|
109
|
+
rescue Exception => e
|
110
|
+
logger.error "Error receiving data: #{e}\n#{e.backtrace.join("\n")}"
|
111
|
+
if @delegate.respond_to?(:data_error)
|
112
|
+
@delegate.data_error(e)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# EventMachine::Connection#unbind
|
120
|
+
# Invoked by EM when connection is closed. Delegates should consider
|
121
|
+
# breaking the event loop and reconnect when they receive this callback.
|
122
|
+
def unbind
|
123
|
+
if @delegate.respond_to?(:unbound)
|
124
|
+
@delegate.unbound(self)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def send_unbind
|
129
|
+
write_pdu Pdu::Unbind.new
|
130
|
+
@state = :unbound
|
131
|
+
end
|
132
|
+
|
133
|
+
# process common PDUs
|
134
|
+
# returns true if no further processing necessary
|
135
|
+
def process_pdu(pdu)
|
136
|
+
case pdu
|
137
|
+
when Pdu::EnquireLinkResponse
|
138
|
+
# nop
|
139
|
+
when Pdu::EnquireLink
|
140
|
+
write_pdu(Pdu::EnquireLinkResponse.new(pdu.sequence_number))
|
141
|
+
when Pdu::Unbind
|
142
|
+
@state = :unbound
|
143
|
+
write_pdu(Pdu::UnbindResponse.new(pdu.sequence_number, Pdu::Base::ESME_ROK))
|
144
|
+
close_connection
|
145
|
+
when Pdu::UnbindResponse
|
146
|
+
logger.info "Unbound OK. Closing connection."
|
147
|
+
close_connection
|
148
|
+
when Pdu::GenericNack
|
149
|
+
logger.warn "Received NACK! (error code #{pdu.error_code})."
|
150
|
+
# we don't take this lightly: close the connection
|
151
|
+
close_connection
|
152
|
+
when Pdu::DeliverSm
|
153
|
+
begin
|
154
|
+
logger.debug "ESM CLASS #{pdu.esm_class}"
|
155
|
+
if pdu.esm_class != 4
|
156
|
+
# MO message
|
157
|
+
if @delegate.respond_to?(:mo_received)
|
158
|
+
@delegate.mo_received(self, pdu)
|
159
|
+
end
|
160
|
+
else
|
161
|
+
# Delivery report
|
162
|
+
if @delegate.respond_to?(:delivery_report_received)
|
163
|
+
@delegate.delivery_report_received(self, pdu)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
write_pdu(Pdu::DeliverSmResponse.new(pdu.sequence_number))
|
167
|
+
rescue => e
|
168
|
+
logger.warn "Send Receiver Temporary App Error due to #{e.inspect} raised in delegate"
|
169
|
+
write_pdu(Pdu::DeliverSmResponse.new(pdu.sequence_number, Pdu::Base::ESME_RX_T_APPN))
|
170
|
+
end
|
171
|
+
when Pdu::BindTransceiverResponse
|
172
|
+
case pdu.command_status
|
173
|
+
when Pdu::Base::ESME_ROK
|
174
|
+
logger.debug "Bound OK."
|
175
|
+
@state = :bound
|
176
|
+
if @delegate.respond_to?(:bound)
|
177
|
+
@delegate.bound(self)
|
178
|
+
end
|
179
|
+
when Pdu::Base::ESME_RINVPASWD
|
180
|
+
logger.warn "Invalid password."
|
181
|
+
# scheduele the connection to close, which eventually will cause the unbound() delegate
|
182
|
+
# method to be invoked.
|
183
|
+
close_connection
|
184
|
+
when Pdu::Base::ESME_RINVSYSID
|
185
|
+
logger.warn "Invalid system id."
|
186
|
+
close_connection
|
187
|
+
else
|
188
|
+
logger.warn "Unexpected BindTransceiverResponse. Command status: #{pdu.command_status}"
|
189
|
+
close_connection
|
190
|
+
end
|
191
|
+
when Pdu::SubmitSmResponse
|
192
|
+
mt_message_id = @ack_ids.delete(pdu.sequence_number)
|
193
|
+
if !mt_message_id
|
194
|
+
raise "Got SubmitSmResponse for unknown sequence_number: #{pdu.sequence_number}"
|
195
|
+
end
|
196
|
+
if pdu.command_status != Pdu::Base::ESME_ROK
|
197
|
+
logger.error "Error status in SubmitSmResponse: #{pdu.command_status}"
|
198
|
+
if @delegate.respond_to?(:message_rejected)
|
199
|
+
@delegate.message_rejected(self, mt_message_id, pdu)
|
200
|
+
end
|
201
|
+
else
|
202
|
+
logger.info "Got OK SubmitSmResponse (#{pdu.message_id} -> #{mt_message_id})"
|
203
|
+
if @delegate.respond_to?(:message_accepted)
|
204
|
+
@delegate.message_accepted(self, mt_message_id, pdu)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
when Pdu::SubmitMultiResponse
|
208
|
+
mt_message_id = @ack_ids[pdu.sequence_number]
|
209
|
+
if !mt_message_id
|
210
|
+
raise "Got SubmitMultiResponse for unknown sequence_number: #{pdu.sequence_number}"
|
211
|
+
end
|
212
|
+
if pdu.command_status != Pdu::Base::ESME_ROK
|
213
|
+
logger.error "Error status in SubmitMultiResponse: #{pdu.command_status}"
|
214
|
+
if @delegate.respond_to?(:message_rejected)
|
215
|
+
@delegate.message_rejected(self, mt_message_id, pdu)
|
216
|
+
end
|
217
|
+
else
|
218
|
+
logger.info "Got OK SubmitMultiResponse (#{pdu.message_id} -> #{mt_message_id})"
|
219
|
+
if @delegate.respond_to?(:message_accepted)
|
220
|
+
@delegate.message_accepted(self, mt_message_id, pdu)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
when Pdu::BindReceiverResponse
|
224
|
+
case pdu.command_status
|
225
|
+
when Pdu::Base::ESME_ROK
|
226
|
+
logger.debug "Bound OK."
|
227
|
+
@state = :bound
|
228
|
+
if @delegate.respond_to?(:bound)
|
229
|
+
@delegate.bound(self)
|
230
|
+
end
|
231
|
+
when Pdu::Base::ESME_RINVPASWD
|
232
|
+
logger.warn "Invalid password."
|
233
|
+
# scheduele the connection to close, which eventually will cause the unbound() delegate
|
234
|
+
# method to be invoked.
|
235
|
+
close_connection
|
236
|
+
when Pdu::Base::ESME_RINVSYSID
|
237
|
+
logger.warn "Invalid system id."
|
238
|
+
close_connection
|
239
|
+
else
|
240
|
+
logger.warn "Unexpected BindReceiverResponse. Command status: #{pdu.command_status}"
|
241
|
+
close_connection
|
242
|
+
end
|
243
|
+
else
|
244
|
+
logger.warn "(#{self.class.name}) Received unexpected PDU: #{pdu.to_human}."
|
245
|
+
close_connection
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
private
|
250
|
+
def write_pdu(pdu)
|
251
|
+
logger.debug "<- #{pdu.to_human}"
|
252
|
+
hex_debug pdu.data, "<- "
|
253
|
+
send_data pdu.data
|
254
|
+
end
|
255
|
+
|
256
|
+
def read_pdu(data)
|
257
|
+
pdu = nil
|
258
|
+
# we may either receive a new request or a response to a previous response.
|
259
|
+
begin
|
260
|
+
pdu = Pdu::Base.create(data)
|
261
|
+
if !pdu
|
262
|
+
logger.warn "Not able to parse PDU!"
|
263
|
+
else
|
264
|
+
logger.debug "-> " + pdu.to_human
|
265
|
+
end
|
266
|
+
hex_debug data, "-> "
|
267
|
+
rescue Exception => ex
|
268
|
+
logger.error "Exception while reading PDUs: #{ex} in #{ex.backtrace[0]}"
|
269
|
+
raise
|
270
|
+
end
|
271
|
+
pdu
|
272
|
+
end
|
273
|
+
|
274
|
+
def hex_debug(data, prefix = "")
|
275
|
+
Base.hex_debug(data, prefix)
|
276
|
+
end
|
277
|
+
|
278
|
+
def Base.hex_debug(data, prefix = "")
|
279
|
+
logger.debug do
|
280
|
+
message = "Hex dump follows:\n"
|
281
|
+
hexdump(data).each_line do |line|
|
282
|
+
message << (prefix + line.chomp + "\n")
|
283
|
+
end
|
284
|
+
message
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def Base.hexdump(target)
|
289
|
+
width=16
|
290
|
+
group=2
|
291
|
+
|
292
|
+
output = ""
|
293
|
+
n=0
|
294
|
+
ascii=''
|
295
|
+
target.each_byte { |b|
|
296
|
+
if n%width == 0
|
297
|
+
output << "%s\n%08x: "%[ascii,n]
|
298
|
+
ascii='| '
|
299
|
+
end
|
300
|
+
output << "%02x"%b
|
301
|
+
output << ' ' if (n+=1)%group==0
|
302
|
+
ascii << "%s"%b.chr.tr('^ -~','.')
|
303
|
+
}
|
304
|
+
output << ' '*(((2+width-ascii.size)*(2*group+1))/group.to_f).ceil+ascii
|
305
|
+
output[1..-1]
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'iconv'
|
2
|
+
|
3
|
+
module Smpp
|
4
|
+
module Encoding
|
5
|
+
|
6
|
+
# This class is not required by smpp.rb at all, you need to bring it in yourself.
|
7
|
+
# This class also requires iconv, you'll need to ensure it is installed.
|
8
|
+
class Utf8Encoder
|
9
|
+
|
10
|
+
EURO_TOKEN = "_X_EURO_X_"
|
11
|
+
|
12
|
+
GSM_ESCAPED_CHARACTERS = {
|
13
|
+
?( => "\173", # {
|
14
|
+
?) => "\175", # }
|
15
|
+
184 => "\174", # |
|
16
|
+
?< => "\133", # [
|
17
|
+
?> => "\135", # ]
|
18
|
+
?= => "\176", # ~
|
19
|
+
?/ => "\134", # \
|
20
|
+
134 => "\252", # ^
|
21
|
+
?e => EURO_TOKEN
|
22
|
+
}
|
23
|
+
|
24
|
+
def encode(data_coding, short_message)
|
25
|
+
if data_coding < 2
|
26
|
+
sm = short_message.gsub(/\215./) { |match| GSM_ESCAPED_CHARACTERS[match[1]] }
|
27
|
+
sm = Iconv.conv("UTF-8", "HP-ROMAN8", sm)
|
28
|
+
sm.gsub(EURO_TOKEN, "\342\202\254")
|
29
|
+
elsif data_coding == 8
|
30
|
+
Iconv.conv("UTF-8", "UTF-16BE", short_message)
|
31
|
+
else
|
32
|
+
short_message
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|