cimd 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.
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ script: bundle exec rspec spec
3
+ rvm:
4
+ - 1.9.3
5
+ - 1.8.7
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
@@ -0,0 +1,42 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ cimd (0.0.1)
5
+ eventmachine (>= 0.12)
6
+ thor
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ diff-lcs (1.1.3)
12
+ eventmachine (0.12.10)
13
+ ffi (1.0.11)
14
+ guard (1.3.2)
15
+ listen (>= 0.4.2)
16
+ thor (>= 0.14.6)
17
+ guard-rspec (1.2.1)
18
+ guard (>= 1.1)
19
+ listen (0.5.0)
20
+ rake (0.9.2.2)
21
+ rb-inotify (0.8.8)
22
+ ffi (>= 0.5.0)
23
+ rspec (2.6.0)
24
+ rspec-core (~> 2.6.0)
25
+ rspec-expectations (~> 2.6.0)
26
+ rspec-mocks (~> 2.6.0)
27
+ rspec-core (2.6.4)
28
+ rspec-expectations (2.6.0)
29
+ diff-lcs (~> 1.1.2)
30
+ rspec-mocks (2.6.0)
31
+ thor (0.16.0)
32
+
33
+ PLATFORMS
34
+ ruby
35
+
36
+ DEPENDENCIES
37
+ cimd!
38
+ guard (>= 1.3)
39
+ guard-rspec (>= 1.2)
40
+ rake
41
+ rb-inotify (>= 0.8.8)
42
+ rspec
@@ -0,0 +1,35 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ #guard 'coffeescript', :input => 'app/assets/javascripts'
5
+
6
+ #guard 'livereload' do
7
+ #watch(%r{app/views/.+\.(erb|haml|slim)})
8
+ #watch(%r{app/helpers/.+\.rb})
9
+ #watch(%r{public/.+\.(css|js|html)})
10
+ #watch(%r{config/locales/.+\.yml})
11
+ ## Rails Assets Pipeline
12
+ #watch(%r{(app|vendor)/assets/\w+/(.+\.(css|js|html)).*}) { |m| "/assets/#{m[2]}" }
13
+ #end
14
+
15
+ guard 'rspec', :version => 2 do
16
+ watch(%r{^spec/lib/.+_spec\.rb$})
17
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/cimd_spec.rb" }
18
+ #watch('spec/spec_helper.rb') { "spec" }
19
+
20
+ ## Rails example
21
+ #watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
22
+ #watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
23
+ #watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
24
+ #watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
25
+ #watch('config/routes.rb') { "spec/routing" }
26
+ #watch('app/controllers/application_controller.rb') { "spec/controllers" }
27
+
28
+ ## Capybara request specs
29
+ #watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
30
+
31
+ ## Turnip features and steps
32
+ #watch(%r{^spec/acceptance/(.+)\.feature$})
33
+ #watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
34
+ end
35
+
@@ -0,0 +1,49 @@
1
+ [![Dependency Status](https://gemnasium.com/musashimm/cimd.png)](https://gemnasium.com/musashimm/cimd)
2
+ [![Build Status](https://secure.travis-ci.org/musashimm/cimd.png)](http://travis-ci.org/musashimm/cimd)
3
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/musashimm/cimd)
4
+
5
+ CIMD
6
+ ======
7
+
8
+ Overview
9
+ --------
10
+
11
+ Classes and binaries to handle SMS Cimd protocol.
12
+
13
+ Usage
14
+ -----
15
+
16
+ ### cimd_cli.rb receivesms ###
17
+ `cimd_cli.rb receivesms` receives smses endlessly. If You want to stop reciving just press ^C.
18
+
19
+ cimd_cli.rb receivesms --msisdn=MSISDN --password=PASSWORD --server=SERVER --user-identity=USER_IDENTITY
20
+
21
+ Options:
22
+ --server=SERVER # Address of SMSC server (can be DNS name)
23
+ [--port=PORT] # Port number for CIMD protocol
24
+ # Default: 9971
25
+ --user-identity=USER_IDENTITY # Username of CIMD account
26
+ --password=PASSWORD # Password for CIMD account USERNAME
27
+ [--message=MESSAGE] # Message to be send
28
+ # Default: SMS test message
29
+ --msisdn=MSISDN # MSISDN number to be send
30
+ [--alpha-orig-address=ALPHA_ORIG_ADDRESS] # Identity of sender
31
+ # Default: Sms Service
32
+ ### cimd_cli.rb sendsms ###
33
+ `cimd_cli.rb sendsms` sends text SMS to MSISDN number
34
+
35
+ cimd_cli.rb sendsms --msisdn=MSISDN --password=PASSWORD --server=SERVER --user-identity=USER_IDENTITY
36
+
37
+ Options:
38
+ --server=SERVER # Address of SMSC server (can be DNS name)
39
+ [--port=PORT] # Port number for CIMD protocol
40
+ # Default: 9971
41
+ --user-identity=USER_IDENTITY # Username of CIMD account
42
+ --password=PASSWORD # Password for CIMD account USERNAME
43
+ [--message=MESSAGE] # Message to be send
44
+ # Default: SMS test message
45
+ --msisdn=MSISDN # MSISDN number to be send
46
+ [--alpha-orig-address=ALPHA_ORIG_ADDRESS] # Identity of sender
47
+ # Default: Sms Service
48
+
49
+
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'eventmachine'
5
+ require 'cimd'
6
+ require 'thor'
7
+
8
+ module CIMD
9
+
10
+ def self.start_eventmachine(conn,messages,options)
11
+ EventMachine.run do
12
+ Signal.trap("INT") { CIMD::Loop.logout_request }
13
+ Signal.trap("TERM") { CIMD::Loop.logout_request }
14
+ EventMachine.connect conn.server, conn.port, CIMD::Loop do |c|
15
+ c.messages = messages
16
+ c.conn = conn
17
+ c.options = options
18
+ end
19
+ end
20
+ end
21
+
22
+ class MyCLI < Thor
23
+
24
+ desc "sendsms", "Sends text SMS to MSISDN number"
25
+ option :server, :required => true,:desc => "Address of SMSC server (can be DNS name)"
26
+ option :port, :desc => "Port number for CIMD protocol",:default => 9971
27
+ option :user_identity, :required => true,:desc => "Username of CIMD account"
28
+ option :password, :required => true,:desc => "Password for CIMD account USERNAME"
29
+ option :message, :desc => "Message to be send",:default=>"SMS test message"
30
+ option :msisdn, :required => true,:desc => "MSISDN number to be send"
31
+ option :alpha_orig_address, :desc => "Identity of sender",:default => "Sms Service"
32
+ def sendsms()
33
+ puts "Sending sms message: #{options[:message]} to #{options[:msisdn]}"
34
+ conn = CIMD::Connection.new(options[:server],options[:port],options[:user_identity],options[:password],options[:alpha_orig_address],1,60)
35
+ messages = EM::Queue.new
36
+ messages.push(CIMD::login_message(conn))
37
+ messages.push(CIMD::submit_text_message(conn,options[:msisdn],options[:message]))
38
+ messages.push(CIMD::logout_message)
39
+ CIMD::start_eventmachine(conn,messages,{})
40
+ end
41
+
42
+ desc "receivesms", "Receives smses and sends echo"
43
+ long_desc <<-LONGDESC
44
+ `cimd_cli.rb receivesms` receives smses endlessly. If You want to stop
45
+ reciving just press ^C.
46
+ LONGDESC
47
+ option :server, :required => true,:desc => "Address of SMSC server (can be DNS name)"
48
+ option :port, :desc => "Port number for CIMD protocol",:default => 9971
49
+ option :user_identity, :required => true,:desc => "Username of CIMD account"
50
+ option :password, :required => true,:desc => "Password for CIMD account USERNAME"
51
+ option :message, :desc => "Message to be send",:default=>"SMS test message"
52
+ option :msisdn, :required => true,:desc => "MSISDN number to be send"
53
+ option :alpha_orig_address, :desc => "Identity of sender",:default => "Sms Service"
54
+ def receivesms()
55
+ puts "Start receiving"
56
+ conn = CIMD::Connection.new(options[:server],options[:port],options[:user_identity],options[:password],options[:alpha_orig_address],1,60)
57
+ messages = EM::Queue.new
58
+ messages.push(CIMD::login_message(conn))
59
+ CIMD::start_eventmachine(conn,messages,{})
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+
66
+ CIMD::MyCLI.start(ARGV)
67
+
68
+
69
+
70
+
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "cimd/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "cimd"
7
+ s.version = CIMD::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Wojciech Todryk"]
10
+ s.email = ["wojciech@todryk.pl"]
11
+ s.homepage = "http://todryk.pl/cimd"
12
+ s.summary = %q{Utils for CIMD protocol}
13
+ s.description = %q{Utils for CIMD protocol}
14
+
15
+ s.rubyforge_project = "cimd"
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ["lib"]
21
+ s.add_dependency "eventmachine", [">= 0.12"]
22
+ s.add_dependency "thor", [">= 0"]
23
+ s.add_development_dependency "rspec", [">= 0"]
24
+ s.add_development_dependency "rake", [">= 0"]
25
+ s.add_development_dependency "guard", [">= 1.3"]
26
+ s.add_development_dependency "guard-rspec", [">= 1.2"]
27
+ s.add_development_dependency "rb-inotify", [">= 0.8.8"]
28
+
29
+ end
@@ -0,0 +1,4 @@
1
+ require 'cimd_constants'
2
+ require 'cimd_structures'
3
+ require 'cimd_messages'
4
+ require 'cimd_loop'
@@ -0,0 +1,5 @@
1
+ # Set of extensions
2
+ module CIMD
3
+ # Gem version
4
+ VERSION = "0.0.1"
5
+ end
@@ -0,0 +1,91 @@
1
+ module CIMD
2
+
3
+ STX = 0x02
4
+ ETX = 0x03
5
+ TAB = "\t"
6
+ CC = 0xFF
7
+ PARAM_SEP = ":"
8
+
9
+ # OP - OPERATION CODES
10
+ OP_LOGIN = 1
11
+ OP_LOGOUT = 2
12
+ OP_SUBMIT= 3
13
+ OP_ENQUIRE_MESSAGE_STATUS = 4
14
+ OP_DELIVERY_REQUEST = 5
15
+ OP_CANCEL_MESSAGE = 6
16
+ OP_SET = 8
17
+ OP_GET = 9
18
+ OP_DELIVERY_MESSAGE = 20
19
+ OP_DELIVERY_STATUS_REPORT = 23
20
+ OP_ALIVE = 40
21
+ OP_LOGIN_RESPONSE = 51
22
+ OP_LOGOUT_RESPONSE = 52
23
+ OP_SUBMIT_RESPONSE = 53
24
+ OP_ENQUIRE_MESSAGE_STATUS_RESPONSE = 54
25
+ OP_DELIVERY_REQUEST_RESPONSE = 55
26
+ OP_CANCEL_MESSAGE_RESPONSE = 56
27
+ OP_SET_RESPONSE = 58
28
+ OP_GET_RESPONSE = 59
29
+ OP_DELIVERY_MESSAGE_RESPONSE = 70
30
+ OP_DELIVERY_STATUS_REPORT_RESPONSE = 73
31
+ OP_ALIVE_RESPONSE = 90
32
+ OP_GENERAL_ERROR_RESPONSE = 98
33
+ OP_NACK = 99
34
+
35
+ def self.opcode_description(op_code)
36
+ descs = {
37
+ OP_LOGIN => "OP_LOGIN",
38
+ OP_LOGOUT => "OP_LOGOUT",
39
+ OP_SUBMIT=> "OP_SUBMIT",
40
+ OP_ENQUIRE_MESSAGE_STATUS => "OP_ENQUIRE_MESSAGE_STATUS",
41
+ OP_DELIVERY_REQUEST => "OP_DELIVERY_REQUEST",
42
+ OP_CANCEL_MESSAGE => "OP_CANCEL_MESSAGE",
43
+ OP_SET => "OP_SET",
44
+ OP_GET => "OP_GET",
45
+ OP_DELIVERY_MESSAGE => "OP_DELIVERY_MESSAGE",
46
+ OP_DELIVERY_STATUS_REPORT => "OP_DELIVERY_STATUS_REPORT",
47
+ OP_ALIVE => "OP_ALIVE",
48
+ OP_LOGIN_RESPONSE => "OP_LOGIN_RESPONSE",
49
+ OP_LOGOUT_RESPONSE => "OP_LOGOUT_RESPONSE",
50
+ OP_SUBMIT_RESPONSE => "OP_SUBMIT_RESPONSE",
51
+ OP_ENQUIRE_MESSAGE_STATUS_RESPONSE => "OP_ENQUIRE_MESSAGE_STATUS_RESPONSE",
52
+ OP_DELIVERY_REQUEST_RESPONSE => "OP_DELIVERY_REQUEST_RESPONSE",
53
+ OP_CANCEL_MESSAGE_RESPONSE => "OP_CANCEL_MESSAGE_RESPONSE",
54
+ OP_SET_RESPONSE => "OP_SET_RESPONSE",
55
+ OP_GET_RESPONSE => "OP_GET_RESPONSE",
56
+ OP_DELIVERY_MESSAGE_RESPONSE => "OP_DELIVERY_MESSAGE_RESPONSE",
57
+ OP_DELIVERY_STATUS_REPORT_RESPONSE => "OP_DELIVERY_STATUS_REPORT_RESPONSE",
58
+ OP_ALIVE_RESPONSE => "OP_ALIVE_RESPONSE",
59
+ OP_GENERAL_ERROR_RESPONSE => "OP_GENERAL_ERROR_RESPONSE",
60
+ OP_NACK => "",
61
+ }
62
+ return descs.has_key?(op_code) ? "(#{descs[op_code]})" : "(UNKNOWN)"
63
+ end
64
+
65
+ # P - PARAMETER
66
+ P_USER_IDENTITY = 10
67
+ P_PASSWORD = 11
68
+ P_SUBADDR = 12
69
+ P_WINDOW_SIZE = 19
70
+ P_DESTINATION_ADDRESS = 21
71
+ P_ORIGINATOR_ADDRESS = 23
72
+ P_ALPHA_ORIG_ADDRESS = 27
73
+ P_DATA_CODING_SCHEME = 30
74
+ P_USER_DATA_HEADER = 32
75
+ P_USER_DATA = 33
76
+ P_USER_DATA_BINARY = 34
77
+ P_PROTOCOL_IDENTIFIER = 52
78
+ P_SERVICE_CENTRE_TIME_STAMP = 60
79
+ P_ERROR_CODE = 900
80
+ P_ERROR_TEXT = 901
81
+
82
+ DEFAULT_WINDOW_SIZE = 1
83
+ DEFAULT_KEEP_ALIVE = 60
84
+
85
+ ALPHABET_DEFAULT = 0
86
+ ALPHABET_8BIT = 1
87
+ ALPHABET_UCS2 = 2
88
+ ALPHABET_RESERVED = 3
89
+
90
+ ALPHABET_BINARY = 8
91
+ end
@@ -0,0 +1,173 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'rubygems'
3
+ require 'eventmachine'
4
+ require 'cimd'
5
+
6
+ module CIMD
7
+
8
+ class Loop < EventMachine::Connection
9
+
10
+ @@logout_request = false
11
+
12
+ STATE=[:not_connected,:connected,:banner_received,:ready_to_send,:suspend]
13
+ MAX_NO_ACTIVITY_TIME = 180
14
+ attr_accessor :state
15
+ attr_accessor :messages
16
+ attr_accessor :conn
17
+ attr_accessor :keep_alive_counter
18
+ attr_accessor :no_activity_counter
19
+ attr_accessor :options
20
+ attr_accessor :logfile
21
+ attr_accessor :debug
22
+
23
+ def to_log(message)
24
+ #puts @debug
25
+ #puts @logfile
26
+ puts "#{Time.now.strftime("%Y-%m-%d/%H:%M:%S")} #{message}"
27
+ #puts message if @debug
28
+ #@logfile.puts message if @logfile
29
+ $stdout.flush
30
+ end
31
+
32
+ def change_state(new_state)
33
+ if @state != new_state
34
+ to_log("#### Transition: #{@state.to_s} ==> #{new_state.to_s} (no_act:#{ Time.now.tv_sec - @no_activity_counter}, keep:#{Time.now.tv_sec - @keep_alive_counter}, queue:#{@messages.size})")
35
+ @state = new_state
36
+ end
37
+ end
38
+
39
+ def self.logout_request
40
+ @@logout_request = true
41
+ end
42
+
43
+ def initialize
44
+ @state = :not_connected
45
+ @debug = false
46
+ @log = nil
47
+ @keep_alive_counter = Time.now.tv_sec
48
+ @no_activity_counter = Time.now.tv_sec
49
+ #@debug = true if @options[:debug]
50
+ #begin
51
+ # @log = File.new("ss", "w") if options[:logfile]
52
+ #rescue Exception => e
53
+ # puts e.to_s
54
+ # exit
55
+ #end
56
+ end
57
+
58
+ def post_init
59
+
60
+ @timer = EM.add_periodic_timer(1) do
61
+
62
+ if @@logout_request == true
63
+ change_state(:logout_request)
64
+ @@logout_request = false
65
+ end
66
+
67
+ change_state(:alive) if Time.now.tv_sec - @keep_alive_counter > @conn.keep_alive
68
+ change_state(:no_activity) if Time.now.tv_sec - @no_activity_counter > MAX_NO_ACTIVITY_TIME
69
+
70
+ #puts "No act:#{ Time.now.tv_sec - @no_activity_counter} Keep:#{Time.now.tv_sec - @keep_alive_counter} Queue: #{@messages.size}"
71
+ case @state
72
+ when :banner_received
73
+ change_state(:login_request)
74
+ @messages.pop{ |message| sending_message(message)}
75
+ when :login_sucessfull
76
+ change_state(:ready_to_send)
77
+ when :ready_to_send
78
+ change_state(:suspend)
79
+ @messages.pop{ |message| sending_message(message)}
80
+ when :no_activity,:logout_done
81
+ close_connection
82
+ when :alive
83
+ change_state(:suspend)
84
+ to_log("<<<< #{CIMD::alive_message}")
85
+ sending_message(CIMD::alive_message)
86
+ when :logout_request
87
+ @messages = EM::Queue.new
88
+ to_log("<<<< #{CIMD::logout_message}")
89
+ messages.push(CIMD::logout_message);
90
+ change_state(:ready_to_send)
91
+ when :suspend
92
+ #puts "Suspend"
93
+ end
94
+ end
95
+
96
+ end # post_init
97
+
98
+ def sending_message(message)
99
+ message.packet_number = @conn.packet_number!
100
+ to_log("<<<< #{message.to_s}")
101
+ send_data message.to_binary
102
+ @keep_alive_counter = Time.now.tv_sec
103
+ end
104
+
105
+ def sending_response_message(message)
106
+ m = CIMD::only_response_message(message)
107
+ to_log("<<<< #{m.to_s}")
108
+ send_data m.to_binary
109
+ end
110
+
111
+ def connection_completed
112
+ change_state(:connected)
113
+ end
114
+
115
+ def receive_data(data)
116
+ #puts "Received plain data: #{data}"
117
+
118
+ case data
119
+ when /CIMD2-A/
120
+ change_state(:banner_received)
121
+ else
122
+
123
+ (@buffer ||= BufferedTokenizer.new("\003")).extract(data).each do |line|
124
+ message = Message.parse(line)
125
+
126
+ to_log(">>>> #{message.to_s}")
127
+ # sprawdzic checsum
128
+ if message.is_binary?
129
+ message.parse_binary_data
130
+ to_log("**** Decoded binary: #{message.to_s}")
131
+ end
132
+ @no_activity_counter = Time.now.tv_sec
133
+
134
+ case message.operation_code
135
+ when CIMD::OP_LOGIN_RESPONSE
136
+ if message.has_error?
137
+ to_log("!!!! Error: #{message.error}")
138
+ close_connection
139
+ else
140
+ change_state(:login_sucessfull)
141
+ end
142
+ when CIMD::OP_SUBMIT_RESPONSE
143
+ if message.has_error?
144
+ to_log("!!!! Error: #{message.error}")
145
+ end
146
+ change_state(:ready_to_send)
147
+ when CIMD::OP_DELIVERY_STATUS_REPORT
148
+ sending_response_message(message)
149
+ when CIMD::OP_DELIVERY_MESSAGE
150
+ sending_response_message(message)
151
+ messages.push(CIMD::submit_text_message(@conn,message.parameter_value(CIMD::P_ORIGINATOR_ADDRESS),message.parameter_value(CIMD::P_USER_DATA)))
152
+ when CIMD::OP_LOGOUT_RESPONSE
153
+ change_state(:logout_done)
154
+ when CIMD::OP_ALIVE_RESPONSE
155
+ when CIMD::OP_GENERAL_ERROR_RESPONSE
156
+ to_log("!!!! Error: #{message.error}")
157
+ close_connection
158
+ else
159
+ to_log("???? Unknown message type: #{data}")
160
+ end # case message.operation_code
161
+ end
162
+ end # case data
163
+ end # receive_data
164
+
165
+ def unbind
166
+ @logfile.close unless @logfile.nil?
167
+ change_state(:not_connected)
168
+ EventMachine.stop_event_loop
169
+ end
170
+
171
+ end # class Loop
172
+
173
+ end # module CIMD
@@ -0,0 +1,56 @@
1
+ module CIMD
2
+
3
+ def self.login_message(connection)
4
+ m = Message.new(OP_LOGIN)
5
+ p_login = Parameter.new(P_USER_IDENTITY,connection.user_identity)
6
+ p_password = Parameter.new(P_PASSWORD,connection.password)
7
+ m.add(p_login)
8
+ m.add(p_password)
9
+ return m
10
+ end
11
+
12
+ def self.logout_message
13
+ m = Message.new(OP_LOGOUT)
14
+ return m
15
+ end
16
+
17
+ def self.submit_text_message(connection,msisdn,text)
18
+ m = Message.new(OP_SUBMIT)
19
+ p_msisdn = Parameter.new(P_DESTINATION_ADDRESS,msisdn)
20
+ p_text = Parameter.new(P_USER_DATA,text)
21
+ m.add(p_msisdn)
22
+ m.add(p_text)
23
+ unless connection.alpha_orig_address.nil?
24
+ p_orig = Parameter.new(P_ALPHA_ORIG_ADDRESS,connection.alpha_orig_address)
25
+ m.add(p_orig)
26
+ end
27
+ return m
28
+ end
29
+
30
+ def self.submit_binary_message(connection,msisdn,binary_text)
31
+ m = Message.new(OP_SUBMIT)
32
+ p_msisdn = Parameter.new(P_DESTINATION_ADDRESS,msisdn)
33
+ p_text = Parameter.new(P_USER_DATA_BINARY,binary_text)
34
+ p_dcs = Parameter.new(P_DATA_CODING_SCHEME,ALPHABET_BINARY)
35
+ m.add(p_msisdn)
36
+ m.add(p_text)
37
+ m.add(p_dcs)
38
+ unless connection.alpha_orig_address.nil?
39
+ p_orig = Parameter.new(P_ALPHA_ORIG_ADDRESS,connection.alpha_orig_address)
40
+ m.add(p_orig)
41
+ end
42
+ return m
43
+ end
44
+
45
+ def self.only_response_message(message)
46
+ m = Message.new(message.operation_code + 50)
47
+ m.packet_number = message.packet_number
48
+ return m
49
+ end
50
+
51
+ def self.alive_message
52
+ m = Message.new(OP_ALIVE)
53
+ return m
54
+ end
55
+
56
+ end
@@ -0,0 +1,237 @@
1
+ module CIMD
2
+
3
+ class Connection
4
+
5
+ attr_accessor :server
6
+ attr_accessor :port
7
+ attr_accessor :user_identity
8
+ attr_accessor :password
9
+ attr_accessor :keep_alive
10
+ attr_accessor :packet_number
11
+ attr_accessor :window_size
12
+ attr_accessor :alpha_orig_address
13
+
14
+ def initialize(server,port,user_identity,password,alpha_orig_address,window_size,keep_alive)
15
+ @server = server
16
+ @port = port
17
+ @user_identity = user_identity
18
+ @password = password
19
+ @keep_alive = keep_alive
20
+ @packet_number = 1
21
+ @alpha_orig_address = alpha_orig_address
22
+ @window_size = window_size
23
+ end
24
+
25
+ def to_s
26
+ return sprintf "\n\n*** Connection object:\nServer: %s\n,Port: %s\n,User identity: %s\nPassword: %s\nKeep Alive: %3d\nAlpha Orig Address: %s\n Packet number: %d\n\n",@server,@port,@user_identity,@password,@keep_alive,@alpha_orig_address,@packet_number
27
+ end
28
+
29
+ def packet_number?
30
+ return @packet_number
31
+ end
32
+
33
+ def packet_number!
34
+ f = @packet_number
35
+ (@packet_number += 2) > 255 ? @packet_number = 1 : @packet_number
36
+ return f
37
+ end
38
+
39
+ end #connection
40
+
41
+ class Parameter
42
+
43
+ attr_accessor :code
44
+ attr_accessor :value
45
+
46
+ def initialize(code,value)
47
+ @code = code
48
+ @value = value
49
+ end
50
+
51
+ def to_s
52
+ return sprintf "%03d:%s",@code,@value
53
+ end
54
+
55
+ def self.parse(data)
56
+ fields = data.split(":")
57
+ p = Parameter.new((fields[0]).to_i,fields[1])
58
+ return p
59
+ end
60
+
61
+ end #parameter
62
+
63
+ class Dcs
64
+
65
+ attr_accessor :value
66
+
67
+ def initialize(value)
68
+ @value = value
69
+ end
70
+
71
+ def set_value(value)
72
+ @value = value.to_i
73
+ end
74
+
75
+ def coding_group
76
+ return @value & 0xF0
77
+ end
78
+
79
+ def has_coding_group_zero?
80
+ (@value & 0b11000000).zero?
81
+ end
82
+
83
+ def has_default_alphabet?
84
+ value.zero?
85
+ end
86
+
87
+ def alphabet
88
+ if has_coding_group_zero?
89
+ return (@value & 0b00001100) >> 2
90
+ else
91
+ return 0
92
+ end
93
+ end
94
+
95
+ def alphabet_set_UCS2
96
+ value = 8
97
+ end
98
+
99
+ end #dcs
100
+
101
+ class Message
102
+
103
+ attr_accessor :operation_code
104
+ attr_accessor :parameters
105
+ attr_accessor :packet_number
106
+ attr_accessor :checksum
107
+ attr_accessor :dcs
108
+
109
+ def initialize(operation_code)
110
+ @operation_code = operation_code
111
+ @packet_number = 1
112
+ @parameters = Array.new
113
+ @checksum = 0
114
+ @dcs = Dcs.new(0)
115
+ end
116
+
117
+ def alphabet
118
+ @dcs.alphabet
119
+ end
120
+
121
+ def has_alphabet_ucs2?
122
+ @dcs.alphabet == ALPHABET_UCS2
123
+ end
124
+
125
+ def has_error?
126
+ return has_parameter?(P_ERROR_CODE) ? true : false
127
+ end
128
+
129
+ def error
130
+ return has_error? ? "(#{parameter_value(CIMD::P_ERROR_CODE)}) #{parameter_value(CIMD::P_ERROR_TEXT)}" : nil
131
+ end
132
+
133
+ def calc_checksum
134
+ checksum = 0
135
+ s = String.new
136
+ s << STX
137
+ s << (sprintf "%02d:%03d",@operation_code,@packet_number)
138
+ s << TAB
139
+ @parameters.each do |p|
140
+ s << p.to_s
141
+ s << TAB
142
+ end
143
+
144
+ s.each_byte do |b|
145
+ checksum += b
146
+ checksum &= 0xFF
147
+ end
148
+ return checksum
149
+ end
150
+
151
+ def add(parameter)
152
+ @parameters.push(parameter)
153
+ end
154
+
155
+ def to_s
156
+ s = sprintf "<STX>%02d:%03d<TAB>",@operation_code,@packet_number
157
+ #add(Parameter.new(P_DATA_CODING_SCHEME,@dcs.value)) @operation_code == OP_SUBMIT
158
+ @parameters.each do |p|
159
+ s << p.to_s
160
+ s << "<TAB>"
161
+ end
162
+ s << (sprintf "%02x",@checksum).upcase
163
+ s << "<ETX> "
164
+ s << CIMD::opcode_description(@operation_code)
165
+ return s
166
+ end
167
+
168
+ def to_binary
169
+ s = String.new
170
+ s << STX
171
+ s << (sprintf "%02d:%03d",@operation_code,@packet_number)
172
+ s << TAB
173
+ #add(Parameter.new(P_DATA_CODING_SCHEME,@dcs.value)) if @operation_code == OP_SUBMIT
174
+ @parameters.each do |p|
175
+ s << p.to_s
176
+ s << TAB
177
+ end
178
+ s << (sprintf "%02x",calc_checksum)
179
+ s << ETX
180
+ return s
181
+ end
182
+
183
+ def is_binary?
184
+ has_parameter?(P_USER_DATA_BINARY)
185
+ end
186
+
187
+ def self.parse(data)
188
+ data.slice!(0)
189
+ fields = data.split(TAB)
190
+ header = fields[0].split(PARAM_SEP)
191
+ operation_code = header[0].to_i
192
+ m = Message.new(operation_code)
193
+ m.packet_number = header[1].to_i
194
+ fields.delete_at(0)
195
+ m.checksum = (fields.delete_at(fields.size - 1)).hex.to_i
196
+ fields.each do |p|
197
+ m.parameters << Parameter.parse(p)
198
+ end
199
+ m.dcs.set_value(m.parameter_value(P_DATA_CODING_SCHEME)) if m.has_parameter?(P_DATA_CODING_SCHEME)
200
+ return m
201
+ end
202
+
203
+ def parse_binary_data
204
+ if has_alphabet_ucs2? and @operation_code == OP_DELIVERY_MESSAGE
205
+ data = parameter_value(P_USER_DATA_BINARY)
206
+ i = 0
207
+ s = ""
208
+ while i < data.length do
209
+ s << data[i,2].hex
210
+ i += 2
211
+ end
212
+ p = Parameter.new(P_USER_DATA,Iconv.iconv("UTF-8", "UCS-2BE",s).first)
213
+ add(p)
214
+ end
215
+ end
216
+
217
+ def has_parameter?(code)
218
+ @parameters.each do |p|
219
+ return true if p.code == code
220
+ end
221
+ return false
222
+ end
223
+
224
+ def parameter_value(code)
225
+ @parameters.each do |p|
226
+ return p.value if p.code == code
227
+ end
228
+ return nil
229
+ end
230
+
231
+ end # message
232
+
233
+ end # CIMD
234
+
235
+
236
+
237
+
@@ -0,0 +1,158 @@
1
+ require 'cimd'
2
+ require 'iconv'
3
+
4
+ describe CIMD do
5
+
6
+ it "has constants" do
7
+ CIMD::OP_LOGIN.should == 1
8
+ CIMD::OP_LOGOUT.should == 2
9
+ CIMD::OP_SUBMIT.should == 3
10
+ CIMD::ALPHABET_BINARY.should == 8
11
+ end
12
+
13
+ context CIMD::Connection do
14
+
15
+ let(:cn) { CIMD::Connection.new('server',1234,'user1','password1',nil,1,120) }
16
+ let(:c) { CIMD::Connection.new('server',1234,'user1','password1','alpha',1,120) }
17
+
18
+ it "returns connection object with nil alpha orig number" do
19
+ cn.user_identity.should eq('user1')
20
+ cn.password.should eq('password1')
21
+ cn.server.should eq('server')
22
+ cn.port.should eq(1234)
23
+ cn.alpha_orig_address.should be_nil
24
+ cn.window_size.should eq(1)
25
+ cn.keep_alive.should eq(120)
26
+ end
27
+
28
+ it "returns connection object with not nil alpha orig number" do
29
+ c.user_identity.should eq('user1')
30
+ c.password.should eq('password1')
31
+ c.alpha_orig_address.should eq('alpha')
32
+ end
33
+
34
+ it "returns first packet number" do
35
+ c.packet_number?.should eq(1)
36
+ end
37
+
38
+ it "returns 10th packet number" do
39
+ 10.times { c.packet_number! }
40
+ c.packet_number?.should eq(21)
41
+ end
42
+
43
+ it "returns 130th packet number" do
44
+ 130.times { c.packet_number! }
45
+ c.packet_number?.should eq(5)
46
+ end
47
+
48
+ it "opcode description" do
49
+ CIMD::opcode_description("as").should eq("(UNKNOWN)")
50
+ CIMD::opcode_description(CIMD::OP_LOGIN).should eq("(OP_LOGIN)")
51
+ end
52
+
53
+
54
+ end # context CIMD::Connection
55
+
56
+ context CIMD::Message do
57
+
58
+ let(:cn) { CIMD::Connection.new('server',1234,'user1','password1',nil,1,120) }
59
+ let(:c) { CIMD::Connection.new('server',1234,'user1','password1','alpha',1,120) }
60
+ let(:lm) { CIMD::login_message(cn) }
61
+ let(:lo) { CIMD::logout_message }
62
+ let(:submitn) { CIMD::submit_text_message(cn,'1234567789','textn') }
63
+ let(:submit) {CIMD::submit_text_message(c,'1234567789','text') }
64
+
65
+ it "returns login message" do
66
+ lm.has_parameter?(CIMD::P_USER_IDENTITY).should be_true
67
+ lm.has_parameter?(CIMD::P_PASSWORD).should be_true
68
+ lm.parameter_value(CIMD::P_USER_IDENTITY).should eq('user1')
69
+ lm.parameter_value(CIMD::P_PASSWORD).should eq('password1')
70
+ lm.has_parameter?(CIMD::P_DESTINATION_ADDRESS).should be_false
71
+ lm.parameter_value(CIMD::P_DESTINATION_ADDRESS).should be_nil
72
+ end
73
+
74
+ it "returns logout message" do
75
+ lo.parameters.size.should eq(0)
76
+ end
77
+
78
+ it "to_s" do
79
+ submit.to_s.should eq("<STX>03:001<TAB>021:1234567789<TAB>033:text<TAB>027:alpha<TAB>00<ETX> (OP_SUBMIT)")
80
+ submitn.to_s.should eq("<STX>03:001<TAB>021:1234567789<TAB>033:textn<TAB>00<ETX> (OP_SUBMIT)")
81
+ end
82
+
83
+ it "submit message with no alpha" do
84
+ #puts submitn
85
+ submitn.parameters.size.should eq(2)
86
+ submitn.has_parameter?(CIMD::P_ALPHA_ORIG_ADDRESS).should be_false
87
+ end
88
+
89
+ it "submit message with alpha" do
90
+ #puts submit
91
+ submit.parameters.size.should eq(3)
92
+ submit.has_parameter?(CIMD::P_ALPHA_ORIG_ADDRESS).should be_true
93
+ submit.parameter_value(CIMD::P_ALPHA_ORIG_ADDRESS).should eq('alpha')
94
+ end
95
+
96
+ it "return confirmation frame" do
97
+ submit.packet_number = 5
98
+ m = CIMD::only_response_message(submit)
99
+ m.packet_number.should eq(submit.packet_number)
100
+ m.operation_code.should eq(submit.operation_code+50)
101
+ end
102
+
103
+ it "inserts packet number to message" do
104
+ 10.times { c.packet_number! }
105
+ submit.packet_number = c.packet_number!
106
+ submit.packet_number.should eq(21)
107
+ end
108
+
109
+ it "calculates checksum test" do
110
+ m = CIMD::Message.new(CIMD::OP_GENERAL_ERROR_RESPONSE)
111
+ m.packet_number = 1
112
+ m.add(CIMD::Parameter.new(CIMD::P_ERROR_CODE,'2'))
113
+ m.add(CIMD::Parameter.new(CIMD::P_ERROR_TEXT,'Syntax error'))
114
+ sprintf("%02x",m.calc_checksum).upcase.should eq('03')
115
+ end
116
+
117
+ it "parses message" do
118
+ s = ""
119
+ s << CIMD::STX
120
+ s << (sprintf("%02d:%03d\t",1,23))
121
+ s << (sprintf("%03d:%s\t",11,'admin1'))
122
+ s << (sprintf("%03d:%s\t",10,'jan'))
123
+ s << "57"
124
+ s << CIMD::ETX
125
+ m = CIMD::Message.parse(s)
126
+ m.operation_code.should eq(1)
127
+ m.packet_number.should eq(23)
128
+ m.parameter_value(CIMD::P_USER_IDENTITY).should eq('jan')
129
+ m.parameter_value(CIMD::P_PASSWORD).should eq('admin1')
130
+ sprintf("%02x",m.calc_checksum).upcase.should eq('57')
131
+ end
132
+
133
+ it "parses binary message" do
134
+ s ="\00220:000\t021:48661001723\t023:48601130651\t060:120210100902\t034:0044006600760067006700720067006200760142006400660076007600660105\t052:0\t030:8\t1A\003"
135
+ m = CIMD::Message.parse(s)
136
+ sprintf("%02x",m.calc_checksum).upcase.should eq('1A')
137
+ m.is_binary?.should be_true
138
+ m.dcs.coding_group.should be_zero
139
+ m.dcs.has_coding_group_zero?.should be_true
140
+ m.alphabet.should eq(2)
141
+ m.has_alphabet_ucs2?.should be_true
142
+ m.parse_binary_data
143
+ m.parameter_value(CIMD::P_USER_DATA).should match(/^D/)
144
+ end
145
+
146
+ end
147
+
148
+ #context CIMD::Loop do
149
+ #let(:loop) { CIMD::Loop.new({}) }
150
+
151
+ #it ("create new loop instance") do
152
+ #puts loop
153
+ #end
154
+
155
+ #end
156
+
157
+ end
158
+
metadata ADDED
@@ -0,0 +1,174 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cimd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Wojciech Todryk
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: eventmachine
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0.12'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0.12'
30
+ - !ruby/object:Gem::Dependency
31
+ name: thor
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: guard
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '1.3'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '1.3'
94
+ - !ruby/object:Gem::Dependency
95
+ name: guard-rspec
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '1.2'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '1.2'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rb-inotify
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: 0.8.8
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: 0.8.8
126
+ description: Utils for CIMD protocol
127
+ email:
128
+ - wojciech@todryk.pl
129
+ executables:
130
+ - cimd_cli.rb
131
+ extensions: []
132
+ extra_rdoc_files: []
133
+ files:
134
+ - .travis.yml
135
+ - Gemfile
136
+ - Gemfile.lock
137
+ - Guardfile
138
+ - README.md
139
+ - Rakefile
140
+ - bin/cimd_cli.rb
141
+ - cimd.gemspec
142
+ - lib/cimd.rb
143
+ - lib/cimd/version.rb
144
+ - lib/cimd_constants.rb
145
+ - lib/cimd_loop.rb
146
+ - lib/cimd_messages.rb
147
+ - lib/cimd_structures.rb
148
+ - spec/lib/cimd_spec.rb
149
+ homepage: http://todryk.pl/cimd
150
+ licenses: []
151
+ post_install_message:
152
+ rdoc_options: []
153
+ require_paths:
154
+ - lib
155
+ required_ruby_version: !ruby/object:Gem::Requirement
156
+ none: false
157
+ requirements:
158
+ - - ! '>='
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ required_rubygems_version: !ruby/object:Gem::Requirement
162
+ none: false
163
+ requirements:
164
+ - - ! '>='
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ requirements: []
168
+ rubyforge_project: cimd
169
+ rubygems_version: 1.8.24
170
+ signing_key:
171
+ specification_version: 3
172
+ summary: Utils for CIMD protocol
173
+ test_files:
174
+ - spec/lib/cimd_spec.rb