sip-lab 1.12.0 → 1.12.3

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.
package/README.md CHANGED
@@ -35,6 +35,10 @@ Then install sip-lab by doing:
35
35
  npm install sip-lab
36
36
  ```
37
37
 
38
+ Be patient because we will need to download pjproject and build it.
39
+
40
+ We will also download rapidjson.
41
+
38
42
  To test from within this repo just build and install by doing:
39
43
  ```
40
44
  npm install -g node-gyp
@@ -44,6 +48,8 @@ And run some sample script from subfolder samples:
44
48
  ```
45
49
  node samples/simple.js
46
50
  ```
51
+ The above script has detailed comments.
52
+ Please read it to undestand how to write your own test scripts.
47
53
 
48
54
  The module is known to work properly in Ubuntu 18.04.4, Ubuntu 20.04.4, Debian 8 and Debian 10 (and it is expected to work in Debian 9).
49
55
  It was originally developed with node v.10 and tested with v.12 and v16.13.1 and it is expected to work with latest versions of node.
@@ -65,4 +71,9 @@ But if you do so, you will need to set NODE_PATH for node to find it by doing:
65
71
  export NODE_PATH=$(npm root --quiet -g)
66
72
  ```
67
73
 
74
+ ### About the code
75
+
76
+ Although the code in written in *.cpp/*.hpp named files, this is not actually a C++ project.
77
+
78
+ It is mostly written in C using some C++ facilities.
68
79
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sip-lab",
3
- "version": "1.12.0",
3
+ "version": "1.12.3",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "engines": {
@@ -28,7 +28,11 @@ async function test() {
28
28
  domain,
29
29
  server,
30
30
  username: 'user1',
31
- password: 'pass1'
31
+ password: 'pass1',
32
+ headers: {
33
+ 'X-MyHeader1': 'aaa',
34
+ 'X-MyHeader2': 'bbb',
35
+ },
32
36
  })
33
37
 
34
38
  sip.account.register(a1, {auto_register: true})
@@ -42,6 +46,8 @@ async function test() {
42
46
  $fd: domain,
43
47
  $tU: 'user1',
44
48
  $td: domain,
49
+ '$hdr(X-MyHeader1)': 'aaa',
50
+ '$hdr(X-MyHeader2)': 'bbb',
45
51
  }),
46
52
  },
47
53
  ], 1000)
@@ -21,7 +21,7 @@ async function test() {
21
21
  console.log("t1", t1)
22
22
  console.log("t2", t2)
23
23
 
24
- oc = sip.call.create(t1.id, {from_uri: 'sip:a@t', to_uri: 'sip:b@127.0.0.1:5092'})
24
+ oc = sip.call.create(t1.id, {from_uri: 'sip:alice@test.com', to_uri: `sip:bob@${t2.address}:${t2.port}`})
25
25
 
26
26
  await z.wait([
27
27
  {
@@ -37,9 +37,9 @@ async function test() {
37
37
  $rr: 'Trying',
38
38
  '$(hdrcnt(via))': 1,
39
39
  '$hdr(call-id)': m.collect('sip_call_id'),
40
- $fU: 'a',
41
- $fd: 't',
42
- $tU: 'b',
40
+ $fU: 'alice',
41
+ $fd: 'test.com',
42
+ $tU: 'bob',
43
43
  '$hdr(l)': '0',
44
44
  }),
45
45
  },
@@ -75,9 +75,9 @@ async function test() {
75
75
  $rs: '200',
76
76
  $rr: 'OK',
77
77
  '$(hdrcnt(VIA))': 1,
78
- $fU: 'a',
79
- $fd: 't',
80
- $tU: 'b',
78
+ $fU: 'alice',
79
+ $fd: 'test.com',
80
+ $tU: 'bob',
81
81
  '$hdr(content-type)': 'application/sdp',
82
82
  $rb: '!{_}a=sendrecv',
83
83
  }),
package/samples/simple.js CHANGED
@@ -1,32 +1,45 @@
1
- var sip = require ('../index.js')
2
- var Zester = require('zester')
1
+ // This test creates 2 UDP SIP endpoints, makes a call between them and disconeects.
2
+
3
+ const sip = require ('../index.js')
4
+ const Zester = require('zester')
5
+ const m = require('data-matching')
6
+ const sip_msg = require('sip-matching')
7
+
8
+ // here we create our Zester instance
3
9
  var z = new Zester()
4
- var m = require('data-matching')
5
- var sip_msg = require('sip-matching')
6
10
 
7
- async function test() {
8
- //sip.set_log_level(6)
9
- sip.dtmf_aggregation_on(500)
10
11
 
12
+ async function test() {
13
+ // here we set our zester instance to trap events generated by sip-lab event_source
11
14
  z.trap_events(sip.event_source, 'event', (evt) => {
12
15
  var e = evt.args[0]
13
16
  return e
14
17
  })
15
18
 
19
+ // here we start sip-lab
16
20
  console.log(sip.start((data) => { console.log(data)} ))
17
21
 
18
- t1 = sip.transport.create({address: "127.0.0.1", port: 5090, type: 'udp'})
19
- t2 = sip.transport.create({address: "127.0.0.1", port: 5092, type: 'udp'})
22
+ // Here we create the SIP endpoints (transports).
23
+ // Since we don't specify the port, an available port will be allocated.
24
+ // Since we don't specify the type ('udp' or 'tcp' or 'tls'), 'udp' will be used by default.
25
+ const t1 = sip.transport.create({address: "127.0.0.1"})
26
+ const t2 = sip.transport.create({address: "127.0.0.1"})
20
27
 
28
+ // here we just print the transports
21
29
  console.log("t1", t1)
22
30
  console.log("t2", t2)
23
31
 
24
- oc = sip.call.create(t1.id, {from_uri: 'sip:alice@test.com', to_uri: `sip:bob@${t2.address}:${t2.port}`})
32
+ // make the call from t1 to t2
33
+ const oc = sip.call.create(t1.id, {from_uri: 'sip:alice@test.com', to_uri: `sip:bob@${t2.address}:${t2.port}`})
25
34
 
35
+ // Here we will wait for the call to arrive at t2
36
+ // We will also get a '100 Trying' that is sent by sip-lab automatically
37
+ // We will wait for at most 1000ms. If all events don't arrive within 1000ms, an exception will be thrown and the test will fail due to timeout.
26
38
  await z.wait([
27
39
  {
28
40
  event: "incoming_call",
29
41
  call_id: m.collect("call_id"),
42
+ transport_id: t2.id,
30
43
  },
31
44
  {
32
45
  event: 'response',
@@ -44,29 +57,39 @@ async function test() {
44
57
  }),
45
58
  },
46
59
  ], 1000)
47
-
48
- ic = {
60
+ // Details about zester wait(list_of_events_to_wait_for, timeout_in_ms):
61
+ // The order of events in the list is irrelevant.
62
+ // What matters is that all events arrive within the specified timeout.
63
+ // When specifying events, you can be as detailed or succinct as you need.
64
+ // For example, the above event 'response' is waiting for a SIP '100 Trying' to arrive,
65
+ // but we are specifying things to match just to show that we can be very detailed when performing a match.
66
+ // But it could have been just like this:
67
+ //
68
+ // {
69
+ // event: 'response',
70
+ // call_id: oc.id,
71
+ // method: 'INVITE',
72
+ // msg: sip_msg({
73
+ // $rs: '100',
74
+ // }),
75
+ // }
76
+ // Regarding the function sip_msg() this is a special matching function provided by https://github.com/MayamaTakeshi/sip-matching that makes it
77
+ // easy to match a SIP message using openser/kamailio/opensips pseudo-variables syntax.
78
+
79
+
80
+ // Here we store data for the incoming call
81
+ // just to organize our code (not really needed)
82
+ const ic = {
49
83
  id: z.store.call_id,
50
84
  sip_call_id: z.store.sip_call_id,
51
85
  }
52
86
 
87
+ // Now we answer the call at t2 side
53
88
  sip.call.respond(ic.id, {code: 200, reason: 'OK'})
54
89
 
90
+ // Then we wait for the '200 OK' at the t1 side
91
+ // We will also get event 'media_status' for both sides indicating media streams (RTP) were set up successfully
55
92
  await z.wait([
56
- {
57
- event: 'media_status',
58
- call_id: oc.id,
59
- status: 'setup_ok',
60
- local_mode: 'sendrecv',
61
- remote_mode: 'sendrecv',
62
- },
63
- {
64
- event: 'media_status',
65
- call_id: ic.id,
66
- status: 'setup_ok',
67
- local_mode: 'sendrecv',
68
- remote_mode: 'sendrecv',
69
- },
70
93
  {
71
94
  event: 'response',
72
95
  call_id: oc.id,
@@ -82,19 +105,27 @@ async function test() {
82
105
  $rb: '!{_}a=sendrecv',
83
106
  }),
84
107
  },
85
- ], 1000)
86
-
87
- sip.call.terminate(oc.id)
88
-
89
- await z.wait([
90
108
  {
91
- event: 'call_ended',
109
+ event: 'media_status',
92
110
  call_id: oc.id,
111
+ status: 'setup_ok',
112
+ local_mode: 'sendrecv',
113
+ remote_mode: 'sendrecv',
93
114
  },
94
115
  {
95
- event: 'call_ended',
116
+ event: 'media_status',
96
117
  call_id: ic.id,
118
+ status: 'setup_ok',
119
+ local_mode: 'sendrecv',
120
+ remote_mode: 'sendrecv',
97
121
  },
122
+ ], 1000)
123
+
124
+ // now we terminate the call from t1 side
125
+ sip.call.terminate(oc.id)
126
+
127
+ // and wait for termination events
128
+ await z.wait([
98
129
  {
99
130
  event: 'response',
100
131
  call_id: oc.id,
@@ -104,6 +135,14 @@ async function test() {
104
135
  $rr: 'OK',
105
136
  }),
106
137
  },
138
+ {
139
+ event: 'call_ended',
140
+ call_id: oc.id,
141
+ },
142
+ {
143
+ event: 'call_ended',
144
+ call_id: ic.id,
145
+ },
107
146
  ], 1000)
108
147
 
109
148
  console.log("Success")
@@ -0,0 +1,333 @@
1
+ var sip = require ('../index.js')
2
+ var Zester = require('zester')
3
+ var z = new Zester()
4
+ var m = require('data-matching')
5
+ var sip_msg = require('sip-matching')
6
+
7
+ async function test() {
8
+ //sip.set_log_level(6)
9
+ sip.dtmf_aggregation_on(500)
10
+
11
+ z.trap_events(sip.event_source, 'event', (evt) => {
12
+ var e = evt.args[0]
13
+ return e
14
+ })
15
+
16
+ console.log(sip.start((data) => { console.log(data)} ))
17
+
18
+ t1 = sip.transport.create({address: "127.0.0.1", port: 5090, type: 'tcp'})
19
+ t2 = sip.transport.create({address: "127.0.0.1", port: 5092, type: 'tcp'})
20
+
21
+ console.log("t1", t1)
22
+ console.log("t2", t2)
23
+
24
+ oc = sip.call.create(t1.id, {
25
+ from_uri: '"abc"<sip:alice@test.com>',
26
+ to_uri: `sip:bob@${t2.address}:${t2.port}`,
27
+ headers: {
28
+ 'X-MyHeader1': 'abc',
29
+ 'X-MyHeader2': 'def',
30
+ },
31
+ })
32
+
33
+ await z.wait([
34
+ {
35
+ event: "incoming_call",
36
+ call_id: m.collect("call_id"),
37
+ msg: sip_msg({
38
+ $rm: 'INVITE',
39
+ $fU: 'alice',
40
+ $fd: 'test.com',
41
+ $tU: 'bob',
42
+ '$hdr(X-MyHeader1)': 'abc',
43
+ '$hdr(X-MyHeader2)': 'def',
44
+ }),
45
+ },
46
+ {
47
+ event: 'response',
48
+ call_id: oc.id,
49
+ method: 'INVITE',
50
+ msg: sip_msg({
51
+ $rs: '100',
52
+ $rr: 'Trying',
53
+ '$(hdrcnt(via))': 1,
54
+ '$hdr(call-id)': m.collect('sip_call_id'),
55
+ $fU: 'alice',
56
+ $fd: 'test.com',
57
+ $tU: 'bob',
58
+ '$hdr(l)': '0',
59
+ }),
60
+ },
61
+ ], 1000)
62
+
63
+ ic = {
64
+ id: z.store.call_id,
65
+ sip_call_id: z.store.sip_call_id,
66
+ }
67
+
68
+ sip.call.respond(ic.id, {
69
+ code: 200,
70
+ reason:'OK',
71
+ headers: {
72
+ 'X-MyHeader3': 'ghi',
73
+ 'X-MyHeader4': 'jkl',
74
+ },
75
+ })
76
+
77
+ await z.wait([
78
+ {
79
+ event: 'media_status',
80
+ call_id: oc.id,
81
+ status: 'setup_ok',
82
+ local_mode: 'sendrecv',
83
+ remote_mode: 'sendrecv',
84
+ },
85
+ {
86
+ event: 'media_status',
87
+ call_id: ic.id,
88
+ status: 'setup_ok',
89
+ local_mode: 'sendrecv',
90
+ remote_mode: 'sendrecv',
91
+ },
92
+ {
93
+ event: 'response',
94
+ call_id: oc.id,
95
+ method: 'INVITE',
96
+ msg: sip_msg({
97
+ $rs: '200',
98
+ $rr: 'OK',
99
+ '$(hdrcnt(VIA))': 1,
100
+ $fU: 'alice',
101
+ $fd: 'test.com',
102
+ $tU: 'bob',
103
+ '$hdr(content-type)': 'application/sdp',
104
+ $rb: '!{_}a=sendrecv',
105
+ '$hdr(X-MyHeader3)': 'ghi',
106
+ '$hdr(X-MyHeader4)': 'jkl',
107
+ }),
108
+ },
109
+ ], 1000)
110
+
111
+ sip.call.start_recording(oc.id, {file: './oc.wav'})
112
+ sip.call.start_recording(ic.id, {file: './ic.wav'})
113
+
114
+ sip.call.send_dtmf(oc.id, {digits: '1234', mode: 0})
115
+ sip.call.send_dtmf(ic.id, {digits: '4321', mode: 1})
116
+
117
+ await z.wait([
118
+ {
119
+ event: 'dtmf',
120
+ call_id: ic.id,
121
+ digits: '1234',
122
+ mode: 0,
123
+ },
124
+ {
125
+ event: 'dtmf',
126
+ call_id: oc.id,
127
+ digits: '4321',
128
+ mode: 1,
129
+ },
130
+ ], 2000)
131
+
132
+
133
+ sip.call.reinvite(oc.id, { hold: true })
134
+
135
+ await z.wait([
136
+ {
137
+ event: 'response',
138
+ call_id: oc.id,
139
+ method: 'INVITE',
140
+ msg: sip_msg({
141
+ $rs: '200',
142
+ $rr: 'OK',
143
+ $rb: '!{_}a=recvonly',
144
+ }),
145
+ },
146
+ {
147
+ event: 'media_status',
148
+ call_id: oc.id,
149
+ status: 'setup_ok',
150
+ local_mode: 'sendonly',
151
+ remote_mode: 'recvonly',
152
+ },
153
+ {
154
+ event: 'media_status',
155
+ call_id: ic.id,
156
+ status: 'setup_ok',
157
+ local_mode: 'recvonly',
158
+ remote_mode: 'sendonly',
159
+ },
160
+ ], 500)
161
+
162
+ sip.call.send_dtmf(oc.id, {digits: '1234', mode: 0})
163
+ sip.call.send_dtmf(ic.id, {digits: '4321', mode: 0}) // this will not generate event 'dtmf' as the call is on hold
164
+
165
+ await z.wait([
166
+ {
167
+ event: 'dtmf',
168
+ call_id: ic.id,
169
+ digits: '1234',
170
+ mode: 0,
171
+ },
172
+ ], 2000)
173
+
174
+ sip.call.reinvite(ic.id)
175
+
176
+ await z.wait([
177
+ {
178
+ event: 'response',
179
+ call_id: ic.id,
180
+ method: 'INVITE',
181
+ msg: sip_msg({
182
+ $rs: '200',
183
+ $rr: 'OK',
184
+ $rb: '!{_}a=sendonly',
185
+ }),
186
+ },
187
+ {
188
+ event: 'media_status',
189
+ call_id: oc.id,
190
+ status: 'setup_ok',
191
+ local_mode: 'sendonly',
192
+ remote_mode: 'recvonly',
193
+ },
194
+ {
195
+ event: 'media_status',
196
+ call_id: ic.id,
197
+ status: 'setup_ok',
198
+ local_mode: 'recvonly',
199
+ remote_mode: 'sendonly',
200
+ },
201
+ ], 500)
202
+
203
+ sip.call.send_dtmf(oc.id, {digits: '1234', mode: 0})
204
+ sip.call.send_dtmf(ic.id, {digits: '4321', mode: 1}) // this will not generate event 'dtmf' as the call is on hold
205
+
206
+ await z.wait([
207
+ {
208
+ event: 'dtmf',
209
+ call_id: ic.id,
210
+ digits: '1234',
211
+ mode: 0,
212
+ },
213
+ ], 2000)
214
+
215
+ sip.call.send_request(oc.id, {method: 'INFO'})
216
+
217
+ await z.wait([
218
+ {
219
+ event: 'request',
220
+ call_id: ic.id,
221
+ msg: sip_msg({
222
+ $rm: 'INFO',
223
+ }),
224
+ },
225
+ {
226
+ event: 'response',
227
+ call_id: oc.id,
228
+ method: 'INFO',
229
+ msg: sip_msg({
230
+ $rs: '200',
231
+ $rr: 'OK',
232
+ }),
233
+ },
234
+ ], 500)
235
+
236
+ sip.call.reinvite(oc.id)
237
+
238
+ await z.wait([
239
+ {
240
+ event: 'response',
241
+ call_id: oc.id,
242
+ method: 'INVITE',
243
+ msg: sip_msg({
244
+ $rs: '200',
245
+ $rr: 'OK',
246
+ $rb: '!{_}a=sendrecv',
247
+ }),
248
+ },
249
+ {
250
+ event: 'media_status',
251
+ call_id: oc.id,
252
+ status: 'setup_ok',
253
+ local_mode: 'sendrecv',
254
+ remote_mode: 'sendrecv',
255
+ },
256
+ {
257
+ event: 'media_status',
258
+ call_id: ic.id,
259
+ status: 'setup_ok',
260
+ local_mode: 'sendrecv',
261
+ remote_mode: 'sendrecv',
262
+ },
263
+ ], 500)
264
+
265
+ sip.call.send_dtmf(oc.id, {digits: '1234', mode: 0})
266
+ sip.call.send_dtmf(ic.id, {digits: '4321', mode: 1})
267
+
268
+ await z.wait([
269
+ {
270
+ event: 'dtmf',
271
+ call_id: ic.id,
272
+ digits: '1234',
273
+ mode: 0,
274
+ },
275
+ {
276
+ event: 'dtmf',
277
+ call_id: oc.id,
278
+ digits: '4321',
279
+ mode: 1,
280
+ },
281
+ ], 2000)
282
+
283
+ sip.call.start_playing(oc.id, {file: '/home/takeshi/work/src/svn/brastel/SIP-Tools/trunk/sip-tester/yosemitesam.wav'})
284
+ sip.call.start_playing(ic.id, {file: '/home/takeshi/work/src/svn/brastel/SIP-Tools/trunk/sip-tester/yosemitesam.wav'})
285
+
286
+ await z.sleep(2000)
287
+
288
+ stat1 = sip.call.get_stream_stat(oc.id)
289
+ stat2 = sip.call.get_stream_stat(ic.id)
290
+
291
+ console.log("stat1", stat1)
292
+ console.log("stat2", stat2)
293
+
294
+ sip.call.stop_recording(oc.id)
295
+ sip.call.stop_recording(ic.id)
296
+
297
+
298
+ sip.call.terminate(oc.id)
299
+
300
+ await z.wait([
301
+ {
302
+ event: 'call_ended',
303
+ call_id: oc.id,
304
+ },
305
+ {
306
+ event: 'call_ended',
307
+ call_id: ic.id,
308
+ },
309
+ {
310
+ event: 'response',
311
+ call_id: oc.id,
312
+ method: 'BYE',
313
+ msg: sip_msg({
314
+ $rs: '200',
315
+ $rr: 'OK',
316
+ }),
317
+ },
318
+ ], 1000)
319
+
320
+ await z.sleep(1000)
321
+
322
+ console.log("Success")
323
+
324
+ sip.stop()
325
+ }
326
+
327
+
328
+ test()
329
+ .catch(e => {
330
+ console.error(e)
331
+ process.exit(1)
332
+ })
333
+
@@ -14,7 +14,6 @@ PJ_DECL(pj_status_t) chainlink_fax_port_create( pj_pool_t *pool,
14
14
  void *user_data,
15
15
  int result),
16
16
  void *user_data,
17
- int is_caller,
18
17
  int is_sender,
19
18
  const char *file,
20
19
  pjmedia_port **p_port);
@@ -61,7 +61,6 @@ struct fax_device
61
61
  fax_state_t fax;
62
62
  void (*fax_cb)(pjmedia_port*, void*, int);
63
63
  void *fax_cb_user_data;
64
- int is_caller;
65
64
  int is_sender;
66
65
 
67
66
  pj_lock_t *lock;
@@ -121,7 +120,6 @@ PJ_DEF(pj_status_t) chainlink_fax_port_create( pj_pool_t *pool,
121
120
  void *user_data,
122
121
  int result),
123
122
  void *user_data,
124
- int is_caller,
125
123
  int is_sender,
126
124
  const char *file,
127
125
  pjmedia_port **p_port)
@@ -145,7 +143,7 @@ PJ_DEF(pj_status_t) chainlink_fax_port_create( pj_pool_t *pool,
145
143
  fd->link.port.put_frame = &fax_put_frame;
146
144
  fd->link.port.on_destroy = &fax_on_destroy;
147
145
 
148
- fax_init(&fd->fax, is_caller);
146
+ fax_init(&fd->fax, is_sender);
149
147
  //fax_set_transmit_on_idle(&fd->fax,1);
150
148
 
151
149
  t30_state_t *t30 = fax_get_t30_state(&fd->fax);
@@ -162,7 +160,6 @@ PJ_DEF(pj_status_t) chainlink_fax_port_create( pj_pool_t *pool,
162
160
  //printf("setting document_handler with user_data=%p\n", (void*)fd);
163
161
  t30_set_document_handler(t30, &document_handler, (void*)fd);
164
162
 
165
- fd->is_caller = is_caller;
166
163
  fd->is_sender = is_sender;
167
164
 
168
165
  pj_status_t status = pj_lock_create_simple_mutex(pool, "fax", &fd->lock);
package/src/sip.cpp CHANGED
@@ -426,9 +426,10 @@ static void server_on_evsub_rx_refresh(pjsip_evsub *sub, pjsip_rx_data *rdata, i
426
426
 
427
427
  bool dlg_create(pjsip_dialog **dlg, Transport *transport, const char *from_uri, const char *to_uri, const char *request_uri, const char *realm, const char *username, const char *password, const char *local_contact);
428
428
 
429
- static int call_create(Transport *t, unsigned flags, pjsip_dialog *dlg, const char *proxy_uri, const char *additional_headers);
429
+ static int call_create(Transport *t, unsigned flags, pjsip_dialog *dlg, const char *proxy_uri, Document &document);
430
430
 
431
- bool subscription_subscribe(Subscription *s, int expires, const char *additional_headers);
431
+ bool subscription_subscribe_no_headers(Subscription *s, int expires);
432
+ bool subscription_subscribe(Subscription *s, int expires, Document &document);
432
433
 
433
434
  static pjmedia_transport *create_media_transport(const pj_str_t *addr);
434
435
  void close_media_transport(pjmedia_transport *med_transport);
@@ -446,11 +447,12 @@ bool set_proxy(pjsip_dialog *dlg, const char *proxy_uri);
446
447
  void build_local_contact(char *dest, pjsip_transport *transport, const char *contact_username);
447
448
  void build_local_contact_from_tpfactory(char *dest, pjsip_tpfactory *tpfactory, const char *contact_username, pjsip_transport_type_e type);
448
449
 
449
- pj_bool_t add_additional_headers(pj_pool_t *pool, pjsip_tx_data *tdata, const char *additional_headers);
450
+ //pj_bool_t add_additional_headers(pj_pool_t *pool, pjsip_tx_data *tdata, const char *additional_headers);
451
+ pj_bool_t add_headers(pj_pool_t *pool, pjsip_tx_data *tdata, Document &document);
450
452
 
451
- pj_bool_t add_additional_headers_for_account(pjsip_regc* regc, const char *additional_headers);
453
+ pj_bool_t add_headers_for_account(pjsip_regc* regc, Document &document);
452
454
 
453
- pj_bool_t get_content_type_and_subtype_from_additional_headers(const char *additional_headers, char *type, char *subtype);
455
+ pj_bool_t get_content_type_and_subtype_from_headers(Document &document, char *type, char *subtype);
454
456
 
455
457
  bool build_subscribe_info(ostringstream *oss, pjsip_rx_data *rdata, Subscriber *s);
456
458
  //bool build_notify_info(pjsip_rx_data *rdata, Subscription *s);
@@ -1256,7 +1258,6 @@ int pjw_account_create(int t_id, const char *json, int *out_acc_id)
1256
1258
  char *password;
1257
1259
  char *c_to_uri = NULL;
1258
1260
  int expires = 60;
1259
- char *additional_headers = NULL;
1260
1261
 
1261
1262
  pj_str_t server_uri;
1262
1263
  pj_str_t from_uri;
@@ -1322,11 +1323,8 @@ int pjw_account_create(int t_id, const char *json, int *out_acc_id)
1322
1323
  goto out;
1323
1324
  }
1324
1325
 
1325
- if(additional_headers) {
1326
- if(!add_additional_headers_for_account(regc, additional_headers)) {
1327
- set_error("add_additional_headers_for_account failed");
1328
- goto out;
1329
- }
1326
+ if(!add_headers_for_account(regc, document)) {
1327
+ goto out;
1330
1328
  }
1331
1329
 
1332
1330
  if(!g_account_ids.add((long)regc, acc_id)){
@@ -1542,8 +1540,6 @@ int pjw_call_respond(long call_id, const char *json)
1542
1540
 
1543
1541
  Call *call;
1544
1542
 
1545
- char *additional_headers = NULL;
1546
-
1547
1543
  char buffer[MAX_JSON_INPUT];
1548
1544
 
1549
1545
  Document document;
@@ -1602,7 +1598,7 @@ int pjw_call_respond(long call_id, const char *json)
1602
1598
  goto out;
1603
1599
  }
1604
1600
 
1605
- if(!add_additional_headers(call->inv->dlg->pool, tdata, additional_headers)) {
1601
+ if(!add_headers(call->inv->dlg->pool, tdata, document)) {
1606
1602
  goto out;
1607
1603
  }
1608
1604
  }
@@ -1634,8 +1630,6 @@ int pjw_call_terminate(long call_id, const char *json)
1634
1630
  char *reason = (char*)"";
1635
1631
  pj_str_t r;// = pj_str((char*)reason);
1636
1632
 
1637
- const char *additional_headers;
1638
-
1639
1633
  Call *call;
1640
1634
 
1641
1635
  char buffer[MAX_JSON_INPUT];
@@ -1677,8 +1671,7 @@ int pjw_call_terminate(long call_id, const char *json)
1677
1671
  goto out;
1678
1672
  }
1679
1673
 
1680
- if(!add_additional_headers(call->inv->dlg->pool, tdata, additional_headers)) {
1681
- set_error("add_additional_headers failed");
1674
+ if(!add_headers(call->inv->dlg->pool, tdata, document)) {
1682
1675
  goto out;
1683
1676
  }
1684
1677
 
@@ -1720,8 +1713,6 @@ int pjw_call_create(long t_id, const char *json, long *out_call_id, char *out_si
1720
1713
  char *request_uri = NULL;
1721
1714
  char *proxy_uri = NULL;
1722
1715
 
1723
- char *headers = NULL;
1724
-
1725
1716
  char *realm = NULL;
1726
1717
  char *username = NULL;
1727
1718
  char *password = NULL;
@@ -1761,16 +1752,6 @@ int pjw_call_create(long t_id, const char *json, long *out_call_id, char *out_si
1761
1752
  goto out;
1762
1753
  }
1763
1754
 
1764
- /*
1765
- const Value& headers = document["headers"];
1766
- if(document.HasMember("headers") {
1767
- if(!headers.IsArray()) {
1768
- set_error("headers must be an array");
1769
- goto out;
1770
- }
1771
- }
1772
- */
1773
-
1774
1755
  if(document.HasMember("auth")) {
1775
1756
  if(!document["auth"].IsObject()) {
1776
1757
  set_error("Parameter auth must be an object");
@@ -1828,7 +1809,7 @@ int pjw_call_create(long t_id, const char *json, long *out_call_id, char *out_si
1828
1809
  goto out;
1829
1810
  }
1830
1811
 
1831
- call_id = call_create(t, flags, dlg, proxy_uri, headers);
1812
+ call_id = call_create(t, flags, dlg, proxy_uri, document);
1832
1813
  if(call_id < 0) {
1833
1814
  goto out;
1834
1815
  }
@@ -2007,7 +1988,7 @@ bool dlg_create(pjsip_dialog **dlg, Transport *transport, const char *from_uri,
2007
1988
  }
2008
1989
 
2009
1990
 
2010
- int call_create(Transport *t, unsigned flags, pjsip_dialog *dlg, const char *proxy_uri, const char *additional_headers)
1991
+ int call_create(Transport *t, unsigned flags, pjsip_dialog *dlg, const char *proxy_uri, Document &document)
2011
1992
  {
2012
1993
  pjsip_inv_session *inv;
2013
1994
  //in_addr addr;
@@ -2092,7 +2073,7 @@ int call_create(Transport *t, unsigned flags, pjsip_dialog *dlg, const char *pro
2092
2073
 
2093
2074
 
2094
2075
 
2095
- if(!add_additional_headers(dlg->pool, tdata, additional_headers)) {
2076
+ if(!add_headers(dlg->pool, tdata, document)) {
2096
2077
  g_call_ids.remove(call_id, (long &)call);
2097
2078
  close_media_transport(med_transport); //Todo:
2098
2079
  status = pjsip_dlg_terminate(dlg); //ToDo:
@@ -2382,7 +2363,6 @@ int pjw_call_send_request(long call_id, const char *json)
2382
2363
  clear_error();
2383
2364
 
2384
2365
  char *method = NULL;
2385
- char *additional_headers = NULL;
2386
2366
  char *body = NULL;
2387
2367
  char *ct_type = NULL;
2388
2368
  char *ct_subtype = NULL;
@@ -2454,7 +2434,7 @@ int pjw_call_send_request(long call_id, const char *json)
2454
2434
  goto out;
2455
2435
  }
2456
2436
 
2457
- if(!add_additional_headers(call->inv->dlg->pool, tdata, additional_headers)) {
2437
+ if(!add_headers(call->inv->dlg->pool, tdata, document)) {
2458
2438
  goto out;
2459
2439
  }
2460
2440
 
@@ -3704,17 +3684,13 @@ static void on_rx_offer(pjsip_inv_session *inv, const pjmedia_sdp_session *offer
3704
3684
  // Keep call on-hold by setting 'sendonly' attribute.
3705
3685
  // (See RFC 3264 Section 8.4 and RFC 4317 Section 3.1)
3706
3686
  if(call->remote_hold) {
3707
- printf("p1\n");
3708
3687
  attr = pjmedia_sdp_attr_create(inv->pool, "inactive", NULL);
3709
3688
  } else {
3710
- printf("p2\n");
3711
3689
  attr = pjmedia_sdp_attr_create(inv->pool, "sendonly", NULL);
3712
3690
  }
3713
3691
  } else if(call->remote_hold) {
3714
- printf("p3\n");
3715
3692
  attr = pjmedia_sdp_attr_create(inv->pool, "recvonly", NULL);
3716
3693
  } else {
3717
- printf("p4\n");
3718
3694
  attr = pjmedia_sdp_attr_create(inv->pool, "sendrecv", NULL);
3719
3695
  }
3720
3696
  pjmedia_sdp_media_add_attr(answer->media[0], attr);
@@ -4370,7 +4346,6 @@ bool prepare_fax(Call *c, bool is_sender, const char *file) {
4370
4346
  PJMEDIA_PIA_BITS(&stream_port->info),
4371
4347
  on_fax_result,
4372
4348
  c,
4373
- c->outgoing,
4374
4349
  is_sender,
4375
4350
  file,
4376
4351
  (pjmedia_port**)&c->fax);
@@ -4433,7 +4408,7 @@ static void on_client_refresh( pjsip_evsub *sub ) {
4433
4408
  goto out;
4434
4409
  }
4435
4410
 
4436
- if(!subscription_subscribe(subscription, -1, "")) {
4411
+ if(!subscription_subscribe_no_headers(subscription, -1)) {
4437
4412
  goto out;
4438
4413
  }
4439
4414
 
@@ -4852,7 +4827,6 @@ int pjw_call_refer(long call_id, const char *json, long *out_subscription_id)
4852
4827
  clear_error();
4853
4828
 
4854
4829
  char *dest_uri;
4855
- char *additional_headers;
4856
4830
 
4857
4831
  long val;
4858
4832
  Call *call;
@@ -4895,7 +4869,7 @@ int pjw_call_refer(long call_id, const char *json, long *out_subscription_id)
4895
4869
  s_dest_uri = pj_str((char*)dest_uri);
4896
4870
  status = pjsip_xfer_initiate(sub, &s_dest_uri, &tdata);
4897
4871
 
4898
- if(!add_additional_headers(call->inv->dlg->pool, tdata, additional_headers)) {
4872
+ if(!add_headers(call->inv->dlg->pool, tdata, document)) {
4899
4873
  goto out;
4900
4874
  }
4901
4875
 
@@ -4960,7 +4934,7 @@ int pjw_call_get_info(long call_id, const char *required_info, char *out_info)
4960
4934
  return 0;
4961
4935
  }
4962
4936
 
4963
- bool notify(pjsip_evsub *evsub, const char *content_type, const char *body, int subscription_state, const char *reason, const char *additional_headers) {
4937
+ bool notify(pjsip_evsub *evsub, const char *content_type, const char *body, int subscription_state, const char *reason, Document &document) {
4964
4938
  //pj_str_t s_content_type;
4965
4939
  //pj_str_t s_body;
4966
4940
  pj_str_t s_reason;
@@ -5014,7 +4988,9 @@ bool notify(pjsip_evsub *evsub, const char *content_type, const char *body, int
5014
4988
  }
5015
4989
  s_content_type_subtype = pj_str(tok);
5016
4990
 
5017
- add_additional_headers(tdata->pool, tdata, additional_headers);
4991
+ if(!add_headers(tdata->pool, tdata, document)) {
4992
+ return false;
4993
+ }
5018
4994
 
5019
4995
  msg_body->content_type.type = s_content_type_type;
5020
4996
  msg_body->content_type.subtype = s_content_type_subtype;
@@ -5045,7 +5021,6 @@ int pjw_notify(long subscriber_id, const char *json)
5045
5021
  char *body = NULL;
5046
5022
  int subscription_state;
5047
5023
  char *reason = NULL;
5048
- char *additional_headers = NULL;
5049
5024
 
5050
5025
  long val;
5051
5026
 
@@ -5086,7 +5061,7 @@ int pjw_notify(long subscriber_id, const char *json)
5086
5061
  goto out;
5087
5062
  }
5088
5063
 
5089
- if(!notify(subscriber->evsub, content_type, body, subscription_state, reason, additional_headers)){
5064
+ if(!notify(subscriber->evsub, content_type, body, subscription_state, reason, document)){
5090
5065
  goto out;
5091
5066
  }
5092
5067
 
@@ -5174,6 +5149,7 @@ out:
5174
5149
  return 0;
5175
5150
  }
5176
5151
 
5152
+ /*
5177
5153
  pj_bool_t add_additional_headers(pj_pool_t *pool, pjsip_tx_data *tdata, const char *additional_headers) {
5178
5154
 
5179
5155
  if(additional_headers && additional_headers[0]){
@@ -5210,94 +5186,133 @@ pj_bool_t add_additional_headers(pj_pool_t *pool, pjsip_tx_data *tdata, const ch
5210
5186
  }
5211
5187
  return PJ_TRUE;
5212
5188
  }
5189
+ */
5213
5190
 
5214
- pj_bool_t add_additional_headers_for_account(pjsip_regc *regc, const char *additional_headers) {
5215
5191
 
5216
- if(additional_headers && additional_headers[0]){
5217
- pjsip_hdr hdr_list;
5218
- pj_list_init(&hdr_list);
5192
+ pj_bool_t add_headers(pj_pool_t *pool, pjsip_tx_data *tdata, Document &document) {
5193
+ if(!document.HasMember("headers")) {
5194
+ return PJ_TRUE;
5195
+ }
5219
5196
 
5220
- char pool_buf[4096];
5221
- pj_pool_t *pool;
5222
- pool = pj_pool_create_on_buf(NULL, pool_buf, sizeof(pool_buf));
5197
+ if(!document["headers"].IsObject()) {
5198
+ set_error("Parameter headers must be an object");
5199
+ return PJ_FALSE;
5200
+ }
5223
5201
 
5224
- char buf[2048];
5225
- strcpy(buf,additional_headers);
5226
- char *saved;
5227
- char *token = strtok_r(buf, "\n", &saved);
5228
- while(token){
5229
- char *name = strtok(token, ":");
5230
- char *value = strtok(NULL, "\n");
5231
- //addon_log(LOG_LEVEL_DEBUG, "Adding %s:%s\n", name, value);
5202
+ Value headers = document["headers"].GetObject();
5232
5203
 
5233
- if(!name || !value) {
5234
- set_error("Invalid additional_header");
5235
- return PJ_FALSE;
5236
- }
5204
+ for (Value::ConstMemberIterator itr = headers.MemberBegin(); itr != headers.MemberEnd(); ++itr) {
5205
+ printf("%s => '%s'\n", itr->name.GetString(), itr->value.GetString());
5237
5206
 
5238
- pj_str_t hname = pj_str(name);
5239
- pjsip_hdr *hdr = (pjsip_hdr*)pjsip_parse_hdr(pool,
5240
- &hname,
5241
- value,
5242
- strlen(value),
5243
- NULL);
5207
+ const char *name = itr->name.GetString();
5208
+ if(!itr->value.IsString()) {
5209
+ set_error("Parameter headers key '%s' found with non-string value", name);
5210
+ return PJ_FALSE;
5211
+ }
5244
5212
 
5245
- if(!hdr) {
5246
- set_error("Failed to parse additional header to INVITE");
5247
- return PJ_FALSE;
5248
- }
5249
-
5250
- pj_list_push_back(&hdr_list, hdr);
5251
- token = strtok_r(NULL, "\n", &saved);
5252
- }
5253
- pjsip_regc_add_headers(regc, &hdr_list);
5254
- }
5213
+ const char *value = itr->value.GetString();
5214
+
5215
+ pj_str_t hname = pj_str((char*)name);
5216
+ pjsip_hdr *hdr = (pjsip_hdr*)pjsip_parse_hdr(pool,
5217
+ &hname,
5218
+ (char*)value,
5219
+ strlen(value),
5220
+ NULL);
5221
+
5222
+ if(!hdr) {
5223
+ set_error("Failed to parse header '%s' => '%s'", name, value);
5224
+ return PJ_FALSE;
5225
+ }
5226
+ pjsip_hdr *clone_hdr = (pjsip_hdr*) pjsip_hdr_clone(pool, hdr);
5227
+ pjsip_msg_add_hdr(tdata->msg, clone_hdr);
5228
+ }
5255
5229
  return PJ_TRUE;
5256
5230
  }
5257
5231
 
5258
- pj_bool_t get_content_type_and_subtype_from_additional_headers(const char *additional_headers, char *type, char *subtype) {
5232
+ pj_bool_t add_headers_for_account(pjsip_regc *regc, Document &document) {
5233
+ pjsip_hdr hdr_list;
5234
+ pj_list_init(&hdr_list);
5259
5235
 
5260
- if(!additional_headers || !additional_headers[0]){
5261
- set_error("Header Content-Type not supplied");
5262
- return PJ_FALSE;
5263
- }
5236
+ char pool_buf[4096];
5237
+ pj_pool_t *pool;
5238
+ pool = pj_pool_create_on_buf(NULL, pool_buf, sizeof(pool_buf));
5264
5239
 
5265
- char buf[1000];
5266
- strcpy(buf,additional_headers);
5267
- char *saved;
5268
- char *token = strtok_r(buf, "\n", &saved);
5269
- while(token){
5270
- char *name = strtok(token, ":");
5271
- char *value = strtok(NULL, " ");
5272
- addon_log(LOG_LEVEL_DEBUG, "Checking %s: %s\n", name, value);
5273
-
5274
- if(!name || !value) {
5275
- set_error("Invalid additional_header");
5276
- return PJ_FALSE;
5277
- }
5240
+ if(!document.HasMember("headers")) {
5241
+ return PJ_TRUE;
5242
+ }
5278
5243
 
5279
- if(strcmp(name, "Content-Type")==0) {
5280
- char *token_type = strtok(value, "/");
5281
- if(!token_type) {
5282
- set_error("No type specified in header Content-Type");
5283
- return PJ_FALSE;
5284
- }
5285
-
5286
- char *token_subtype = strtok(NULL, "\n");
5287
- if(!token_subtype) {
5288
- set_error("No subtype specified in header Content-Type");
5289
- return PJ_FALSE;
5290
- }
5244
+ if(!document["headers"].IsObject()) {
5245
+ set_error("Parameter headers must be an object");
5246
+ return PJ_FALSE;
5247
+ }
5291
5248
 
5292
- strcpy(type, token_type);
5293
- strcpy(subtype, token_subtype);
5294
- addon_log(LOG_LEVEL_DEBUG, "Checking parsing of Content-Type. type=%s: subtype=%s\n", type, subtype);
5295
- return PJ_TRUE;
5296
- }
5297
- token = strtok_r(NULL, "\n", &saved);
5249
+ Value headers = document["headers"].GetObject();
5250
+
5251
+ for (Value::ConstMemberIterator itr = headers.MemberBegin(); itr != headers.MemberEnd(); ++itr) {
5252
+ printf("%s => '%s'\n", itr->name.GetString(), itr->value.GetString());
5253
+
5254
+ const char *name = itr->name.GetString();
5255
+ if(!itr->value.IsString()) {
5256
+ set_error("Parameter headers key '%s' found with non-string value", name);
5257
+ return PJ_FALSE;
5258
+ }
5259
+
5260
+ const char *value = itr->value.GetString();
5261
+
5262
+ pj_str_t hname = pj_str((char*)name);
5263
+ pjsip_hdr *hdr = (pjsip_hdr*)pjsip_parse_hdr(pool,
5264
+ &hname,
5265
+ (char*)value,
5266
+ strlen(value),
5267
+ NULL);
5268
+
5269
+ if(!hdr) {
5270
+ set_error("Failed to parse header %s", name);
5271
+ return PJ_FALSE;
5272
+ }
5273
+
5274
+ pj_list_push_back(&hdr_list, hdr);
5298
5275
  }
5299
- set_error("Header Content-Type not supplied");
5300
- return PJ_FALSE;
5276
+
5277
+ pjsip_regc_add_headers(regc, &hdr_list);
5278
+ return PJ_TRUE;
5279
+ }
5280
+
5281
+ pj_bool_t get_content_type_and_subtype_from_headers(Document &document, char *type, char *subtype) {
5282
+ if(!document.HasMember("headers")) {
5283
+ set_error("Parameter headers absent");
5284
+ return PJ_FALSE;
5285
+ }
5286
+
5287
+ if(!document["headers"].IsObject()) {
5288
+ set_error("Parameter headers must be an object");
5289
+ return PJ_FALSE;
5290
+ }
5291
+
5292
+ Value headers = document["headers"].GetObject();
5293
+
5294
+ if(!headers.HasMember("Content-Type")) {
5295
+ set_error("Parameter headers doesn't contain key Content-Type");
5296
+ return PJ_FALSE;
5297
+ }
5298
+
5299
+ const char *content_type = headers["Content-Type"].GetString();
5300
+
5301
+ const char *slash;
5302
+ int index;
5303
+
5304
+ slash = strchr(content_type, '/');
5305
+ if(!slash) {
5306
+ set_error("Invalid header Content-Type");
5307
+ return PJ_FALSE;
5308
+ }
5309
+
5310
+ index = (int)(slash - content_type);
5311
+
5312
+ strncpy(type, content_type, index-1);
5313
+ strcpy(subtype, content_type+index);
5314
+ addon_log(LOG_LEVEL_DEBUG, "Checking parsing of Content-Type. type=%s: subtype=%s\n", type, subtype);
5315
+ return PJ_TRUE;
5301
5316
  }
5302
5317
 
5303
5318
 
@@ -5505,7 +5520,37 @@ out:
5505
5520
  return 0;
5506
5521
  }
5507
5522
 
5508
- bool subscription_subscribe(Subscription *s, int expires, const char *additional_headers) {
5523
+ bool subscription_subscribe_no_headers(Subscription *s, int expires) {
5524
+ pj_status_t status;
5525
+ pjsip_tx_data *tdata;
5526
+
5527
+ status = pjsip_evsub_initiate(s->evsub,
5528
+ NULL,
5529
+ expires,
5530
+ &tdata);
5531
+ if(status != PJ_SUCCESS) {
5532
+ set_error("pjsip_evsub_initiate failed");
5533
+ return false;
5534
+ }
5535
+
5536
+ status = pjsip_evsub_send_request(s->evsub, tdata);
5537
+ if(status != PJ_SUCCESS) {
5538
+ set_error("pjsip_inv_send_msg failed");
5539
+ return false;
5540
+ }
5541
+
5542
+ //Without this, on_rx_response will not be called
5543
+ status = pjsip_dlg_add_usage(s->dlg, &mod_tester, s);
5544
+ if(status != PJ_SUCCESS) {
5545
+ set_error("pjsip_dlg_add_usage failed");
5546
+ return false;
5547
+ }
5548
+
5549
+ return true;
5550
+ }
5551
+
5552
+
5553
+ bool subscription_subscribe(Subscription *s, int expires, Document &document) {
5509
5554
  pj_status_t status;
5510
5555
  pjsip_tx_data *tdata;
5511
5556
 
@@ -5518,7 +5563,7 @@ bool subscription_subscribe(Subscription *s, int expires, const char *additional
5518
5563
  return false;
5519
5564
  }
5520
5565
 
5521
- if(!add_additional_headers(s->dlg->pool, tdata, additional_headers)) {
5566
+ if(!add_headers(s->dlg->pool, tdata, document)) {
5522
5567
  return false;
5523
5568
  }
5524
5569
 
@@ -5544,7 +5589,6 @@ int pjw_subscription_subscribe(long subscription_id, const char *json) {
5544
5589
  clear_error();
5545
5590
 
5546
5591
  int expires;
5547
- char *additional_headers = NULL;
5548
5592
 
5549
5593
  Subscription *subscription;
5550
5594
 
@@ -5568,7 +5612,7 @@ int pjw_subscription_subscribe(long subscription_id, const char *json) {
5568
5612
  goto out;
5569
5613
  }
5570
5614
 
5571
- if(!subscription_subscribe(subscription, expires, additional_headers)) {
5615
+ if(!subscription_subscribe(subscription, expires, document)) {
5572
5616
  goto out;
5573
5617
  }
5574
5618