/// <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();
                    }
                    else if (response.CSeq.RequestMethod.ToUpper() == SIP_Methods.REFER)
                    {
                        dialog = new SIP_Dialog_Refer();
                    }
                    else
                    {
                        throw new ArgumentException("Method '" + response.CSeq.RequestMethod + "' has no dialog handler.");
                    }

                    dialog.Init(m_pStack, transaction, response);
                    dialog.StateChanged += delegate(object s, EventArgs a){
                        if (dialog.State == SIP_DialogState.Terminated)
                        {
                            m_pDialogs.Remove(dialog.ID);
                        }
                    };
                    m_pDialogs.Add(dialog.ID, dialog);
                }

                return(dialog);
            }
        }
예제 #2
0
        /// <summary>
        /// Adds transaction to dialog transactions list.
        /// </summary>
        /// <param name="transaction"></param>
        internal void AddTransaction(SIP_Transaction transaction)
        {
            if (transaction == null)
            {
                throw new ArgumentNullException("transaction");
            }

            m_pTransactions.Add(transaction);
            transaction.Disposed += new EventHandler(delegate(object s, EventArgs e){
                m_pTransactions.Remove(transaction);
            });
        }
예제 #3
0
        /// <summary>
        /// Cleans up any resources being used.
        /// </summary>
        public override void Dispose()
        {
            lock (this.SyncRoot){
                if (this.State == SIP_DialogState.Disposed)
                {
                    return;
                }

                m_pActiveInvite = null;

                base.Dispose();
            }
        }
예제 #4
0
        /// <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>
        internal protected 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);
                }
                else if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                {
                    SetState(SIP_DialogState.Early, false);

                    m_pActiveInvite = transaction;
                    m_pActiveInvite.StateChanged += delegate(object s, EventArgs a){
                        if (m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated)
                        {
                            m_pActiveInvite = null;

                            /* RFC 3261 13.3.1.4.
                             *  If the server retransmits the 2xx response for 64*T1 seconds without
                             *  receiving an ACK, the dialog is confirmed, but the session SHOULD be
                             *  terminated.
                             */
                            if (this.State == SIP_DialogState.Early)
                            {
                                this.SetState(SIP_DialogState.Confirmed, true);
                                Terminate("ACK was not received for initial INVITE 2xx response.", true);
                            }
                            else if (this.State == SIP_DialogState.Terminating)
                            {
                                this.SetState(SIP_DialogState.Confirmed, false);
                                Terminate(m_TerminateReason, true);
                            }
                        }
                    };
                }
                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);
                }
                else if (response.StatusCodeType == SIP_StatusCodeType.Provisional)
                {
                    SetState(SIP_DialogState.Early, false);

                    m_pActiveInvite = transaction;
                    m_pActiveInvite.StateChanged += delegate(object s, EventArgs a){
                        if (m_pActiveInvite != null && m_pActiveInvite.State == SIP_TransactionState.Terminated)
                        {
                            m_pActiveInvite = null;
                        }
                    };

                    // Once we receive 2xx response, dialog will switch to confirmed state.
                    ((SIP_ClientTransaction)transaction).ResponseReceived += delegate(object s, SIP_ResponseReceivedEventArgs a){
                        if (a.Response.StatusCodeType == SIP_StatusCodeType.Success)
                        {
                            SetState(SIP_DialogState.Confirmed, true);
                        }
                    };
                }
                else
                {
                    throw new ArgumentException("Argument 'response' has invalid status code, 1xx - 2xx is only allowed.");
                }
            }
        }
예제 #5
0
        /// <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>
        internal protected 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)
            {
                m_IsSecure      = ((SIP_Uri)transaction.Request.RequestLine.Uri).IsSecure;
                m_pRouteSet     = (SIP_t_AddressParam[])Net_Utils.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;

                List <string> allow = new List <string>();
                foreach (SIP_t_Method m in response.Allow.GetAllValues())
                {
                    allow.Add(m.Method);
                }
                m_pRemoteAllow = allow.ToArray();

                List <string> supported = new List <string>();
                foreach (SIP_t_OptionTag s in response.Supported.GetAllValues())
                {
                    supported.Add(s.OptionTag);
                }
                m_pRemoteSupported = supported.ToArray();
            }

            #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[])Net_Utils.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;

                List <string> allow = new List <string>();
                foreach (SIP_t_Method m in response.Allow.GetAllValues())
                {
                    allow.Add(m.Method);
                }
                m_pRemoteAllow = allow.ToArray();

                List <string> supported = new List <string>();
                foreach (SIP_t_OptionTag s in response.Supported.GetAllValues())
                {
                    supported.Add(s.OptionTag);
                }
                m_pRemoteSupported = supported.ToArray();
            }

            #endregion

            m_pFlow = transaction.Flow;
            AddTransaction(transaction);
        }