/// <summary> /// Cleans up any resources being used. /// </summary> public override void Dispose() { lock (SyncRoot) { if (State == SIP_DialogState.Disposed) { return; } foreach (UasInvite2xxRetransmit t in m_pUasInvite2xxRetransmits.ToArray()) { t.Dispose(); } m_pUasInvite2xxRetransmits = null; foreach (UacInvite2xxRetransmissionWaiter w in m_pUacInvite2xxRetransmitWaits.ToArray()) { w.Dispose(); } m_pUacInvite2xxRetransmitWaits = null; m_pActiveInvite = null; base.Dispose(); } }
/// <summary> /// Gets existing or creates new dialog. /// </summary> /// <param name="transaction">Owner transaction what forces to create dialog.</param> /// <param name="response">Response what forces to create dialog.</param> /// <returns>Returns dialog.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>transaction</b> or <b>response</b> is null.</exception> public SIP_Dialog GetOrCreateDialog(SIP_Transaction transaction, SIP_Response response) { if (transaction == null) { throw new ArgumentNullException("transaction"); } if (response == null) { throw new ArgumentNullException("response"); } string dialogID = ""; if (transaction is SIP_ServerTransaction) { dialogID = response.CallID + "-" + response.To.Tag + "-" + response.From.Tag; } else { dialogID = response.CallID + "-" + response.From.Tag + "-" + response.To.Tag; } lock (m_pDialogs) { SIP_Dialog dialog = null; m_pDialogs.TryGetValue(dialogID, out dialog); // Dialog doesn't exist, create it. if (dialog == null) { if (response.CSeq.RequestMethod.ToUpper() == SIP_Methods.INVITE) { dialog = new SIP_Dialog_Invite(); dialog.Init(m_pStack, transaction, response); dialog.StateChanged += delegate { if (dialog.State == SIP_DialogState.Terminated) { m_pDialogs.Remove(dialog.ID); } }; m_pDialogs.Add(dialog.ID, dialog); } else { throw new ArgumentException("Method '" + response.CSeq.RequestMethod + "' has no dialog handler."); } } return(dialog); } }
/// <summary> /// Processes specified request through this dialog. /// </summary> /// <param name="e">Method arguments.</param> /// <returns>Returns true if this dialog processed specified request, otherwise false.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>e</b> is null reference.</exception> protected internal override bool ProcessRequest(SIP_RequestReceivedEventArgs e) { if (e == null) { throw new ArgumentNullException("e"); } if (base.ProcessRequest(e)) { return true; } // We must support: INVITE(re-invite),UPDATE,ACK, [BYE will be handled by base class] #region INVITE if (e.Request.RequestLine.Method == SIP_Methods.INVITE) { /* RFC 3261 14.2. A UAS that receives a second INVITE before it sends the final response to a first INVITE with a lower CSeq sequence number on the same dialog MUST return a 500 (Server Internal Error) response to the second INVITE and MUST include a Retry-After header field with a randomly chosen value of between 0 and 10 seconds. A UAS that receives an INVITE on a dialog while an INVITE it had sent on that dialog is in progress MUST return a 491 (Request Pending) response to the received INVITE. */ if (m_pActiveInvite != null && m_pActiveInvite is SIP_ServerTransaction && (m_pActiveInvite).Request.CSeq.SequenceNumber < e.Request.CSeq.SequenceNumber) { SIP_Response response = Stack.CreateResponse( SIP_ResponseCodes.x500_Server_Internal_Error + ": INVITE with a lower CSeq is pending(RFC 3261 14.2).", e.Request); response.RetryAfter = new SIP_t_RetryAfter("10"); e.ServerTransaction.SendResponse(response); return true; } if (m_pActiveInvite != null && m_pActiveInvite is SIP_ClientTransaction) { e.ServerTransaction.SendResponse( Stack.CreateResponse(SIP_ResponseCodes.x491_Request_Pending, e.Request)); return true; } // Force server transaction creation and set it as active INVITE transaction. m_pActiveInvite = e.ServerTransaction; m_pActiveInvite.StateChanged += delegate { if (m_pActiveInvite.State == SIP_TransactionState.Terminated) { m_pActiveInvite = null; } }; // Once we send 2xx response, we need to retransmit it while get ACK or timeout. (RFC 3261 13.3.1.4.) ((SIP_ServerTransaction) m_pActiveInvite).ResponseSent += delegate(object s, SIP_ResponseSentEventArgs a) { if (a.Response.StatusCodeType == SIP_StatusCodeType.Success) { m_pUasInvite2xxRetransmits.Add(new UasInvite2xxRetransmit(this, a.Response)); } }; OnReinvite(((SIP_ServerTransaction) m_pActiveInvite)); return true; } #endregion #region ACK else if (e.Request.RequestLine.Method == SIP_Methods.ACK) { // Search corresponding INVITE 2xx retransmit entry and dispose it. foreach (UasInvite2xxRetransmit t in m_pUasInvite2xxRetransmits) { if (t.MatchAck(e.Request)) { t.Dispose(); if (State == SIP_DialogState.Early) { SetState(SIP_DialogState.Confirmed, true); // TODO: If Terminating } return true; } } return false; } #endregion #region UPDATE //else if(request.RequestLine.Method == SIP_Methods.UPDATE){ // TODO: //} #endregion // RFC 5057 5.6. Refusing New Usages. Decline(603 Decline) new dialog usages. else if (SIP_Utils.MethodCanEstablishDialog(e.Request.RequestLine.Method)) { e.ServerTransaction.SendResponse( Stack.CreateResponse( SIP_ResponseCodes.x603_Decline + " : New dialog usages not allowed (RFC 5057).", e.Request)); return true; } else { return false; } }
/// <summary> /// Initializes dialog. /// </summary> /// <param name="stack">Owner stack.</param> /// <param name="transaction">Owner transaction.</param> /// <param name="response">SIP response what caused dialog creation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>transaction</b> or <b>response</b>.</exception> protected internal override void Init(SIP_Stack stack, SIP_Transaction transaction, SIP_Response response) { if (stack == null) { throw new ArgumentNullException("stack"); } if (transaction == null) { throw new ArgumentNullException("transaction"); } if (response == null) { throw new ArgumentNullException("response"); } base.Init(stack, transaction, response); if (transaction is SIP_ServerTransaction) { if (response.StatusCodeType == SIP_StatusCodeType.Success) { SetState(SIP_DialogState.Early, false); // We need to retransmit 2xx response while we get ACK or timeout. (RFC 3261 13.3.1.4.) m_pUasInvite2xxRetransmits.Add(new UasInvite2xxRetransmit(this, response)); } else if (response.StatusCodeType == SIP_StatusCodeType.Provisional) { SetState(SIP_DialogState.Early, false); m_pActiveInvite = transaction; m_pActiveInvite.StateChanged += delegate { if (m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated) { m_pActiveInvite = null; } }; // Once we send 2xx response, we need to retransmit it while get ACK or timeout. (RFC 3261 13.3.1.4.) ((SIP_ServerTransaction) m_pActiveInvite).ResponseSent += delegate(object s, SIP_ResponseSentEventArgs a) { if (a.Response.StatusCodeType == SIP_StatusCodeType.Success) { m_pUasInvite2xxRetransmits.Add(new UasInvite2xxRetransmit(this, a.Response)); } }; } else { throw new ArgumentException( "Argument 'response' has invalid status code, 1xx - 2xx is only allowed."); } } else { if (response.StatusCodeType == SIP_StatusCodeType.Success) { SetState(SIP_DialogState.Confirmed, false); // Wait for retransmited 2xx responses. (RFC 3261 13.2.2.4.) m_pUacInvite2xxRetransmitWaits.Add(new UacInvite2xxRetransmissionWaiter(this, transaction. Request)); } else if (response.StatusCodeType == SIP_StatusCodeType.Provisional) { SetState(SIP_DialogState.Early, false); m_pActiveInvite = transaction; m_pActiveInvite.StateChanged += delegate { if (m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated) { m_pActiveInvite = null; } }; // Once we receive 2xx response, we need to wait for retransmitted 2xx responses. (RFC 3261 13.2.2.4) ((SIP_ClientTransaction) m_pActiveInvite).ResponseReceived += delegate(object s, SIP_ResponseReceivedEventArgs a) { if (a.Response.StatusCodeType == SIP_StatusCodeType.Success) { UacInvite2xxRetransmissionWaiter waiter = new UacInvite2xxRetransmissionWaiter(this, m_pActiveInvite.Request); m_pUacInvite2xxRetransmitWaits.Add(waiter); // Force to send initial ACK to 2xx response. waiter.Process(a.Response); SetState(SIP_DialogState.Confirmed, true); } }; } else { throw new ArgumentException( "Argument 'response' has invalid status code, 1xx - 2xx is only allowed."); } } }
/// <summary> /// Processes specified request through this dialog. /// </summary> /// <param name="e">Method arguments.</param> /// <returns>Returns true if this dialog processed specified request, otherwise false.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>e</b> is null reference.</exception> protected internal override bool ProcessRequest(SIP_RequestReceivedEventArgs e) { if (e == null) { throw new ArgumentNullException("e"); } if (base.ProcessRequest(e)) { return(true); } // We must support: INVITE(re-invite),UPDATE,ACK, [BYE will be handled by base class] #region INVITE if (e.Request.RequestLine.Method == SIP_Methods.INVITE) { /* RFC 3261 14.2. * A UAS that receives a second INVITE before it sends the final * response to a first INVITE with a lower CSeq sequence number on the * same dialog MUST return a 500 (Server Internal Error) response to the * second INVITE and MUST include a Retry-After header field with a * randomly chosen value of between 0 and 10 seconds. * * A UAS that receives an INVITE on a dialog while an INVITE it had sent * on that dialog is in progress MUST return a 491 (Request Pending) * response to the received INVITE. */ if (m_pActiveInvite != null && m_pActiveInvite is SIP_ServerTransaction && (m_pActiveInvite).Request.CSeq.SequenceNumber < e.Request.CSeq.SequenceNumber) { SIP_Response response = Stack.CreateResponse( SIP_ResponseCodes.x500_Server_Internal_Error + ": INVITE with a lower CSeq is pending(RFC 3261 14.2).", e.Request); response.RetryAfter = new SIP_t_RetryAfter("10"); e.ServerTransaction.SendResponse(response); return(true); } if (m_pActiveInvite != null && m_pActiveInvite is SIP_ClientTransaction) { e.ServerTransaction.SendResponse( Stack.CreateResponse(SIP_ResponseCodes.x491_Request_Pending, e.Request)); return(true); } // Force server transaction creation and set it as active INVITE transaction. m_pActiveInvite = e.ServerTransaction; m_pActiveInvite.StateChanged += delegate { if (m_pActiveInvite.State == SIP_TransactionState.Terminated) { m_pActiveInvite = null; } }; // Once we send 2xx response, we need to retransmit it while get ACK or timeout. (RFC 3261 13.3.1.4.) ((SIP_ServerTransaction)m_pActiveInvite).ResponseSent += delegate(object s, SIP_ResponseSentEventArgs a) { if (a.Response.StatusCodeType == SIP_StatusCodeType.Success) { m_pUasInvite2xxRetransmits.Add(new UasInvite2xxRetransmit(this, a.Response)); } }; OnReinvite(((SIP_ServerTransaction)m_pActiveInvite)); return(true); } #endregion #region ACK else if (e.Request.RequestLine.Method == SIP_Methods.ACK) { // Search corresponding INVITE 2xx retransmit entry and dispose it. foreach (UasInvite2xxRetransmit t in m_pUasInvite2xxRetransmits) { if (t.MatchAck(e.Request)) { t.Dispose(); if (State == SIP_DialogState.Early) { SetState(SIP_DialogState.Confirmed, true); // TODO: If Terminating } return(true); } } return(false); } #endregion #region UPDATE //else if(request.RequestLine.Method == SIP_Methods.UPDATE){ // TODO: //} #endregion // RFC 5057 5.6. Refusing New Usages. Decline(603 Decline) new dialog usages. else if (SIP_Utils.MethodCanEstablishDialog(e.Request.RequestLine.Method)) { e.ServerTransaction.SendResponse( Stack.CreateResponse( SIP_ResponseCodes.x603_Decline + " : New dialog usages not allowed (RFC 5057).", e.Request)); return(true); } else { return(false); } }
/// <summary> /// Initializes dialog. /// </summary> /// <param name="stack">Owner stack.</param> /// <param name="transaction">Owner transaction.</param> /// <param name="response">SIP response what caused dialog creation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>transaction</b> or <b>response</b>.</exception> protected internal override void Init(SIP_Stack stack, SIP_Transaction transaction, SIP_Response response) { if (stack == null) { throw new ArgumentNullException("stack"); } if (transaction == null) { throw new ArgumentNullException("transaction"); } if (response == null) { throw new ArgumentNullException("response"); } base.Init(stack, transaction, response); if (transaction is SIP_ServerTransaction) { if (response.StatusCodeType == SIP_StatusCodeType.Success) { SetState(SIP_DialogState.Early, false); // We need to retransmit 2xx response while we get ACK or timeout. (RFC 3261 13.3.1.4.) m_pUasInvite2xxRetransmits.Add(new UasInvite2xxRetransmit(this, response)); } else if (response.StatusCodeType == SIP_StatusCodeType.Provisional) { SetState(SIP_DialogState.Early, false); m_pActiveInvite = transaction; m_pActiveInvite.StateChanged += delegate { if (m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated) { m_pActiveInvite = null; } }; // Once we send 2xx response, we need to retransmit it while get ACK or timeout. (RFC 3261 13.3.1.4.) ((SIP_ServerTransaction)m_pActiveInvite).ResponseSent += delegate(object s, SIP_ResponseSentEventArgs a) { if (a.Response.StatusCodeType == SIP_StatusCodeType.Success) { m_pUasInvite2xxRetransmits.Add(new UasInvite2xxRetransmit(this, a.Response)); } }; } else { throw new ArgumentException( "Argument 'response' has invalid status code, 1xx - 2xx is only allowed."); } } else { if (response.StatusCodeType == SIP_StatusCodeType.Success) { SetState(SIP_DialogState.Confirmed, false); // Wait for retransmited 2xx responses. (RFC 3261 13.2.2.4.) m_pUacInvite2xxRetransmitWaits.Add(new UacInvite2xxRetransmissionWaiter(this, transaction. Request)); } else if (response.StatusCodeType == SIP_StatusCodeType.Provisional) { SetState(SIP_DialogState.Early, false); m_pActiveInvite = transaction; m_pActiveInvite.StateChanged += delegate { if (m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated) { m_pActiveInvite = null; } }; // Once we receive 2xx response, we need to wait for retransmitted 2xx responses. (RFC 3261 13.2.2.4) ((SIP_ClientTransaction)m_pActiveInvite).ResponseReceived += delegate(object s, SIP_ResponseReceivedEventArgs a) { if (a.Response.StatusCodeType == SIP_StatusCodeType.Success) { UacInvite2xxRetransmissionWaiter waiter = new UacInvite2xxRetransmissionWaiter(this, m_pActiveInvite.Request); m_pUacInvite2xxRetransmitWaits.Add(waiter); // Force to send initial ACK to 2xx response. waiter.Process(a.Response); SetState(SIP_DialogState.Confirmed, true); } }; } else { throw new ArgumentException( "Argument 'response' has invalid status code, 1xx - 2xx is only allowed."); } } }
/// <summary> /// Gets existing or creates new dialog. /// </summary> /// <param name="transaction">Owner transaction what forces to create dialog.</param> /// <param name="response">Response what forces to create dialog.</param> /// <returns>Returns dialog.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>transaction</b> or <b>response</b> is null.</exception> public SIP_Dialog GetOrCreateDialog(SIP_Transaction transaction, SIP_Response response) { if (transaction == null) { throw new ArgumentNullException("transaction"); } if (response == null) { throw new ArgumentNullException("response"); } string dialogID = ""; if (transaction is SIP_ServerTransaction) { dialogID = response.CallID + "-" + response.To.Tag + "-" + response.From.Tag; } else { dialogID = response.CallID + "-" + response.From.Tag + "-" + response.To.Tag; } lock (m_pDialogs) { SIP_Dialog dialog = null; m_pDialogs.TryGetValue(dialogID, out dialog); // Dialog doesn't exist, create it. if (dialog == null) { if (response.CSeq.RequestMethod.ToUpper() == SIP_Methods.INVITE) { dialog = new SIP_Dialog_Invite(); dialog.Init(m_pStack, transaction, response); dialog.StateChanged += delegate { if (dialog.State == SIP_DialogState.Terminated) { m_pDialogs.Remove(dialog.ID); } }; m_pDialogs.Add(dialog.ID, dialog); } else { throw new ArgumentException("Method '" + response.CSeq.RequestMethod + "' has no dialog handler."); } } return dialog; } }
/// <summary> /// Initializes dialog. /// </summary> /// <param name="stack">Owner stack.</param> /// <param name="transaction">Owner transaction.</param> /// <param name="response">SIP response what caused dialog creation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>transaction</b> or <b>response</b>.</exception> protected internal virtual void Init(SIP_Stack stack, SIP_Transaction transaction, SIP_Response response) { if (stack == null) { throw new ArgumentNullException("stack"); } if (transaction == null) { throw new ArgumentNullException("transaction"); } if (response == null) { throw new ArgumentNullException("response"); } m_pStack = stack; #region UAS /* RFC 3261 12.1.1. * The UAS then constructs the state of the dialog. This state MUST be * maintained for the duration of the dialog. * * If the request arrived over TLS, and the Request-URI contained a SIPS * URI, the "secure" flag is set to TRUE. * * The route set MUST be set to the list of URIs in the Record-Route * header field from the request, taken in order and preserving all URI * parameters. If no Record-Route header field is present in the * request, the route set MUST be set to the empty set. This route set, * even if empty, overrides any pre-existing route set for future * requests in this dialog. The remote target MUST be set to the URI * from the Contact header field of the request. * * The remote sequence number MUST be set to the value of the sequence * number in the CSeq header field of the request. The local sequence * number MUST be empty. The call identifier component of the dialog ID * MUST be set to the value of the Call-ID in the request. The local * tag component of the dialog ID MUST be set to the tag in the To field * in the response to the request (which always includes a tag), and the * remote tag component of the dialog ID MUST be set to the tag from the * From field in the request. A UAS MUST be prepared to receive a * request without a tag in the From field, in which case the tag is * considered to have a value of null. * * This is to maintain backwards compatibility with RFC 2543, which * did not mandate From tags. * * The remote URI MUST be set to the URI in the From field, and the * local URI MUST be set to the URI in the To field. */ if (transaction is SIP_ServerTransaction) { // TODO: Validate request or client transaction must do it ? m_IsSecure = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure; m_pRouteSet = (SIP_t_AddressParam[])Core.ReverseArray(transaction.Request.RecordRoute.GetAllValues()); m_pRemoteTarget = (SIP_Uri)transaction.Request.Contact.GetTopMostValue().Address.Uri; m_RemoteSeqNo = transaction.Request.CSeq.SequenceNumber; m_LocalSeqNo = 0; m_CallID = transaction.Request.CallID; m_LocalTag = response.To.Tag; m_RemoteTag = transaction.Request.From.Tag; m_pRemoteUri = transaction.Request.From.Address.Uri; m_pLocalUri = transaction.Request.To.Address.Uri; m_pLocalContact = (SIP_Uri)response.Contact.GetTopMostValue().Address.Uri; } #endregion #region UAC /* RFC 3261 12.1.2. * When a UAC receives a response that establishes a dialog, it * constructs the state of the dialog. This state MUST be maintained * for the duration of the dialog. * * If the request was sent over TLS, and the Request-URI contained a * SIPS URI, the "secure" flag is set to TRUE. * * The route set MUST be set to the list of URIs in the Record-Route * header field from the response, taken in reverse order and preserving * all URI parameters. If no Record-Route header field is present in * the response, the route set MUST be set to the empty set. This route * set, even if empty, overrides any pre-existing route set for future * requests in this dialog. The remote target MUST be set to the URI * from the Contact header field of the response. * * The local sequence number MUST be set to the value of the sequence * number in the CSeq header field of the request. The remote sequence * number MUST be empty (it is established when the remote UA sends a * request within the dialog). The call identifier component of the * dialog ID MUST be set to the value of the Call-ID in the request. * The local tag component of the dialog ID MUST be set to the tag in * the From field in the request, and the remote tag component of the * dialog ID MUST be set to the tag in the To field of the response. A * UAC MUST be prepared to receive a response without a tag in the To * field, in which case the tag is considered to have a value of null. * * This is to maintain backwards compatibility with RFC 2543, which * did not mandate To tags. * * The remote URI MUST be set to the URI in the To field, and the local * URI MUST be set to the URI in the From field. */ else { // TODO: Validate request or client transaction must do it ? m_IsSecure = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure; m_pRouteSet = (SIP_t_AddressParam[])Core.ReverseArray(response.RecordRoute.GetAllValues()); m_pRemoteTarget = (SIP_Uri)response.Contact.GetTopMostValue().Address.Uri; m_LocalSeqNo = transaction.Request.CSeq.SequenceNumber; m_RemoteSeqNo = 0; m_CallID = transaction.Request.CallID; m_LocalTag = transaction.Request.From.Tag; m_RemoteTag = response.To.Tag; m_pRemoteUri = transaction.Request.To.Address.Uri; m_pLocalUri = transaction.Request.From.Address.Uri; m_pLocalContact = (SIP_Uri)transaction.Request.Contact.GetTopMostValue().Address.Uri; } #endregion m_pFlow = transaction.Flow; }
/// <summary> /// Initializes dialog. /// </summary> /// <param name="stack">Owner stack.</param> /// <param name="transaction">Owner transaction.</param> /// <param name="response">SIP response what caused dialog creation.</param> /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>transaction</b> or <b>response</b>.</exception> protected internal virtual void Init(SIP_Stack stack, SIP_Transaction transaction, SIP_Response response) { if (stack == null) { throw new ArgumentNullException("stack"); } if (transaction == null) { throw new ArgumentNullException("transaction"); } if (response == null) { throw new ArgumentNullException("response"); } m_pStack = stack; #region UAS /* RFC 3261 12.1.1. The UAS then constructs the state of the dialog. This state MUST be maintained for the duration of the dialog. If the request arrived over TLS, and the Request-URI contained a SIPS URI, the "secure" flag is set to TRUE. The route set MUST be set to the list of URIs in the Record-Route header field from the request, taken in order and preserving all URI parameters. If no Record-Route header field is present in the request, the route set MUST be set to the empty set. This route set, even if empty, overrides any pre-existing route set for future requests in this dialog. The remote target MUST be set to the URI from the Contact header field of the request. The remote sequence number MUST be set to the value of the sequence number in the CSeq header field of the request. The local sequence number MUST be empty. The call identifier component of the dialog ID MUST be set to the value of the Call-ID in the request. The local tag component of the dialog ID MUST be set to the tag in the To field in the response to the request (which always includes a tag), and the remote tag component of the dialog ID MUST be set to the tag from the From field in the request. A UAS MUST be prepared to receive a request without a tag in the From field, in which case the tag is considered to have a value of null. This is to maintain backwards compatibility with RFC 2543, which did not mandate From tags. The remote URI MUST be set to the URI in the From field, and the local URI MUST be set to the URI in the To field. */ if (transaction is SIP_ServerTransaction) { // TODO: Validate request or client transaction must do it ? m_IsSecure = ((SIP_Uri) transaction.Request.RequestLine.Uri).IsSecure; m_pRouteSet = (SIP_t_AddressParam[]) Core.ReverseArray(transaction.Request.RecordRoute.GetAllValues()); m_pRemoteTarget = (SIP_Uri) transaction.Request.Contact.GetTopMostValue().Address.Uri; m_RemoteSeqNo = transaction.Request.CSeq.SequenceNumber; m_LocalSeqNo = 0; m_CallID = transaction.Request.CallID; m_LocalTag = response.To.Tag; m_RemoteTag = transaction.Request.From.Tag; m_pRemoteUri = transaction.Request.From.Address.Uri; m_pLocalUri = transaction.Request.To.Address.Uri; m_pLocalContact = (SIP_Uri) response.Contact.GetTopMostValue().Address.Uri; } #endregion #region UAC /* RFC 3261 12.1.2. When a UAC receives a response that establishes a dialog, it constructs the state of the dialog. This state MUST be maintained for the duration of the dialog. If the request was sent over TLS, and the Request-URI contained a SIPS URI, the "secure" flag is set to TRUE. The route set MUST be set to the list of URIs in the Record-Route header field from the response, taken in reverse order and preserving all URI parameters. If no Record-Route header field is present in the response, the route set MUST be set to the empty set. This route set, even if empty, overrides any pre-existing route set for future requests in this dialog. The remote target MUST be set to the URI from the Contact header field of the response. The local sequence number MUST be set to the value of the sequence number in the CSeq header field of the request. The remote sequence number MUST be empty (it is established when the remote UA sends a request within the dialog). The call identifier component of the dialog ID MUST be set to the value of the Call-ID in the request. The local tag component of the dialog ID MUST be set to the tag in the From field in the request, and the remote tag component of the dialog ID MUST be set to the tag in the To field of the response. A UAC MUST be prepared to receive a response without a tag in the To field, in which case the tag is considered to have a value of null. This is to maintain backwards compatibility with RFC 2543, which did not mandate To tags. The remote URI MUST be set to the URI in the To field, and the local URI MUST be set to the URI in the From field. */ else { // TODO: Validate request or client transaction must do it ? m_IsSecure = ((SIP_Uri) transaction.Request.RequestLine.Uri).IsSecure; m_pRouteSet = (SIP_t_AddressParam[]) Core.ReverseArray(response.RecordRoute.GetAllValues()); m_pRemoteTarget = (SIP_Uri) response.Contact.GetTopMostValue().Address.Uri; m_LocalSeqNo = transaction.Request.CSeq.SequenceNumber; m_RemoteSeqNo = 0; m_CallID = transaction.Request.CallID; m_LocalTag = transaction.Request.From.Tag; m_RemoteTag = response.To.Tag; m_pRemoteUri = transaction.Request.To.Address.Uri; m_pLocalUri = transaction.Request.From.Address.Uri; m_pLocalContact = (SIP_Uri) transaction.Request.Contact.GetTopMostValue().Address.Uri; } #endregion m_pFlow = transaction.Flow; }