Ejemplo n.º 1
0
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="request">The initiating <see cref="SipRequest" />.</param>
 /// <param name="dialog">The <see cref="SipDialog" /> for requests that initiate a dialog (or <c>null</c>).</param>
 /// <param name="agent">The <see cref="ISipAgent" /> that sent the request and received the response.</param>
 /// <param name="response">The final <see cref="SipResponse" />.</param>
 public SipResult(SipRequest request, SipDialog dialog, ISipAgent agent, SipResponse response)
 {
     this.Request  = request;
     this.Response = response;
     this.Status   = response.Status;
     this.Dialog   = dialog;
     this.Agent    = agent;
 }
Ejemplo n.º 2
0
 /// <summary>
 /// Constructor.
 /// </summary>
 /// <param name="request">The initiating <see cref="SipRequest" />.</param>
 /// <param name="dialog">The <see cref="SipDialog" /> for requests that initiate a dialog (or <c>null</c>).</param>
 /// <param name="agent">The <see cref="ISipAgent" /> that sent the request and received the response.</param>
 /// <param name="status">The final operation status.</param>
 public SipResult(SipRequest request, SipDialog dialog, ISipAgent agent, SipStatus status)
 {
     this.Request  = request;
     this.Response = null;
     this.Status   = status;
     this.Dialog   = dialog;
     this.Agent    = agent;
 }
Ejemplo n.º 3
0
 /// <summary>
 /// Constuctor.
 /// </summary>
 /// <param name="status">The <see cref="SipStatus" />.</param>
 /// <param name="response">The received <see cref="SipResponse" /> (or <c>null</c>).</param>
 /// <param name="transaction">The <see cref="SipClientTransaction" />.</param>
 /// <param name="dialog">The associated <see cref="SipDialog" /> (or <c>null</c>).</param>
 /// <param name="agent">The <see cref="SipClientAgent" /> that processed the response.</param>
 /// <param name="core">The <see cref="SipCore" /> that raised the event.</param>
 internal SipResponseEventArgs(SipStatus status, SipResponse response, SipClientTransaction transaction,
                               SipDialog dialog, SipClientAgent agent, SipCore core)
 {
     this.Status      = status;
     this.Response    = response;
     this.Transaction = transaction;
     this.Dialog      = dialog;
     this.Agent       = agent;
     this.Core        = core;
 }
Ejemplo n.º 4
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="statusCode">The SIP response status code.</param>
        /// <param name="reasonPhrase">The reason phrase (or <c>null</c>).</param>
        /// <param name="sipVersion">The SIP version string (or <c>null</c>).</param>
        public SipResponse(int statusCode, string reasonPhrase, string sipVersion)
            : base(false, sipVersion)
        {
            if (statusCode < 0)
            {
                throw new SipException("Cannot assign stack specific error codes to a SIP response.");
            }

            if (statusCode < 100 || statusCode >= 700)
            {
                throw new SipException("Invalid status code [{0}].", statusCode);
            }

            this.status       = (SipStatus)statusCode;
            this.reasonPhrase = reasonPhrase != null ? reasonPhrase : SipHelper.GetReasonPhrase(statusCode);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Called when an INVITE transaction completes.
        /// </summary>
        /// <param name="transaction">The source <see cref="SipClientTransaction" />.</param>
        /// <param name="status">The completion status.</param>
        /// <param name="response">The final response (or <c>null</c>).</param>
        /// <remarks>
        /// <para>
        /// The <paramref name="response"/> parameter will be passed as <c>null</c> if
        /// the transaction was completed without receiving a final message (such
        /// as a timeout).  In this case, the agent should look to the <paramref name="status"/>
        /// property for the final disposition of the transaction.
        /// </para>
        /// <para>
        /// This method also handles the resubmission of the request with additional
        /// authentication information if necessary.
        /// </para>
        /// <para>
        /// The method may create a custom ACK <see cref="SipResponse" /> to be delivered
        /// back to the server by saving the response in the <paramref name="response"/> parameter.
        /// Otherwise, if this value is left as <c>null</c>, the client transaction will
        /// generate a default ACK response and send it.
        /// </para>
        /// </remarks>
        internal void OnInviteComplete(SipClientTransaction transaction, SipStatus status, SipResponse response)
        {
            var arClient = (ClientAsyncResult)transaction.AgentState;
            var args     = new SipResponseEventArgs(status, response, transaction, arClient.Dialog, this, this.core);

            if (response == null)
            {
                // The operation has completed without receiving a final response (probably
                // due to a time out or some kind of transport related problem).

                arClient.SipResult = new SipResult(arClient.Request, arClient.Dialog, this, status);
                arClient.Notify();

                core.OnResponseReceived(args);
                core.OnInviteFailed(args, status);
                return;
            }

            // We have the final response.  Compute the dialog ID from the response's
            // Call-ID, and To/From tags and assign it to the dialog.

            arClient.Dialog.ID = SipDialog.GetDialogID(response);

            // Call the core's OnInviteConfirmed() method so it can perform
            // any dialog specific activities.

            if (response.IsSuccess)
            {
                core.OnInviteConfirmed(args);
            }
            else
            {
                core.OnInviteFailed(args, status);
            }

            // Signal completion of the async operation.

            arClient.SipResult = new SipResult(arClient.Request, arClient.Dialog, this, response);
            arClient.Notify();
        }
Ejemplo n.º 6
0
        /// <summary>
        /// Called when a non-INVITE transaction completes.
        /// </summary>
        /// <param name="transaction">The source <see cref="SipClientTransaction" />.</param>
        /// <param name="status">The completion status.</param>
        /// <param name="response">The final response (or <c>null</c>).</param>
        /// <remarks>
        /// <para>
        /// The <paramref name="response"/> parameter will be passed as <c>null</c> if
        /// the transaction was completed without receiving a final message (such
        /// as a timeout).  In this case, the agent should look to the <paramref name="status"/>
        /// property for the final disposition of the transaction.
        /// </para>
        /// <para>
        /// This method also handles the resubmission of the request with additional
        /// authentication information if necessary.
        /// </para>
        /// </remarks>
        internal void OnComplete(SipClientTransaction transaction, SipStatus status, SipResponse response)
        {
            var arClient = (ClientAsyncResult)transaction.AgentState;

            if (response == null)
            {
                // The operation has completed without receiving a final response (probably
                // due to a time out or some kind of transport related problem).

                arClient.SipResult = new SipResult(arClient.Request, arClient.Dialog, this, status);
                arClient.Notify();

                core.OnResponseReceived(new SipResponseEventArgs(status, response, transaction, arClient.Dialog, this, this.core));
                return;
            }

            // We have the final response.

            arClient.SipResult = new SipResult(arClient.Request, arClient.Dialog, this, response);
            arClient.Notify();

            core.OnResponseReceived(new SipResponseEventArgs(response.Status, response, transaction, arClient.Dialog, this, this.core));
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Creates a <see cref="SipResponse" /> for this request, copying the minimum
        /// required headers from the request to the response.
        /// </summary>
        /// <param name="status">The status code.</param>
        /// <param name="reasonPhrase">The reason phrase (or <c>null</c>).</param>
        /// <returns>The <see cref="SipResponse" />.</returns>
        /// <remarks>
        /// <para>
        /// The procedure for doing this is described in RFC 3261 on page 50.
        /// </para>
        /// <note>
        /// This method assumes that the <b>tag</b> parameter on the <b>To</b>
        /// header has already been added (if necessary).
        /// </note>
        /// </remarks>
        public SipResponse CreateResponse(SipStatus status, string reasonPhrase)
        {
            SipResponse response = new SipResponse(status, reasonPhrase, this.SipVersion);
            SipHeader   header;

            header = this[SipHeader.Via];
            if (header != null)
            {
                response.Headers.Add(SipHeader.Via, header.Clone());
            }

            header = this[SipHeader.To];
            if (header != null)
            {
                response.Headers.Add(SipHeader.To, header.Clone());
            }

            header = this[SipHeader.From];
            if (header != null)
            {
                response.Headers.Add(SipHeader.From, header.Clone());
            }

            header = this[SipHeader.CallID];
            if (header != null)
            {
                response.Headers.Add(SipHeader.CallID, header.Clone());
            }

            header = this[SipHeader.CSeq];
            if (header != null)
            {
                response.Headers.Add(SipHeader.CSeq, header.Clone());
            }

            return(response);
        }
Ejemplo n.º 8
0
        /// <summary>
        /// This method will be called periodically on a background thread to handle
        /// message resending as well as timeout related state transitions.
        /// </summary>
        public override void OnBkTask()
        {
            bool        callOnComplete   = false;
            bool        callOnProceeding = false;
            SipResponse callbackMsg      = null;
            SipStatus   status           = SipStatus.OK;

            try
            {
                using (TimedLock.Lock(agent))
                {
                    switch (base.State)
                    {
                    default:
                    case SipTransactionState.Unknown:

                        break;

                    case SipTransactionState.InviteCalling:

                        if (base.TimerB.HasFired)
                        {
                            // Request has timed out.

                            SetState(SipTransactionState.Terminated);

                            // Setup to call the agent's completion method

                            callOnComplete = true;
                            callbackMsg    = null;
                            status         = SipStatus.RequestTimeout;
                            return;
                        }

                        if (base.TimerA.HasFired)
                        {
                            // Retransmit the request and reset the
                            // timer for exponential backoff

                            transport.Send(remoteEP, request);
                            base.TimerA.Interval = new TimeSpan(base.TimerA.Interval.Ticks * 2);
                        }
                        break;

                    case SipTransactionState.InviteProceeding:

                        if (base.TimerB.HasFired)
                        {
                            // Request has timed out.

                            SetState(SipTransactionState.Terminated);

                            // Setup to call the agent's completion method

                            callOnComplete = true;
                            callbackMsg    = null;
                            status         = SipStatus.RequestTimeout;
                            return;
                        }
                        break;

                    case SipTransactionState.InviteCompleted:

                        if (base.TimerD.HasFired)
                        {
                            SetState(SipTransactionState.Terminated);
                        }

                        break;

                    case SipTransactionState.Trying:

                        if (base.TimerF.HasFired)
                        {
                            // Request has timed out.

                            SetState(SipTransactionState.Terminated);

                            // Setup to call the agent's completion method

                            callOnComplete = true;
                            callbackMsg    = null;
                            status         = SipStatus.RequestTimeout;
                            return;
                        }

                        if (isUdp && base.TimerE.HasFired)
                        {
                            // Retransmit for UDP

                            transport.Send(remoteEP, request);
                            base.TimerE.Interval = Helper.Min(base.BaseTimers.T2, Helper.Multiply(base.TimerE.Interval, 2));
                        }
                        break;

                    case SipTransactionState.Proceeding:

                        if (base.TimerF.HasFired)
                        {
                            // Request has timed out.

                            SetState(SipTransactionState.Terminated);

                            // Setup to call the agent's completion method

                            callOnComplete = true;
                            callbackMsg    = null;
                            status         = SipStatus.RequestTimeout;
                            return;
                        }

                        if (base.TimerE.HasFired)
                        {
                            // Retransmit for all transports.

                            transport.Send(remoteEP, request);
                            base.TimerE.Interval = base.BaseTimers.T2;
                        }
                        break;

                    case SipTransactionState.Completed:

                        if (base.TimerK.HasFired)
                        {
                            SetState(SipTransactionState.Terminated);
                        }

                        break;

                    case SipTransactionState.Terminated:

                        break;
                    }
                }
            }
            finally
            {
                // Handle the agent callbacks outside of the lock to avoid
                // deadlock issues.

                if (callOnComplete)
                {
                    agent.OnComplete(this, status, callbackMsg);
                }

                if (callOnProceeding)
                {
                    agent.OnProceeding(this, (SipResponse)callbackMsg);
                }
            }
        }
Ejemplo n.º 9
0
        /// <summary>
        /// The managing <see cref="ISipAgent" /> is responsible for calling this
        /// method whenever it receives responses correlated to this transaction.
        /// </summary>
        /// <param name="transport">The source <see cref="ISipTransport" />.</param>
        /// <param name="response">The received <see cref="SipResponse" />.</param>
        public void OnResponse(ISipTransport transport, SipResponse response)
        {
            bool         callOnComplete       = false;
            bool         callOnProceeding     = false;
            bool         callOnInviteComplete = false;
            SipResponse  callbackMsg          = null;
            SipStatus    status = SipStatus.OK;
            SipCSeqValue vCSeq;

            this.transport = transport;

            try
            {
                response.SourceTransaction = this;

                using (TimedLock.Lock(agent))
                {
                    // Ignore messages without a sequence number
                    //
                    // $todo(jeff.lill): Probably should check the method too

                    vCSeq = response.GetHeader <SipCSeqValue>(SipHeader.CSeq);
                    if (vCSeq == null)
                    {
                        return;
                    }

                    // Handle state specific processing

                    switch (base.State)
                    {
                    default:
                    case SipTransactionState.Unknown:

                        SysLog.LogError("Unexpected SIP transaction state.");
                        SetState(SipTransactionState.Terminated);

                        // Setup to call the agent's completion method

                        callOnComplete = true;
                        callbackMsg    = null;
                        status         = SipStatus.Stack_ProtocolError;
                        return;

                    case SipTransactionState.InviteCalling:

                        if (!request.MatchCSeq(response))
                        {
                            return;         // Ignore responses whose CSeq header doesn't match the request
                        }
                        if (response.IsProvisional)
                        {
                            // Provisional response.

                            SetState(SipTransactionState.InviteProceeding);

                            // Setup to call the agent's proceeding method

                            callOnProceeding = true;
                            callbackMsg      = response;
                            status           = response.Status;
                            return;
                        }

                        if (response.IsNonSuccessFinal)
                        {
                            // Final response non-2xx response.  Generate and
                            // send the ACK request to the server to squelch
                            // any further responses and then enter the
                            // InviteCompleted state to absorb any responses
                            // that do make it through.

                            ackRequest = CreateAckRequest(request, response);
                            transport.Send(remoteEP, ackRequest);

                            SetState(SipTransactionState.InviteCompleted);

                            // Setup to call the agent's invite completed method

                            callOnInviteComplete = true;
                            callbackMsg          = response;
                            status = response.Status;
                            return;
                        }

                        // Must be a 2xx response.  Setup to call the agent's
                        // completed method and enter the terminated state
                        // without sending an ACK request.
                        //
                        // Note that the agent is required to do this as
                        // described in RFC 3261 on pages 128-129.

                        SetState(SipTransactionState.Terminated);

                        callOnInviteComplete = true;
                        callbackMsg          = response;
                        status = response.Status;
                        break;

                    case SipTransactionState.InviteProceeding:

                        if (!request.MatchCSeq(response))
                        {
                            return;         // Ignore responses whose CSeq header doesn't match the request
                        }
                        if (response.IsProvisional)
                        {
                            // Setup to call the agent's proceeding method

                            callOnProceeding = true;
                            callbackMsg      = response;
                            status           = response.Status;
                            return;
                        }

                        if (response.IsNonSuccessFinal)
                        {
                            // Final response non-2xx response.  Generate and
                            // send the ACK request to the server to squelch
                            // any further responses and then enter the
                            // InviteCompleted state to absorb any responses
                            // that do make it through.

                            // $todo(jeff.lill):
                            //
                            // I need to figure out a way to
                            // map to the dialog so that it
                            // can generate the ACK rather than
                            // doing this locally.

                            ackRequest = CreateAckRequest(request, response);
                            transport.Send(remoteEP, ackRequest);

                            SetState(SipTransactionState.InviteCompleted);

                            // Setup to call the agent's invite completed method

                            callOnInviteComplete = true;
                            callbackMsg          = response;
                            status = response.Status;
                            return;
                        }

                        // Must be a 2xx response.  Setup to call the agent's
                        // completed method and enter the terminated state
                        // without sending an ACK request.
                        //
                        // Note that the agent is required to do this as
                        // described in RFC 3261 on pages 128-129.

                        SetState(SipTransactionState.Terminated);

                        callOnInviteComplete = true;
                        callbackMsg          = response;
                        status = response.Status;
                        break;

                    case SipTransactionState.InviteCompleted:

                        // Retransmit the ACK if we get another final response

                        if (response.IsFinal)
                        {
                            transport.Send(remoteEP, ackRequest);
                        }

                        break;

                    case SipTransactionState.Trying:

                        if (!request.MatchCSeq(response))
                        {
                            return;         // Ignore responses whose CSeq header doesn't match the request
                        }
                        if (response.IsProvisional)
                        {
                            // Provisional response.

                            SetState(SipTransactionState.Proceeding);

                            // Setup to call the agent's proceeding method

                            callOnProceeding = true;
                            callbackMsg      = response;
                            status           = response.Status;
                            return;
                        }
                        else
                        {
                            // Final response

                            SetState(SipTransactionState.Completed);

                            // Setup to call the agent's completion method

                            callOnComplete = true;
                            callbackMsg    = response;
                            status         = response.Status;
                            return;
                        }

                    case SipTransactionState.Proceeding:

                        if (!request.MatchCSeq(response))
                        {
                            return;         // Ignore responses whose CSeq header doesn't match the request
                        }
                        if (response.IsProvisional)
                        {
                            // Setup to call the agent's proceeding method

                            callOnProceeding = true;
                            callbackMsg      = response;
                            status           = response.Status;
                            return;
                        }

                        // Final response.

                        SetState(SipTransactionState.Completed);

                        // Setup to call the agent's completion method

                        callOnComplete = true;
                        callbackMsg    = response;
                        status         = response.Status;
                        return;

                    case SipTransactionState.Completed:

                        break;

                    case SipTransactionState.Terminated:

                        break;
                    }
                }
            }
            finally
            {
                // Handle the agent callbacks outside of the lock to avoid
                // deadlock issues.

                if (callOnProceeding)
                {
                    agent.OnProceeding(this, callbackMsg);
                }

                if (callOnComplete)
                {
                    agent.OnComplete(this, status, callbackMsg);
                }

                if (callOnInviteComplete)
                {
                    agent.OnInviteComplete(this, status, response);
                }
            }
        }
Ejemplo n.º 10
0
 /// <summary>
 /// Returns <c>true</c> if <see cref="SipStatus" /> code is in
 /// the range of 100-199, indicating a provisional response.
 /// </summary>
 /// <param name="status">The <see cref="SipStatus" /> code to check.</param>
 public static bool IsProvisional(SipStatus status)
 {
     return((int)status <= 199);
 }
Ejemplo n.º 11
0
 /// <summary>
 /// Returns <c>true</c> if <see cref="SipStatus" /> code is in
 /// the range of 400-699, indicating an error response.
 /// </summary>
 /// <param name="status">The <see cref="SipStatus" /> code to check.</param>
 public static bool IsError(SipStatus status)
 {
     return((int)status >= 400);
 }
Ejemplo n.º 12
0
 /// <summary>
 /// Returns <c>true</c> if <see cref="SipStatus" /> code is in
 /// the range of 200-299, indicating a successful response.
 /// </summary>
 /// <param name="status">The <see cref="SipStatus" /> code to check.</param>
 public static bool IsSuccess(SipStatus status)
 {
     return(200 <= (int)status && (int)status <= 299);
 }
Ejemplo n.º 13
0
 /// <summary>
 /// Returns <c>true</c> if <see cref="SipStatus" /> code is in
 /// the range of 200-699, indicating a final response.
 /// </summary>
 /// <param name="status">The <see cref="SipStatus" /> code to check.</param>
 public static bool IsFinal(SipStatus status)
 {
     return((int)status >= 200);
 }
Ejemplo n.º 14
0
 /// <summary>
 /// Returns the textual reason phrase for a <see cref="SipStatus" /> value.
 /// </summary>
 /// <param name="status">The status.</param>
 /// <returns>The phrase.</returns>
 public static string GetReasonPhrase(SipStatus status)
 {
     return(GetReasonPhrase((int)status));
 }