opensips-mi 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,122 @@
1
+ module Opensips
2
+ module MI
3
+ class Response
4
+ attr_reader :code, :success, :message
5
+ attr_reader :rawdata # raw data array
6
+ attr_reader :result # formatted data
7
+
8
+ def initialize(data)
9
+ raise InvalidResponseData,
10
+ 'Invalid parameter' unless data.is_a? Array
11
+ raise EmptyResponseData,
12
+ 'Empty parameter array' if data.empty?
13
+
14
+ if /^(?<code>\d+) (?<message>.+)$/ =~ data.shift.to_s
15
+ @code = code.to_i
16
+ @message = message
17
+ else
18
+ raise InvalidResponseData,
19
+ 'Invalid response parameter. Can not parse'
20
+ end
21
+
22
+ @success = (200..299).include?(@code)
23
+
24
+ # successfull responses have additional new line
25
+ data.pop if @success
26
+ @rawdata = data
27
+ @result = nil
28
+ end
29
+
30
+ # Parse user locations records to Hash
31
+ def ul_dump
32
+ return nil unless /^Domain:: location table=\d+ records=(\d+)$/ =~ @rawdata.shift
33
+ records = Hash.new
34
+ aor = ''
35
+ @rawdata.each do |r|
36
+ if /\tAOR:: (?<peer>.+)$/ =~ r
37
+ aor = peer
38
+ records[aor] = Hash.new
39
+ end
40
+ if /^\t{2,3}(?<key>[^:]+):: (?<val>.*)$/ =~ r
41
+ records[aor][key] = val if aor
42
+ end
43
+ end
44
+ @result = records
45
+ self
46
+ end
47
+
48
+ # returns struct
49
+ def uptime
50
+ res = Hash.new
51
+ @rawdata.each do |r|
52
+ next if /^Now::/ =~ r
53
+ if /^Up since:: [^\s]+ (?'mon'[^\s]+)\s+(?'d'\d+) (?'h'\d+):(?'m'\d+):(?'s'\d+) (?'y'\d+)/ =~ r
54
+ res[:since] = Time.local(y,mon,d,h,m,s)
55
+ end
56
+ if /^Up time:: (?'sec'\d+) / =~ r
57
+ res[:uptime] = sec.to_i
58
+ end
59
+ end
60
+ @result = OpenStruct.new res
61
+ self
62
+ end
63
+
64
+ # returns struct
65
+ def cache_fetch
66
+ res = Hash.new
67
+ @rawdata.each do |r|
68
+ if /^(?'label'[^=]+)=\s+\[(?'value'[^\]]+)\]/ =~ r
69
+ label.strip!
70
+ res[label.to_sym] = value
71
+ end
72
+ end
73
+ @result = OpenStruct.new res
74
+ self
75
+ end
76
+
77
+ # returns Array of registered contacts
78
+ def ul_show_contact
79
+ res = Array.new
80
+ @rawdata.each do |r|
81
+ cont = Hash.new
82
+ r.split(?;).each do |rec|
83
+ if /^Contact:: (.*)$/ =~ rec
84
+ cont[:contact] = $1
85
+ else
86
+ key,val = rec.split ?=
87
+ cont[key.to_sym] = val
88
+ end
89
+ end
90
+ res << cont
91
+ end
92
+ @result = res
93
+ self
94
+ end
95
+
96
+ # returns hash of dialogs
97
+ def dlg_list
98
+ # parse dialogs information into array
99
+ # assuming new block always starts with "dialog:: hash=..."
100
+ calls, key = Hash.new, nil
101
+ @rawdata.each do |l|
102
+ l.strip!
103
+ if l.match(/^dialog::\s+hash=(.*)$/)
104
+ key = $1
105
+ calls[key] = Hash.new
106
+ next
107
+ end
108
+ # building dialog array
109
+ if l.match(/^([^:]+)::\s+(.*)$/)
110
+ calls[key][$1.to_sym] = $2
111
+ end
112
+ end
113
+ @result = calls
114
+ self
115
+ end
116
+
117
+ end
118
+
119
+ class InvalidResponseData < Exception;end
120
+ class EmptyResponseData < Exception;end
121
+ end
122
+ end
@@ -0,0 +1,10 @@
1
+ require_relative "transport/fifo"
2
+ require_relative "transport/datagram"
3
+ require_relative "transport/xmlrpc"
4
+
5
+ module Opensips
6
+ module MI
7
+ module Transport
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,40 @@
1
+ module Opensips
2
+ module MI
3
+ module Transport
4
+ class Datagram < Opensips::MI::Command
5
+ RECVMAXLEN = 2**16 - 1
6
+
7
+ class << self
8
+ def init(params)
9
+ Datagram.new params
10
+ end
11
+ end
12
+
13
+ def initialize(params)
14
+ raise ArgumentError,
15
+ 'Missing socket host' if params[:host].nil?
16
+ raise ArgumentError,
17
+ 'Missing socket port' if params[:port].nil?
18
+ Socket.getaddrinfo(params[:host], nil) rescue
19
+ raise SocketError, "Invalid host #{params[:host]}"
20
+ raise SocketError,
21
+ "Invalid port #{params[:port]}" unless (1..(2**16-1)).include?(params[:port])
22
+ @sock = UDPSocket.new
23
+ @sock.connect params[:host], params[:port]
24
+ end
25
+
26
+ def command(cmd, params = [])
27
+ request = ":#{cmd}:\n"
28
+ params.each do |c|
29
+ request << "#{c}\n"
30
+ end
31
+ @sock.send request, 0
32
+ # will raise Errno::ECONNREFUSED if failed to connect
33
+ response, = @sock.recvfrom RECVMAXLEN
34
+ Opensips::MI::Response.new response.split(?\n)
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,90 @@
1
+ module Opensips
2
+ module MI
3
+ module Transport
4
+ class Fifo < Opensips::MI::Command
5
+ PERMISIONS = '0666'
6
+ attr_accessor :reply_fifo # name of the reply fifo file
7
+ attr_accessor :fifo_name # OpenSIPs fifo file. See mi_fifo module
8
+ attr_accessor :reply_dir # path to directory with where the reply fif is located
9
+
10
+ class << self
11
+ def init(params)
12
+ fifo = Fifo.new params
13
+ fifo.open
14
+ end
15
+ end
16
+
17
+ def initialize(params)
18
+ # set default values
19
+ @reply_fifo = if params[:reply_fifo].nil?
20
+ "opensips_reply_" << Digest::MD5.hexdigest(rand.to_s)[0,8]
21
+ else
22
+ @reply_fifo = params[:reply_fifo]
23
+ end
24
+
25
+ @reply_dir = if params[:reply_dir].nil?
26
+ '/tmp/'
27
+ else
28
+ params[:reply_dir]
29
+ end
30
+ raise ArgumentError,
31
+ "Fifo reply directory does not exists #{@reply_dir}" unless Dir.exists? @reply_dir
32
+
33
+ # fifo_name is required parameter
34
+ raise ArgumentError,
35
+ 'Missing required parameter fifo_name' if params[:fifo_name].nil?
36
+
37
+ @fifo_name = params[:fifo_name]
38
+ raise ArgumentError,
39
+ "OpenSIPs fifo_name file does not exist: #{@fifo_name}" unless File.exists? @fifo_name
40
+ raise ArgumentError,
41
+ "File #{@fifo_name} is not pipe" unless File.pipe? @fifo_name
42
+
43
+ # set finalizing method
44
+ reply_file = File.expand_path(@reply_fifo, @reply_dir)
45
+ ObjectSpace.define_finalizer(self, proc{self.class.finalize(reply_file)})
46
+ end
47
+
48
+ def open
49
+ # create fifo file
50
+ fifo_file = File.expand_path(@reply_fifo, @reply_dir)
51
+ Kernel.system "mkfifo -m #{PERMISIONS} #{fifo_file}"
52
+ raise SystemCallError,
53
+ "Can not create reply pipe: #{fifo_file}" unless File.pipe?(fifo_file)
54
+ self
55
+ end
56
+
57
+ def command(cmd, params = [])
58
+ fd_w = IO::sysopen(@fifo_name, Fcntl::O_WRONLY)
59
+ fifo_w = IO.open(fd_w)
60
+
61
+ request = ":#{cmd}:#{@reply_fifo}\n"
62
+ params.each do |c|
63
+ request << "#{c}\n"
64
+ end
65
+ # additional new line to terminate command
66
+ request << ?\n
67
+ fifo_w.syswrite request
68
+
69
+ # read response
70
+ file = File.expand_path(File.expand_path(@reply_fifo,@reply_dir))
71
+ fd_r = IO::sysopen(file, Fcntl::O_RDONLY )
72
+ fifo_r = IO.open(fd_r)
73
+
74
+ response = Array[]
75
+ response << $_.chomp while fifo_r.gets
76
+ Opensips::MI::Response.new response
77
+ ensure
78
+ # make sure we always close files' descriptors
79
+ fifo_r.close if fifo_r
80
+ fifo_w.close if fifo_w
81
+ end
82
+
83
+ def self.finalize(reply_file)
84
+ File.unlink(reply_file) if File.exists?(reply_file)
85
+ end
86
+
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,43 @@
1
+ module Opensips
2
+ module MI
3
+ module Transport
4
+ class Xmlrpc < Opensips::MI::Command
5
+ RPCSEG = 'RPC2'
6
+ class << self
7
+ def init(params)
8
+ Xmlrpc.new params
9
+ end
10
+ end
11
+
12
+ def initialize(params)
13
+ raise ArgumentError,
14
+ 'Missing socket host' if params[:host].nil?
15
+ raise ArgumentError,
16
+ 'Missing socket port' if params[:port].nil?
17
+ Socket.getaddrinfo(params[:host], nil) rescue
18
+ raise SocketError, "Invalid host #{params[:host]}"
19
+ raise SocketError,
20
+ "Invalid port #{params[:port]}" unless (1..(2**16-1)).include?(params[:port])
21
+ uri = "http://#{params[:host]}:#{params[:port]}/#{RPCSEG}"
22
+ @client = XMLRPC::Client.new_from_uri(uri, nil, 3)
23
+ rescue => e
24
+ raise e.class,
25
+ "Can not connect OpenSIPs server.\n#{e.message}"
26
+ end
27
+
28
+ def command(cmd, params = [])
29
+ response = ["200 OK"]
30
+ response += @client.call(cmd, *params).split(?\n)
31
+ response << ""
32
+ Opensips::MI::Response.new response
33
+ rescue => e
34
+ response = ["600 " << e.message]
35
+ Opensips::MI::Response.new response
36
+ end
37
+
38
+ def set_header(header);header;end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,5 @@
1
+ module Opensips
2
+ module MI
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'opensips/mi/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "opensips-mi"
8
+ gem.version = Opensips::MI::VERSION
9
+ gem.authors = ["Stas Kobzar"]
10
+ gem.email = ["stas@modulis.ca"]
11
+ gem.description = %q{Ruby module for interacting with OpenSIPs management interface}
12
+ gem.summary = %q{OpenSIPs management interface}
13
+ gem.homepage = "http://github.com/staskobzar/opensips-mi"
14
+
15
+ gem.files = `git ls-files`.split($/).reject{|f| %r|^examples/.*|.match f}
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency('bundler', '~> 1.3')
21
+ gem.add_development_dependency('rdoc')
22
+ gem.add_development_dependency('rake')
23
+ gem.add_development_dependency('mocha')
24
+ end
@@ -0,0 +1,20 @@
1
+ 200 OK
2
+ dialog:: hash=3212:2099935485
3
+ state:: 4
4
+ user_flags:: 0
5
+ timestart:: 1365817158
6
+ timeout:: 1365824358
7
+ callid:: 1854719653
8
+ from_uri:: sip:7747@voip.etsmtl.ca
9
+ to_uri:: sip:95142842020@voip.etsmtl.ca
10
+ caller_tag:: 1614235294
11
+ caller_contact:: sip:7747@10.130.8.100
12
+ callee_cseq:: 0
13
+ caller_route_set::
14
+ caller_bind_addr:: udp:10.130.8.21:5060
15
+ callee_tag:: as2e60b080
16
+ callee_contact:: sip:95142842020@10.130.8.240:5060
17
+ caller_cseq:: 20
18
+ callee_route_set::
19
+ callee_bind_addr:: udp:10.130.8.21:5060
20
+
@@ -0,0 +1,168 @@
1
+ 200 OK
2
+ Domain:: location table=512 records=15
3
+ AOR:: 7962
4
+ Contact:: sip:7962@10.164.112.13 Q=
5
+ Expires:: 143
6
+ Callid:: 5e7a1e47da91c41c
7
+ Cseq:: 31401
8
+ User-agent:: Avaya IP Phone 1120E (SIP1120e.04.03.12.00)
9
+ State:: CS_SYNC
10
+ Flags:: 0
11
+ Cflag:: 0
12
+ Socket:: udp:10.130.8.21:5060
13
+ Methods:: 7551
14
+ AOR:: 7329
15
+ Contact:: sip:7329@10.132.113.171 Q=
16
+ Expires:: 95
17
+ Callid:: 504e5ee5baf3e7d8
18
+ Cseq:: 10412
19
+ User-agent:: Avaya IP Phone 1120E (SIP1120e.04.03.12.00)
20
+ State:: CS_SYNC
21
+ Flags:: 0
22
+ Cflag:: 0
23
+ Socket:: udp:10.130.8.21:5060
24
+ Methods:: 7551
25
+ AOR:: 7863
26
+ Contact:: sip:7863@10.132.113.172 Q=
27
+ Expires:: 142
28
+ Callid:: d808729f9d47ec2d
29
+ Cseq:: 28091
30
+ User-agent:: Avaya IP Phone 1120E (SIP1120e.04.03.12.00)
31
+ State:: CS_SYNC
32
+ Flags:: 0
33
+ Cflag:: 0
34
+ Socket:: udp:10.130.8.21:5060
35
+ Methods:: 7551
36
+ AOR:: 3812
37
+ Contact:: sip:3812@10.132.113.163 Q=
38
+ Expires:: 58
39
+ Callid:: a59c4a46f79d7317
40
+ Cseq:: 19050
41
+ User-agent:: Avaya IP Phone 1120E (SIP1120e.04.03.12.00)
42
+ State:: CS_SYNC
43
+ Flags:: 0
44
+ Cflag:: 0
45
+ Socket:: udp:10.130.8.21:5060
46
+ Methods:: 7551
47
+ AOR:: 3810
48
+ Contact:: sip:3810@10.132.113.166 Q=
49
+ Expires:: 87
50
+ Callid:: dd38e788e386a79d
51
+ Cseq:: 18259
52
+ User-agent:: Avaya IP Phone 1140E (SIP1140e.04.03.12.00)
53
+ State:: CS_SYNC
54
+ Flags:: 0
55
+ Cflag:: 0
56
+ Socket:: udp:10.130.8.21:5060
57
+ Methods:: 7551
58
+ AOR:: 7519
59
+ Contact:: sip:7519@10.132.113.174 Q=
60
+ Expires:: 63
61
+ Callid:: f0a2566a33a42295
62
+ Cseq:: 28149
63
+ User-agent:: Avaya IP Phone 1120E (SIP1120e.04.03.12.00)
64
+ State:: CS_SYNC
65
+ Flags:: 0
66
+ Cflag:: 0
67
+ Socket:: udp:10.130.8.21:5060
68
+ Methods:: 7551
69
+ AOR:: 7259
70
+ Contact:: sip:7259@10.164.112.18 Q=
71
+ Expires:: 32
72
+ Callid:: 7f0d07e8c1415bbc
73
+ Cseq:: 32493
74
+ User-agent:: Avaya IP Phone 1120E (SIP1120e.04.03.12.00)
75
+ State:: CS_SYNC
76
+ Flags:: 0
77
+ Cflag:: 0
78
+ Socket:: udp:10.130.8.21:5060
79
+ Methods:: 7551
80
+ AOR:: 7577
81
+ Contact:: sip:7577@10.132.113.178 Q=
82
+ Expires:: 159
83
+ Callid:: 5eca2b3d1062416b
84
+ Cseq:: 12767
85
+ User-agent:: Avaya IP Phone 1140E (SIP1140e.04.03.12.00)
86
+ State:: CS_SYNC
87
+ Flags:: 0
88
+ Cflag:: 0
89
+ Socket:: udp:10.130.8.21:5060
90
+ Methods:: 7551
91
+ AOR:: 3901
92
+ Contact:: sip:3901@10.132.113.160 Q=
93
+ Expires:: 132
94
+ Callid:: a57d709ba8c7d6d7
95
+ Cseq:: 27476
96
+ User-agent:: Avaya IP Phone 1120E (SIP1120e.04.03.12.00)
97
+ State:: CS_SYNC
98
+ Flags:: 0
99
+ Cflag:: 0
100
+ Socket:: udp:10.130.8.21:5060
101
+ Methods:: 7551
102
+ AOR:: 7806
103
+ Contact:: sip:7806@10.132.113.175 Q=
104
+ Expires:: 132
105
+ Callid:: 0fb4baf2782a89b3
106
+ Cseq:: 23944
107
+ User-agent:: Avaya IP Phone 1120E (SIP1120e.04.03.12.00)
108
+ State:: CS_SYNC
109
+ Flags:: 0
110
+ Cflag:: 0
111
+ Socket:: udp:10.130.8.21:5060
112
+ Methods:: 7551
113
+ AOR:: 7402
114
+ Contact:: sip:7402@10.132.113.168 Q=
115
+ Expires:: 47981
116
+ Callid:: ef8fa87d63302751
117
+ Cseq:: 13537
118
+ User-agent:: Avaya IP Phone 1120E (SIP1120e.04.03.12.00)
119
+ State:: CS_SYNC
120
+ Flags:: 0
121
+ Cflag:: 0
122
+ Socket:: udp:10.130.8.21:5060
123
+ Methods:: 7551
124
+ AOR:: 3822
125
+ Contact:: sip:3822@10.132.113.162 Q=
126
+ Expires:: 97
127
+ Callid:: f534f0b8d8a070e7
128
+ Cseq:: 50129
129
+ User-agent:: Avaya IP Phone 1220 (SIP12x0.04.03.12.00)
130
+ State:: CS_SYNC
131
+ Flags:: 0
132
+ Cflag:: 0
133
+ Socket:: udp:10.130.8.21:5060
134
+ Methods:: 7551
135
+ AOR:: 7641
136
+ Contact:: sip:7641@10.164.112.28 Q=
137
+ Expires:: 98
138
+ Callid:: df0f22a80302737a
139
+ Cseq:: 33571
140
+ User-agent:: Avaya IP Phone 1140E (SIP1140e.04.03.12.00)
141
+ State:: CS_SYNC
142
+ Flags:: 0
143
+ Cflag:: 0
144
+ Socket:: udp:10.130.8.21:5060
145
+ Methods:: 7551
146
+ AOR:: 7423
147
+ Contact:: sip:7423@10.164.113.25 Q=
148
+ Expires:: 109
149
+ Callid:: ab10dabda2f0f257
150
+ Cseq:: 39074
151
+ User-agent:: Avaya IP Phone 1120E (SIP1120e.04.03.12.00)
152
+ State:: CS_SYNC
153
+ Flags:: 0
154
+ Cflag:: 0
155
+ Socket:: udp:10.130.8.21:5060
156
+ Methods:: 7551
157
+ AOR:: 7747
158
+ Contact:: sip:7747@10.132.113.198 Q=
159
+ Expires:: 69
160
+ Callid:: 8c026d6f-9afd173e-c4d03305@10.132.113.198
161
+ Cseq:: 41286
162
+ User-agent:: PolycomSoundStationIP-SSIP_6000-UA/3.3.5.0247_0004f2f18103
163
+ State:: CS_SYNC
164
+ Flags:: 0
165
+ Cflag:: 0
166
+ Socket:: udp:10.130.8.21:5060
167
+ Methods:: 8063
168
+