cimd 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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