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 +11 -0
- package/package.json +1 -1
- package/samples/register_subscribe.js +7 -1
- package/samples/send_and_receive_fax.js +7 -7
- package/samples/simple.js +72 -33
- package/samples/tcp_and_extra_headers.js +333 -0
- package/src/pjmedia/include/chainlink/chainlink_fax.h +0 -1
- package/src/pjmedia/src/chainlink/chainlink_fax.c +1 -4
- package/src/sip.cpp +168 -124
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
|
@@ -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:
|
|
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: '
|
|
41
|
-
$fd: '
|
|
42
|
-
$tU: '
|
|
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: '
|
|
79
|
-
$fd: '
|
|
80
|
-
$tU: '
|
|
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
|
-
|
|
2
|
-
|
|
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
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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: '
|
|
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: '
|
|
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
|
+
|
|
@@ -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,
|
|
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,
|
|
429
|
+
static int call_create(Transport *t, unsigned flags, pjsip_dialog *dlg, const char *proxy_uri, Document &document);
|
|
430
430
|
|
|
431
|
-
bool
|
|
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
|
|
453
|
+
pj_bool_t add_headers_for_account(pjsip_regc* regc, Document &document);
|
|
452
454
|
|
|
453
|
-
pj_bool_t
|
|
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
|
-
|
|
1326
|
-
|
|
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(!
|
|
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(!
|
|
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,
|
|
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,
|
|
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(!
|
|
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(!
|
|
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(!
|
|
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(!
|
|
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,
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
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
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5197
|
+
if(!document["headers"].IsObject()) {
|
|
5198
|
+
set_error("Parameter headers must be an object");
|
|
5199
|
+
return PJ_FALSE;
|
|
5200
|
+
}
|
|
5223
5201
|
|
|
5224
|
-
|
|
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
|
-
|
|
5234
|
-
|
|
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
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
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
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
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
|
|
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
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
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
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
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
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
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
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
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
|
-
|
|
5300
|
-
|
|
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
|
|
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(!
|
|
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,
|
|
5615
|
+
if(!subscription_subscribe(subscription, expires, document)) {
|
|
5572
5616
|
goto out;
|
|
5573
5617
|
}
|
|
5574
5618
|
|