opensips-mi 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MjczNzYxY2E2ZWRkNjEwOWFiYThmNmQ2YTU1YTA2MzBlMjZlZDYzNg==
5
+ data.tar.gz: !binary |-
6
+ MjIwNDQwZTk0NTY4NDVmNmJjODM0ZjBiYTRiNDVmNzExODVhNGY1MQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ YThmNzdmNzRkNmZiOTdlMWE2NDJkZWZhMTU4MTEzNGU1MDMzNjlmNGVkMmY3
10
+ ZGEyZTJjYWQyOTg0OTA4YTIyYTg1OTczNTI4MzExNTNlYWM5NzRiMTkwOTQz
11
+ Mzg1ZjM1ODYyZTdiZDcwMmM0NmUxMzgxNWI2OTAyNDMxOWY1Y2Y=
12
+ data.tar.gz: !binary |-
13
+ YzMwZTc1OWNjM2FkYTZmY2ZhOTk0YmQzNDJlN2JjNzI0OGU0YTcyMjE5ZTBj
14
+ ZmFiYmU1ZmRhMTRkODg5NjhmM2MzNTMyNmJiMDk1MTJhNjM5OWI5MjMwZDdh
15
+ NmJhYWNiODBkZTZlYzdmNzYyYTBhNzI3ZWI0YTE5YmY4YTRmNjQ=
data/.gitignore ADDED
@@ -0,0 +1,28 @@
1
+ # Ignore temporary files
2
+ .*swp
3
+ *~
4
+
5
+ *.gem
6
+ *.rbc
7
+ Gemfile*.lock
8
+ .bundle
9
+ .config
10
+ coverage
11
+ InstalledFiles
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+
20
+ # YARD artifacts
21
+ .yardoc
22
+ _yardoc
23
+ doc/
24
+
25
+ .DS_Store
26
+ results.html
27
+ html
28
+
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Stas Kobzar
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,275 @@
1
+ # Opensips::Mi
2
+
3
+ OpenSIPs management interface API.
4
+ This library support following management interface OpenSIPs modules:
5
+
6
+ * mi_fifo
7
+ * mi_datagram
8
+ * mi_xmlrpc
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'opensips-mi'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install opensips-mi
23
+
24
+ ## Connecting management interfaces
25
+
26
+ ### Generic connection interface
27
+
28
+ Using generic function to connect management interface:
29
+ ```ruby
30
+ require 'opensips-mi'
31
+ Opensips::MI.connect INTERFACE, PARAMS
32
+ ```
33
+ Parameters:
34
+
35
+ *INTRFACE* - interface key. One of the following:
36
+
37
+ * :fifo
38
+ * :datagram
39
+ * :xmlrpc
40
+
41
+ *PARAMS* - connection parameters. Depends on interface. See below.
42
+
43
+ This function will raise exceptions if there are parameters or environment errors.
44
+ Function returns instance of one of the following classes:
45
+
46
+ * Opensips::MI::Transport::Fifo
47
+ * Opensips::MI::Transport::Datagram
48
+ * Opensips::MI::Transport::Xmlrpc
49
+
50
+ ### FIFO
51
+
52
+ ```ruby
53
+ require 'opensips-mi'
54
+ opensips = Opensips::MI.connect :fifo,
55
+ :fifo_name => '/tmp/opensips_fifo',
56
+ :reply_fifo => 'opensips_reply' . $$,
57
+ :reply_dir => '/tmp'
58
+
59
+ ```
60
+
61
+ **Parameters hash:**
62
+
63
+ * fifo_name: OpenSIPs fifo file. See mi_fifo module parameter: `modparam("mi_fifo", "fifo_name", "/tmp/opensips_fifo")`.
64
+ * reply_fifo: (OPTIONAL) Name of the reply fifo file. If not used will generate random file in *reply_dir* and delete after use.
65
+ * reply_dir: (OPTIONAL) Path to directory of reply fifo file.
66
+
67
+ ### Datagram
68
+ ```ruby
69
+ require 'opensips-mi'
70
+ opensips = Opensips::MI.connect :datagram,
71
+ :host => "sipproxy.com",
72
+ :port => 8809
73
+ ```
74
+ **Parameters hash:**
75
+
76
+ * host: Hostname or IP address of OpenSIPs server
77
+ * port: Datagram port. See mi_datagram module configuration parameter: `modparam("mi_datagram", "socket_name", "udp:192.168.2.133:8809")`
78
+
79
+ ### XMLRPC
80
+ ```ruby
81
+ require 'opensips-mi'
82
+ opensips = Opensips::MI.connect :xmlrpc,
83
+ :host => "192.168.122.128",
84
+ :port => 8080
85
+ ```
86
+ **Parameters hash:**
87
+
88
+ * host: Hostname or IP address of OpenSIPs server
89
+ * port: Datagram port. See mi_xmlrpc module configuration parameter: `modparam("mi_xmlrpc", "port", 8080)`
90
+
91
+ ### Command function
92
+
93
+ Function "*command*" expects fifo command as first argument following by command parameters.
94
+ For example:
95
+
96
+ ```ruby
97
+ require 'opensips-mi'
98
+ opensips = Opensips::MI.connect :fifo,
99
+ :fifo_name => '/tmp/opensips_fifo'
100
+
101
+ opensips.command('which')
102
+ opensips.command('get_statistics', 'dialog','tm')
103
+ ```
104
+
105
+ ### Command method interface
106
+
107
+ It is also possible to use command names as a method interface:
108
+ ```ruby
109
+ require 'opensips-mi'
110
+ opensips = Opensips::MI.connect :datagram,
111
+ :host => "192.168.122.128",
112
+ :port => 8809
113
+
114
+ opensips.which
115
+ opensips.get_statistics('dialog','tm')
116
+ opensips.uptime
117
+ opensips.ul_show_contact('location', 'alice')
118
+ ```
119
+
120
+ Such methods first check if fifo function exists against `which` fifo command.
121
+
122
+ ### Response
123
+
124
+ Command function returns `Opensips::MI::Response` class. This class containe following class members which can be used to process responses:
125
+
126
+ * code: *Integer* Response code: 200, 404 etc
127
+ * message: *String* Response messages: "OK", "Bad headers"
128
+ * rawdata: *Array* Raw response data as array
129
+ * result: *Mixed* Struct/Hash/Array/Nil. This is member used by helper response methods for pretty formated result. See below.
130
+
131
+ ### Response helpers methods
132
+
133
+ There are several helper methods which return conveniently formatted data:
134
+ * ul_dump
135
+ * uptime
136
+ * cache_fetch
137
+ * ul_show_contact
138
+ * dlg_list
139
+
140
+ See example files for details.
141
+
142
+ ## Dialog methods
143
+
144
+ Dialog methods are interface to `t_uac_dlg` function of OpenSIPs' *tm* (transactions) module.
145
+ Module generates and sends a local SIP request.
146
+
147
+ ### Interface to t_uac_dlg function of transaction (tm) module
148
+ Very cool method from OpenSIPs. Can generate and send SIP request method to destination.
149
+ Example of usage:
150
+ * Send NOTIFY with special Event header to force restart SIP phone (equivalent of ASterisk's "sip notify peer")
151
+ * Send PUBLISH to trigger device state change notification
152
+ * Send REFER to transfer call
153
+ * etc., etc., etc.
154
+
155
+ **Headers***
156
+ Headers parameter "hf" is a hash of headers of format:
157
+ ```
158
+ header-name => header-value
159
+ ```
160
+ Example:
161
+ ```
162
+ hf["From"] => "Alice Liddell <sip:alice@wanderland.com>;tag=843887163"
163
+ ```
164
+
165
+ Special "nl" header with any value is used to input additional "\r\n". This is
166
+ useful, for example, for message-summary event to separate application body. This is
167
+ because t_uac_dlg expect body parameter as xml only.
168
+
169
+ Thus, using multiple headers with same header-name is not possible with header hash.
170
+ However, it is possible to use multiple header-values comma separated (rfc3261, section 7.3.1):
171
+ ```
172
+ hf["Route"] => "<sip:alice@atlanta.com>, <sip:bob@biloxi.com>"
173
+ ```
174
+ Is equivalent to:
175
+ ```
176
+ Route: <sip:alice@atlanta.com>
177
+ Route: <sip:bob@biloxi.com>
178
+ ```
179
+ If there is headers To and From not found, then exception ArgumentError is raised. Also if
180
+ body part present, Content-Type and Content-length are also mandatory and exception is raised.
181
+
182
+ **Parameters**
183
+ * method: SIP request method (NOTIFY, PUBLISH etc)
184
+ * ruri: Request URI, ex.: sip:555@10.0.0.55:5060
185
+ * hf: Headers array. Additional headers will be added to request. At least "From" and "To" headers must be specify.
186
+ * nhop: Next hop SIP URI (OBP); use "." if no value.
187
+ * socket: Local socket to be used for sending the request; use "." if no value. Ex.: udp:10.130.8.21:5060
188
+ * body: (optional, may not be present) request body (if present, requires the "Content-Type" and "Content-length" headers)
189
+
190
+ **Example of usage**
191
+ ```ruby
192
+ opensips.uac_dlg "NOTIFY", "sip:alice@127.0.0.1:5066",
193
+ {
194
+ "From" => "<sip:alice@wanderland.com>;tag=8755a8d01a12f7e903a6f4ccaf393f04",
195
+ "To" => "<sip:alice@wanderland.com>",
196
+ "Event" => "check-sync"
197
+ }
198
+ ```
199
+
200
+ ### NOTIFY check-sync like event
201
+ NOTIFY Events to restart phone, force configuration reload or
202
+ report for some SIP IP phone models.
203
+ The events list was taken from Asterisk configuration file (sip_notify.conf)
204
+ Note that SIP IP phones usually should be configured to accept special notify
205
+ event to reboot. For example, Polycom configuration option to enable special
206
+ event would be:
207
+ ```
208
+ voIpProt.SIP.specialEvent.checkSync.alwaysReboot="1"
209
+ ```
210
+ This function will generate To/From/Event headers. Will use random tag for
211
+ From header.
212
+ *NOTE*: This function will not generate To header tag. This is not complying with
213
+ SIP protocol specification (rfc3265). NOTIFY must be part of a subscription
214
+ dialog. However, it works for the most of the SIP IP phone models.
215
+ **Parameters**
216
+ * uri: Valid client contact URI (sip:alice@10.0.0.100:5060). To get client URI use *ul_show_contact => contact* function
217
+ * event: One of the events from EVENTNOTIFY constant hash
218
+ * hf: Header fields. Add To/From header fields here if you do not want them to be auto-generated. Header field example: `hf['To'] => '<sip:alice@wanderland.com>'`
219
+
220
+ **Example of usage**
221
+ Will reboot Polycom phone:
222
+ ```ruby
223
+ opensips.event_notify 'sip:alice@127.0.0.1:5060', :polycom_check_cfg
224
+ ```
225
+
226
+ **List of available events' keys:**
227
+
228
+ * :astra_check_cfg
229
+ * :aastra_xml
230
+ * :digium_check_cfg
231
+ * :linksys_cold_restart
232
+ * :linksys_warm_restart
233
+ * :polycom_check_cfg
234
+ * :sipura_check_cfg
235
+ * :sipura_get_report
236
+ * :snom_check_cfg
237
+ * :snom_reboot
238
+ * :cisco_check_cfg
239
+ * :avaya_check_cfg
240
+
241
+ ### Presence MWI
242
+
243
+ Send message-summary NOTIFY Event to update phone voicemail status.
244
+
245
+ **Parameters**
246
+
247
+ * uri: Request URI (sip:alice@wanderland.com:5060). To get client URI use *ul_show_contact => contact* function
248
+ * vmaccount:Message Account value. Ex.: sip:*97@asterisk.com
249
+ * new: Number of new messages. If more than 0 then Messages-Waiting header will be "yes". Set to 0 to clear phone MWI
250
+ * old: (optional) Old messages
251
+ * urg_new: (optional) New urgent messages
252
+ * urg_old: (optional) Old urgent messages
253
+
254
+ **Example of usage**
255
+ ```ruby
256
+ opensips.mwi_update 'sip:alice@wanderland.com:5060', 'sip:*97@voicemail.pbx.com', 5
257
+ ```
258
+
259
+ ## Examples
260
+
261
+ There are some sample files in *examples* directory.
262
+
263
+ ## TODO:
264
+
265
+ Support for mi_xmlrpc_ng
266
+
267
+ ----
268
+ ## Contributing
269
+
270
+ 1. Fork it
271
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
272
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
273
+ 4. Push to the branch (`git push origin my-new-feature`)
274
+ 5. Create new Pull Request
275
+
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/clean"
3
+ require 'opensips/mi/version'
4
+
5
+ require "rdoc/task"
6
+ Rake::RDocTask.new do |rd|
7
+ rd.rdoc_dir = 'rdoc'
8
+ rd.main = "README.md"
9
+ rd.rdoc_files.include("README.md","lib/**/*.rb")
10
+ rd.title = "OpenSIPs management interface " << Opensips::MI::VERSION
11
+ end
12
+
13
+ require 'rake/testtask'
14
+ Rake::TestTask.new(:test) do |test|
15
+ test.libs << 'lib' << 'test'
16
+ test.pattern = 'test/**/test_*.rb'
17
+ #test.verbose = true
18
+ end
19
+
20
+ task :default => :test
data/lib/opensips.rb ADDED
@@ -0,0 +1,4 @@
1
+ require "opensips/mi"
2
+
3
+ module Opensips
4
+ end
@@ -0,0 +1,23 @@
1
+ require 'ostruct'
2
+ require 'fcntl'
3
+ require 'securerandom'
4
+ require 'socket'
5
+ require 'xmlrpc/client'
6
+
7
+ require "opensips/mi/version"
8
+ require "opensips/mi/response"
9
+ require "opensips/mi/command"
10
+ require "opensips/mi/transport"
11
+
12
+ module Opensips
13
+ module MI
14
+ def self.connect(transport, params)
15
+ class_name = transport.to_s.capitalize
16
+ # send to transport class
17
+ Transport.const_get(class_name).init params
18
+ rescue NameError => e
19
+ raise NameError, "Unknown transport method: " << transport.to_s
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,167 @@
1
+ module Opensips
2
+ module MI
3
+ class Command
4
+ EVENTNOTIFY = {
5
+ # Aastra
6
+ aastra_check_cfg: 'check-sync',
7
+ aastra_xml: 'aastra-xml',
8
+ # Digium
9
+ digium_check_cfg: 'check-sync',
10
+ # Linksys
11
+ linksys_cold_restart: 'reboot_now',
12
+ linksys_warm_restart: 'restart_now',
13
+ # Polycom
14
+ polycom_check_cfg: 'check-sync',
15
+ # Sipura
16
+ sipura_check_cfg: 'resync',
17
+ sipura_get_report: 'report',
18
+ # Snom
19
+ snom_check_cfg: 'check-sync;reboot=false',
20
+ snom_reboot: 'check-sync;reboot=true',
21
+ # Cisco
22
+ cisco_check_cfg: 'check-sync',
23
+ # 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 'which'
30
+ raise NoMethodError,
31
+ "Method #{md} does not exists" unless response.rawdata.include?(md.to_s)
32
+ response = command md.to_s, params
33
+ # return special helper output if exists
34
+ return response unless response.success
35
+ if response.respond_to?(md)
36
+ response.send md
37
+ else
38
+ response
39
+ end
40
+ end
41
+
42
+ # = Interface to t_uac_dlg function of transaction (tm) module
43
+ # Very cool method from OpenSIPs. Can generate and send SIP request method to destination.
44
+ # Example of usage:
45
+ # - Send NOTIFY with special Event header to force restart SIP phone (equivalent of ASterisk's "sip notify peer")
46
+ # - Send PUBLISH to trigger device state change notification
47
+ # - Send REFER to transfer call
48
+ # - etc., etc., etc.
49
+ #
50
+ # == Headers
51
+ # Headers parameter "hf" is a hash of headers of format:
52
+ # header-name => header-value
53
+ # Example:
54
+ # hf["From"] => "Alice Liddell <sip:alice@wanderland.com>;tag=843887163"
55
+ #
56
+ # Special "nl" header with any value is used to input additional "\r\n". This is
57
+ # useful, for example, for message-summary event to separate application body. This is
58
+ # because t_uac_dlg expect body parameter as xml only.
59
+ #
60
+ # Thus, using multiple headers with same header-name is not possible with header hash.
61
+ # However, it is possible to use multiple header-values comma separated (rfc3261, section 7.3.1):
62
+ # hf["Route"] => "<sip:alice@atlanta.com>, <sip:bob@biloxi.com>"
63
+ # Is equivalent to:
64
+ # Route: <sip:alice@atlanta.com>
65
+ # Route: <sip:bob@biloxi.com>
66
+ #
67
+ # If there is headers To and From not found, then exception ArgumentError is raised. Also if
68
+ # body part present, Content-Type and Content-length are also mandatory and exception is raised.
69
+ #
70
+ # == Parameters
71
+ # method: SIP request method (NOTIFY, PUBLISH etc)
72
+ # ruri: Request URI, ex.: sip:555@10.0.0.55:5060
73
+ # hf: Headers array. Additional headers will be added to request.
74
+ # At least "From" and "To" headers must be specify
75
+ # nhop: Next hop SIP URI (OBP); use "." if no value.
76
+ # socket: Local socket to be used for sending the request; use "." if no value. Ex.: udp:10.130.8.21:5060
77
+ # body: (optional, may not be present) request body (if present, requires the "Content-Type" and "Content-length" headers)
78
+ #
79
+ def uac_dlg method, ruri, hf, next_hop = ?., socket = ?., body = nil
80
+ mandatory_hf = Array['To', 'From']
81
+ mandatory_hf += ['Content-Type', 'Content-Length'] unless body.nil?
82
+ mandatory_hf.map{|h|h.downcase}.each do |n|
83
+ raise ArgumentError,
84
+ "Missing mandatory header #{n.capitalize}" unless hf.keys.map{|h| h.downcase}.include?(n)
85
+ end
86
+ # compile headers to string
87
+ headers = hf.map{|name,val| name.eql?("nl") ? "" : "#{name}: #{val}"}.join "\r\n"
88
+ headers << "\r\n"
89
+
90
+ # hack for xmlrpc which fails if headers are quoted
91
+ headers = set_header(headers)
92
+ #params = [method, ruri, next_hop, socket, "\"#{headers}\""]
93
+ params = [method, ruri, next_hop, socket, headers]
94
+ params << body unless body.nil?
95
+ # send it and return Response
96
+ command 't_uac_dlg', params
97
+ end
98
+
99
+ # = NOTIFY check-sync like event
100
+ # NOTIFY Events to restart phone, force configuration reload or
101
+ # report for some SIP IP phone models.
102
+ # The events list was taken from Asterisk configuration file (sip_notify.conf)
103
+ # Note that SIP IP phones usually should be configured to accept special notify
104
+ # event to reboot. For example, Polycom configuration option to enable special
105
+ # event would be:
106
+ # voIpProt.SIP.specialEvent.checkSync.alwaysReboot="1"
107
+ #
108
+ # This function will generate To/From/Event headers. Will use random tag for
109
+ # From header.
110
+ # *NOTE*: This function will not generate To header tag. This is not complying with
111
+ # SIP protocol specification (rfc3265). NOTIFY must be part of a subscription
112
+ # dialog. However, it works for the most of the SIP IP phone models.
113
+ # == Parameters
114
+ # - uri: Valid client contact URI (sip:alice@10.0.0.100:5060).
115
+ # To get client URI use *ul_show_contact => contact* function
116
+ # - event: One of the events from EVENTNOTIFY constant hash
117
+ # - hf: Header fields. Add To/From header fields here if you do not want them
118
+ # to be auto-generated. Header field example:
119
+ # hf['To'] => '<sip:alice@wanderland.com>'
120
+ #
121
+ def event_notify uri, event, hf = {}
122
+ raise ArgumentError,
123
+ "Invalid notify event: #{event.to_s}" unless EVENTNOTIFY.keys.include?(event)
124
+ hf['To'] = "<#{uri}>" unless hf.keys.map{|k|k.downcase}.include?('to')
125
+ hf['From'] = "<#{uri}>;tag=#{SecureRandom.hex}" unless hf.keys.map{|k|k.downcase}.include?('from')
126
+ hf['Event'] = EVENTNOTIFY[event]
127
+
128
+ uac_dlg "NOTIFY", uri, hf
129
+ end
130
+
131
+ # = Presence MWI
132
+ # Send message-summary NOTIFY Event to update phone voicemail status.
133
+ #
134
+ # == Parameters
135
+ # - uri: Request URI (sip:alice@wanderland.com:5060)
136
+ # To get client URI use *ul_show_contact => contact* function
137
+ # - vmaccount:Message Account value. Ex.: sip:*97@asterisk.com
138
+ # - new: Number of new messages. If more than 0 then Messages-Waiting header
139
+ # will be "yes". Set to 0 to clear phone MWI
140
+ # - old: (optional) Old messages
141
+ # - urg_new: (optional) New urgent messages
142
+ # - urg_old: (optional) Old urgent messages
143
+ #
144
+ def mwi_update uri, vmaccount, new, old = 0, urg_new = 0, urg_old = 0
145
+ mbody = Hash[
146
+ 'Messages-Waiting' => (new > 0 ? "yes" : "no"),
147
+ 'Message-Account' => vmaccount,
148
+ 'Voice-Message' => "#{new}/#{old} (#{urg_new}/#{urg_old})",
149
+ ]
150
+ hf = Hash[
151
+ 'To' => "<#{uri}>",
152
+ 'From' => "<#{uri}>;tag=#{SecureRandom.hex}",
153
+ 'Event' => "message-summary",
154
+ 'Subscription-State'=> "active",
155
+ 'Content-Type' => "application/simple-message-summary",
156
+ 'Content-Length' => mbody.map{|k,v| "#{k}: #{v}"}.join("\r\n").length,
157
+ 'nl' => "",
158
+ ]
159
+
160
+ uac_dlg "NOTIFY", uri, hf.merge(mbody)
161
+ end
162
+
163
+ def set_header(header);"\"#{header}\"";end
164
+
165
+ end
166
+ end
167
+ end