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,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
|