Add PDU mode support and GSM 7Bit encoding / decoding
parent
4ca3d259c1
commit
a001604ee7
|
@ -1,5 +1,6 @@
|
||||||
require 'biju/version'
|
require 'biju/version'
|
||||||
require 'biju/modem'
|
require 'biju/modem'
|
||||||
|
require 'biju/pdu'
|
||||||
require 'biju/to_hayes'
|
require 'biju/to_hayes'
|
||||||
require 'biju/hayes'
|
require 'biju/hayes'
|
||||||
require 'biju/sms'
|
require 'biju/sms'
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
require 'biju/pdu/gsm7bit'
|
||||||
|
|
||||||
|
module Biju
|
||||||
|
module PDU
|
||||||
|
ENCODING = {
|
||||||
|
gsm7bit: GSM7Bit,
|
||||||
|
}
|
||||||
|
|
||||||
|
def self.encode(hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.encode_user_data(message, encoding = :gsm7bit)
|
||||||
|
raise ArgumentError, "Unknown encoding" unless ENCODING.has_key?(encoding)
|
||||||
|
ENCODING[encoding].encode(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.decode(string)
|
||||||
|
res = {
|
||||||
|
smsc_length: string[0..1],
|
||||||
|
smsc_type: string[2..3],
|
||||||
|
smsc_number: string[4..15],
|
||||||
|
|
||||||
|
address_length: string[18..19].to_i(16),
|
||||||
|
address_type: string[20..21],
|
||||||
|
|
||||||
|
protocol_identifier: string[34..35],
|
||||||
|
data_coding_scheme: string[36..37],
|
||||||
|
timestamp: DateTime.strptime(
|
||||||
|
"#{string[38..49].reverse}+#{string[50..51].reverse}",
|
||||||
|
'%S%M%H%d%m%y%Z'),
|
||||||
|
user_data_length: string[52..53],
|
||||||
|
}
|
||||||
|
res[:sender_number] = string[22..(22 + res[:address_length])]
|
||||||
|
res[:user_data] = PDU.decode_user_data(
|
||||||
|
string[54..-1], res[:data_coding_scheme])
|
||||||
|
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.decode_user_data(message, encoding = '00')
|
||||||
|
encoding = data_coding_scheme(encoding) unless encoding.is_a?(Symbol)
|
||||||
|
|
||||||
|
raise ArgumentError, "Unknown encoding" unless ENCODING.has_key?(:gsm7bit)
|
||||||
|
ENCODING[:gsm7bit].decode(message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.data_coding_scheme(dcs)
|
||||||
|
dcs = dcs.hex if dcs.is_a?(String)
|
||||||
|
if dcs & 0b11000000 == 0
|
||||||
|
case dcs & 0b00001100
|
||||||
|
when 0
|
||||||
|
:gsm7bit
|
||||||
|
when 4
|
||||||
|
:gsm8bit
|
||||||
|
when 8
|
||||||
|
:ucs2
|
||||||
|
when 12
|
||||||
|
:reserved
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,81 @@
|
||||||
|
module Biju::PDU
|
||||||
|
class GSM7Bit
|
||||||
|
BASIC_7BIT_CHARACTER_SET = [
|
||||||
|
'@', '£', '$', '¥', 'è', 'é', 'ù', 'ì', 'ò', 'Ç', "\n", 'Ø', 'ø', "\r", 'Å', 'å',
|
||||||
|
"\u0394", '_', "\u03a6", "\u0393", "\u039b", "\u03a9", "\u03a0","\u03a8", "\u03a3", "\u0398", "\u039e", "\e", 'Æ', 'æ', 'ß', 'É',
|
||||||
|
' ', '!', '"', '#', '¤', '%', '&', '\'', '(', ')','*', '+', ',', '-', '.', '/',
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7','8', '9', ':', ';', '<', '=', '>', '?',
|
||||||
|
'¡', 'A', 'B', 'C', 'D', 'E','F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
|
||||||
|
'P', 'Q', 'R', 'S','T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'Ä', 'Ö', 'Ñ', 'Ü', '§',
|
||||||
|
'¿', 'a','b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
|
||||||
|
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'ä', 'ö', 'ñ','ü', 'à'
|
||||||
|
]
|
||||||
|
|
||||||
|
BASIC_7BIT_CHARACTER_SET_EXTENSION = {
|
||||||
|
0x0A => "\n",
|
||||||
|
0x0D => '',
|
||||||
|
0x14 => '^',
|
||||||
|
0x1B => '',
|
||||||
|
0x28 => '{',
|
||||||
|
0x29 => '}',
|
||||||
|
0x2F => '\\',
|
||||||
|
0x3C => '[',
|
||||||
|
0x3D => '~',
|
||||||
|
0x3E => ']',
|
||||||
|
0x40 => '|',
|
||||||
|
0x65 => '€',
|
||||||
|
}
|
||||||
|
|
||||||
|
def self.decode(string)
|
||||||
|
res = ''
|
||||||
|
next_char = 0
|
||||||
|
|
||||||
|
string.scan(/../).map(&:hex).each_with_index do |octet, i|
|
||||||
|
index = i % 7
|
||||||
|
current = ((octet & (2 ** (7 - index) - 1)) << index) | next_char
|
||||||
|
|
||||||
|
res = add_char(res, current)
|
||||||
|
|
||||||
|
next_char = octet >> (7 - index)
|
||||||
|
if index == 6
|
||||||
|
res = add_char(res, next_char)
|
||||||
|
next_char = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.encode(string)
|
||||||
|
res = ''
|
||||||
|
string.chars.each do |char|
|
||||||
|
if get_septet(char)
|
||||||
|
res << get_septet(char).reverse
|
||||||
|
elsif get_septet(char, escape: true)
|
||||||
|
res << get_septet("\e").reverse
|
||||||
|
res << get_septet(char, escape: true).reverse
|
||||||
|
end
|
||||||
|
end
|
||||||
|
res << ("0" * (8 - (res.length % 8))) unless res.length % 8 == 0
|
||||||
|
|
||||||
|
res.scan(/.{8}/).map { |octet| "%02x" % octet.reverse.to_i(2) }.join
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def self.add_char(string, char)
|
||||||
|
if string[-1] == "\e"
|
||||||
|
string.chop << BASIC_7BIT_CHARACTER_SET_EXTENSION[char]
|
||||||
|
else
|
||||||
|
string << BASIC_7BIT_CHARACTER_SET[char]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.get_septet(char, escape: false)
|
||||||
|
char = (!escape ? BASIC_7BIT_CHARACTER_SET.index(char) : BASIC_7BIT_CHARACTER_SET_EXTENSION.key(char))
|
||||||
|
|
||||||
|
return nil unless char
|
||||||
|
"%07b" % char
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,6 +5,13 @@ module Biju
|
||||||
attr_accessor :id, :phone_number, :message
|
attr_accessor :id, :phone_number, :message
|
||||||
attr_reader :datetime
|
attr_reader :datetime
|
||||||
|
|
||||||
|
def self.from_pdu(string)
|
||||||
|
sms_infos = PDU.decode(string)
|
||||||
|
new(phone_number: sms_infos[:sender_number],
|
||||||
|
datetime: sms_infos[:timestamp],
|
||||||
|
message: sms_infos[:user_data])
|
||||||
|
end
|
||||||
|
|
||||||
def initialize(params={})
|
def initialize(params={})
|
||||||
params.each do |attr, value|
|
params.each do |attr, value|
|
||||||
self.public_send("#{attr}=", value)
|
self.public_send("#{attr}=", value)
|
||||||
|
@ -23,7 +30,10 @@ module Biju
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
"#{id} - #{phone_number} - #{datetime} - #{message}"
|
"[#{id}] (#{phone_number}) #{datetime} '#{message}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_pdu
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue