ruby_ucp 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +7 -5
- data/README +59 -4
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/ruby_ucp.rb +36 -10
- data/lib/samples/main.rb +8 -8
- data/lib/ucp/pdu/ucp51_result.rb +1 -1
- data/lib/ucp/pdu/ucp60.rb +2 -2
- data/lib/ucp/pdu/ucp60_operation.rb +1 -1
- data/lib/ucp/pdu/ucp61.rb +2 -2
- data/lib/ucp/util/auth_request.rb +32 -0
- data/lib/ucp/util/ucp.rb +30 -142
- data/lib/ucp/util/ucp_server.rb +23 -11
- metadata +5 -4
data/CHANGELOG
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
============ RELEASED ==============
|
2
2
|
|
3
|
-
= 0.
|
3
|
+
= 0.2.0 (2011-02-06)
|
4
|
+
* miscelaneous cleanup
|
5
|
+
* basic documentation of methods in Ucp::Util::UCP
|
6
|
+
* fixed issues #1, #2, #3, #4, #6, #7
|
7
|
+
* added authentication handler/callback in Ucp::Util::UcpServer
|
8
|
+
|
9
|
+
= 0.1.0 (2011-01-16)
|
4
10
|
* first public version of "ruby_ucp" gem
|
5
11
|
* multiple PDUs support (operations 30,31,51,52,53,54,55,56,57,58,60,61)
|
6
12
|
* added a class to implement an UCP server (e.g. for simulating a SMSC)
|
@@ -36,7 +42,3 @@
|
|
36
42
|
|
37
43
|
= 0.3.0
|
38
44
|
* methods documentation
|
39
|
-
|
40
|
-
= 0.2.0
|
41
|
-
* miscelaneous cleanup
|
42
|
-
|
data/README
CHANGED
@@ -2,9 +2,64 @@
|
|
2
2
|
|
3
3
|
A pure Ruby implementation of the EMI/UCP protocol v4.6 that is used when communicating
|
4
4
|
with a SMSC.
|
5
|
-
|
6
|
-
|
5
|
+
Please consult up-to-date information in ruby_ucp's wiki: https://github.com/bitcoder/ruby_ucp/wiki.
|
6
|
+
Official releases can be obtained directly from RubyGems: https://rubygems.org/gems/ruby_ucp.
|
7
|
+
The following information may not be up-to-date; it is here just for quick access. See the wiki site.
|
7
8
|
|
8
|
-
|
9
|
+
Sergio Freire <sergio.freire at gmail.com>
|
10
|
+
|
11
|
+
|
12
|
+
Introduction
|
13
|
+
============
|
14
|
+
|
15
|
+
About “ruby_ucp”
|
16
|
+
|
17
|
+
“ruby_ucp” is a pure Ruby library implementation of the EMI/UCP v.4.6 specification/protocol used by Large Accounts (e.g. applications) to communicate with SMSC’s, in order to send and receive short messages (“SMS”).
|
18
|
+
It is licensed under LGPL v2.1, so feel free to use it and contribute with your code or feedback in order to improve it.
|
19
|
+
The main focus of this library is the correct encoding/decoding of UCP PDUs. By combining low-level with helper methods, the user has full control of the built PDUs, all with ease.
|
20
|
+
Besides this, two classes are given: one for an UCP client and another for an UCP server. These classes are not perfect (e.g. they dont implement states) but may be used in most scenarios, specially the client, and serve as inspiration to build other ones.
|
21
|
+
|
22
|
+
The gem is available for download directly from the RubyGems repository, and is named ruby_ucp.
|
23
|
+
The UCP specification itself: EMI/UCP v4.6.
|
24
|
+
Some local info about UCP PDUs: https://github.com/bitcoder/ruby_ucp/wiki/UCP-PDUs.
|
25
|
+
Some local info on SMS alphabets/encodings: https://github.com/bitcoder/ruby_ucp/wiki/SMS-Alphabets.
|
26
|
+
|
27
|
+
Features
|
28
|
+
========
|
29
|
+
|
30
|
+
Currently, it supports:
|
31
|
+
|
32
|
+
- encoding/decoding most UCP pdu’s: 01,30,31,51,52,53,54,55,56,57,58,60 and 61
|
33
|
+
- direct access to UCP pdu fields
|
34
|
+
- automatic checksum/length calculation
|
35
|
+
- instantiate an UCP pdu object from a given string
|
36
|
+
- helper methods within pdu’s, to easily build “submit messages” (AO)
|
37
|
+
- support alphanumeric originators
|
38
|
+
- en/decoding of 7bit GSM default alphabet text messages for basic text encoding
|
39
|
+
- en/decoding of 16bit (UCS2) messages for full character support
|
40
|
+
- simple UCP client (MULA), with authentication support
|
41
|
+
- simple UCP server (e.g. for simulating a SMSC or a LA application working in UCP SILA mode)
|
42
|
+
- automatic create multiple concatenated messages, for a given message text
|
43
|
+
- extern, smarter and more correct UCP clients/servers may be implemented using just the UCP pdu encoding/decoding features
|
44
|
+
|
45
|
+
Soon:
|
46
|
+
|
47
|
+
- automatic text encoding selection (7bit vs 16bit), for optimum concatenated message creation (reduce the number of SM parts; use only UCS-2 if necessary). (now 7bit is forced in make_multi_ucps() method)
|
48
|
+
|
49
|
+
|
50
|
+
Not so good features :)
|
51
|
+
========================
|
52
|
+
|
53
|
+
- the library is thought for correctness, not performance (you should not find a problem though)
|
54
|
+
- the provided UCP client is synchronous; it does not have a state machine in order to handle UCP windowing features
|
55
|
+
- the provided UCP server is also synchronous; it does not support UCP windowing
|
56
|
+
- no Ruby v1.9.x support (it would require rewriting sensible parts due to encoding issues and string handling)
|
57
|
+
- unitary tests unspecified, yet
|
58
|
+
- documentation lacking, yet
|
59
|
+
|
60
|
+
Requirements
|
61
|
+
============
|
62
|
+
|
63
|
+
Ruby v1.8.6 or v.1.8.7. No v1.9.x support!
|
64
|
+
(tested with Ubuntu 9.10, Redhat AS 5.x)
|
9
65
|
|
10
|
-
Sergio Freire
|
data/Rakefile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/ruby_ucp.rb
CHANGED
@@ -21,22 +21,48 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
21
21
|
$:.unshift(File.dirname(__FILE__))
|
22
22
|
|
23
23
|
require "ucp/base.rb"
|
24
|
+
require "ucp/pdu/base.rb"
|
24
25
|
require "ucp/pdu/ucpmessage.rb"
|
26
|
+
require "ucp/pdu/ucp_operation.rb"
|
27
|
+
require "ucp/pdu/ucp_result.rb"
|
25
28
|
require "ucp/pdu/ucp01.rb"
|
29
|
+
require "ucp/pdu/ucp01_operation.rb"
|
30
|
+
require "ucp/pdu/ucp01_result.rb"
|
26
31
|
require "ucp/pdu/ucp30.rb"
|
32
|
+
require "ucp/pdu/ucp30_operation.rb"
|
33
|
+
require "ucp/pdu/ucp30_result.rb"
|
27
34
|
require "ucp/pdu/ucp31.rb"
|
35
|
+
require "ucp/pdu/ucp31_operation.rb"
|
36
|
+
require "ucp/pdu/ucp31_result.rb"
|
28
37
|
require "ucp/pdu/ucp5x.rb"
|
38
|
+
require "ucp/pdu/ucp51_result.rb"
|
39
|
+
require "ucp/pdu/ucp51_operation.rb"
|
40
|
+
require "ucp/pdu/ucp52_operation.rb"
|
41
|
+
require "ucp/pdu/ucp52_result.rb"
|
42
|
+
require "ucp/pdu/ucp53_operation.rb"
|
43
|
+
require "ucp/pdu/ucp53_result.rb"
|
44
|
+
require "ucp/pdu/ucp54_operation.rb"
|
45
|
+
require "ucp/pdu/ucp54_result.rb"
|
46
|
+
require "ucp/pdu/ucp55_operation.rb"
|
47
|
+
require "ucp/pdu/ucp55_result.rb"
|
48
|
+
require "ucp/pdu/ucp56_operation.rb"
|
49
|
+
require "ucp/pdu/ucp56_result.rb"
|
50
|
+
require "ucp/pdu/ucp57_operation.rb"
|
51
|
+
require "ucp/pdu/ucp57_result.rb"
|
52
|
+
require "ucp/pdu/ucp58_operation.rb"
|
53
|
+
require "ucp/pdu/ucp58_result.rb"
|
29
54
|
require "ucp/pdu/ucp60.rb"
|
55
|
+
require "ucp/pdu/ucp60_operation.rb"
|
56
|
+
require "ucp/pdu/ucp60_result.rb"
|
30
57
|
require "ucp/pdu/ucp61.rb"
|
58
|
+
require "ucp/pdu/ucp61_result.rb"
|
59
|
+
require "ucp/pdu/ucp61_operation.rb"
|
31
60
|
require "ucp/util/base.rb"
|
61
|
+
require "ucp/util/packed_msg.rb"
|
62
|
+
require "ucp/util/gsm_packed_msg.rb"
|
63
|
+
require "ucp/util/ucp_client.rb"
|
64
|
+
require "ucp/util/sms_request.rb"
|
65
|
+
require "ucp/util/ucp.rb"
|
66
|
+
require "ucp/util/ucp_server.rb"
|
67
|
+
require "ucp/util/ucs2_packed_msg.rb"
|
32
68
|
|
33
|
-
|
34
|
-
# Load all YCP PDUs
|
35
|
-
Dir.glob(File.join(File.dirname(__FILE__), 'ucp', 'pdu', '*.rb')) do |f|
|
36
|
-
require f unless f.match('ucpmessage.rb$')
|
37
|
-
end
|
38
|
-
|
39
|
-
# Load all auxiliary classes
|
40
|
-
Dir.glob(File.join(File.dirname(__FILE__), 'ucp', 'util', '*.rb')) do |f|
|
41
|
-
require f unless f.match('base.rb$')
|
42
|
-
end
|
data/lib/samples/main.rb
CHANGED
@@ -35,6 +35,12 @@ def handle_me(smsreq)
|
|
35
35
|
puts "handle_me [#{smsreq.source_ip}:#{smsreq.source_port}] (#{smsreq.originator},#{smsreq.recipient}) (#{smsreq.part_nr}/#{smsreq.total_parts}): #{smsreq.text}\n"
|
36
36
|
end
|
37
37
|
|
38
|
+
def auth_handler(authreq)
|
39
|
+
puts "auth_handler [#{authreq.source_ip}:#{authreq.source_port}] (#{authreq.account},#{authreq.password})\n"
|
40
|
+
return true
|
41
|
+
end
|
42
|
+
|
43
|
+
|
38
44
|
#ucp=UCP.parse_str("*00/00113/O/51/961234567/1234/////////////////4/120/B49AED86CBC162B219AD66BBE17230//////////0106050003450202///90#")
|
39
45
|
#puts "UCP: #{ucp.to_s}"
|
40
46
|
#exit
|
@@ -89,7 +95,7 @@ puts ucp.to_s
|
|
89
95
|
|
90
96
|
port=12009
|
91
97
|
server=UcpServer.new(method(:handle_me),port)
|
92
|
-
|
98
|
+
server.set_authentication_handler(method(:auth_handler))
|
93
99
|
|
94
100
|
cliente=UcpClient.new("localhost",port,{:login=>8006,:password=>"timor"})
|
95
101
|
#sent=cliente.send_message(1234,961234567,"1234567890"*17+"ola custa 1€ sabias?")
|
@@ -116,14 +122,8 @@ puts ucp.to_s
|
|
116
122
|
|
117
123
|
|
118
124
|
puts UCP.pack7bits("€")
|
119
|
-
#puts UCP.packucs2("ola")
|
120
125
|
puts UCP.decode7bitgsm(UCP.pack7bits("1234567890"*17))
|
121
|
-
|
122
|
-
puts "septets: #{gsmparts.required_septets} ; unenc: #{gsmparts.unencoded}"
|
123
|
-
gsmparts= UCP.multi_pack7bits2("1234567890"*17,134)
|
124
|
-
gsmparts.each { |gsmpart|
|
125
|
-
puts "part: #{gsmpart.unencoded}"
|
126
|
-
}
|
126
|
+
|
127
127
|
|
128
128
|
UCP.make_multi_ucps(1235,961234568,"1234567890"*17)
|
129
129
|
|
data/lib/ucp/pdu/ucp51_result.rb
CHANGED
data/lib/ucp/pdu/ucp60.rb
CHANGED
@@ -18,11 +18,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
18
18
|
=end
|
19
19
|
|
20
20
|
|
21
|
-
class Ucp::Pdu::
|
21
|
+
class Ucp::Pdu::UCP60 < Ucp::Pdu::UCPMessage
|
22
22
|
|
23
23
|
def initialize()
|
24
24
|
super()
|
25
|
-
@operation="
|
25
|
+
@operation="60"
|
26
26
|
end
|
27
27
|
|
28
28
|
|
@@ -51,7 +51,7 @@ class Ucp::Pdu::Ucp60Operation < Ucp::Pdu::UCP60
|
|
51
51
|
#super()
|
52
52
|
#@operation_type="O"
|
53
53
|
|
54
|
-
@h={:oadc=>originator, :pwd=>UCP.ascii2ira(password), :vers=>"0100", :styp=>"1", :oton=>"6",:onpi=>"5"}
|
54
|
+
@h={:oadc=>originator, :pwd=>UCP.ascii2ira(password).encoded, :vers=>"0100", :styp=>"1", :oton=>"6",:onpi=>"5"}
|
55
55
|
@h=@h.merge ucpfields
|
56
56
|
end
|
57
57
|
|
data/lib/ucp/pdu/ucp61.rb
CHANGED
@@ -19,11 +19,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
19
19
|
|
20
20
|
|
21
21
|
|
22
|
-
class Ucp::Pdu::
|
22
|
+
class Ucp::Pdu::UCP61 < Ucp::Pdu::UCPMessage
|
23
23
|
|
24
24
|
def initialize()
|
25
25
|
super()
|
26
|
-
@operation="
|
26
|
+
@operation="61"
|
27
27
|
end
|
28
28
|
|
29
29
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
=begin
|
2
|
+
Ruby library implementation of EMI/UCP protocol v4.6 for SMS
|
3
|
+
Copyright (C) 2011, Sergio Freire <sergio.freire@gmail.com>
|
4
|
+
|
5
|
+
This library is free software; you can redistribute it and/or
|
6
|
+
modify it under the terms of the GNU Lesser General Public
|
7
|
+
License as published by the Free Software Foundation; either
|
8
|
+
version 2.1 of the License, or (at your option) any later version.
|
9
|
+
|
10
|
+
This library is distributed in the hope that it will be useful,
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
13
|
+
Lesser General Public License for more details.
|
14
|
+
|
15
|
+
You should have received a copy of the GNU Lesser General Public
|
16
|
+
License along with this library; if not, write to the Free Software
|
17
|
+
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
18
|
+
=end
|
19
|
+
|
20
|
+
|
21
|
+
class Ucp::Util::AuthRequest
|
22
|
+
|
23
|
+
attr_reader :source_ip, :source_port, :account, :password
|
24
|
+
|
25
|
+
def initialize(account,password,source_ip=nil,source_port=nil)
|
26
|
+
@source_ip=source_ip
|
27
|
+
@source_port=source_port
|
28
|
+
@account=account
|
29
|
+
@password=password
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/lib/ucp/util/ucp.rb
CHANGED
@@ -32,18 +32,21 @@ class Ucp::Util::UCP
|
|
32
32
|
@extensiontable={}
|
33
33
|
@extensiontable_rev={}
|
34
34
|
|
35
|
+
# add a character mapping to the 7bit GSM default alphabet
|
35
36
|
:private
|
36
37
|
def self.add_char(value,char)
|
37
38
|
@gsmtable[char]=value
|
38
39
|
@asciitable[value]=char
|
39
40
|
end
|
40
41
|
|
42
|
+
# add an extesion character to the 7bit GSM default alphabet
|
41
43
|
:private
|
42
44
|
def self.add_extchar(value,char)
|
43
45
|
@extensiontable[char]=value
|
44
46
|
@extensiontable_rev[value]=char
|
45
47
|
end
|
46
48
|
|
49
|
+
# build/initialize the GSM default alphabet mapping tables
|
47
50
|
def self.initialize_ascii2ira
|
48
51
|
|
49
52
|
('A'..'Z').each { |c|
|
@@ -104,10 +107,6 @@ class Ucp::Util::UCP
|
|
104
107
|
add_char(0x3E,">")
|
105
108
|
add_char(0x3F,"?")
|
106
109
|
|
107
|
-
|
108
|
-
|
109
|
-
#@extensiontable["€"]=0x65
|
110
|
-
#@extensiontable_rev[0x65]="€"
|
111
110
|
add_extchar(0x65,"€")
|
112
111
|
add_extchar(0x14,"^")
|
113
112
|
add_extchar(0x28,"{")
|
@@ -117,13 +116,11 @@ class Ucp::Util::UCP
|
|
117
116
|
add_extchar(0x3D,"~")
|
118
117
|
add_extchar(0x3E,"]")
|
119
118
|
add_extchar(0x40,"|")
|
120
|
-
|
121
|
-
|
122
|
-
|
123
119
|
end
|
124
120
|
|
121
|
+
# pack a given text string in 7bit GSM default alphabet
|
122
|
+
# return it as an hexadecimal string
|
125
123
|
def self.pack7bits(str)
|
126
|
-
#UCP.initialize_ascii2ira
|
127
124
|
|
128
125
|
s=""
|
129
126
|
str.each_char { |c|
|
@@ -178,105 +175,9 @@ class Ucp::Util::UCP
|
|
178
175
|
i-=8
|
179
176
|
end
|
180
177
|
|
181
|
-
|
182
|
-
|
183
178
|
return hexstr
|
184
179
|
end
|
185
180
|
|
186
|
-
|
187
|
-
def self.pack7bits2(str,max_bytes)
|
188
|
-
|
189
|
-
tainted=false
|
190
|
-
s=""
|
191
|
-
idx=0
|
192
|
-
str.each_char { |c|
|
193
|
-
|
194
|
-
ext=""
|
195
|
-
gsmchar=@gsmtable[c]
|
196
|
-
|
197
|
-
if gsmchar.nil?
|
198
|
-
if @extensiontable.has_key?(c)
|
199
|
-
ext="0011011" # 1B
|
200
|
-
gsmchar=@extensiontable[c]
|
201
|
-
else
|
202
|
-
gsmchar=@gsmtable[" "]
|
203
|
-
tainted=true
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
#gsmchar=c[0]
|
208
|
-
#puts "#{c} : #{gsmchar} xxx"
|
209
|
-
#gsmchar=@gsmtable[" "] if gsmchar.nil?
|
210
|
-
|
211
|
-
tmp= gsmchar.to_s(2)
|
212
|
-
|
213
|
-
remainder=tmp.length%7
|
214
|
-
if remainder!=0
|
215
|
-
nfillbits=7-remainder
|
216
|
-
tmp="0"*nfillbits+tmp
|
217
|
-
end
|
218
|
-
|
219
|
-
# if adding this character exceeds the max allowed nr of bytes, break
|
220
|
-
if ((tmp+ext+s).length*1.0/8).ceil>max_bytes
|
221
|
-
break
|
222
|
-
end
|
223
|
-
|
224
|
-
s=tmp+ext+s
|
225
|
-
|
226
|
-
# if reached the max allowed nr of bytes, break
|
227
|
-
if (s.length*1.0/8).ceil==max_bytes
|
228
|
-
idx+=1
|
229
|
-
break
|
230
|
-
end
|
231
|
-
|
232
|
-
idx+=1
|
233
|
-
}
|
234
|
-
|
235
|
-
|
236
|
-
required_septets=s.length/7
|
237
|
-
remainder=s.length%8
|
238
|
-
if remainder!=0
|
239
|
-
nfillbits=8-remainder
|
240
|
-
s="0"*nfillbits+s
|
241
|
-
end
|
242
|
-
|
243
|
-
#puts "S: #{s}"
|
244
|
-
|
245
|
-
i=s.length-8
|
246
|
-
hexstr=""
|
247
|
-
while i>=0
|
248
|
-
c=s[i,8]
|
249
|
-
|
250
|
-
tmp=c.to_i(2).to_s(16).upcase
|
251
|
-
if tmp.length==1
|
252
|
-
tmp="0"+tmp
|
253
|
-
end
|
254
|
-
# puts tmp
|
255
|
-
hexstr+=tmp
|
256
|
-
i-=8
|
257
|
-
end
|
258
|
-
|
259
|
-
|
260
|
-
return GsmPackedMsg.new(hexstr,str[0,idx],idx,required_septets,tainted)
|
261
|
-
end
|
262
|
-
|
263
|
-
# @deprecated remove because unecessary
|
264
|
-
def self.multi_pack7bits(str,max_bytes)
|
265
|
-
msgparts=[]
|
266
|
-
idx=0
|
267
|
-
while true
|
268
|
-
packedmsg=UCP.pack7bits2(str[idx..-1],max_bytes)
|
269
|
-
msgparts<<packedmsg
|
270
|
-
if idx+packedmsg.chars<str.length
|
271
|
-
idx+=packedmsg.chars
|
272
|
-
else
|
273
|
-
break
|
274
|
-
end
|
275
|
-
end
|
276
|
-
return msgparts
|
277
|
-
end
|
278
|
-
|
279
|
-
|
280
181
|
# convert standard string to IRA encoded hexstring
|
281
182
|
def self.ascii2ira(str,max_bytes=nil)
|
282
183
|
|
@@ -323,7 +224,8 @@ class Ucp::Util::UCP
|
|
323
224
|
return GsmPackedMsg.new(s,UCP.utf8_substr(str,0,idx-1),idx,required_septets,tainted)
|
324
225
|
end
|
325
226
|
|
326
|
-
|
227
|
+
# a dirty UTF-8 substring implementation
|
228
|
+
# returns a substring of an UTF-8 encoded string, given the string, start end end characters
|
327
229
|
def self.utf8_substr(str,idxs,idxe=nil)
|
328
230
|
s=""
|
329
231
|
i=0
|
@@ -335,6 +237,8 @@ class Ucp::Util::UCP
|
|
335
237
|
return s
|
336
238
|
end
|
337
239
|
|
240
|
+
# given a text, automatically split it if necessary and encode each part text in (IRA5) GSM default alphabet
|
241
|
+
# returns an array of IRA5 hexadecimal strings, one per part
|
338
242
|
def self.multi_ascii2ira(str,max_bytes)
|
339
243
|
msgparts=[]
|
340
244
|
idx=0
|
@@ -368,7 +272,8 @@ class Ucp::Util::UCP
|
|
368
272
|
return msgparts
|
369
273
|
end
|
370
274
|
|
371
|
-
|
275
|
+
# convert a given UTF-8 string to "UCS-2"
|
276
|
+
# returns an object representing the UCS-2 packed message
|
372
277
|
def self.str2ucs2(str,max_bytes=nil)
|
373
278
|
hexstr=""
|
374
279
|
str=Iconv.iconv("utf-16be", "utf-8", str).first
|
@@ -384,6 +289,8 @@ class Ucp::Util::UCP
|
|
384
289
|
return Ucs2PackedMsg.new(hexstr,UCP.utf8_substr(str,0,i-1),i/2,i)
|
385
290
|
end
|
386
291
|
|
292
|
+
# given a text, automatically split it if necessary and encode each part text in UCS-2
|
293
|
+
# returns an array of UCS-2 hexadecimal strings, one per part
|
387
294
|
def self.multi_ucs2(str,max_bytes)
|
388
295
|
msgparts=[]
|
389
296
|
idx=0
|
@@ -415,8 +322,9 @@ class Ucp::Util::UCP
|
|
415
322
|
return msgparts
|
416
323
|
end
|
417
324
|
|
418
|
-
|
419
|
-
|
325
|
+
# automatically build the necessary UCP pdu's in order to encode a given submit message
|
326
|
+
# returns an array of UCP51 pdu's
|
327
|
+
# (for now, it does NOT select automatically the SM encoding; it forces/assumes 7bit GSM)
|
420
328
|
def self.make_multi_ucps(originator,recipient,message,mr=0)
|
421
329
|
ucps=[]
|
422
330
|
|
@@ -459,25 +367,8 @@ class Ucp::Util::UCP
|
|
459
367
|
return ucps
|
460
368
|
end
|
461
369
|
|
462
|
-
|
463
|
-
|
464
|
-
def self.packucs2(str)
|
465
|
-
hexstr=""
|
466
|
-
s= Iconv.iconv("ucs-2be", "utf-8", str).first
|
467
|
-
s.each_char{ |c|
|
468
|
-
|
469
|
-
tmp=c[0].to_s(16).upcase
|
470
|
-
if tmp.length==1
|
471
|
-
tmp="0"+tmp
|
472
|
-
end
|
473
|
-
|
474
|
-
hexstr+=tmp
|
475
|
-
|
476
|
-
}
|
477
|
-
#puts s
|
478
|
-
return hexstr
|
479
|
-
end
|
480
|
-
|
370
|
+
# return an encoded originator alphanumeric address
|
371
|
+
# to be used in the "oadc" field, if alphanumeric
|
481
372
|
def self.packoadc(oa)
|
482
373
|
packedoa=UCP.pack7bits(oa)
|
483
374
|
|
@@ -493,6 +384,7 @@ class Ucp::Util::UCP
|
|
493
384
|
return tmp+packedoa
|
494
385
|
end
|
495
386
|
|
387
|
+
# convert an hexadecimal string to a binary string
|
496
388
|
def self.hextobin(hstr)
|
497
389
|
bstr=""
|
498
390
|
i=0
|
@@ -528,12 +420,8 @@ class Ucp::Util::UCP
|
|
528
420
|
return bstr
|
529
421
|
end
|
530
422
|
|
531
|
-
|
532
|
-
def self.decode7bitgsm(str)
|
533
|
-
|
534
|
-
# retirar isto!!
|
535
|
-
#initialize_ascii2ira
|
536
|
-
|
423
|
+
# decode a 7bit packed GSM default alphabet hexstring
|
424
|
+
def self.decode7bitgsm(str)
|
537
425
|
unencoded=""
|
538
426
|
#puts "#{str}"
|
539
427
|
bstr=UCP.hextobin_reversed(str)
|
@@ -582,7 +470,7 @@ class Ucp::Util::UCP
|
|
582
470
|
end
|
583
471
|
|
584
472
|
|
585
|
-
|
473
|
+
# decode an IRA5 hexadecimal represented string to string
|
586
474
|
def self.decode_ira(str)
|
587
475
|
unencoded=""
|
588
476
|
|
@@ -611,6 +499,7 @@ class Ucp::Util::UCP
|
|
611
499
|
|
612
500
|
|
613
501
|
|
502
|
+
# convert an integer to an hex string, two (or given) nibbles wide
|
614
503
|
def self.int2hex(i,max_nibbles=2)
|
615
504
|
tmp=i.to_s(16).upcase
|
616
505
|
if tmp.length%2!=0
|
@@ -627,7 +516,7 @@ class Ucp::Util::UCP
|
|
627
516
|
|
628
517
|
|
629
518
|
|
630
|
-
#
|
519
|
+
# given a UCP pdu string, return a UCP pdu object
|
631
520
|
def self.parse_str(ustr)
|
632
521
|
#puts "parse_str(#{ustr})"
|
633
522
|
|
@@ -759,21 +648,17 @@ class Ucp::Util::UCP
|
|
759
648
|
|
760
649
|
|
761
650
|
|
762
|
-
#
|
651
|
+
# given an UCP pdu object, build a corresponding result pdu
|
763
652
|
def self.make_ucp_result(ucp)
|
764
|
-
puts "make_ucp_result(#{ucp.to_s})"
|
653
|
+
#puts "make_ucp_result(#{ucp.to_s})"
|
765
654
|
|
766
655
|
if ucp.nil?
|
767
656
|
return nil
|
768
657
|
end
|
769
658
|
|
770
659
|
trn=ucp.trn
|
771
|
-
#puts "#{trn}"
|
772
|
-
# length
|
773
660
|
operation_type=ucp.operation_type
|
774
|
-
#puts "#{operation_type}"
|
775
661
|
operation=ucp.operation
|
776
|
-
#puts "#{operation}"
|
777
662
|
|
778
663
|
ucpmsg=nil
|
779
664
|
|
@@ -811,12 +696,14 @@ class Ucp::Util::UCP
|
|
811
696
|
return ucpmsg
|
812
697
|
end
|
813
698
|
|
699
|
+
# convert hexadecimal represent string to string, assuming a byte per character
|
814
700
|
def self.hex2str(hexstr)
|
815
701
|
str=""
|
816
702
|
hexstr.scan(/../).each { | tuple | str+=tuple.hex.chr }
|
817
703
|
return str
|
818
704
|
end
|
819
705
|
|
706
|
+
# given an UCP pdu object, decode message field
|
820
707
|
def self.decode_ucp_msg(ucp)
|
821
708
|
text=nil
|
822
709
|
dcs=ucp.dcs.to_i(16)
|
@@ -828,7 +715,7 @@ class Ucp::Util::UCP
|
|
828
715
|
else
|
829
716
|
|
830
717
|
if ucp.operation.eql?("51") || ucp.operation.eql?("52") || ucp.operation.eql?("53")
|
831
|
-
mt=ucp.get_field(:
|
718
|
+
mt=ucp.get_field(:mt)
|
832
719
|
if mt.eql?("2")
|
833
720
|
# numeric message.. return it as it is
|
834
721
|
text=ucp.get_field(:msg)
|
@@ -857,6 +744,7 @@ class Ucp::Util::UCP
|
|
857
744
|
return text
|
858
745
|
end
|
859
746
|
|
747
|
+
# given an UCP pdu object, decode the originator address
|
860
748
|
def self.decode_ucp_oadc(ucp)
|
861
749
|
otoa=nil
|
862
750
|
oadc=ucp.get_field(:oadc)
|
data/lib/ucp/util/ucp_server.rb
CHANGED
@@ -19,16 +19,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
19
19
|
|
20
20
|
require 'socket'
|
21
21
|
|
22
|
-
class ClientQuitError < RuntimeError; end
|
23
|
-
|
24
|
-
#class UcpServer
|
25
|
-
|
26
22
|
include Ucp::Pdu
|
27
23
|
include Ucp::Util
|
28
24
|
|
29
25
|
class Ucp::Util::UcpServer
|
30
26
|
|
31
27
|
@server=nil
|
28
|
+
@auth_handler=nil
|
32
29
|
|
33
30
|
def initialize(handler,port,host=nil)
|
34
31
|
|
@@ -69,7 +66,6 @@ class Ucp::Util::UcpServer
|
|
69
66
|
|
70
67
|
begin
|
71
68
|
while line = s.gets(3.chr) # read a line at a time
|
72
|
-
raise ClientQuitError if line =~ /^die\r?$/
|
73
69
|
|
74
70
|
#puts "#{addr} [#{Time.now}]: #{line}"
|
75
71
|
|
@@ -83,23 +79,37 @@ class Ucp::Util::UcpServer
|
|
83
79
|
smsreq=SmsRequest.new(UCP.decode_ucp_oadc(ucp),ucp.get_field(:adc),text,account,addr,port)
|
84
80
|
smsreq.set_parts_info(ucp.message_ref, ucp.part_nr, ucp.total_parts)
|
85
81
|
handler.call(smsreq)
|
82
|
+
elsif ucp.operation.eql?("60")
|
83
|
+
if !@auth_handler.nil?
|
84
|
+
authreq=AuthRequest.new(ucp.get_field(:oadc),UCP.decode_ira(ucp.get_field(:pwd)),addr,port)
|
85
|
+
auth_result=@auth_handler.call(authreq)
|
86
|
+
if !auth_result
|
87
|
+
reply_ucp=UCP.make_ucp_result(ucp)
|
88
|
+
reply_ucp.nack("01","authentication failed")
|
89
|
+
#puts "reply #{reply_ucp.to_s}"
|
90
|
+
s.print reply_ucp.to_s
|
91
|
+
puts "Ssent: #{reply_ucp.to_s}\n"
|
92
|
+
puts "*** #{name}:#{port} forced disconnected"
|
93
|
+
s.close # close socket
|
94
|
+
break
|
95
|
+
else
|
96
|
+
account=authreq.account
|
97
|
+
end
|
98
|
+
end
|
86
99
|
end
|
87
100
|
|
88
101
|
reply_ucp=UCP.make_ucp_result(ucp)
|
89
|
-
#puts "bbbbbbbbbbbbb1"
|
90
102
|
reply_ucp.ack("ok")
|
91
|
-
|
103
|
+
|
92
104
|
#puts "reply #{reply_ucp.to_s}"
|
93
105
|
s.print reply_ucp.to_s
|
94
106
|
puts "Ssent: #{reply_ucp.to_s}\n"
|
95
107
|
end
|
96
|
-
rescue ClientQuitError
|
97
|
-
puts "*** #{name}:#{port} disconnected"
|
98
108
|
ensure
|
109
|
+
puts "*** #{name}:#{port} disconnected/closed"
|
99
110
|
s.close # close socket on error
|
100
111
|
end
|
101
112
|
|
102
|
-
puts "*** done with #{name}:#{port}"
|
103
113
|
# end Thread
|
104
114
|
end
|
105
115
|
|
@@ -117,6 +127,8 @@ class Ucp::Util::UcpServer
|
|
117
127
|
@server.close
|
118
128
|
end
|
119
129
|
|
120
|
-
|
130
|
+
def set_authentication_handler(handler)
|
131
|
+
@auth_handler=handler
|
132
|
+
end
|
121
133
|
|
122
134
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby_ucp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Sergio Freire
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-
|
18
|
+
date: 2011-02-06 00:00:00 +00:00
|
19
19
|
default_executable:
|
20
20
|
dependencies: []
|
21
21
|
|
@@ -74,6 +74,7 @@ files:
|
|
74
74
|
- lib/ucp/pdu/ucp_operation.rb
|
75
75
|
- lib/ucp/pdu/ucp_result.rb
|
76
76
|
- lib/ucp/pdu/ucpmessage.rb
|
77
|
+
- lib/ucp/util/auth_request.rb
|
77
78
|
- lib/ucp/util/base.rb
|
78
79
|
- lib/ucp/util/gsm_packed_msg.rb
|
79
80
|
- lib/ucp/util/packed_msg.rb
|