@@ -2,7 +2,8 @@ use super::dialog::DialogInnerRef;
22use super :: DialogId ;
33use crate :: dialog:: {
44 authenticate:: handle_client_authenticate,
5- dialog:: { DialogState , TerminatedReason } ,
5+ dialog:: { DialogState , TerminatedReason , TransactionHandle } ,
6+ subscription:: ClientSubscriptionDialog ,
67} ;
78use crate :: rsip_ext:: RsipResponseExt ;
89use crate :: transaction:: transaction:: Transaction ;
@@ -12,7 +13,7 @@ use rsip::{prelude::HeadersExt, Header};
1213use rsip:: { Response , SipMessage , StatusCode } ;
1314use std:: sync:: atomic:: Ordering ;
1415use tokio_util:: sync:: CancellationToken ;
15- use tracing:: { info, trace} ;
16+ use tracing:: { debug , info, trace} ;
1617
1718/// Client-side INVITE Dialog (UAC)
1819///
@@ -257,8 +258,9 @@ impl ClientInviteDialog {
257258 match resp {
258259 Ok ( Some ( ref resp) ) => {
259260 if resp. status_code == StatusCode :: OK {
261+ let ( handle, _) = TransactionHandle :: new ( ) ;
260262 self . inner
261- . transition ( DialogState :: Updated ( self . id ( ) , request) ) ?;
263+ . transition ( DialogState :: Updated ( self . id ( ) , request, handle ) ) ?;
262264 }
263265 }
264266 _ => { }
@@ -370,6 +372,104 @@ impl ClientInviteDialog {
370372 . make_request ( rsip:: Method :: Options , None , None , None , headers, body) ?;
371373 self . inner . do_request ( request. clone ( ) ) . await
372374 }
375+
376+ /// Send a generic in-dialog request
377+ ///
378+ /// This method allows sending any SIP request within the dialog.
379+ /// It automatically handles CSeq increment, Call-ID, From/To tags, and Route set.
380+ pub async fn request (
381+ & self ,
382+ method : rsip:: Method ,
383+ headers : Option < Vec < rsip:: Header > > ,
384+ body : Option < Vec < u8 > > ,
385+ ) -> Result < Option < rsip:: Response > > {
386+ if !self . inner . is_confirmed ( ) {
387+ return Ok ( None ) ;
388+ }
389+ info ! ( id=%self . id( ) , "sending {} request" , method) ;
390+ let request = self
391+ . inner
392+ . make_request ( method, None , None , None , headers, body) ?;
393+ self . inner . do_request ( request) . await
394+ }
395+
396+ /// Send a NOTIFY request
397+ pub async fn notify (
398+ & self ,
399+ headers : Option < Vec < rsip:: Header > > ,
400+ body : Option < Vec < u8 > > ,
401+ ) -> Result < Option < rsip:: Response > > {
402+ self . request ( rsip:: Method :: Notify , headers, body) . await
403+ }
404+
405+ /// Send a REFER request
406+ ///
407+ /// Sends a REFER request to transfer the call to another destination.
408+ ///
409+ /// # Parameters
410+ ///
411+ /// * `refer_to` - The URI to refer to (Refer-To header value)
412+ /// * `headers` - Optional additional headers
413+ /// * `body` - Optional message body
414+ pub async fn refer (
415+ & self ,
416+ refer_to : rsip:: Uri ,
417+ headers : Option < Vec < rsip:: Header > > ,
418+ body : Option < Vec < u8 > > ,
419+ ) -> Result < Option < rsip:: Response > > {
420+ let mut headers = headers. unwrap_or_default ( ) ;
421+ headers. push ( rsip:: Header :: Other (
422+ "Refer-To" . into ( ) ,
423+ format ! ( "<{}>" , refer_to) . into ( ) ,
424+ ) ) ;
425+ self . request ( rsip:: Method :: Refer , Some ( headers) , body) . await
426+ }
427+
428+ /// Send a REFER progress notification (RFC 3515)
429+ ///
430+ /// This is used by the REFER recipient to notify the sender about the
431+ /// progress of the referred action.
432+ ///
433+ /// # Parameters
434+ ///
435+ /// * `status` - The status of the referred action (e.g., 100 Trying, 200 OK)
436+ /// * `sub_state` - The subscription state (e.g., "active", "terminated;reason=noresource")
437+ pub async fn notify_refer (
438+ & self ,
439+ status : rsip:: StatusCode ,
440+ sub_state : & str ,
441+ ) -> Result < Option < rsip:: Response > > {
442+ let headers = vec ! [
443+ rsip:: Header :: Other ( "Event" . into( ) , "refer" . into( ) ) ,
444+ rsip:: Header :: Other ( "Subscription-State" . into( ) , sub_state. into( ) ) ,
445+ rsip:: Header :: ContentType ( "message/sipfrag" . into( ) ) ,
446+ ] ;
447+
448+ let body = format ! ( "SIP/2.0 {} {:?}" , u16 :: from( status. clone( ) ) , status) . into_bytes ( ) ;
449+
450+ self . notify ( Some ( headers) , Some ( body) ) . await
451+ }
452+
453+ /// Convert this INVITE dialog to a subscription dialog
454+ ///
455+ /// This is useful for handling implicit subscriptions created by REFER.
456+ pub fn as_subscription ( & self ) -> ClientSubscriptionDialog {
457+ ClientSubscriptionDialog {
458+ inner : self . inner . clone ( ) ,
459+ }
460+ }
461+
462+ /// Send a MESSAGE request
463+ ///
464+ /// Sends an instant message within the dialog.
465+ pub async fn message (
466+ & self ,
467+ headers : Option < Vec < rsip:: Header > > ,
468+ body : Option < Vec < u8 > > ,
469+ ) -> Result < Option < rsip:: Response > > {
470+ self . request ( rsip:: Method :: Message , headers, body) . await
471+ }
472+
373473 /// Handle incoming transaction for this dialog
374474 ///
375475 /// Processes incoming SIP requests that are routed to this dialog.
@@ -420,6 +520,9 @@ impl ClientInviteDialog {
420520 rsip:: Method :: Info => return self . handle_info ( tx) . await ,
421521 rsip:: Method :: Options => return self . handle_options ( tx) . await ,
422522 rsip:: Method :: Update => return self . handle_update ( tx) . await ,
523+ rsip:: Method :: Refer => return self . handle_refer ( tx) . await ,
524+ rsip:: Method :: Message => return self . handle_message ( tx) . await ,
525+ rsip:: Method :: Notify => return self . handle_notify ( tx) . await ,
423526 _ => {
424527 info ! ( id=%self . id( ) , "invalid request method: {:?}" , tx. original. method) ;
425528 tx. reply ( rsip:: StatusCode :: MethodNotAllowed ) . await ?;
@@ -448,34 +551,36 @@ impl ClientInviteDialog {
448551 }
449552
450553 async fn handle_info ( & mut self , tx : & mut Transaction ) -> Result < ( ) > {
451- info ! ( id=%self . id( ) , "received info {}" , tx. original. uri) ;
554+ debug ! ( id=%self . id( ) , "received info {}" , tx. original. uri) ;
555+ let ( handle, rx) = TransactionHandle :: new ( ) ;
452556 self . inner
453- . transition ( DialogState :: Info ( self . id ( ) , tx. original . clone ( ) ) ) ?;
454- tx. reply ( rsip:: StatusCode :: OK ) . await ?;
455- Ok ( ( ) )
557+ . transition ( DialogState :: Info ( self . id ( ) , tx. original . clone ( ) , handle) ) ?;
558+ self . inner . process_transaction_handle ( tx, rx) . await
456559 }
457560
458561 async fn handle_options ( & mut self , tx : & mut Transaction ) -> Result < ( ) > {
459- info ! ( id=%self . id( ) , "received options {}" , tx. original. uri) ;
562+ debug ! ( id=%self . id( ) , "received options {}" , tx. original. uri) ;
563+ let ( handle, rx) = TransactionHandle :: new ( ) ;
460564 self . inner
461- . transition ( DialogState :: Options ( self . id ( ) , tx. original . clone ( ) ) ) ?;
462- tx. reply ( rsip:: StatusCode :: OK ) . await ?;
463- Ok ( ( ) )
565+ . transition ( DialogState :: Options ( self . id ( ) , tx. original . clone ( ) , handle) ) ?;
566+ self . inner . process_transaction_handle ( tx, rx) . await
464567 }
465568
466569 async fn handle_update ( & mut self , tx : & mut Transaction ) -> Result < ( ) > {
467- info ! ( id=%self . id( ) , "received update {}" , tx. original. uri) ;
570+ debug ! ( id=%self . id( ) , "received update {}" , tx. original. uri) ;
571+ let ( handle, rx) = TransactionHandle :: new ( ) ;
468572 self . inner
469- . transition ( DialogState :: Updated ( self . id ( ) , tx. original . clone ( ) ) ) ?;
470- tx. reply ( rsip:: StatusCode :: OK ) . await ?;
471- Ok ( ( ) )
573+ . transition ( DialogState :: Updated ( self . id ( ) , tx. original . clone ( ) , handle) ) ?;
574+ self . inner . process_transaction_handle ( tx, rx) . await
472575 }
473576
474577 async fn handle_reinvite ( & mut self , tx : & mut Transaction ) -> Result < ( ) > {
475- info ! ( id=%self . id( ) , "received reinvite {}" , tx. original. uri) ;
578+ debug ! ( id=%self . id( ) , "received reinvite {}" , tx. original. uri) ;
579+ let ( handle, rx) = TransactionHandle :: new ( ) ;
476580 self . inner
477- . transition ( DialogState :: Updated ( self . id ( ) , tx. original . clone ( ) ) ) ?;
478- tx. reply ( rsip:: StatusCode :: OK ) . await ?;
581+ . transition ( DialogState :: Updated ( self . id ( ) , tx. original . clone ( ) , handle) ) ?;
582+
583+ self . inner . process_transaction_handle ( tx, rx) . await ?;
479584
480585 // wait for ACK
481586 while let Some ( msg) = tx. receive ( ) . await {
@@ -490,6 +595,32 @@ impl ClientInviteDialog {
490595 Ok ( ( ) )
491596 }
492597
598+ async fn handle_refer ( & mut self , tx : & mut Transaction ) -> Result < ( ) > {
599+ debug ! ( id=%self . id( ) , "received refer {}" , tx. original. uri) ;
600+ let ( handle, rx) = TransactionHandle :: new ( ) ;
601+ self . inner
602+ . transition ( DialogState :: Refer ( self . id ( ) , tx. original . clone ( ) , handle) ) ?;
603+
604+ self . inner . process_transaction_handle ( tx, rx) . await
605+ }
606+
607+ async fn handle_message ( & mut self , tx : & mut Transaction ) -> Result < ( ) > {
608+ debug ! ( id=%self . id( ) , "received message {}" , tx. original. uri) ;
609+ let ( handle, rx) = TransactionHandle :: new ( ) ;
610+ self . inner
611+ . transition ( DialogState :: Message ( self . id ( ) , tx. original . clone ( ) , handle) ) ?;
612+
613+ self . inner . process_transaction_handle ( tx, rx) . await
614+ }
615+
616+ async fn handle_notify ( & mut self , tx : & mut Transaction ) -> Result < ( ) > {
617+ debug ! ( id=%self . id( ) , "received notify {}" , tx. original. uri) ;
618+ let ( handle, rx) = TransactionHandle :: new ( ) ;
619+ self . inner
620+ . transition ( DialogState :: Notify ( self . id ( ) , tx. original . clone ( ) , handle) ) ?;
621+ self . inner . process_transaction_handle ( tx, rx) . await
622+ }
623+
493624 pub async fn process_invite (
494625 & self ,
495626 mut tx : Transaction ,
0 commit comments