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.
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