@babblevoice/babble-drachtio-callmanager 3.6.5 → 3.6.9
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/lib/call.js +96 -29
- package/lib/callmanager.js +3 -0
- package/package.json +2 -2
- package/test/interface/call.js +8 -7
- package/test/interface/latecall.js +2 -3
- package/test/interface/xfer.js +5 -3
package/lib/call.js
CHANGED
|
@@ -1316,14 +1316,23 @@ class call {
|
|
|
1316
1316
|
}
|
|
1317
1317
|
|
|
1318
1318
|
const channeldef = await this.#createchannelremotedef()
|
|
1319
|
-
if( !channeldef )
|
|
1320
|
-
|
|
1319
|
+
if( !channeldef ) {
|
|
1320
|
+
this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
|
|
1321
|
+
return
|
|
1322
|
+
}
|
|
1321
1323
|
|
|
1322
|
-
if ( this.channels.audio )
|
|
1323
|
-
this.channels.audio.
|
|
1324
|
+
if ( this.channels.audio ) {
|
|
1325
|
+
this.channels.audio.close()
|
|
1326
|
+
delete this.channels.audio
|
|
1327
|
+
}
|
|
1324
1328
|
|
|
1325
1329
|
await this.#openrelatedchannel( channeldef )
|
|
1326
1330
|
|
|
1331
|
+
if ( !this.channels.audio ) {
|
|
1332
|
+
this.hangup( hangupcodes.SERVER_ERROR )
|
|
1333
|
+
return
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1327
1336
|
this.sdp.local = sdpgen.create()
|
|
1328
1337
|
.addcodecs( selectedcodec )
|
|
1329
1338
|
.setconnectionaddress( this.channels.audio.local.address )
|
|
@@ -1656,6 +1665,10 @@ class call {
|
|
|
1656
1665
|
} else {
|
|
1657
1666
|
|
|
1658
1667
|
await this.#openrelatedchannel( channeldef )
|
|
1668
|
+
if( !this.channels.audio ) {
|
|
1669
|
+
this.hangup( hangupcodes.SERVER_ERROR )
|
|
1670
|
+
return
|
|
1671
|
+
}
|
|
1659
1672
|
|
|
1660
1673
|
this.sdp.local = sdpgen.create()
|
|
1661
1674
|
.addcodecs( this.sdp.remote.selected.name )
|
|
@@ -1715,6 +1728,12 @@ class call {
|
|
|
1715
1728
|
this._em.emit( "call.early", this )
|
|
1716
1729
|
callmanager.options.em.emit( "call.early", this )
|
|
1717
1730
|
} else {
|
|
1731
|
+
|
|
1732
|
+
if ( !this.sdp.local ) {
|
|
1733
|
+
this.hangup( hangupcodes.SERVER_ERROR )
|
|
1734
|
+
return
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1718
1737
|
const dialog = await callmanager.options.srf.createUAS( this._req, this._res, {
|
|
1719
1738
|
localSdp: this.sdp.local.toString(),
|
|
1720
1739
|
headers: {
|
|
@@ -1743,9 +1762,8 @@ class call {
|
|
|
1743
1762
|
}
|
|
1744
1763
|
|
|
1745
1764
|
this.channels.count--
|
|
1746
|
-
|
|
1747
1765
|
if ( 0 === this.channels.count ) {
|
|
1748
|
-
this.channels.audio
|
|
1766
|
+
delete this.channels.audio
|
|
1749
1767
|
|
|
1750
1768
|
if( this._state._onhangup ) {
|
|
1751
1769
|
this._cleanup()
|
|
@@ -1756,6 +1774,7 @@ class call {
|
|
|
1756
1774
|
// based on the above flag
|
|
1757
1775
|
this.hangup()
|
|
1758
1776
|
}
|
|
1777
|
+
|
|
1759
1778
|
}
|
|
1760
1779
|
|
|
1761
1780
|
/**
|
|
@@ -1832,7 +1851,12 @@ class call {
|
|
|
1832
1851
|
*/
|
|
1833
1852
|
waitforanyevent( constraints, timeout = 500 ) {
|
|
1834
1853
|
|
|
1835
|
-
if( this.destroyed )
|
|
1854
|
+
if( this.destroyed ) {
|
|
1855
|
+
const ourerror = Error( "Call already destroyed" )
|
|
1856
|
+
// @ts-ignore
|
|
1857
|
+
ourerror.normalandsilent = true
|
|
1858
|
+
throw ourerror
|
|
1859
|
+
}
|
|
1836
1860
|
if ( this._promises.promise.channelevent ) return this._promises.promise.channelevent
|
|
1837
1861
|
|
|
1838
1862
|
this._eventconstraints = constraints
|
|
@@ -2139,6 +2163,11 @@ class call {
|
|
|
2139
2163
|
|
|
2140
2164
|
const oldchannel = othercall.channels.audio
|
|
2141
2165
|
const newchannel = await this.#openchannel( channeldef, othercall )
|
|
2166
|
+
if( !newchannel ) {
|
|
2167
|
+
this.hangup( hangupcodes.SERVER_ERROR )
|
|
2168
|
+
return
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2142
2171
|
await othercall.bond( this ).reinvite( channeldef, newchannel )
|
|
2143
2172
|
|
|
2144
2173
|
await oldchannel.unmix()
|
|
@@ -2390,8 +2419,6 @@ class call {
|
|
|
2390
2419
|
this._req = req
|
|
2391
2420
|
this._res = res
|
|
2392
2421
|
|
|
2393
|
-
if( !this.other ) return res.send( 400, "1 legged calls" )
|
|
2394
|
-
|
|
2395
2422
|
/* Auth the request - todo make a way of allow un authed refer - but we should nudge to secure */
|
|
2396
2423
|
if( this.referauthrequired ) {
|
|
2397
2424
|
if( this._auth.has( this._req ) ) {
|
|
@@ -2485,6 +2512,23 @@ class call {
|
|
|
2485
2512
|
console.trace( e )
|
|
2486
2513
|
}
|
|
2487
2514
|
}
|
|
2515
|
+
|
|
2516
|
+
/**
|
|
2517
|
+
* For 3-legged attended xfer
|
|
2518
|
+
* @param { object } b_1 - legb
|
|
2519
|
+
* @param { object } b_2 - lega
|
|
2520
|
+
* @param { object } req
|
|
2521
|
+
* @param { object } res
|
|
2522
|
+
* @returns
|
|
2523
|
+
*/
|
|
2524
|
+
async #handle3leggedxfer( b_1, b_2, req, res ) {
|
|
2525
|
+
await b_2._runblindxfer( req, res, { "uri": "sip:" + b_1.destination.user + "@" + b_1.destination.host } )
|
|
2526
|
+
if( b_1.channels.audio ) {
|
|
2527
|
+
b_1.channels.audio.unmix()
|
|
2528
|
+
await b_1.waitforanyevent( { "action": "mix", "event": "finished" }, 0.5 )
|
|
2529
|
+
}
|
|
2530
|
+
b_1.hangup( b_1.hangupcodes.ATTENDED_TRANSFER )
|
|
2531
|
+
}
|
|
2488
2532
|
|
|
2489
2533
|
/**
|
|
2490
2534
|
Attended xfers have 4 calls. 2 pairs.
|
|
@@ -2493,7 +2537,8 @@ class call {
|
|
|
2493
2537
|
|
|
2494
2538
|
b wants to connect a and c and then step out of the way
|
|
2495
2539
|
where b_2 is the active call and b_1 is the one b placed on hold
|
|
2496
|
-
therefore b_2 = this
|
|
2540
|
+
therefore b_2 = this. Thus, a_1 is the call that receives the REFER
|
|
2541
|
+
and will be transferred to c_1 (c_1 callid present in replaces header)
|
|
2497
2542
|
|
|
2498
2543
|
It is also pottential this could be requested
|
|
2499
2544
|
a_1 -> b_1
|
|
@@ -2517,7 +2562,7 @@ class call {
|
|
|
2517
2562
|
b_2 - conference
|
|
2518
2563
|
|
|
2519
2564
|
ends up with
|
|
2520
|
-
a_1 -
|
|
2565
|
+
a_1 - conference
|
|
2521
2566
|
|
|
2522
2567
|
@private
|
|
2523
2568
|
*/
|
|
@@ -2547,11 +2592,11 @@ class call {
|
|
|
2547
2592
|
if( !b_1.sdp.remote ) return res.send( 400, "No remote sdp negotiated (b_1)!" )
|
|
2548
2593
|
const b_2 = this /* so we can follow the above terminology */
|
|
2549
2594
|
|
|
2550
|
-
|
|
2551
|
-
if( !c_1 )
|
|
2595
|
+
const c_1 = b_2.other
|
|
2596
|
+
if( !c_1 ) return res.send( 400, "No call to transfer!" )
|
|
2552
2597
|
|
|
2553
2598
|
const a_1 = b_1.other
|
|
2554
|
-
if( !a_1 ) return
|
|
2599
|
+
if( !a_1 ) return await this.#handle3leggedxfer( b_1, b_2, req, res)
|
|
2555
2600
|
if( !a_1.sdp.remote ) return res.send( 400, "No remote sdp negotiated (a_1)!" )
|
|
2556
2601
|
|
|
2557
2602
|
if( !a_1.channels.audio ) return res.send( 400, "No channel (a_1)" )
|
|
@@ -2876,16 +2921,18 @@ class call {
|
|
|
2876
2921
|
/* flag destroyed so when we receive our close event we know what to do */
|
|
2877
2922
|
this.destroyed = true
|
|
2878
2923
|
if( this.channels.audio ) {
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2924
|
+
try {
|
|
2925
|
+
const cl = await this.channels.audio.close( { timeout: 60 * 1000 } )
|
|
2926
|
+
/* This probably needs tidying for consistency. If projectrtp is loaded as a modyule then this will return tru or false
|
|
2927
|
+
If it is a remote node we will get the close object. If this happens then this has also been passed back to us as
|
|
2928
|
+
an event on the event listener so we can safely return here and not call _cleanup */
|
|
2929
|
+
if( "object" == typeof cl ) return
|
|
2930
|
+
} catch( e ) {
|
|
2931
|
+
console.error( "timed out waiting for channel close" )
|
|
2932
|
+
}
|
|
2888
2933
|
}
|
|
2934
|
+
|
|
2935
|
+
this._cleanup()
|
|
2889
2936
|
}
|
|
2890
2937
|
|
|
2891
2938
|
/**
|
|
@@ -2972,14 +3019,17 @@ class call {
|
|
|
2972
3019
|
* @param { object } [ remote ]
|
|
2973
3020
|
*/
|
|
2974
3021
|
async update( remote ) {
|
|
2975
|
-
|
|
2976
3022
|
/* if we are asked to update it suggests we have received new information and overrides should go */
|
|
2977
3023
|
delete this.options.callerid
|
|
2978
3024
|
delete this.options.calledid
|
|
2979
3025
|
|
|
2980
3026
|
if( remote ) {
|
|
2981
3027
|
if( remote.callerid ) {
|
|
2982
|
-
|
|
3028
|
+
if ( remote.callerid.number )
|
|
3029
|
+
this.callerid = remote.callerid.number
|
|
3030
|
+
else
|
|
3031
|
+
this.callerid = remote.callerid.user
|
|
3032
|
+
|
|
2983
3033
|
this.calleridname = remote.callerid.name
|
|
2984
3034
|
}
|
|
2985
3035
|
}
|
|
@@ -3196,6 +3246,10 @@ class call {
|
|
|
3196
3246
|
} else {
|
|
3197
3247
|
|
|
3198
3248
|
await this.#openrelatedchannel( channeldef )
|
|
3249
|
+
if( !this.channels.audio ) {
|
|
3250
|
+
this.hangup( hangupcodes.SERVER_ERROR )
|
|
3251
|
+
return
|
|
3252
|
+
}
|
|
3199
3253
|
|
|
3200
3254
|
this.sdp.local = sdpgen.create()
|
|
3201
3255
|
.addcodecs( this.options.preferedcodecs )
|
|
@@ -3221,6 +3275,7 @@ class call {
|
|
|
3221
3275
|
* @param { object } callbacks
|
|
3222
3276
|
* @returns
|
|
3223
3277
|
*/
|
|
3278
|
+
// eslint-disable-next-line complexity
|
|
3224
3279
|
async #onnewuacsuccess( callbacks ) {
|
|
3225
3280
|
|
|
3226
3281
|
if( this.destroyedcancelledorhungup ) {
|
|
@@ -3246,8 +3301,12 @@ class call {
|
|
|
3246
3301
|
this.sip.tags.remote = this._dialog.sip.remoteTag
|
|
3247
3302
|
|
|
3248
3303
|
let cookie = undefined
|
|
3249
|
-
|
|
3250
|
-
|
|
3304
|
+
try {
|
|
3305
|
+
if( callbacks.prebridge )
|
|
3306
|
+
cookie = await callbacks.prebridge( this )
|
|
3307
|
+
} catch( e ) {
|
|
3308
|
+
console.trace( e )
|
|
3309
|
+
}
|
|
3251
3310
|
|
|
3252
3311
|
if( true === this.#noack ) {
|
|
3253
3312
|
await this._onlatebridge()
|
|
@@ -3255,7 +3314,11 @@ class call {
|
|
|
3255
3314
|
await this._onearlybridge()
|
|
3256
3315
|
}
|
|
3257
3316
|
|
|
3258
|
-
|
|
3317
|
+
try {
|
|
3318
|
+
if( callbacks.confirm ) await callbacks.confirm( this, cookie )
|
|
3319
|
+
} catch( e ) {
|
|
3320
|
+
console.trace( e )
|
|
3321
|
+
}
|
|
3259
3322
|
|
|
3260
3323
|
this._em.emit( "call.answered", this )
|
|
3261
3324
|
callmanager.options.em.emit( "call.answered", this )
|
|
@@ -3293,7 +3356,11 @@ class call {
|
|
|
3293
3356
|
}
|
|
3294
3357
|
|
|
3295
3358
|
const callerid = this.callerid
|
|
3296
|
-
const
|
|
3359
|
+
const firsthalf = `"${callerid.name}"`
|
|
3360
|
+
let calleridstr = `<sip:${callerid.user}@${callerid.host}>`
|
|
3361
|
+
|
|
3362
|
+
if ( callerid.name )
|
|
3363
|
+
calleridstr = firsthalf + " " + calleridstr
|
|
3297
3364
|
|
|
3298
3365
|
let privacy = ""
|
|
3299
3366
|
if( true === this.options.privacy ) {
|
package/lib/callmanager.js
CHANGED
|
@@ -62,8 +62,11 @@ class callmanager {
|
|
|
62
62
|
/* auth failed or timed out excluded as this is noral(ish) */
|
|
63
63
|
if( 403 === e.code ) return
|
|
64
64
|
if( 408 === e.code ) return
|
|
65
|
+
if( true === e.normalandsilent ) return
|
|
65
66
|
|
|
66
67
|
console.trace( e )
|
|
68
|
+
|
|
69
|
+
if( c.destroyed ) return
|
|
67
70
|
c.hangup( this.hangupcodes.SERVER_ERROR )
|
|
68
71
|
}
|
|
69
72
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@babblevoice/babble-drachtio-callmanager",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.9",
|
|
4
4
|
"description": "Call processing to create a PBX",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"uuid": "^8.3.2"
|
|
26
26
|
},
|
|
27
27
|
"optionalDependencies": {
|
|
28
|
-
"@babblevoice/projectrtp": "^2.
|
|
28
|
+
"@babblevoice/projectrtp": "^2.5.28"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@typescript-eslint/eslint-plugin": "^5.48.2",
|
package/test/interface/call.js
CHANGED
|
@@ -258,9 +258,10 @@ describe( "call object", function() {
|
|
|
258
258
|
child.update()
|
|
259
259
|
|
|
260
260
|
expect( requestoptions.method ).to.equal( "UPDATE" )
|
|
261
|
-
expect( requestoptions.headers[ "remote-party-id" ] ).to.equal( "
|
|
261
|
+
expect( requestoptions.headers[ "remote-party-id" ] ).to.equal( "<sip:0123456789@someotherrealm.com>;party=calling;screen=yes" )
|
|
262
262
|
|
|
263
|
-
|
|
263
|
+
/* simulate wire to propogate hangup */
|
|
264
|
+
await call.hangup( call.hangupcodes.NORMAL_CLEARING, false, "wire" )
|
|
264
265
|
|
|
265
266
|
expect( await callstore.stats() ).to.deep.include( {
|
|
266
267
|
"storebycallid": 0,
|
|
@@ -321,7 +322,7 @@ describe( "call object", function() {
|
|
|
321
322
|
child3newcalls[ 1 ].hangup()
|
|
322
323
|
] )
|
|
323
324
|
|
|
324
|
-
await inboundcall.hangup()
|
|
325
|
+
await inboundcall.hangup( call.hangupcodes.NORMAL_CLEARING, false, "wire" )
|
|
325
326
|
|
|
326
327
|
expect( await callstore.stats() ).to.deep.include( {
|
|
327
328
|
"storebycallid": 0,
|
|
@@ -405,8 +406,8 @@ describe( "call object", function() {
|
|
|
405
406
|
expect( children[ 0 ].hangup_cause.reason ).equal( "USER_BUSY" )
|
|
406
407
|
|
|
407
408
|
expect( await callstore.stats() ).to.deep.include( {
|
|
408
|
-
"storebycallid":
|
|
409
|
-
"storebyuuid":
|
|
409
|
+
"storebycallid": 1,
|
|
410
|
+
"storebyuuid": 1,
|
|
410
411
|
"storebyentity": 0
|
|
411
412
|
} )
|
|
412
413
|
|
|
@@ -789,7 +790,7 @@ describe( "call object", function() {
|
|
|
789
790
|
|
|
790
791
|
await call.newuac( options, { "early": ( c ) => c.hangup() } )
|
|
791
792
|
|
|
792
|
-
expect( createuacoptions.headers[ "remote-party-id" ] ).to.equal( "
|
|
793
|
+
expect( createuacoptions.headers[ "remote-party-id" ] ).to.equal( "<sip:0000000000@localhost.localdomain>;party=calling;screen=yes" )
|
|
793
794
|
expect( createuacoptions.late ).to.be.true
|
|
794
795
|
} )
|
|
795
796
|
|
|
@@ -1172,7 +1173,7 @@ describe( "call object", function() {
|
|
|
1172
1173
|
|
|
1173
1174
|
|
|
1174
1175
|
/* no default configured */
|
|
1175
|
-
expect( c.options.headers[ "remote-party-id" ] ).to.equal( "
|
|
1176
|
+
expect( c.options.headers[ "remote-party-id" ] ).to.equal( "<sip:012345789@localhost.localdomain>;party=calling;screen=yes" )
|
|
1176
1177
|
|
|
1177
1178
|
c._onhangup( "wire" )
|
|
1178
1179
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
|
|
2
2
|
const expect = require( "chai" ).expect
|
|
3
|
-
const call = require( "../../lib/call.js" )
|
|
4
3
|
const srf = require( "../mock/srf.js" )
|
|
5
4
|
|
|
6
5
|
/* These DO NOT form part of our interface */
|
|
@@ -117,8 +116,8 @@ describe( "uas.newuac - late", function() {
|
|
|
117
116
|
} )
|
|
118
117
|
|
|
119
118
|
|
|
120
|
-
/* Hangup parent - child should follow */
|
|
121
|
-
await call.hangup()
|
|
119
|
+
/* Hangup parent - child should follow (if the wire is the source) */
|
|
120
|
+
await call.hangup( call.hangupcodes.NORMAL_CLEARING, false, "wire" )
|
|
122
121
|
|
|
123
122
|
expect( await callstore.stats() ).to.deep.include( {
|
|
124
123
|
"storebycallid": 0,
|
package/test/interface/xfer.js
CHANGED
|
@@ -16,9 +16,12 @@ describe( "xfer", function() {
|
|
|
16
16
|
clearcallmanager()
|
|
17
17
|
} )
|
|
18
18
|
|
|
19
|
-
it
|
|
19
|
+
/* TODO - this needs fixing as it requires auth now 1 legged is expected to do something and checking a blind xfer happened */
|
|
20
|
+
xit( "call blind xfer single leg should fail on auth", async function() {
|
|
20
21
|
|
|
21
|
-
const srfscenario = new srf.srfscenario(
|
|
22
|
+
const srfscenario = new srf.srfscenario( {
|
|
23
|
+
userlookup: () => {}
|
|
24
|
+
} )
|
|
22
25
|
|
|
23
26
|
const call = await new Promise( ( resolve ) => {
|
|
24
27
|
srfscenario.oncall( async ( call ) => { resolve( call ) } )
|
|
@@ -40,7 +43,6 @@ describe( "xfer", function() {
|
|
|
40
43
|
await call.hangup()
|
|
41
44
|
|
|
42
45
|
expect( sipcodesent ).to.equal( 400 )
|
|
43
|
-
|
|
44
46
|
} )
|
|
45
47
|
|
|
46
48
|
it( "call blind xfer 2 leg no auth", async function() {
|