@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 CHANGED
@@ -1316,14 +1316,23 @@ class call {
1316
1316
  }
1317
1317
 
1318
1318
  const channeldef = await this.#createchannelremotedef()
1319
- if( !channeldef )
1320
- return this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
1319
+ if( !channeldef ) {
1320
+ this.hangup( hangupcodes.INCOMPATIBLE_DESTINATION )
1321
+ return
1322
+ }
1321
1323
 
1322
- if ( this.channels.audio )
1323
- this.channels.audio.destroy()
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 = undefined
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 ) throw Error( "Call already 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 - b_2 (rtp channel)
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
- let c_1 = b_2.other
2551
- if( !c_1 ) c_1 = b_2
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 res.send( 400, "Can't attened xfer 1 legged calls" )
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
- this.channels.audio.close()
2880
- this._timers.cleanup = setTimeout( () => {
2881
- console.trace( this.uuid + " Timeout waiting for channel close, cleaning up anyway, chan uuid: " + this.channels.audio.uuid + ", channel count: " + this.channels.count )
2882
- this._cleanup()
2883
- }, 60 * 1000 )
2884
-
2885
- await this.waitforhangup()
2886
- } else {
2887
- this._cleanup()
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
- this.callerid = remote.callerid.number
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
- if( callbacks.prebridge )
3250
- cookie = await callbacks.prebridge( this )
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
- if( callbacks.confirm ) await callbacks.confirm( this, cookie )
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 calleridstr = `"${callerid.name}" <sip:${callerid.user}@${callerid.host}>`
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 ) {
@@ -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.5",
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.3.7"
28
+ "@babblevoice/projectrtp": "^2.5.28"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@typescript-eslint/eslint-plugin": "^5.48.2",
@@ -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( "\"\" <sip:0123456789@someotherrealm.com>;party=calling;screen=yes" )
261
+ expect( requestoptions.headers[ "remote-party-id" ] ).to.equal( "<sip:0123456789@someotherrealm.com>;party=calling;screen=yes" )
262
262
 
263
- await call.hangup()
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": 2,
409
- "storebyuuid": 2,
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( "\"\" <sip:0000000000@localhost.localdomain>;party=calling;screen=yes" )
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( "\"\" <sip:012345789@localhost.localdomain>;party=calling;screen=yes" )
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,
@@ -16,9 +16,12 @@ describe( "xfer", function() {
16
16
  clearcallmanager()
17
17
  } )
18
18
 
19
- it( "call blind xfer single leg should fail", async function() {
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() {