@babblevoice/babble-drachtio-callmanager 3.7.24 → 3.7.25

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.
Files changed (2) hide show
  1. package/lib/call.js +185 -177
  2. package/package.json +1 -1
package/lib/call.js CHANGED
@@ -18,8 +18,8 @@ const parseuriheadersre = /[^&=]+=[^&=]+/g
18
18
  const sessionexpirevaluere = /^(\d+)(?:;refresher=(uac|uas))?$/
19
19
 
20
20
  /**
21
- *
22
- * @param { string } s
21
+ *
22
+ * @param { string } s
23
23
  */
24
24
  function parseuri( s ) {
25
25
  if( "object" === typeof s )
@@ -49,8 +49,8 @@ function parseuri( s ) {
49
49
 
50
50
  /**
51
51
  * Util to convert key names to lowercase
52
- * @param { object } obj
53
- * @returns
52
+ * @param { object } obj
53
+ * @returns
54
54
  */
55
55
  function keynameslower( obj ) {
56
56
  return Object.keys( obj ).reduce( ( accumulator, currentvalue ) => {
@@ -77,7 +77,7 @@ function getheadervalue( headers, targetKey ) {
77
77
 
78
78
  /**
79
79
  * Used in caller id etc
80
- * @param { string } input
80
+ * @param { string } input
81
81
  * @returns { string }
82
82
  */
83
83
  function sanitizecalleridnumber( input ) {
@@ -88,7 +88,7 @@ function sanitizecalleridnumber( input ) {
88
88
 
89
89
  /**
90
90
  * used in caller id etc
91
- * @param { string } input
91
+ * @param { string } input
92
92
  * @returns { string }
93
93
  */
94
94
  function sanitizecalleridname( input ) {
@@ -270,7 +270,7 @@ class call {
270
270
  "audio": {
271
271
  "offer": true,
272
272
  "dtlscontroller": false,
273
- "reinvite": false
273
+ "roleset": false
274
274
  }
275
275
  },
276
276
  "count": 0
@@ -400,7 +400,7 @@ class call {
400
400
  this._em.setMaxListeners( 0 )
401
401
 
402
402
  /**
403
- * copy default
403
+ * copy default
404
404
  * @type { calloptions }
405
405
  */
406
406
  this.options = { ...callmanager.options }
@@ -428,7 +428,7 @@ class call {
428
428
  }
429
429
 
430
430
  /**
431
- *
431
+ *
432
432
  * @returns { entity }
433
433
  */
434
434
  #getentityforuac() {
@@ -438,7 +438,7 @@ class call {
438
438
  }
439
439
 
440
440
  /**
441
- *
441
+ *
442
442
  * @returns { entity }
443
443
  */
444
444
  #getentityforuas() {
@@ -533,7 +533,7 @@ class call {
533
533
 
534
534
 
535
535
  /**
536
- *
536
+ *
537
537
  * @returns { object }
538
538
  */
539
539
  #getremotefromheaders() {
@@ -562,9 +562,9 @@ class call {
562
562
  }
563
563
 
564
564
  /**
565
- *
566
- * @param { object } parsed
567
- * @returns
565
+ *
566
+ * @param { object } parsed
567
+ * @returns
568
568
  */
569
569
  #fixparseduriobj( parsed ) {
570
570
 
@@ -581,14 +581,14 @@ class call {
581
581
  }
582
582
 
583
583
  /**
584
- * Remember: this is from the perspective of us - not the phone.
584
+ * Remember: this is from the perspective of us - not the phone.
585
585
  * The phone perspective is the opposite
586
586
  * Inbound
587
587
  * A call is received - i.e. we receive a SIP invite
588
- *
588
+ *
589
589
  * Outbound
590
590
  * We make an outbound call. i.e. we send an INVITE
591
- *
591
+ *
592
592
  * Exceptions
593
593
  * partycalled - we send an INVITE but that is an inbound call
594
594
  * so the invite is in the opposite direction.
@@ -608,8 +608,8 @@ class call {
608
608
  }
609
609
 
610
610
  /**
611
- *
612
- * @param { object } startingpoint
611
+ *
612
+ * @param { object } startingpoint
613
613
  */
614
614
  #overridecallerid( startingpoint ) {
615
615
 
@@ -621,8 +621,8 @@ class call {
621
621
  }
622
622
 
623
623
  /**
624
- *
625
- * @param { object } startingpoint
624
+ *
625
+ * @param { object } startingpoint
626
626
  */
627
627
  #overridecalledid( startingpoint ) {
628
628
  startingpoint.privacy = this.options.privacy
@@ -637,9 +637,9 @@ class call {
637
637
  }
638
638
 
639
639
  /**
640
- *
641
- * @param { object } startingpoint
642
- * @returns
640
+ *
641
+ * @param { object } startingpoint
642
+ * @returns
643
643
  */
644
644
  #fromremoteheaders( startingpoint ) {
645
645
  const parsed = this.#fixparseduriobj( this.#getremotefromheaders() )
@@ -650,8 +650,8 @@ class call {
650
650
  }
651
651
 
652
652
  /**
653
- *
654
- * @param { object } startingpoint
653
+ *
654
+ * @param { object } startingpoint
655
655
  * @returns { boolean }
656
656
  */
657
657
  #fromrefertouri( startingpoint ) {
@@ -667,11 +667,11 @@ class call {
667
667
 
668
668
  return true
669
669
  }
670
-
670
+
671
671
  /**
672
- *
673
- * @param { object } startingpoint
674
- * @returns
672
+ *
673
+ * @param { object } startingpoint
674
+ * @returns
675
675
  */
676
676
  #fromcontact( startingpoint ) {
677
677
 
@@ -683,9 +683,9 @@ class call {
683
683
  }
684
684
 
685
685
  /**
686
- *
687
- * @param { object } startingpoint
688
- * @returns
686
+ *
687
+ * @param { object } startingpoint
688
+ * @returns
689
689
  */
690
690
  #fromdestination( startingpoint ) {
691
691
  const dest = this.destination
@@ -694,9 +694,9 @@ class call {
694
694
  }
695
695
 
696
696
  /**
697
- *
698
- * @param { object } startingpoint
699
- * @returns
697
+ *
698
+ * @param { object } startingpoint
699
+ * @returns
700
700
  */
701
701
  #fromoutentity( startingpoint ) {
702
702
 
@@ -708,13 +708,13 @@ class call {
708
708
  startingpoint.user = entity.username
709
709
  startingpoint.host = entity.realm
710
710
  return true
711
-
711
+
712
712
  }
713
713
 
714
714
  /**
715
- *
716
- * @param { object } startingpoint
717
- * @returns
715
+ *
716
+ * @param { object } startingpoint
717
+ * @returns
718
718
  */
719
719
  #frominentity( startingpoint ) {
720
720
  const entity = this._entity
@@ -730,7 +730,7 @@ class call {
730
730
 
731
731
  /**
732
732
  * We have received an INVITE
733
- * @param { object } startingpoint
733
+ * @param { object } startingpoint
734
734
  */
735
735
  #calledidforuas( startingpoint ) {
736
736
  this.#fromdestination( startingpoint )
@@ -738,7 +738,7 @@ class call {
738
738
 
739
739
  /**
740
740
  * We have sent an INVITE - which could also be 3rd party
741
- * @param { object } startingpoint
741
+ * @param { object } startingpoint
742
742
  */
743
743
  #calledidforuac( startingpoint ) {
744
744
  if( !this.#fromoutentity( startingpoint ) ) {
@@ -750,7 +750,7 @@ class call {
750
750
  /**
751
751
  * We have sent an INVITE - but it could be 3rd party
752
752
  * @param { object } startingpoint
753
- * @returns
753
+ * @returns
754
754
  */
755
755
  #calleridforuac( startingpoint ) {
756
756
  if( !this.#fromoutentity( startingpoint ) ) {
@@ -758,8 +758,8 @@ class call {
758
758
  }
759
759
  }
760
760
 
761
- /**
762
- * We have received an INVITE - so caller ID comes from headers / auth
761
+ /**
762
+ * We have received an INVITE - so caller ID comes from headers / auth
763
763
  * @param { object } startingpoint
764
764
  */
765
765
  #calleridforuas( startingpoint ) {
@@ -768,9 +768,9 @@ class call {
768
768
  }
769
769
 
770
770
  /**
771
- * Get the caller id when we have another leg.
772
- * @param { object } startingpoint
773
- * @param { object } other
771
+ * Get the caller id when we have another leg.
772
+ * @param { object } startingpoint
773
+ * @param { object } other
774
774
  */
775
775
  #calleridother( startingpoint, other ) {
776
776
  if( "uas" === other.type ) {
@@ -783,9 +783,9 @@ class call {
783
783
  }
784
784
 
785
785
  /**
786
- *
787
- * @param { object } startingpoint
788
- * @param { object } other
786
+ *
787
+ * @param { object } startingpoint
788
+ * @param { object } other
789
789
  */
790
790
  #calledidother( startingpoint, other ) {
791
791
  if( "uas" === other.type ) {
@@ -797,7 +797,7 @@ class call {
797
797
  }
798
798
 
799
799
  /**
800
- *
800
+ *
801
801
  * @returns { object }
802
802
  */
803
803
  #callerid() {
@@ -851,7 +851,7 @@ class call {
851
851
  }
852
852
 
853
853
  /**
854
- *
854
+ *
855
855
  * @returns { object }
856
856
  */
857
857
  #calledid() {
@@ -1031,7 +1031,7 @@ class call {
1031
1031
  * @param { boolean } r - the new state
1032
1032
  */
1033
1033
  set ringing( r ) {
1034
- this.state.ringing = r
1034
+ this.state.ringing = r
1035
1035
  }
1036
1036
 
1037
1037
  /**
@@ -1170,7 +1170,7 @@ class call {
1170
1170
  this._em.removeAllListeners( ev )
1171
1171
  return
1172
1172
  }
1173
-
1173
+
1174
1174
  this._em.off( ev, cb )
1175
1175
  }
1176
1176
 
@@ -1278,7 +1278,7 @@ class call {
1278
1278
  @event call.destroyed
1279
1279
  @type {call}
1280
1280
  */
1281
-
1281
+
1282
1282
  /**
1283
1283
  Emitted immediatly called after call.destroyed
1284
1284
  @event call.reporting
@@ -1329,7 +1329,7 @@ class call {
1329
1329
 
1330
1330
  /**
1331
1331
  * Logically adopt a child call
1332
- * @param { object } other
1332
+ * @param { object } other
1333
1333
  * @param { boolean } [ mix ]
1334
1334
  * @return { call }
1335
1335
  */
@@ -1351,9 +1351,9 @@ class call {
1351
1351
 
1352
1352
  /**
1353
1353
  * Create a bond between us and another call. This is currently only used
1354
- * to provide other channels we know we might need to open a channel on
1354
+ * to provide other channels we know we might need to open a channel on
1355
1355
  * the same node as. (replaces preferredcall)
1356
- * @param { object } relative
1356
+ * @param { object } relative
1357
1357
  * @return { call }
1358
1358
  */
1359
1359
  bond( relative ) {
@@ -1396,17 +1396,17 @@ class call {
1396
1396
  try {
1397
1397
  if( this.state.established ) return
1398
1398
  if( !this._res || !this._res.msg || !this._res.msg.body ) return
1399
-
1399
+
1400
1400
  /* we have this._res */
1401
1401
  this.sdp.remote = sdpgen.create( this._res.msg.body )
1402
1402
  this.#parsesrtpsetup()
1403
1403
  await this.answer( { "early": true } )
1404
-
1404
+
1405
1405
  if( !this.parent || this.parent.state.established ) return
1406
-
1406
+
1407
1407
  await this.parent.answer( { "early": true } )
1408
1408
  await this.channels.audio.mix( this.parent.channels.audio )
1409
-
1409
+
1410
1410
  this.parent._res.send( 183, {
1411
1411
  headers: {
1412
1412
  "User-Agent": "project",
@@ -1420,7 +1420,7 @@ class call {
1420
1420
  }
1421
1421
 
1422
1422
  /**
1423
- *
1423
+ *
1424
1424
  * @returns { boolean } - true if an error has occured
1425
1425
  */
1426
1426
  #answerparenterrors() {
@@ -1446,7 +1446,7 @@ class call {
1446
1446
  }
1447
1447
 
1448
1448
  /**
1449
- *
1449
+ *
1450
1450
  * @returns { Promise< object > } - return ourself
1451
1451
  */
1452
1452
  async #answerparent() {
@@ -1576,7 +1576,7 @@ class call {
1576
1576
  this.hangup( hangupcodes.USER_GONE )
1577
1577
  return
1578
1578
  }
1579
-
1579
+
1580
1580
  this.established = true
1581
1581
  this.#addevents( this._dialog )
1582
1582
 
@@ -1619,7 +1619,7 @@ class call {
1619
1619
  */
1620
1620
  async auth() {
1621
1621
 
1622
- if( undefined === callmanager.options.userlookup ) {
1622
+ if( undefined === callmanager.options.userlookup ) {
1623
1623
  console.trace( "no userlookup function provided" )
1624
1624
  return
1625
1625
  }
@@ -1774,7 +1774,7 @@ class call {
1774
1774
 
1775
1775
  if( this._promises.resolve.events ) {
1776
1776
  const r = this._promises.resolve.events
1777
-
1777
+
1778
1778
  this._promises.resolve.events = undefined
1779
1779
  this._promises.promise.events = undefined
1780
1780
  r( ourmatch[ 0 ] )
@@ -1897,8 +1897,8 @@ class call {
1897
1897
  }
1898
1898
 
1899
1899
  /**
1900
- *
1901
- * @param { object } channeldef
1900
+ *
1901
+ * @param { object } channeldef
1902
1902
  */
1903
1903
  async #openchannelsforanswer( channeldef ) {
1904
1904
  if( this.channels.audio ) {
@@ -2031,9 +2031,9 @@ class call {
2031
2031
  }
2032
2032
 
2033
2033
  /**
2034
- *
2035
- * @param { object } e
2036
- * @returns
2034
+ *
2035
+ * @param { object } e
2036
+ * @returns
2037
2037
  */
2038
2038
  #handlechannelwaiting( e ) {
2039
2039
  if( this._eventconstraints ) {
@@ -2052,7 +2052,7 @@ class call {
2052
2052
  }
2053
2053
  }
2054
2054
 
2055
- /*
2055
+ /*
2056
2056
  We get here if the contraints match OR it is a tel event.
2057
2057
  A close event will be caught on call clean up.
2058
2058
  */
@@ -2164,7 +2164,7 @@ class call {
2164
2164
  * @param { object } soup - sound soup as described in projectrtp
2165
2165
  */
2166
2166
  set moh( soup ) {
2167
- this._moh = soup
2167
+ this._moh = soup
2168
2168
  }
2169
2169
 
2170
2170
  /**
@@ -2182,7 +2182,7 @@ class call {
2182
2182
  * @param { object } soup - sound soup as described in projectrtp
2183
2183
  */
2184
2184
  set ringsoup( soup ) {
2185
- this._ringsoup = soup
2185
+ this._ringsoup = soup
2186
2186
  }
2187
2187
 
2188
2188
  /**
@@ -2201,9 +2201,9 @@ class call {
2201
2201
  * @param { object } soup - sound soup as described in projectrtp
2202
2202
  */
2203
2203
  set engagedsoup( soup ) {
2204
- this._engagedsoup = soup
2204
+ this._engagedsoup = soup
2205
2205
  }
2206
-
2206
+
2207
2207
  /**
2208
2208
  * Return the current engagedsoup
2209
2209
  * @returns { object } sound soup
@@ -2220,9 +2220,9 @@ class call {
2220
2220
  * @param { object } soup - sound soup as described in projectrtp
2221
2221
  */
2222
2222
  set unobtainablesoup( soup ) {
2223
- this._unobtainablesoup = soup
2223
+ this._unobtainablesoup = soup
2224
2224
  }
2225
-
2225
+
2226
2226
  /**
2227
2227
  * Return the current unobtainable
2228
2228
  * @returns { object } sound soup
@@ -2374,7 +2374,7 @@ class call {
2374
2374
  "body": "SIP/2.0 100 Trying\r\n"
2375
2375
  }
2376
2376
 
2377
- try{
2377
+ try{
2378
2378
  await this._dialog.request( opts )
2379
2379
  } catch( e ) {
2380
2380
  /* silent */
@@ -2384,7 +2384,7 @@ class call {
2384
2384
  /**
2385
2385
  Send out pre-modified SDP to get the audio to the new location.
2386
2386
  This method might be use, but there are problems - so when it is used
2387
- it can be re-written but should use a mthod to create a codec - which is the
2387
+ it can be re-written but should use a mthod to create a codec - which is the
2388
2388
  same as used elsewhere. If a bond has been attached to the call, we will
2389
2389
  try to reuse an existing channel.
2390
2390
  @param { object } [ newchannel ]
@@ -2448,7 +2448,7 @@ class call {
2448
2448
 
2449
2449
  /**
2450
2450
  * hangs up and throws exception to get out
2451
- * @param { string } reason
2451
+ * @param { string } reason
2452
2452
  */
2453
2453
  #servererror( reason ) {
2454
2454
  this.hangup( hangupcodes.SERVER_ERROR )
@@ -2461,8 +2461,8 @@ class call {
2461
2461
  }
2462
2462
 
2463
2463
  /**
2464
- *
2465
- * @param { call } othercall
2464
+ *
2465
+ * @param { call } othercall
2466
2466
  * @param { boolean } [ sendreinvite=true ] Auto send reinvite if the channel is modified
2467
2467
  * @returns { Promise }
2468
2468
  */
@@ -2520,7 +2520,7 @@ class call {
2520
2520
  }
2521
2521
 
2522
2522
  bind.channels.count++
2523
-
2523
+
2524
2524
  return chan
2525
2525
  } catch( e ) {
2526
2526
  if( this.channels.audio )
@@ -2531,10 +2531,10 @@ class call {
2531
2531
  }
2532
2532
 
2533
2533
  /**
2534
- *
2535
- * @param { object } req
2536
- * @param { object } res
2537
- * @returns
2534
+ *
2535
+ * @param { object } req
2536
+ * @param { object } res
2537
+ * @returns
2538
2538
  */
2539
2539
  #getreferedto( req, res ) {
2540
2540
 
@@ -2575,8 +2575,8 @@ class call {
2575
2575
 
2576
2576
  /**
2577
2577
  * We receive a modify to a dialgue (re-invite) and it looks like a hold
2578
- * @param { string } direction
2579
- * @param { object } res
2578
+ * @param { string } direction
2579
+ * @param { object } res
2580
2580
  * @returns { boolean }
2581
2581
  */
2582
2582
  #handlehold( direction, res ) {
@@ -2602,7 +2602,7 @@ class call {
2602
2602
  /**
2603
2603
  * We receive a request to unhold.
2604
2604
  * @param { string | undefined } direction undefined is the same as "sendrecv"
2605
- * @param { object } res
2605
+ * @param { object } res
2606
2606
  * @returns { boolean }
2607
2607
  */
2608
2608
  #handleoffhold( direction, res ) {
@@ -2634,20 +2634,21 @@ class call {
2634
2634
  }
2635
2635
 
2636
2636
  /**
2637
- *
2637
+ *
2638
2638
  */
2639
2639
  #checkthischannelforwebrtcdirection() {
2640
- if( this._iswebrtc && this.channels?.audio ) {
2641
- /* we are sending invite so we MUST offer be actpass */
2642
- this.channels.secure.audio.offer = true
2643
- this.sdp.local
2644
- .secure( this.channels.audio.local.dtls.fingerprint, this.#getsrtpsetup( this.channels.secure.audio ) )
2645
- }
2640
+ if( !this._iswebrtc ) return
2641
+ if( !this.channels?.audio ) return
2642
+
2643
+ /* we are sending invite so we MUST offer be actpass */
2644
+ this.channels.secure.audio.offer = true
2645
+ this.sdp.local
2646
+ .secure( this.channels.audio.local.dtls.fingerprint, this.#getsrtpsetup( this.channels.secure.audio ) )
2646
2647
  }
2647
2648
 
2648
2649
  /**
2649
2650
  * Add events for the drachtio dialog object that this object requires.
2650
- * @param { object } dialog
2651
+ * @param { object } dialog
2651
2652
  */
2652
2653
  #addevents( dialog ) {
2653
2654
 
@@ -2675,7 +2676,7 @@ class call {
2675
2676
  body: this.sdp.local.toString(),
2676
2677
  headers: this.options.headers
2677
2678
  }
2678
-
2679
+
2679
2680
  const res = await dialog.request( opts )
2680
2681
 
2681
2682
  if( this.destroyed ) return
@@ -2718,7 +2719,7 @@ class call {
2718
2719
 
2719
2720
  return res.send( 200 )
2720
2721
  }
2721
-
2722
+
2722
2723
  return res.send( 415, "Unsupported Media Type" )
2723
2724
  } )
2724
2725
 
@@ -2727,15 +2728,13 @@ class call {
2727
2728
  this.#expirelasttouch = +new Date()
2728
2729
  if( "INVITE" !== req.msg.method ) return
2729
2730
 
2730
- this.channels.secure.audio.reinvite = true
2731
2731
  const sdp = this.#getsdpformodify( req )
2732
2732
 
2733
2733
  if( this._iswebrtc && this.channels?.audio ) {
2734
2734
  this.channels.secure.audio.offer = false
2735
- this.channels.secure.audio.dtlscontroller = true
2736
2735
 
2737
2736
  this.sdp.local
2738
- .secure( this.channels.audio.local.dtls.fingerprint,
2737
+ .secure( this.channels.audio.local.dtls.fingerprint,
2739
2738
  this.#getsrtpsetup( this.channels.secure.audio ) )
2740
2739
  }
2741
2740
 
@@ -2863,14 +2862,14 @@ class call {
2863
2862
  console.trace( e )
2864
2863
  }
2865
2864
  }
2866
-
2865
+
2867
2866
  /**
2868
2867
  * For 3-legged attended xfer
2869
2868
  * @param { object } b_1 - legb
2870
2869
  * @param { object } b_2 - lega
2871
2870
  * @param { object } req
2872
2871
  * @param { object } res
2873
- * @returns
2872
+ * @returns
2874
2873
  */
2875
2874
  async #handle3leggedxfer( b_1, b_2, req, res ) {
2876
2875
  await b_2._runblindxfer( req, res, { "uri": "sip:" + b_1.destination.user + "@" + b_1.destination.host } )
@@ -2960,7 +2959,7 @@ class call {
2960
2959
  b_2.detach()
2961
2960
  c_1.detach()
2962
2961
 
2963
- await Promise.all( [
2962
+ await Promise.all( [
2964
2963
  a_1._unhold(),
2965
2964
  b_1._unhold(),
2966
2965
  b_2._unhold(),
@@ -3014,7 +3013,7 @@ class call {
3014
3013
 
3015
3014
  this.hangup_cause = Object.assign( { "src": "wire" }, hangupcodes.ATTENDED_TRANSFER )
3016
3015
  b_1.hangup( hangupcodes.ATTENDED_TRANSFER )
3017
-
3016
+
3018
3017
  a_1._em.emit( "call.updated", a_1 )
3019
3018
  callmanager.options.em.emit( "call.updated", a_1 )
3020
3019
 
@@ -3039,7 +3038,7 @@ class call {
3039
3038
  let candidates = sdp.media[ 0 ].candidates
3040
3039
  if( 0 < candidates.length ) {
3041
3040
  if( callmanager.options.ignoreipv6candidates ) {
3042
- candidates = candidates.filter( ( c ) => {
3041
+ candidates = candidates.filter( ( c ) => {
3043
3042
  const ismatch = net.isIPv6( c.ip )
3044
3043
  return !ismatch
3045
3044
  } )
@@ -3068,41 +3067,50 @@ class call {
3068
3067
  * @returns { "passive" | "active" | "actpass" }
3069
3068
  */
3070
3069
  #getsrtpsetup( secure ) {
3071
- if( secure.offer ) {
3072
- /* ref: https://datatracker.ietf.org/doc/html/draft-ietf-rtcweb-jsep-14#page-34 offer MUST be actpass */
3073
- secure.dtlscontroller = false
3074
- secure.offer = false
3075
- return "actpass"
3076
- }
3077
-
3078
- return secure.dtlscontroller ? "active" : "passive"
3070
+ if( secure.offer ) {
3071
+ // We’re the offerer; RFC says use actpass, but DO NOT reset dtlscontroller
3072
+ secure.offer = false
3073
+ return "actpass"
3079
3074
  }
3080
3075
 
3076
+ // Answer or in-dialog SDP
3077
+ return secure.dtlscontroller ? "active" : "passive"
3078
+ }
3079
+
3081
3080
  /**
3082
3081
  * Parses our remote SDP to decide if we are the controller or not.
3083
3082
  * TODO - parse SDP for other streams.
3084
3083
  * @param { object } [ sdp ] - the sdp object - or leave if use our remote
3085
- * @returns
3084
+ * @returns
3086
3085
  */
3087
- #parsesrtpsetup( sdp ) {
3086
+ #parsesrtpsetup(sdp) {
3088
3087
  if( !sdp ) sdp = this.sdp.remote
3089
3088
  if( !sdp ) return
3090
3089
 
3091
3090
  const audio = sdp.getmedia( "audio" )
3092
- if( !audio ) return
3093
-
3094
- if( "passive" == audio.setup ||
3095
- ( "actpass" == audio.setup && !this.channels.secure.audio.reinvite ) ) {
3096
- /* we might also have to re-negotiate our rtp server - but this should keep it in the right direction */
3097
- this.channels.secure.audio.dtlscontroller = true
3098
- this.channels.secure.audio.reinvite = false
3099
- } else if( "active" == audio.setup ) {
3100
- this.channels.secure.audio.dtlscontroller = false
3091
+ if ( !audio ) return
3092
+
3093
+ const secure = this.channels.secure.audio
3094
+
3095
+ if( !secure.roleset ) {
3096
+ if( "active" === audio.setup ) {
3097
+ // remote is active → we are passive
3098
+ secure.dtlscontroller = false
3099
+ } else if( "passive" === audio.setup ) {
3100
+ // remote is passive → we are active
3101
+ secure.dtlscontroller = true
3102
+ } else if( "actpass" === audio.setup ) {
3103
+ // remote is indecisive; pick a side and stick to it
3104
+ // For this leg, ALWAYS behave as active, for example:
3105
+ secure.dtlscontroller = true
3106
+ }
3107
+ secure.roleset = true
3101
3108
  }
3102
3109
 
3103
- this.channels.secure.audio.offer = false
3110
+ secure.offer = false
3104
3111
  }
3105
3112
 
3113
+
3106
3114
  /**
3107
3115
  * Get the mode string for our media server perspective.
3108
3116
  * @param { object } media object returned by sdp.getmedia()
@@ -3114,7 +3122,7 @@ class call {
3114
3122
  }
3115
3123
 
3116
3124
  /**
3117
- * Returns and object we can pass into an openchannel function,
3125
+ * Returns and object we can pass into an openchannel function,
3118
3126
  * { remote: {} } - the remote can be passed into set remote on
3119
3127
  * an already open channel.
3120
3128
  * @returns { Promise< object | undefined > }
@@ -3124,12 +3132,12 @@ class call {
3124
3132
  const iswebrtc = this._iswebrtc
3125
3133
 
3126
3134
  let target
3127
- try{
3135
+ try{
3128
3136
  target = await this.#getremotetarget( iswebrtc )
3129
3137
  } catch( e ) {
3130
3138
  throw new Error( e )
3131
3139
  }
3132
-
3140
+
3133
3141
 
3134
3142
  if( !target ) return
3135
3143
  const address = target.address
@@ -3222,7 +3230,7 @@ class call {
3222
3230
  callstore.delete( this ).then( () => {
3223
3231
  this._em.emit( "call.destroyed", this )
3224
3232
  callmanager.options.em.emit( "call.destroyed", this )
3225
-
3233
+
3226
3234
  this._em.emit( "call.reporting", this )
3227
3235
  callmanager.options.em.emit( "call.reporting", this )
3228
3236
 
@@ -3285,15 +3293,15 @@ class call {
3285
3293
  }
3286
3294
 
3287
3295
  /**
3288
- *
3289
- * @param { "us"|"wire" } src
3290
- * @returns
3296
+ *
3297
+ * @param { "us"|"wire" } src
3298
+ * @returns
3291
3299
  */
3292
3300
  async #handlewirehangup( src ) {
3293
3301
 
3294
3302
  if( "wire" != src ) return
3295
3303
 
3296
- /*
3304
+ /*
3297
3305
  1. Hangup our children regardless of our state
3298
3306
  2. If we are established and the only child child - hangup our parent (unless otherwise told not to: parent.continueonotherhangup)
3299
3307
  */
@@ -3343,7 +3351,7 @@ class call {
3343
3351
  * @param { object } reason - one of the reasons from the hangupcodes enum
3344
3352
  * @param { boolean } [ tone ] if true play the appropriate tone instead of actaully hanging up
3345
3353
  * @param { "wire" | "us" } [ source ] - who initiated the hangup - typically us - but if the signal came from another network interface - then wire
3346
- *
3354
+ *
3347
3355
  */
3348
3356
  async hangup( reason, tone = false, source = "us" ) {
3349
3357
  try {
@@ -3367,15 +3375,15 @@ class call {
3367
3375
  }
3368
3376
 
3369
3377
  await this.#destroydialog()
3370
- } catch( e ) {
3371
- console.trace( e )
3378
+ } catch( e ) {
3379
+ console.trace( e )
3372
3380
  }
3373
3381
 
3374
3382
  await this._onhangup( source, reason )
3375
3383
  }
3376
3384
 
3377
3385
  /**
3378
- *
3386
+ *
3379
3387
  * @returns { Promise }
3380
3388
  */
3381
3389
  async #destroydialog() {
@@ -3409,7 +3417,7 @@ class call {
3409
3417
 
3410
3418
  /**
3411
3419
  * @param { object } reason - plays the apropropriate tone
3412
- * @param { boolean } tone
3420
+ * @param { boolean } tone
3413
3421
  */
3414
3422
  #hangupestablish( reason, tone ) {
3415
3423
  if( !reason ) return false
@@ -3446,12 +3454,12 @@ class call {
3446
3454
  reason === hangupcodes.PICKED_OFF ||
3447
3455
  reason === hangupcodes.LOSE_RACE
3448
3456
  ) {
3449
- await this._req.cancel( { headers: {
3457
+ await this._req.cancel( { headers: {
3450
3458
  Reason: "SIP;cause=200;text=\"Call completed elsewhere\""
3451
3459
  } } )
3452
3460
  return
3453
3461
  } else if ( reason === hangupcodes.REQUEST_TIMEOUT && this.options.uactimeoutreason ) {
3454
- await this._req.cancel( { headers: {
3462
+ await this._req.cancel( { headers: {
3455
3463
  Reason: this.options.uactimeoutreason
3456
3464
  } } )
3457
3465
  return
@@ -3462,7 +3470,7 @@ class call {
3462
3470
  }
3463
3471
 
3464
3472
  /**
3465
- *
3473
+ *
3466
3474
  * @returns { Promise }
3467
3475
  */
3468
3476
  async waitforhangup() {
@@ -3542,7 +3550,7 @@ class call {
3542
3550
 
3543
3551
  if( !( "headers" in options ) ) options.headers = {}
3544
3552
  /* If max-forwards is not specified then we decrement the parent and pass on */
3545
-
3553
+
3546
3554
  const ouroptionsmaxforwards = getheadervalue( options.headers, "max-forwards" )
3547
3555
  const ourimportsmaxforwards = getheadervalue( this.propagate.headers, "max-forwards" )
3548
3556
 
@@ -3594,9 +3602,9 @@ class call {
3594
3602
 
3595
3603
  return contactinfo
3596
3604
  }
3597
-
3605
+
3598
3606
  /**
3599
- *
3607
+ *
3600
3608
  * @returns { Promise< boolean | object > }
3601
3609
  */
3602
3610
  static async #callcontactfornewuac( options, callbacks ) {
@@ -3679,22 +3687,22 @@ class call {
3679
3687
  if( this.channels.audio ) return
3680
3688
 
3681
3689
  const othercall = this.other
3682
- /* TODO: this is a hack. projectrtp has become too complicated with both a listen and connect
3683
- mechanism. This is causing problems in code like this. There is no interface to
3690
+ /* TODO: this is a hack. projectrtp has become too complicated with both a listen and connect
3691
+ mechanism. This is causing problems in code like this. There is no interface to
3684
3692
  detect which mode the channel is in - but the channels property will exist on a connect
3685
3693
  style channel. projectrtp will getrelatives a rewrite to support only one. */
3686
3694
  if( othercall && othercall.channels.audio ) {
3687
3695
  this.channels.audio = await othercall.#openchannel( channeldef, this )
3688
3696
  return
3689
3697
  }
3690
-
3698
+
3691
3699
  for( const other of this.relatives ) {
3692
3700
  if( other.channels && other.channels.audio ) {
3693
3701
  this.channels.audio = await other.#openchannel( channeldef, this )
3694
3702
  return
3695
3703
  }
3696
3704
  }
3697
-
3705
+
3698
3706
  this.channels.audio = await this.#openchannel( channeldef )
3699
3707
  } catch( e ) {
3700
3708
  console.trace( e )
@@ -3734,7 +3742,7 @@ class call {
3734
3742
  }
3735
3743
 
3736
3744
  /**
3737
- * We have an open channel so now configure our local SDP
3745
+ * We have an open channel so now configure our local SDP
3738
3746
  * object to match.
3739
3747
  */
3740
3748
  #configurelocalsdp() {
@@ -3766,9 +3774,9 @@ class call {
3766
3774
  }
3767
3775
 
3768
3776
  /**
3769
- *
3770
- * @param { object } callbacks
3771
- * @returns
3777
+ *
3778
+ * @param { object } callbacks
3779
+ * @returns
3772
3780
  */
3773
3781
  // eslint-disable-next-line complexity
3774
3782
  async #onnewuacsuccess( callbacks ) {
@@ -3782,7 +3790,7 @@ class call {
3782
3790
  for( const child of this.parent.children ) {
3783
3791
  if( child !== this ) {
3784
3792
  child.detach()
3785
- /* do not await - we do not want to delay the winner in
3793
+ /* do not await - we do not want to delay the winner in
3786
3794
  connecting by waiting for the completion of the hangups */
3787
3795
  hangups.push( child.hangup( hangupcodes.LOSE_RACE ) )
3788
3796
  }
@@ -3829,8 +3837,8 @@ class call {
3829
3837
  }
3830
3838
 
3831
3839
  /**
3832
- *
3833
- * @param { object } err
3840
+ *
3841
+ * @param { object } err
3834
3842
  */
3835
3843
  #onnewuaccatch( err ) {
3836
3844
  if( !this._state._hangup ) {
@@ -3899,7 +3907,7 @@ class call {
3899
3907
  * 1. existing to field
3900
3908
  * 2. called id number and entity realm
3901
3909
  * 3. entity full uri
3902
- * @returns
3910
+ * @returns
3903
3911
  */
3904
3912
  #configureto() {
3905
3913
  if( this.options.headers[ "to" ] ) return
@@ -3909,12 +3917,12 @@ class call {
3909
3917
  this.options.headers[ "to" ] = "<sip:" + this.options.calledid.number + "@" + this.options.entity.realm + ">"
3910
3918
  return
3911
3919
  }
3912
-
3920
+
3913
3921
  this.options.headers[ "to" ] = "<sip:" + this.options.entity.uri + ">"
3914
3922
  }
3915
3923
 
3916
3924
  /**
3917
- *
3925
+ *
3918
3926
  * @param { string } contactstr
3919
3927
  */
3920
3928
  #configautoanswerfornewuac( contactstr ) {
@@ -3940,7 +3948,7 @@ class call {
3940
3948
 
3941
3949
  /**
3942
3950
  * Export headers from optioms
3943
- * @param { object } options
3951
+ * @param { object } options
3944
3952
  */
3945
3953
  export( options ) {
3946
3954
  if ( options.headers ) {
@@ -3949,7 +3957,7 @@ class call {
3949
3957
  }
3950
3958
  }
3951
3959
  if ( options.autoanswer ) this.propagate.autoanswer = options.autoanswer
3952
- }
3960
+ }
3953
3961
 
3954
3962
  /**
3955
3963
  * Import headers from parent call
@@ -4011,7 +4019,7 @@ class call {
4011
4019
  * @property { object } [ calledid ]
4012
4020
  * @property { string } [ calledid.number ]
4013
4021
  * @property { string } [ calledid.name ]
4014
- * @property { boolean } [ continueonotherhangup ] - if true continue on other hangup
4022
+ * @property { boolean } [ continueonotherhangup ] - if true continue on other hangup
4015
4023
  * @property { boolean } [ hangupchildrenonhangup ]
4016
4024
  * @property { boolean } [ hangupparentonhangup ]
4017
4025
  * @property { call } [ bond ] - other channel to bond to for pottential channel pairing
@@ -4089,19 +4097,19 @@ class call {
4089
4097
  newcall.options.headers[ "supported" ] = "timer, path, replaces"
4090
4098
  newcall.options.headers[ "min-se" ] = "90"
4091
4099
  newcall.options.headers[ "session-expires" ] = `${callmanager.options.seexpire/1000}`
4092
-
4100
+
4093
4101
  newcall.#confignetwork( options )
4094
4102
 
4095
4103
  let newdialog, earlypromise
4096
4104
  try {
4097
4105
  await newcall.#openchannelsfornewuac()
4098
4106
 
4099
- newdialog = await callmanager.options.srf.createUAC(
4100
- options.contact + newcall.options.contactparams,
4101
- {
4107
+ newdialog = await callmanager.options.srf.createUAC(
4108
+ options.contact + newcall.options.contactparams,
4109
+ {
4102
4110
  ...newcall.options,
4103
4111
  ...{ noAck: newcall.#noack },
4104
- ...{ localSdp: newcall.sdp.local?newcall.sdp.local.toString():""
4112
+ ...{ localSdp: newcall.sdp.local?newcall.sdp.local.toString():""
4105
4113
  } }, {
4106
4114
 
4107
4115
  cbRequest: ( err, req ) => {
@@ -4201,7 +4209,7 @@ class call {
4201
4209
 
4202
4210
  /**
4203
4211
  * assign our dialog and related items
4204
- * @param { object } d
4212
+ * @param { object } d
4205
4213
  */
4206
4214
  #setdialog( d ) {
4207
4215
  clearTimeout( this._timers.newuac )
@@ -4219,8 +4227,8 @@ class call {
4219
4227
  }
4220
4228
 
4221
4229
  /**
4222
- *
4223
- * @param { object } req
4230
+ *
4231
+ * @param { object } req
4224
4232
  */
4225
4233
  #parsesessionexpires( req ) {
4226
4234
  if( !req ) return
@@ -4265,8 +4273,8 @@ class call {
4265
4273
  /**
4266
4274
  * If we are not called - we will assume we support timers - if we are called
4267
4275
  * then we check to see if it is supported to included required in our responce.
4268
- * @param { object } req
4269
- * @returns
4276
+ * @param { object } req
4277
+ * @returns
4270
4278
  */
4271
4279
  #parsetimer( req ) {
4272
4280
  if( !req || !req.get ) return
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@babblevoice/babble-drachtio-callmanager",
3
- "version": "3.7.24",
3
+ "version": "3.7.25",
4
4
  "description": "Call processing to create a PBX",
5
5
  "main": "index.js",
6
6
  "scripts": {