opensips-mi 0.0.11 → 1.1.0

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.
@@ -1,45 +1,61 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Opensips
2
4
  module MI
5
+ # core class to send command to MI
6
+ # and return responses
3
7
  class Command
8
+ attr_reader :transp
9
+
4
10
  EVENTNOTIFY = {
5
11
  # Aastra
6
- aastra_check_cfg: 'check-sync',
7
- aastra_xml: 'aastra-xml',
12
+ aastra_check_cfg: "check-sync",
13
+ aastra_xml: "aastra-xml",
8
14
  # Digium
9
- digium_check_cfg: 'check-sync',
15
+ digium_check_cfg: "check-sync",
10
16
  # Linksys
11
- linksys_cold_restart: 'reboot_now',
12
- linksys_warm_restart: 'restart_now',
17
+ linksys_cold_restart: "reboot_now",
18
+ linksys_warm_restart: "restart_now",
13
19
  # Polycom
14
- polycom_check_cfg: 'check-sync',
20
+ polycom_check_cfg: "check-sync",
15
21
  # Sipura
16
- sipura_check_cfg: 'resync',
17
- sipura_get_report: 'report',
22
+ sipura_check_cfg: "resync",
23
+ sipura_get_report: "report",
18
24
  # Snom
19
- snom_check_cfg: 'check-sync;reboot=false',
20
- snom_reboot: 'check-sync;reboot=true',
25
+ snom_check_cfg: "check-sync;reboot=false",
26
+ snom_reboot: "check-sync;reboot=true",
21
27
  # Cisco
22
- cisco_check_cfg: 'check-sync',
28
+ cisco_check_cfg: "check-sync",
23
29
  # Avaya
24
- avaya_check_cfg: 'check-sync',
25
- }
26
-
27
- # Interface to mi methods direct call
28
- def method_missing(md, *params, &block)
29
- response = command md.to_s, params
30
- # return special helper output if exists
31
- return response unless response.success
32
- if response.respond_to?(md)
33
- response.send md
34
- else
35
- response
36
- end
30
+ avaya_check_cfg: "check-sync"
31
+ }.freeze
32
+
33
+ def initialize(transp)
34
+ @transp = transp
35
+ end
36
+
37
+ # prepare args, pipe them to send to MI using transport
38
+ # and finally format response
39
+ def command(*params)
40
+ raise ErrorParams, "command missing method name" if params.empty?
41
+
42
+ transp.adapter_request(*params)
43
+ .then { |args| transp.send(*args) }
44
+ .then { |resp| transp.adapter_response(resp) }
45
+ end
46
+
47
+ # meta methods call directly
48
+ def method_missing(cmd, *args)
49
+ command(cmd.to_s, *args)
37
50
  end
38
51
 
52
+ def respond_to_missing?(_name, _include_private = false) = true
53
+
39
54
  # = Interface to t_uac_dlg function of transaction (tm) module
40
55
  # Very cool method from OpenSIPs. Can generate and send SIP request method to destination.
41
56
  # Example of usage:
42
- # - Send NOTIFY with special Event header to force restart SIP phone (equivalent of ASterisk's "sip notify peer")
57
+ # - Send NOTIFY with special Event header to force restart SIP phone
58
+ # (equivalent of ASterisk's "sip notify peer")
43
59
  # - Send PUBLISH to trigger device state change notification
44
60
  # - Send REFER to transfer call
45
61
  # - etc., etc., etc.
@@ -50,10 +66,6 @@ module Opensips
50
66
  # Example:
51
67
  # hf["From"] => "Alice Liddell <sip:alice@wanderland.com>;tag=843887163"
52
68
  #
53
- # Special "nl" header with any value is used to input additional "\r\n". This is
54
- # useful, for example, for message-summary event to separate application body. This is
55
- # because t_uac_dlg expect body parameter as xml only.
56
- #
57
69
  # Thus, using multiple headers with same header-name is not possible with header hash.
58
70
  # However, it is possible to use multiple header-values comma separated (rfc3261, section 7.3.1):
59
71
  # hf["Route"] => "<sip:alice@atlanta.com>, <sip:bob@biloxi.com>"
@@ -67,60 +79,52 @@ module Opensips
67
79
  # == Parameters
68
80
  # method: SIP request method (NOTIFY, PUBLISH etc)
69
81
  # ruri: Request URI, ex.: sip:555@10.0.0.55:5060
70
- # hf: Headers array. Additional headers will be added to request.
82
+ # hf: Headers array. Additional headers will be added to request.
71
83
  # At least "From" and "To" headers must be specify
72
84
  # nhop: Next hop SIP URI (OBP); use "." if no value.
73
- # socket: Local socket to be used for sending the request; use "." if no value. Ex.: udp:10.130.8.21:5060
74
- # body: (optional, may not be present) request body (if present, requires the "Content-Type" and "Content-length" headers)
75
- #
76
- def uac_dlg method, ruri, hf, next_hop = ?., socket = ?., body = nil
77
- mandatory_hf = Array['To', 'From']
78
- mandatory_hf += ['Content-Type'] unless body.nil?
79
- mandatory_hf.map{|h|h.downcase}.each do |n|
80
- raise ArgumentError,
81
- "Missing mandatory header #{n.capitalize}" unless hf.keys.map{|h| h.downcase}.include?(n)
82
- end
83
- # compile headers to string
84
- headers = hf.map{|name,val| name.eql?("nl") ? "" : "#{name}: #{val}"}.join "\r\n"
85
+ # socket: Local socket to be used for sending the request; use "." if no value.
86
+ # Ex.: udp:10.130.8.21:5060
87
+ # body: (optional, may not be present) request body (if present, requires the "Content-Type"
88
+ # and "Content-length" headers)
89
+ def uac_dlg(method, ruri, hdrs, next_hop = ".", socket = ".", body = nil)
90
+ validate_hf(hdrs)
91
+
92
+ headers = hdrs.map { |name, val| "#{name}: #{val}" }.join("\r\n")
85
93
  headers << "\r\n\r\n"
86
94
 
87
- # set_header is a hack for xmlrpc which fails if headers are quoted
88
- params = [method, ruri, next_hop, socket, set_header(headers)]
95
+ params = [method, ruri, next_hop, socket, headers]
89
96
  params << body unless body.nil?
90
- # send it and return Response
91
- command 't_uac_dlg', params
97
+ command "t_uac_dlg", params
92
98
  end
93
99
 
94
100
  # = NOTIFY check-sync like event
95
- # NOTIFY Events to restart phone, force configuration reload or
96
- # report for some SIP IP phone models.
101
+ # NOTIFY Events to restart phone, force configuration reload or
102
+ # report for some SIP IP phone models.
97
103
  # The events list was taken from Asterisk configuration file (sip_notify.conf)
98
104
  # Note that SIP IP phones usually should be configured to accept special notify
99
105
  # event to reboot. For example, Polycom configuration option to enable special
100
106
  # event would be:
101
107
  # voIpProt.SIP.specialEvent.checkSync.alwaysReboot="1"
102
108
  #
103
- # This function will generate To/From/Event headers. Will use random tag for
104
- # From header.
109
+ # This function will generate To/From/Event headers. Will use random tag for
110
+ # From header.
105
111
  # *NOTE*: This function will not generate To header tag. This is not complying with
106
- # SIP protocol specification (rfc3265). NOTIFY must be part of a subscription
112
+ # SIP protocol specification (rfc3265). NOTIFY must be part of a subscription
107
113
  # dialog. However, it works for the most of the SIP IP phone models.
108
114
  # == Parameters
109
- # - uri: Valid client contact URI (sip:alice@10.0.0.100:5060).
115
+ # - uri: Valid client contact URI (sip:alice@10.0.0.100:5060).
110
116
  # To get client URI use *ul_show_contact => contact* function
111
117
  # - event: One of the events from EVENTNOTIFY constant hash
112
- # - hf: Header fields. Add To/From header fields here if you do not want them
118
+ # - hf: Header fields. Add To/From header fields here if you do not want them
113
119
  # to be auto-generated. Header field example:
114
120
  # hf['To'] => '<sip:alice@wanderland.com>'
115
- #
116
- def event_notify uri, event, hf = {}
117
- raise ArgumentError,
118
- "Invalid notify event: #{event.to_s}" unless EVENTNOTIFY.keys.include?(event)
119
- hf['To'] = "<#{uri}>" unless hf.keys.map{|k|k.downcase}.include?('to')
120
- hf['From'] = "<#{uri}>;tag=#{SecureRandom.hex}" unless hf.keys.map{|k|k.downcase}.include?('from')
121
- hf['Event'] = EVENTNOTIFY[event]
122
-
123
- uac_dlg "NOTIFY", uri, hf
121
+ def event_notify(uri, event, hdrs = {})
122
+ hnames = hdrs.keys.map { |k| k.to_s.downcase } || []
123
+ hdrs["To"] = "<#{uri}>" unless hnames.include?("to")
124
+ hdrs["From"] = "<#{uri}>;tag=#{rand(1 << 32)}" unless hnames.include?("from")
125
+ hdrs["Event"] = EVENTNOTIFY[event] || event.to_s
126
+
127
+ uac_dlg "NOTIFY", uri, hdrs
124
128
  end
125
129
 
126
130
  # = Presence MWI
@@ -135,39 +139,28 @@ module Opensips
135
139
  # - old: (optional) Old messages
136
140
  # - urg_new: (optional) New urgent messages
137
141
  # - urg_old: (optional) Old urgent messages
138
- #
139
- def mwi_update uri, vmaccount, new, old = 0, urg_new = 0, urg_old = 0
140
- mbody = Hash[
141
- 'Messages-Waiting' => (new > 0 ? "yes" : "no"),
142
- 'Message-Account' => vmaccount,
143
- 'Voice-Message' => "#{new}/#{old} (#{urg_new}/#{urg_old})",
144
- ]
145
- hf = Hash[
146
- 'To' => "<#{uri}>",
147
- 'From' => "<#{uri}>;tag=#{SecureRandom.hex}",
148
- 'Event' => "message-summary",
149
- 'Subscription-State'=> "active",
150
- 'Content-Type' => "application/simple-message-summary",
151
- 'nl' => "",
152
- ]
153
-
154
- uac_dlg "NOTIFY", uri, hf.merge(mbody)
142
+ def mwi_update(uri, vmaccount, newmsgs, old = 0, urg_new = 0, urg_old = 0)
143
+ hbody = { "Messages-Waiting" => (newmsgs.positive? ? "yes" : "no"),
144
+ "Message-Account" => vmaccount,
145
+ "Voice-Message" => "#{newmsgs}/#{old} (#{urg_new}/#{urg_old})" }
146
+ hdrs = { "To" => "<#{uri}>",
147
+ "From" => "<#{uri}>;tag=#{rand(1 << 32)}",
148
+ "Event" => "message-summary",
149
+ "Subscription-State" => "active",
150
+ "Content-Type" => "application/simple-message-summary" }
151
+
152
+ body = hbody.map { |k, v| "#{k}: #{v}" }.join("\r\n") << "\r\n\r\n"
153
+ uac_dlg "NOTIFY", uri, hdrs, ".", ".", body
155
154
  end
156
155
 
157
156
  private
158
- def set_header(header);"\"#{header}\"";end
159
-
160
- def host_valid? params
161
- raise ArgumentError,
162
- 'Missing socket host' if params[:host].nil?
163
- raise ArgumentError,
164
- 'Missing socket port' if params[:port].nil?
165
- Socket.getaddrinfo(params[:host], nil) rescue
166
- raise SocketError, "Invalid host #{params[:host]}"
167
- raise SocketError,
168
- "Invalid port #{params[:port]}" unless (1..(2**16-1)).include?(params[:port])
169
- true
170
- end
157
+
158
+ def validate_hf(headers)
159
+ names = headers&.keys&.map { |k| k.to_s.downcase } || []
160
+ return if names.include?("to") && names.include?("from")
161
+
162
+ raise ArgumentError, "invalid headers value. must be a hash and have 'To' and 'From' headers"
163
+ end
171
164
  end
172
165
  end
173
166
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Opensips
6
+ module MI
7
+ module Transport
8
+ # abstruct class for transport protocols
9
+ class Abstract
10
+ # send a command to connection and return response
11
+ def send(_command)
12
+ raise NotImplementedError
13
+ end
14
+
15
+ # request adapter method
16
+ # by default if does jsonrpc v2 as string
17
+ # xmlrpc overload this message
18
+ def adapter_request(cmd, *args)
19
+ rpc = {
20
+ jsonrpc: "2.0",
21
+ id: rand(1 << 16),
22
+ method: cmd
23
+ }
24
+
25
+ unless args.empty?
26
+ params = args.flatten
27
+ rpc[:params] = params[0].is_a?(Hash) ? params[0] : params
28
+ end
29
+
30
+ JSON.generate(rpc)
31
+ end
32
+
33
+ # response adapter by default parses jsonrpc response
34
+ # to an object. xmlrp overloads this method
35
+ def adapter_response(body)
36
+ resp = JSON.parse(body)
37
+ if resp["result"]
38
+ { result: resp["result"] }
39
+ elsif resp["error"]
40
+ { error: resp["error"] }
41
+ else
42
+ { error: { "message" => "invalid response: #{body}" } }
43
+ end
44
+ rescue JSON::ParserError => e
45
+ { error: { "message" => %(JSON::ParserError: #{e}) } }
46
+ end
47
+
48
+ protected
49
+
50
+ def raise_invalid_params
51
+ raise Opensips::MI::ErrorParams,
52
+ "invalid params. Expecting a hash with :url and optional :timeout"
53
+ end
54
+
55
+ def seturi(url)
56
+ @uri = URI(url)
57
+ rescue URI::InvalidURIError
58
+ raise Opensips::MI::ErrorParams,
59
+ "invalid http host url: #{url}"
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,42 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "abstract"
4
+ require "socket"
5
+ require "timeout"
6
+
1
7
  module Opensips
2
8
  module MI
3
9
  module Transport
4
- class Datagram < Opensips::MI::Command
5
- RECVMAXLEN = 2**16 - 1
6
- TIMEOUT = 3
10
+ # datagram UDP transport to communicate with MI
11
+ class Datagram < Abstract
12
+ def initialize(args)
13
+ super()
14
+ raise_invalid_params unless args.is_a?(Hash)
15
+ @host, @port, @timeout = args.values_at(:host, :port, :timeout)
16
+ raise_invalid_params if @host.nil? || @port.nil?
17
+ raise_invalid_port unless @port.to_i.between?(1, 1 << 16)
18
+ @timeout ||= 5
19
+ connect
20
+ end
7
21
 
8
- class << self
9
- def init(params)
10
- Datagram.new params
22
+ def send(command)
23
+ Timeout.timeout(
24
+ @timeout,
25
+ Opensips::MI::ErrorSendTimeout,
26
+ "timeout send command to #{@host}:#{@port} within #{@timeout} sec"
27
+ ) do
28
+ @sock.send command, 0
29
+ msg, = @sock.recvfrom(1500)
30
+ msg
11
31
  end
12
32
  end
13
33
 
14
- def initialize(params)
15
- host_valid? params
16
- @sock = Socketry::UDP::Socket.connect(params[:host], params[:port])
17
- @timeout = params[:timeout].to_i
18
- end
34
+ protected
19
35
 
20
- def command(cmd, params = [])
21
- request = ":#{cmd}:\n"
22
- params.each do |c|
23
- request << "#{c}\n"
24
- end
25
- response = send(request)
26
- Opensips::MI::Response.new response.split(?\n)
36
+ def raise_invalid_params
37
+ raise Opensips::MI::ErrorParams,
38
+ "invalid params. Expecting a hash with :host, :port and optional :timeout"
27
39
  end
28
40
 
29
- def tout
30
- @timeout > 0 ? @timeout : TIMEOUT
41
+ def raise_invalid_port
42
+ raise Opensips::MI::ErrorParams, "invalid port '#{@port}'"
31
43
  end
32
44
 
33
45
  private
34
- def send(request)
35
- @sock.send request
36
- response = @sock.recvfrom RECVMAXLEN, timeout: tout
37
- response.message
38
- rescue => e
39
- "500 #{e}"
46
+
47
+ def connect
48
+ @sock = UDPSocket.new
49
+ Timeout.timeout(
50
+ @timeout,
51
+ Opensips::MI::ErrorResolveTimeout,
52
+ "failed to resolve address #{@host}:#{@port} within #{@timeout} sec"
53
+ ) { @sock.connect(@host, @port) }
40
54
  end
41
55
  end
42
56
  end
@@ -1,89 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "abstract"
4
+ require "fcntl"
5
+ require "pathname"
6
+ require "tempfile"
7
+ require "timeout"
8
+
1
9
  module Opensips
2
10
  module MI
3
11
  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
12
+ # FIFO transport to communicate with MI
13
+ class Fifo < Abstract
14
+ def initialize(args)
15
+ super()
16
+ raise_invalid_params unless args.is_a?(Hash)
17
+ @fifo_name, @reply_dir, @timeout = args.values_at(:fifo_name, :reply_dir, :timeout)
18
+ raise_invalid_params if @fifo_name.nil?
19
+ @reply_dir ||= "/tmp"
20
+ @timeout ||= 5
15
21
  end
16
22
 
17
- def initialize(params)
18
- # set default values
19
- @reply_fifo = if params[:reply_fifo].nil?
20
- "opensips_reply_" << SecureRandom.hex[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.exist? @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.exist? @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)})
23
+ def send(rpc)
24
+ reply_file = create_reply_file
25
+ write(reply_file, rpc)
26
+ read(reply_file)
27
+ ensure
28
+ reply_file.unlink if reply_file&.exist?
46
29
  end
47
30
 
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
31
+ protected
32
+
33
+ def raise_invalid_params
34
+ raise Opensips::MI::ErrorParams,
35
+ "invalid params. Expecting a hash with :fifo_name and optional :reply_dir"
55
36
  end
56
37
 
57
- def command(cmd, params = [])
58
- fd_w = IO::sysopen(@fifo_name, Fcntl::O_WRONLY)
59
- fifo_w = IO.open(fd_w)
38
+ private
60
39
 
61
- request = ":#{cmd}:#{@reply_fifo}\n"
62
- params.each do |c|
63
- request << "#{c}\n"
40
+ def write(reply_file, rpc)
41
+ Timeout.timeout(@timeout, Opensips::MI::ErrorSendTimeout) do
42
+ fifo_wr = IO.open(IO.sysopen(@fifo_name, Fcntl::O_WRONLY))
43
+ fifo_wr.syswrite(%(:#{reply_file.basename}:#{rpc}\n))
44
+ fifo_wr.flush
45
+ ensure
46
+ fifo_wr&.close
64
47
  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
48
  end
82
49
 
83
- def self.finalize(reply_file)
84
- File.unlink(reply_file) if File.exist?(reply_file)
50
+ def read(reply_file)
51
+ Timeout.timeout(@timeout, Opensips::MI::ErrorSendTimeout) do
52
+ fifo_rd = IO.open(IO.sysopen(reply_file, Fcntl::O_RDONLY))
53
+ fifo_rd.read
54
+ ensure
55
+ fifo_rd&.close
56
+ end
85
57
  end
86
58
 
59
+ def create_reply_file
60
+ tmpfile = Tempfile.create("opensips-mi-reply-", @reply_dir)
61
+ File.unlink(tmpfile)
62
+
63
+ File.mkfifo(tmpfile)
64
+ File.chmod(0o666, tmpfile)
65
+ Pathname.new(tmpfile)
66
+ end
87
67
  end
88
68
  end
89
69
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require_relative "abstract"
5
+
6
+ module Opensips
7
+ module MI
8
+ module Transport
9
+ # HTTP transport to communicate with MI
10
+ class HTTP < Abstract
11
+ def initialize(args)
12
+ super()
13
+ raise_invalid_params unless args.is_a?(Hash)
14
+ url, @timeout = args.values_at(:url, :timeout)
15
+ raise_invalid_params if url.nil?
16
+ seturi(url)
17
+ @timeout ||= 5
18
+ connect
19
+ end
20
+
21
+ def send(cmd)
22
+ resp = @client.post(@uri.path, cmd, { "Content-Type" => "application/json" })
23
+ unless resp.code.eql? "200"
24
+ raise Opensips::MI::ErrorHTTPReq,
25
+ "invalid MI HTTP response: #{resp.message}"
26
+ end
27
+ resp.body
28
+ end
29
+
30
+ private
31
+
32
+ def connect
33
+ @client = Net::HTTP.new(@uri.host, @uri.port)
34
+ @client.read_timeout = @timeout
35
+ @client.write_timeout = @timeout
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,35 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "abstract"
4
+ require "xmlrpc/client"
5
+
1
6
  module Opensips
2
7
  module MI
3
8
  module Transport
4
- class Xmlrpc < Opensips::MI::Command
5
- RPCSEG = 'RPC2'
6
- class << self
7
- def init(params)
8
- Xmlrpc.new params
9
- end
9
+ # XML-RPC protocol for MI
10
+ class Xmlrpc < Abstract
11
+ def initialize(args)
12
+ super()
13
+ raise_invalid_params unless args.is_a?(Hash)
14
+ url, @timeout = args.values_at(:url, :timeout)
15
+ raise_invalid_params if url.nil?
16
+ seturi(url)
17
+ @client = XMLRPC::Client.new2(@uri.to_s, nil, @timeout)
10
18
  end
11
19
 
12
- def initialize(params)
13
- host_valid? params
14
- uri = "http://#{params[:host]}:#{params[:port]}/#{RPCSEG}"
15
- @client = XMLRPC::Client.new_from_uri(uri, nil, 3)
16
- rescue => e
17
- raise e.class,
18
- "Can not connect OpenSIPs server.\n#{e.message}"
20
+ def send(*cmd)
21
+ @client.call(*cmd)
22
+ rescue XMLRPC::FaultException => e
23
+ { error: { "message" => e.message } }
24
+ rescue StandardError => e
25
+ raise Opensips::MI::ErrorHTTPReq, e
19
26
  end
20
27
 
21
- def command(cmd, params = [])
22
- response = ["200 OK"]
23
- response += @client.call(cmd, *params).split(?\n)
24
- response << ""
25
- rescue => e
26
- response = ["600 " << e.message]
27
- ensure
28
- return Opensips::MI::Response.new response
28
+ # overload resonse adapter for xmlrpc
29
+ def adapter_response(resp)
30
+ if resp[:error]
31
+ resp
32
+ else
33
+ { result: resp }
34
+ end
29
35
  end
30
36
 
31
- def set_header(header);header;end
37
+ # overload request adapter for xmlrpc
38
+ def adapter_request(*args)
39
+ args.flatten => [cmd, *rest]
40
+ return [cmd] if rest.empty?
41
+
42
+ rest = rest.flatten
43
+ return [cmd, rest.first.map { |_, v| v }].flatten if rest.first.is_a?(Hash)
32
44
 
45
+ [cmd, rest].flatten
46
+ end
33
47
  end
34
48
  end
35
49
  end
@@ -1,9 +1,13 @@
1
- require_relative "transport/fifo"
1
+ # frozen_string_literal: true
2
+
2
3
  require_relative "transport/datagram"
4
+ require_relative "transport/fifo"
5
+ require_relative "transport/http"
3
6
  require_relative "transport/xmlrpc"
4
7
 
5
8
  module Opensips
6
9
  module MI
10
+ # module transport protocols implementation
7
11
  module Transport
8
12
  end
9
13
  end