/// <summary>
        /// Starts registering.
        /// </summary>
        /// <param name="autoRefresh">If true, registration takes care of refreshing itself to registrar server.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        public void BeginRegister(bool autoRefresh)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(this.GetType().Name);
            }

            // Fix ME: Stack not running, try register on next step.
            // In ideal solution we need to start registering when stack starts.
            if (!m_pStack.IsRunning)
            {
                m_pTimer.Enabled = true;
                return;
            }

            m_AutoRefresh = autoRefresh;
            SetState(SIP_UA_RegistrationState.Registering);

            /* RFC 3261 10.1 Constructing the REGISTER Request.
             *  Request-URI: The Request-URI names the domain of the location service for which the registration is meant (for example,
             *               "sip:chicago.com").  The "userinfo" and "@" components of the SIP URI MUST NOT be present.
             */

            SIP_Request register = m_pStack.CreateRequest(SIP_Methods.REGISTER, new SIP_t_NameAddress(m_pServer.Scheme + ":" + m_AOR), new SIP_t_NameAddress(m_pServer.Scheme + ":" + m_AOR));

            register.RequestLine.Uri = SIP_Uri.Parse(m_pServer.Scheme + ":" + m_AOR.Substring(m_AOR.IndexOf('@') + 1));
            register.Route.Add(m_pServer.ToString());
            register.Contact.Add("<" + this.Contact + ">;expires=" + m_RefreshInterval);

            m_pRegisterSender = m_pStack.CreateRequestSender(register, m_pFlow);
            m_pRegisterSender.ResponseReceived += new EventHandler <SIP_ResponseReceivedEventArgs>(m_pRegisterSender_ResponseReceived);
            m_pRegisterSender.Start();
        }
Example #2
0
        /// <summary>
        /// Calling constructor.
        /// </summary>
        /// <param name="stack">Reference to SIP stack.</param>
        /// <param name="sender">Initial INVITE sender.</param>
        /// <param name="session">Call RTP multimedia session.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stack</b>,<b>sender</b> or <b>session</b> is null reference.</exception>
        internal SIP_Call(SIP_Stack stack,SIP_RequestSender sender,RTP_MultimediaSession session)
        {
            if(stack == null){
                throw new ArgumentNullException("stack");
            }
            if(sender == null){
                throw new ArgumentNullException("sender");
            }
            if(session == null){
                throw new ArgumentNullException("session");
            }

            m_pStack                = stack;
            m_pInitialInviteSender  = sender;
            m_pRtpMultimediaSession = session;

            m_pTags = new Dictionary<string,object>();

            m_pInitialInviteSender.Completed += new EventHandler(delegate(object s,EventArgs e){
                m_pInitialInviteSender = null;

                if(this.State == SIP_CallState.Terminating){
                    SetState(SIP_CallState.Terminated);
                }
            });

            m_CallState = SIP_CallState.Calling;
        }
        /// <summary>
        /// This method is called when REGISTER has finished.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void m_pRegisterSender_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e)
        {
            m_pFlow = e.ClientTransaction.Flow;

            if (e.Response.StatusCodeType == SIP_StatusCodeType.Success)
            {
                SetState(SIP_UA_RegistrationState.Registered);

                OnRegistered();

                m_pFlow.SendKeepAlives = true;
            }
            else
            {
                SetState(SIP_UA_RegistrationState.Error);

                OnError(e);
            }

            // REMOVE ME:
            if (this.AutoFixContact && (m_pContact is SIP_Uri))
            {
                // If Via: received or rport paramter won't match to our sent-by, use received and rport to construct new contact value.

                SIP_Uri       cContact   = ((SIP_Uri)m_pContact);
                IPAddress     cContactIP = Net_Utils.IsIPAddress(cContact.Host) ? IPAddress.Parse(cContact.Host) : null;
                SIP_t_ViaParm via        = e.Response.Via.GetTopMostValue();
                if (via != null && cContactIP != null)
                {
                    IPEndPoint ep = new IPEndPoint(via.Received != null ? via.Received : cContactIP, via.RPort > 0 ? via.RPort : cContact.Port);
                    if (!cContactIP.Equals(ep.Address) || cContact.Port != via.RPort)
                    {
                        // Unregister old contact.
                        BeginUnregister(false);

                        // Fix contact.
                        cContact.Host = ep.Address.ToString();
                        cContact.Port = ep.Port;

                        m_pRegisterSender.Dispose();
                        m_pRegisterSender = null;

                        BeginRegister(m_AutoRefresh);

                        return;
                    }
                }
            }

            if (m_AutoRefresh)
            {
                // Set registration refresh timer.
                m_pTimer.Enabled = true;
            }

            m_pRegisterSender.Dispose();
            m_pRegisterSender = null;
        }
        /// <summary>
        /// Terminates dialog.
        /// </summary>
        /// <param name="reason">Termination reason. This value may be null.</param>
        /// <param name="sendBye">If true BYE is sent to remote party.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        public virtual void Terminate(string reason, bool sendBye)
        {
            lock (m_pLock){
                if (this.State == SIP_DialogState.Disposed)
                {
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if (this.State == SIP_DialogState.Terminating || this.State == SIP_DialogState.Terminated)
                {
                    return;
                }

                /* RFC 3261 15.
                 *  The caller's UA MAY send a BYE for either confirmed or early dialogs, and the callee's UA MAY send a BYE on
                 *  confirmed dialogs, but MUST NOT send a BYE on early dialogs.
                 *
                 * RFC 3261 15.1.
                 *  Once the BYE is constructed, the UAC core creates a new non-INVITE client transaction, and passes it the BYE request.
                 *  The UAC MUST consider the session terminated (and therefore stop sending or listening for media) as soon as the BYE
                 *  request is passed to the client transaction. If the response for the BYE is a 481 (Call/Transaction Does Not Exist)
                 *  or a 408 (Request Timeout) or no response at all is received for the BYE (that is, a timeout is returned by the
                 *  client transaction), the UAC MUST consider the session and the dialog terminated.
                 */

                this.SetState(SIP_DialogState.Terminating, true);

                if (sendBye)
                {
                    // TODO: UAS early

                    if (this.State == SIP_DialogState.Confirmed)
                    {
                        SIP_Request bye = CreateRequest(SIP_Methods.BYE);
                        if (!string.IsNullOrEmpty(reason))
                        {
                            SIP_t_ReasonValue r = new SIP_t_ReasonValue();
                            r.Protocol = "SIP";
                            r.Text     = reason;
                            bye.Reason.Add(r.ToStringValue());
                        }

                        // Send BYE, just wait BYE to complete, we don't care about response code.
                        SIP_RequestSender sender = CreateRequestSender(bye);
                        sender.Completed += delegate(object s, EventArgs a){
                            this.SetState(SIP_DialogState.Terminated, true);
                        };
                        sender.Start();
                    }
                }
                else
                {
                    this.SetState(SIP_DialogState.Terminated, true);
                }
            }
        }
        /// <summary>
        /// This method is called when un-REGISTER has finished.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void m_pUnregisterSender_ResponseReceived(object sender, SIP_ResponseReceivedEventArgs e)
        {
            SetState(SIP_UA_RegistrationState.Unregistered);
            OnUnregistered();

            if (m_AutoDispose)
            {
                Dispose();
            }

            m_pUnregisterSender = null;
        }
        /// <summary>
        /// Creates SIP request sender for the specified request.
        /// </summary>
        /// <remarks>All requests sent through this dialog SHOULD use this request sender to send out requests.</remarks>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns created sender.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>request</b> is null.</exception>
        public SIP_RequestSender CreateRequestSender(SIP_Request request)
        {
            lock (m_pLock){
                if (this.State == SIP_DialogState.Terminated)
                {
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }

                SIP_RequestSender sender = m_pStack.CreateRequestSender(request, this.Flow);

                return(sender);
            }
        }
        /// <summary>
        /// Creates SIP request sender for the specified request.
        /// </summary>
        /// <remarks>All requests sent through this dialog SHOULD use this request sender to send out requests.</remarks>
        /// <param name="request">SIP request.</param>
        /// <returns>Returns created sender.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>request</b> is null.</exception>
        public SIP_RequestSender CreateRequestSender(SIP_Request request)
        {
            lock (m_pLock){
                if (this.State == SIP_DialogState.Terminated)
                {
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }

                // TODO: Request sender must use dialog sequence numbering if authentication done.

                SIP_RequestSender sender = m_pStack.CreateRequestSender(request, this.Flow);

                return(sender);
            }
        }
        /// <summary>
        /// Starts unregistering.
        /// </summary>
        /// <param name="dispose">If true, registration will be disposed after unregister.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        public void BeginUnregister(bool dispose)
        {
            if (m_IsDisposed)
            {
                throw new ObjectDisposedException(this.GetType().Name);
            }

            m_AutoDispose = dispose;

            // Stop register timer, otherwise we may get register and unregister race condition.
            m_pTimer.Enabled = false;

            if (m_State == SIP_UA_RegistrationState.Registered)
            {
                /* RFC 3261 10.1 Constructing the REGISTER Request.
                 *  Request-URI: The Request-URI names the domain of the location service for which the registration is meant (for example,
                 *               "sip:chicago.com").  The "userinfo" and "@" components of the SIP URI MUST NOT be present.
                 */

                SIP_Request unregister = m_pStack.CreateRequest(SIP_Methods.REGISTER, new SIP_t_NameAddress(m_pServer.Scheme + ":" + m_AOR), new SIP_t_NameAddress(m_pServer.Scheme + ":" + m_AOR));
                unregister.RequestLine.Uri = SIP_Uri.Parse(m_pServer.Scheme + ":" + m_AOR.Substring(m_AOR.IndexOf('@') + 1));
                unregister.Route.Add(m_pServer.ToString());
                unregister.Contact.Add("<" + this.Contact + ">;expires=0");

                m_pUnregisterSender = m_pStack.CreateRequestSender(unregister, m_pFlow);
                m_pUnregisterSender.ResponseReceived += new EventHandler <SIP_ResponseReceivedEventArgs>(m_pUnregisterSender_ResponseReceived);
                m_pUnregisterSender.Start();
            }
            else
            {
                SetState(SIP_UA_RegistrationState.Unregistered);
                OnUnregistered();

                if (m_AutoDispose)
                {
                    Dispose();
                }

                m_pUnregisterSender = null;
            }
        }
        /// <summary>
        /// Starts terminating dialog.
        /// </summary>
        /// <param name="reason">Termination reason. This value may be null.</param>
        /// <param name="sendBye">If true BYE is sent to remote party.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception>
        public void Terminate(string reason, bool sendBye)
        {
            lock (this.SyncRoot){
                if (this.State == SIP_DialogState.Disposed)
                {
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if (this.State == SIP_DialogState.Terminating || this.State == SIP_DialogState.Terminated)
                {
                    return;
                }

                m_TerminateReason = reason;

                /* RFC 3261 15.
                 *  The caller's UA MAY send a BYE for either confirmed or early dialogs, and the callee's UA MAY send a BYE on
                 *  confirmed dialogs, but MUST NOT send a BYE on early dialogs.
                 *
                 * RFC 3261 15.1.
                 *  Once the BYE is constructed, the UAC core creates a new non-INVITE client transaction, and passes it the BYE request.
                 *  The UAC MUST consider the session terminated (and therefore stop sending or listening for media) as soon as the BYE
                 *  request is passed to the client transaction. If the response for the BYE is a 481 (Call/Transaction Does Not Exist)
                 *  or a 408 (Request Timeout) or no response at all is received for the BYE (that is, a timeout is returned by the
                 *  client transaction), the UAC MUST consider the session and the dialog terminated.
                 */

                if (sendBye)
                {
                    if ((this.State == SIP_DialogState.Early && m_pActiveInvite is SIP_ClientTransaction) || this.State == SIP_DialogState.Confirmed)
                    {
                        this.SetState(SIP_DialogState.Terminating, true);

                        SIP_Request bye = CreateRequest(SIP_Methods.BYE);
                        if (!string.IsNullOrEmpty(reason))
                        {
                            SIP_t_ReasonValue r = new SIP_t_ReasonValue();
                            r.Protocol = "SIP";
                            r.Text     = reason;
                            bye.Reason.Add(r.ToStringValue());
                        }

                        // Send BYE, just wait BYE to complete, we don't care about response code.
                        SIP_RequestSender sender = CreateRequestSender(bye);
                        sender.Completed += delegate(object s, EventArgs a){
                            this.SetState(SIP_DialogState.Terminated, true);
                        };
                        sender.Start();
                    }
                    else
                    {
                        /* We are "early" UAS dialog, we need todo follwoing:
                         *) If we havent sent final response, send '408 Request terminated' and we are done.
                         *) We have sen't final response, we need to wait ACK to arrive or timeout.
                         *      If will ACK arrives or timeout, send BYE.
                         */

                        if (m_pActiveInvite != null && m_pActiveInvite.FinalResponse == null)
                        {
                            this.Stack.CreateResponse(SIP_ResponseCodes.x408_Request_Timeout, m_pActiveInvite.Request);

                            this.SetState(SIP_DialogState.Terminated, true);
                        }
                        else
                        {
                            // Wait ACK to arrive or timeout.

                            this.SetState(SIP_DialogState.Terminating, true);
                        }
                    }
                }
                else
                {
                    this.SetState(SIP_DialogState.Terminated, true);
                }
            }
        }
Example #10
0
        /// <summary>
        /// Starts unregistering.
        /// </summary>
        /// <param name="dispose">If true, registration will be disposed after unregister.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        public void BeginUnregister(bool dispose)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }

            m_AutoDispose = dispose;

            // Stop register timer, otherwise we may get register and unregister race condition.
            m_pTimer.Enabled = false;

            if(m_State == SIP_UA_RegistrationState.Registered){
                /* RFC 3261 10.1 Constructing the REGISTER Request.
                    Request-URI: The Request-URI names the domain of the location service for which the registration is meant (for example,
                                 "sip:chicago.com").  The "userinfo" and "@" components of the SIP URI MUST NOT be present.
                */

                SIP_Request unregister = m_pStack.CreateRequest(SIP_Methods.REGISTER,new SIP_t_NameAddress(m_pServer.Scheme + ":" + m_AOR),new SIP_t_NameAddress(m_pServer.Scheme + ":" + m_AOR));
                unregister.RequestLine.Uri = SIP_Uri.Parse(m_pServer.Scheme + ":" + m_AOR.Substring(m_AOR.IndexOf('@') + 1));
                unregister.Route.Add(m_pServer.ToString());
                unregister.Contact.Add("<" + this.Contact + ">;expires=0");
            
                m_pUnregisterSender = m_pStack.CreateRequestSender(unregister,m_pFlow);
                m_pUnregisterSender.ResponseReceived += new EventHandler<SIP_ResponseReceivedEventArgs>(m_pUnregisterSender_ResponseReceived);
                m_pUnregisterSender.Start();
            }
            else{
                SetState(SIP_UA_RegistrationState.Unregistered);
                OnUnregistered();

                if(m_AutoDispose){
                    Dispose();
                }

                m_pUnregisterSender = null;
            }
        }
Example #11
0
        /// <summary>
        /// Starts registering.
        /// </summary>
        /// <param name="autoRefresh">If true, registration takes care of refreshing itself to registrar server.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        public void BeginRegister(bool autoRefresh)
        {
            if(m_IsDisposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }

            // Fix ME: Stack not running, try register on next step.
            // In ideal solution we need to start registering when stack starts.
            if(m_pStack.State != SIP_StackState.Started){
                m_pTimer.Enabled = true;
                return;
            }

            m_AutoRefresh = autoRefresh;
            SetState(SIP_UA_RegistrationState.Registering);

            /* RFC 3261 10.1 Constructing the REGISTER Request.
                Request-URI: The Request-URI names the domain of the location service for which the registration is meant (for example,
                             "sip:chicago.com").  The "userinfo" and "@" components of the SIP URI MUST NOT be present.
            */

            SIP_Request register = m_pStack.CreateRequest(SIP_Methods.REGISTER,new SIP_t_NameAddress(m_pServer.Scheme + ":" + m_AOR),new SIP_t_NameAddress(m_pServer.Scheme + ":" + m_AOR));
            register.RequestLine.Uri = SIP_Uri.Parse(m_pServer.Scheme + ":" + m_AOR.Substring(m_AOR.IndexOf('@') + 1));
            register.Route.Add(m_pServer.ToString());
            register.Contact.Add("<" + this.Contact + ">;expires=" + m_RefreshInterval);

            m_pRegisterSender = m_pStack.CreateRequestSender(register,m_pFlow);
            m_pRegisterSender.ResponseReceived += new EventHandler<SIP_ResponseReceivedEventArgs>(m_pRegisterSender_ResponseReceived);
            m_pRegisterSender.Start();
        }
Example #12
0
        /// <summary>
        /// This method is called when un-REGISTER has finished.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void m_pUnregisterSender_ResponseReceived(object sender,SIP_ResponseReceivedEventArgs e)
        {
            SetState(SIP_UA_RegistrationState.Unregistered);
            OnUnregistered();

            if(m_AutoDispose){
                Dispose();
            }

            m_pUnregisterSender = null;
        }
Example #13
0
        /// <summary>
        /// This method is called when REGISTER has finished.
        /// </summary>
        /// <param name="sender">Sender.</param>
        /// <param name="e">Event data.</param>
        private void m_pRegisterSender_ResponseReceived(object sender,SIP_ResponseReceivedEventArgs e)
        {   
            m_pFlow = e.ClientTransaction.Flow;
       
            if(e.Response.StatusCodeType == SIP_StatusCodeType.Provisional){
                return;
            }
            else if(e.Response.StatusCodeType == SIP_StatusCodeType.Success){
                m_pContacts.Clear();
                foreach(SIP_t_ContactParam c in e.Response.Contact.GetAllValues()){
                    m_pContacts.Add(c.Address.Uri);
                }

                SetState(SIP_UA_RegistrationState.Registered);
                                             
                OnRegistered();

                m_pFlow.SendKeepAlives = true;
            }
            else{
                SetState(SIP_UA_RegistrationState.Error);

                OnError(e);
            }

            // REMOVE ME:
            if(this.AutoFixContact && (m_pContact is SIP_Uri)){
                // If Via: received or rport paramter won't match to our sent-by, use received and rport to construct new contact value.
                   
                SIP_Uri       cContact    = ((SIP_Uri)m_pContact);
                IPAddress     cContactIP  = Net_Utils.IsIPAddress(cContact.Host) ? IPAddress.Parse(cContact.Host) : null;
                SIP_t_ViaParm via         = e.Response.Via.GetTopMostValue();
                if(via != null && cContactIP != null){
                    IPEndPoint ep = new IPEndPoint(via.Received != null ? via.Received : cContactIP,via.RPort > 0 ? via.RPort : cContact.Port);
                    if(!cContactIP.Equals(ep.Address) || cContact.Port != via.RPort){
                        // Unregister old contact.
                        BeginUnregister(false);

                        // Fix contact.
                        cContact.Host = ep.Address.ToString();
                        cContact.Port = ep.Port;

                        m_pRegisterSender.Dispose();
                        m_pRegisterSender = null;
                                                
                        BeginRegister(m_AutoRefresh);

                        return;
                    }
                }
            }

            if(m_AutoRefresh){
                // Set registration refresh timer.
                m_pTimer.Enabled = true;
            }

            m_pRegisterSender.Dispose();
            m_pRegisterSender = null;
        }
Example #14
0
        /// <summary>
        /// Starts calling.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when call is not in valid state.</exception>
        public void Start()
        {
            lock(m_pLock){
                if(m_State == SIP_UA_CallState.Disposed){
                    throw new ObjectDisposedException(this.GetType().Name);
                }
                if(m_State != SIP_UA_CallState.WaitingForStart){
                    throw new InvalidOperationException("Start method can be called only in 'SIP_UA_CallState.WaitingForStart' state.");
                }

                SetState(SIP_UA_CallState.Calling);
                        
                m_pInitialInviteSender = m_pUA.Stack.CreateRequestSender(m_pInvite);
                m_pInitialInviteSender.ResponseReceived += new EventHandler<SIP_ResponseReceivedEventArgs>(m_pInitialInviteSender_ResponseReceived);
                m_pInitialInviteSender.Start();
            }
        }
Example #15
0
        /// <summary>
        /// Creates SIP request sender for the specified request.
        /// </summary>
        /// <param name="request">SIP request.</param>
        /// <param name="flow">Data flow.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception>
        /// <exception cref="ArgumentNullException">Is raised when <b>request</b> is null reference.</exception>
        internal SIP_RequestSender CreateRequestSender(SIP_Request request,SIP_Flow flow)
        {
            if(m_State == SIP_StackState.Disposed){
                throw new ObjectDisposedException(this.GetType().Name);
            }
            if(request == null){
                throw new ArgumentNullException("request");
            }

            SIP_RequestSender sender = new SIP_RequestSender(this,request,flow);
            sender.Credentials.AddRange(m_pCredentials);

            return sender;
        }