pdu_tools 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d1b59db9f41bc1d37660c6af01c3d7fda3cc2800
4
+ data.tar.gz: 64d571e2f9da156852825a43a967171fc107eae7
5
+ SHA512:
6
+ metadata.gz: 4b8ec02cc0b4c1fb1eedc474877812cdce0906d61233f1ed5bae8bd47b9943e82f1bd7c9071b4158f33bfe8d12c97a54c101db40926003e102ff5e6dde2b687b
7
+ data.tar.gz: c337b899ae90c7435a1ff38696861ecdf99fda21db98e8aa7e0c90fbd1133c55dad0777fabb47beef86951c5de0ea854efd6380f3daa8f0080af532532337bd4
data/lib/pdu_tools.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'phone'
2
+ require 'active_support/all'
3
+
4
+ require_relative './pdu_tools/helpers'
5
+ require_relative './pdu_tools/pdu'
6
+ require_relative './pdu_tools/message_part'
7
+ require_relative './pdu_tools/decoder'
8
+ require_relative './pdu_tools/encoder'
9
+
10
+ module PDUTools
11
+ ALPHABETS = {
12
+ a7bit: '00',
13
+ a8bit: '01',
14
+ a16bit: '10'
15
+ }
16
+
17
+ MAX_MESSAGE_LENGTH = 39015
18
+ MAX_GSM_MESSAGE_7BIT_PART_LENGTH = 152
19
+ MAX_GSM_MESSAGE_16BIT_PART_LENGTH = 66
20
+ end
@@ -0,0 +1,197 @@
1
+
2
+ module PDUTools
3
+ class Decoder
4
+ include Helpers
5
+ # http://read.pudn.com/downloads150/sourcecode/embed/646395/Short%20Message%20in%20PDU%20Encoding.pdf
6
+ def initialize pdu_hex, direction=:sc_to_ms
7
+ @pdu_hex = pdu_hex
8
+ @direction = direction
9
+ end
10
+
11
+ def decode
12
+ @sca_length = take(2, :integer) * 2 # Service center address length
13
+ if @sca_length > 0
14
+ @sca_type = parse_address_type take(2) # Service center address type
15
+ @sca = parse_address take(@sca_length - 2), @sca_type # Service center address
16
+ end
17
+ @pdu_type = parse_pdu_type take(2, :binary) # PDU type octet
18
+ @message_reference = take(2) if @pdu_type[:mti] == :sms_submit
19
+ @address_length = take(2, :integer)
20
+ @address_type = parse_address_type take(2)
21
+ @address = parse_address take(@address_length), @address_type
22
+ @pid = take(2)
23
+ @data_coding_scheme = parse_data_coding_scheme take(2, :binary)
24
+ @sc_timestamp = parse_7byte_timestamp take(14) if @pdu_type[:mti] == :sms_deliver
25
+ case @pdu_type[:vpf]
26
+ when :absolute
27
+ @validity_period = parse_7byte_timestamp take(14)
28
+ when :relative
29
+ @validity_period = parse_validity_period take(2, :integer)
30
+ end
31
+ @user_data_length = take(2, :integer)
32
+ parse_user_data @user_data_length
33
+
34
+ MessagePart.new @address, @message, @sc_timestamp, @validity_period, @user_data_header
35
+ end
36
+
37
+ def inspect2
38
+ r = "<PDUTools::Decoder"
39
+ r << "PDU: #{@pdu_hex}\n"
40
+ r << "SCA LENGTH: #{@sca_length}\n"
41
+ r << "SCA TYPE: #{@sca_type}\n"
42
+ r << "SCA: #{@sca}\n"
43
+ r << "PDU TYPE: #{@pdu_type}\n"
44
+ r << "MESSAGE REFERENCE: #{@message_reference}\n" if @message_reference
45
+ r << ">"
46
+ r
47
+ end
48
+
49
+ private
50
+
51
+ def take n, format=:string, data=@pdu_hex
52
+ part = data.slice!(0,n)
53
+ case format
54
+ when :string
55
+ return part
56
+ when :integer
57
+ return part.to_i(16)
58
+ when :binary
59
+ bytes = n/2
60
+ return "%0#{bytes*8}b" % part.to_i(16)
61
+ end
62
+ end
63
+
64
+ def parse_address_type type
65
+ case type
66
+ when "91"
67
+ :international
68
+ when "81"
69
+ :national
70
+ else
71
+ raise StandardError, "unknown address type"
72
+ end
73
+ end
74
+
75
+ def parse_address address, type
76
+ address = swapped2normal address
77
+ if type == :international
78
+ address.prepend "+"
79
+ end
80
+ address
81
+ end
82
+
83
+ def parse_pdu_type pdu_type
84
+ rp = pdu_type.slice!(0,1) == "1"
85
+ udhi = pdu_type.slice!(0,1) == "1"
86
+ srr_or_sri = pdu_type.slice!(0,1) == "1"
87
+ vpf = (case pdu_type.slice!(0,2)
88
+ when "00", "01"
89
+ :none
90
+ when "10"
91
+ :relative
92
+ when "11"
93
+ :absolute
94
+ end)
95
+ rd_or_mms = pdu_type.slice!(0,1)
96
+ mti = pdu_type.slice!(0,2)
97
+
98
+ type = { rp: rp, udhi: udhi, vpf: vpf }
99
+ case @direction
100
+ when :sc_to_ms
101
+ type[:mti] = case mti
102
+ when "00"
103
+ :sms_deliver
104
+ when "01"
105
+ :sms_submit_report
106
+ when "10"
107
+ :sms_status_report
108
+ when "11"
109
+ :reserved
110
+ end
111
+ type[:sri] = srr_or_sri
112
+ type[:mms] = rd_or_mms == "0"
113
+ when :ms_to_sc
114
+ type[:mti] = case mti
115
+ when "00"
116
+ :sms_deliver_report
117
+ when "01"
118
+ :sms_submit
119
+ when "10"
120
+ :sms_command
121
+ when "11"
122
+ :reserved
123
+ end
124
+ type[:srr] = srr_or_sri
125
+ type[:rd] = rd_or_mms == "0"
126
+ end
127
+ type
128
+ end
129
+
130
+ def parse_data_coding_scheme scheme
131
+ {
132
+ coding_group: scheme.slice!(0,2),
133
+ compresion: scheme.slice!(0,1) == "1",
134
+ klass_meaning: scheme.slice!(0,1) == "1",
135
+ alphabet: ALPHABETS.key(scheme.slice!(0,2)),
136
+ klass: scheme.slice!(0,2)
137
+ }
138
+ end
139
+
140
+ def parse_7byte_timestamp timestamp
141
+ year, month, day, hour, minute, second, zone = swapped2normal(timestamp).split('').in_groups_of(2).collect(&:join)
142
+ d = "#{year}-#{month}-#{day} #{hour}:#{minute}:#{second} +%02d:00" % (zone.to_i / 4)
143
+ Time.parse(d)
144
+ end
145
+
146
+ def parse_validity_period period
147
+ case period
148
+ when 0..143
149
+ ((period + 1) * 5).minutes
150
+ when 144..167
151
+ 12.hours + ((period - 143) * 30).minutes
152
+ when 168..196
153
+ (period - 166).days
154
+ when 197..255
155
+ (period - 192).weeks
156
+ end
157
+ end
158
+
159
+ def parse_user_data data_length
160
+ if @pdu_type[:udhi]
161
+ @udh_length = take(2, :integer) * 2
162
+ udh = take(@udh_length)
163
+ @user_data_header = parse_user_data_header udh
164
+ end
165
+ case @data_coding_scheme[:alphabet]
166
+ when :a7bit
167
+ @message = decode7bit @pdu_hex, data_length
168
+ when :a8bit
169
+ @message = decode8bit @pdu_hex, data_length
170
+ when :a16bit
171
+ @message = decode16bit @pdu_hex, data_length
172
+ end
173
+ end
174
+
175
+ def parse_user_data_header header
176
+ iei = take 2, :string, header
177
+ header_length = take 2, :integer, header
178
+ case iei
179
+ when "00"
180
+ reference = take 2, :integer, header
181
+ when "08"
182
+ reference = take 4, :integer, header
183
+ else
184
+ binding.pry
185
+ raise StandardError, "unsupported Information Element Identifier in User Data Header: #{iei}"
186
+ end
187
+ parts = take 2, :integer, header
188
+ part_number = take 2, :integer, header
189
+ {
190
+ reference: reference,
191
+ parts: parts,
192
+ part_number: part_number
193
+ }
194
+ end
195
+
196
+ end
197
+ end
@@ -0,0 +1,161 @@
1
+ module PDUTools
2
+ class Encoder
3
+ include Helpers
4
+
5
+ DEFAULT_OPTIONS = {
6
+ klass: nil,
7
+ require_receipt: false,
8
+ expiry_seconds: nil
9
+ }
10
+
11
+ MessagePart = Struct.new(:data, :length)
12
+
13
+ # PDU structure - http://read.pudn.com/downloads150/sourcecode/embed/646395/Short%20Message%20in%20PDU%20Encoding.pdf
14
+ # X Bytes - SMSC - Service Center Address
15
+ # 1 Byte - Flags / PDU Type
16
+ # - 1 bit Reply Path parameter indicator
17
+ # - 1 bit User Data Header Indicator
18
+ # - 1 bit Status Request Report
19
+ # - 2 bits Validity Period Format
20
+ # - 1 bit Reject Duplicates
21
+ # - 2 bits Message Type Indicator
22
+ # 2 Bytes - Message Reference
23
+ # X Bytes - Address length and address
24
+ # 1 Byte - Protocol identificator (PID)
25
+ # 1 Byte - Data Coding Scheme
26
+ # X Bytes - Validity Period
27
+ # 1 Byte - User Data Length
28
+ # X Bytes - User Data
29
+
30
+ def initialize options
31
+ raise ArgumentError, :recipient unless options[:recipient]
32
+ raise ArgumentError, :message unless options[:message]
33
+ @options = DEFAULT_OPTIONS.merge options
34
+
35
+ @smsc = '00' # Phone Specified
36
+ @message_parts, @alphabet = prepare_message options[:message]
37
+ @pdu_type = pdu_type @concatenated_message_reference, options[:require_receipt], options[:expiry_seconds]
38
+ @message_reference = '00' # Phone Specified
39
+ @address = prepare_recipient options[:recipient]
40
+ @protocol_identifier = '00' # SMS
41
+ @data_coding_scheme = data_coding_scheme options[:klass], @alphabet
42
+ @validity_period = validity_period options[:expiry_seconds]
43
+ end
44
+
45
+ def encode
46
+ head = ""
47
+ head << @smsc
48
+ head << @pdu_type
49
+ head << @message_reference
50
+ head << @address
51
+ head << @protocol_identifier
52
+ head << @data_coding_scheme
53
+ head << @validity_period
54
+ pdus = []
55
+ @message_parts.each do |part|
56
+ pdus << PDU.new(head + part.length + part.data)
57
+ end
58
+ pdus
59
+ end
60
+
61
+ private
62
+ def prepare_message message
63
+ if message.ascii_only?
64
+ # parts = message.scan(/.{1,#{MAX_GSM_MESSAGE_7BIT_PART_LENGTH}}/)
65
+ parts = message.split('').in_groups_of(MAX_GSM_MESSAGE_7BIT_PART_LENGTH).collect(&:join)
66
+ message_parts = []
67
+ parts.each_with_index do |part, i|
68
+ part_gsm0338 = utf8_to_gsm0338 part
69
+ part_7bit = encode7bit(part_gsm0338)
70
+ udh = user_data_header parts.size, i+1
71
+ udh_length = (udh.present? ? (udh.length / 2) + 1 : 0)
72
+ part_length = "%02X" % (part_gsm0338.length + udh_length)
73
+ message_parts << MessagePart.new((udh + part_7bit), part_length)
74
+ end
75
+ [message_parts, :a7bit]
76
+ else
77
+ parts = message.split('').in_groups_of(MAX_GSM_MESSAGE_16BIT_PART_LENGTH).collect(&:join)
78
+ message_parts = []
79
+ parts.each_with_index do |part, i|
80
+ part_8bit = encode8bit(part)
81
+ udh = user_data_header parts.size, i+1
82
+ part_length = "%02X" % ((udh + part_8bit).length / 2)
83
+ message_parts << MessagePart.new((udh + part_8bit), part_length)
84
+ end
85
+ [message_parts, :a16bit]
86
+ end
87
+ end
88
+
89
+ # http://en.wikipedia.org/wiki/Concatenated_SMS#Sending_a_concatenated_SMS_using_a_User_Data_Header
90
+ def user_data_header parts_count, part_number
91
+ return '' if parts_count == 1
92
+ @concatenated_message_reference ||= rand((2**16)-1)
93
+ udh = '06' # Length of User Data Header
94
+ udh << '08' # Concatenated short messages, 16-bit reference number
95
+ udh << '04' # Length of the header, excluding the first two fields
96
+ udh << "%04X" % @concatenated_message_reference
97
+ udh << "%02X" % parts_count
98
+ udh << "%02X" % part_number
99
+ udh
100
+ end
101
+
102
+ def prepare_recipient recipient
103
+ Phoner::Phone.default_country_code ||= "421"
104
+
105
+ address_type = "91" # International
106
+ address = Phoner::Phone.parse(recipient).format("%c%a%n")
107
+ address_length = "%02X" % address.length
108
+ address_encoded = normal2swapped address
109
+ address_length + address_type + address_encoded
110
+ end
111
+
112
+ def data_coding_scheme klass, alphabet
113
+ if klass
114
+ klass_meaning = '1'
115
+ klass = ("%02b" % klass)[-2,2]
116
+ else
117
+ klass_meaning = '0'
118
+ klass = '00'
119
+ end
120
+
121
+ scheme_bin = ""
122
+ scheme_bin << '00' # 2 bits - coding_group
123
+ scheme_bin << '0' # 1 bit - compression
124
+ scheme_bin << klass_meaning # 1 bit - klass meaning flag
125
+ scheme_bin << ALPHABETS[alphabet] # 2 bits - alphabet
126
+ scheme_bin << klass # 2 bits - klass
127
+
128
+ data_coding_scheme_dec = scheme_bin.to_i(2)
129
+ dec2hexbyte data_coding_scheme_dec
130
+ end
131
+
132
+ def pdu_type uhdi, srr, vpf
133
+ reply_path = '0'
134
+ uhdi_flag = (uhdi ? '1' : '0') # User Data Header indicator
135
+ srr_flag = (srr ? '1' : '0') # Status Request Report
136
+ vpf_flag = (vpf ? '10' : '00') # Validity Period Format
137
+ rj = '0' # Reject Duplicates
138
+ mti = '01' # Message Type Indicator (SMS-SUBMIT)
139
+ first_octet_dec = (reply_path + uhdi_flag + srr_flag + vpf_flag + rj + mti).to_i(2)
140
+ dec2hexbyte first_octet_dec
141
+ end
142
+
143
+ def validity_period expiry_seconds
144
+ return '' unless expiry_seconds
145
+ raise ArgumentError, "Expiry must be at least 300 seconds (5 minutes)" if expiry_seconds < 5.minutes
146
+ validity_period_dec = case expiry_seconds
147
+ when 5.minutes..12.hours
148
+ (expiry_seconds / 5.minutes) - 1
149
+ when 12.hours..24.hours
150
+ ((expiry_seconds - 12.hours) / 5.minutes) + 143
151
+ when 24.hours..30.days
152
+ (expiry_seconds / 24.hours) + 166
153
+ when 30.days..63.weeks
154
+ (expiry_seconds / 1.week) + 192
155
+ else
156
+ raise ArgumentError, "Expiry must be 38102400 seconds (63 weeks) or less"
157
+ end
158
+ dec2hexbyte validity_period_dec.ceil
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,111 @@
1
+ module PDUTools
2
+ module Helpers
3
+ GSM_03_38_ESCAPES = {
4
+ "@" => "\x00",
5
+ "$" => "\x02",
6
+ "_" => "\x11",
7
+ "^" => "\x1B\x14",
8
+ "{" => "\x1B\x28",
9
+ "}" => "\x1B\x29",
10
+ "\\" => "\x1B\x2F",
11
+ "[" => "\x1B\x3C",
12
+ "~" => "\x1B\x3D",
13
+ "]" => "\x1B\x3E",
14
+ "|" => "\x1B\x40"
15
+ # "\x80" => "\x1B\x65"
16
+ }
17
+
18
+ def utf8_to_gsm0338 string
19
+ GSM_03_38_ESCAPES.each do |find, replace|
20
+ string.gsub! find, replace
21
+ end
22
+ string
23
+ end
24
+
25
+ def dec2hexbyte dec
26
+ "%02X" % dec
27
+ end
28
+
29
+ def encode7bit string, padding=0
30
+ current_byte = 0
31
+ offset = padding
32
+ packed = []
33
+ string.chars.to_a.each_with_index do |char, i|
34
+ # cap off any excess bytes
35
+ septet = char.ord & 0x7F
36
+ # append the septet and then cap off excess bytes
37
+ current_byte |= (septet << offset) & 0xFF
38
+ offset += 7
39
+ if offset > 7
40
+ # the current byte is full, add it to the encoded data.
41
+ packed << current_byte
42
+ # shift left and append the left shifted septet to the current byte
43
+ septet = septet >> (7 - (offset - 8 ))
44
+ current_byte = septet
45
+ # update offset
46
+ offset -= 8
47
+ end
48
+ end
49
+ packed << current_byte if offset > 0 # append the last byte
50
+ packed.collect{|c| "%02X" % c }.join
51
+ end
52
+
53
+ def encode8bit string
54
+ string.chars.to_a.collect do |char|
55
+ "%04X" % char.ord
56
+ end.join
57
+ end
58
+
59
+ def normal2swapped string
60
+ string << "F" if string.length.odd?
61
+ string.scan(/../).collect(&:reverse).join
62
+ end
63
+
64
+ def swapped2normal string
65
+ string.scan(/../).collect(&:reverse).join.gsub(/F$/,'')
66
+ end
67
+
68
+ # def decode7bit data, length
69
+ # septets = data.to_i(16).to_s(2).split('').in_groups_of(7).collect(&:join)[0,length]
70
+ # septets.collect do |s|
71
+ # s.to_i(2).chr
72
+ # end.join
73
+ # end
74
+
75
+ def decode7bit textdata, length
76
+ bytes = []
77
+ textdata.split('').each_slice(2) do |s|
78
+ bytes << "%08b" % s.join.to_i(16)
79
+ end
80
+ bit = (bytes.size % 7)
81
+ bytes.reverse!
82
+ bytes.each_with_index do |byte, index|
83
+ if bit == 0 or index == 0
84
+ bytes.insert(index, "")
85
+ bit = 7 if bit == 0
86
+ next
87
+ else
88
+ bytes[index-1] = "#{bytes[index-1]}#{(bytes[index]||"")[0,bit]}"
89
+ bytes[index] = bytes[index][bit..-1] if bytes[index]
90
+ bit -= 1
91
+ end
92
+ end
93
+ bytes = bytes.reverse.collect{|b| "0#{b}".to_i(2) }.collect{|b| b.zero? ? nil : b }.compact
94
+ bytes.collect{|b| b.chr }.join
95
+ end
96
+
97
+ def decode8bit data, length
98
+ octets = data.split('').in_groups_of(2).collect(&:join)[0, length]
99
+ octets.collect do |o|
100
+ o.to_i(16).chr
101
+ end.join
102
+ end
103
+
104
+ def decode16bit data, length
105
+ dobule_octets = data.split('').in_groups_of(4).collect(&:join)[0, length/2]
106
+ dobule_octets.collect do |o|
107
+ [o.to_i(16)].pack("U")
108
+ end.join
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,21 @@
1
+ module PDUTools
2
+ class MessagePart
3
+ attr_reader :address, :body, :timestamp, :validity_period, :user_data_header
4
+ def initialize address, body, timestamp, validity_period, user_data_header
5
+ @address = address
6
+ @body = body
7
+ @timestamp = timestamp
8
+ @validity_period = validity_period
9
+ @user_data_header = user_data_header
10
+ end
11
+
12
+ def complete?
13
+ return true unless @user_data_header
14
+ if @user_data_header[:parts] > 1
15
+ false
16
+ else
17
+ true
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,19 @@
1
+ module PDUTools
2
+ class PDU
3
+ attr_reader :pdu_hex
4
+ def initialize pdu_hex
5
+ @pdu_hex = pdu_hex
6
+ end
7
+
8
+ def checksum
9
+ @checksum ||= begin
10
+ sum = @pdu_hex.scan(/../).collect{|c| c.to_i(16)}.sum
11
+ "%02X" % (sum & 0xFF)
12
+ end
13
+ end
14
+
15
+ def length
16
+ @length ||= (@pdu_hex.length / 2) - 1
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+
5
+ describe PDUTools::Decoder do
6
+ let(:decoder) { PDUTools::Decoder.new pdu, :ms_to_sc }
7
+
8
+ context "7 bit data" do
9
+ let(:pdu) { "0001000C9124910001100000001654747A0E4ACF416110BD3CA783DAE5F93C7C2E03" }
10
+ it "should decode" do
11
+ message_part = decoder.decode
12
+ expect(message_part.body).to eq "This is a test message"
13
+ expect(message_part.address).to eq "+421900100100"
14
+ end
15
+ end
16
+
17
+ context "16 bit data" do
18
+ let(:pdu) { "0001000C9124910001100000083E0054006800690073002000690073002000640069006100630072006900740069006300730020013E0161010D0165017E00FD00E100ED00E400FA00F40148" }
19
+ it "should decode" do
20
+ message_part = decoder.decode
21
+ expect(message_part.body).to eq "This is diacritics ľščťžýáíäúôň"
22
+ expect(message_part.address).to eq "+421900100100"
23
+ end
24
+ end
25
+
26
+ context "user data header" do
27
+ let(:pdu) { "0041000C912491000110000000A0060804C643020154747A0E4ACF416110BD3CA783DAE5F93C7C2E53D1E939283D078541F4F29C0E6A97E7F3F0B94C45A7E7A0F41C1406D1CB733AA85D9ECFC3E732159D9E83D2735018442FCFE9A076793E0F9FCB54747A0E4ACF416110BD3CA783DAE5F93C7C2E53D1E939283D078541F4F29C0E6A97E7F3F0B94C45A7E7A0F41C1406D1CB733AA85D9ECFC3" }
28
+ it "should decode" do
29
+ message_part = decoder.decode
30
+ expect(message_part.user_data_header).to be_present
31
+ expect(message_part.user_data_header[:parts]).to eq 2
32
+ expect(message_part.user_data_header[:part_number]).to eq 1
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ describe PDUTools::Encoder do
5
+ let(:recipient) { "+421 900 100 100" }
6
+ let(:encoder) { PDUTools::Encoder.new recipient: recipient, message: message }
7
+ context "short" do
8
+ context "7bit text" do
9
+ let(:message) { "This is a test message" }
10
+ it "should encode pdu" do
11
+ pdus = encoder.encode
12
+ expect(pdus.size).to eq(1)
13
+ end
14
+ end
15
+
16
+ context "16bit text" do
17
+ let(:message) { "This is diacritics ľščťžýáíäúôň" }
18
+ it "should encode pdu" do
19
+ pdus = encoder.encode
20
+ expect(pdus.size).to eq(1)
21
+ end
22
+ end
23
+ end
24
+
25
+ context "lonh" do
26
+ context "7bit text" do
27
+ let(:message) { "This is a test message" * 10 }
28
+ it "should encode pdu" do
29
+ pdus = encoder.encode
30
+ expect(pdus.size).to eq(2)
31
+ end
32
+ end
33
+
34
+ context "16bit text" do
35
+ let(:message) { "This is diacritics ľščťžýáíäúôň" * 3 }
36
+ it "should encode pdu" do
37
+ pdus = encoder.encode
38
+ expect(pdus.size).to eq(2)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1 @@
1
+ require 'pdu_tools'
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pdu_tools
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Filip Zachar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2014-10-06 00:00:00 Z
13
+ dependencies: []
14
+
15
+ description: Tools for encoding and decoding GSM SMS PDUs.
16
+ email: tulak45@gmail.com
17
+ executables: []
18
+
19
+ extensions: []
20
+
21
+ extra_rdoc_files: []
22
+
23
+ files:
24
+ - lib/pdu_tools/decoder.rb
25
+ - lib/pdu_tools/encoder.rb
26
+ - lib/pdu_tools/helpers.rb
27
+ - lib/pdu_tools/message_part.rb
28
+ - lib/pdu_tools/pdu.rb
29
+ - lib/pdu_tools.rb
30
+ - spec/decoder_spec.rb
31
+ - spec/encoder_spec.rb
32
+ - spec/spec_helper.rb
33
+ homepage: http://rubygems.org/gems/pdu_tools
34
+ licenses:
35
+ - MIT
36
+ metadata: {}
37
+
38
+ post_install_message:
39
+ rdoc_options: []
40
+
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - &id001
46
+ - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - *id001
52
+ requirements: []
53
+
54
+ rubyforge_project:
55
+ rubygems_version: 2.0.4
56
+ signing_key:
57
+ specification_version: 4
58
+ summary: Tools for encoding and decoding GSM SMS PDUs.
59
+ test_files: []
60
+