/// <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(); }
/// <summary> /// Re-invites remote party. /// </summary> /// <param name="contact">New contact value. Value null means current contact value used.</param> /// <param name="sdp">SDP media offer.</param> /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception> /// <exception cref="InvalidOperationException">Is raised when there is pending invite and this method is called or dialog is in invalid state.</exception> /// <exception cref="ArgumentNullException">Is raised when <b>sdp</b> is null reference.</exception> public void ReInvite(SIP_Uri contact, SDP_Message sdp) { if (State == SIP_DialogState.Disposed) { throw new ObjectDisposedException(GetType().Name); } if (HasPendingInvite) { throw new InvalidOperationException("There is pending INVITE."); } if (State != SIP_DialogState.Confirmed) { throw new InvalidOperationException("ReInvite is only available in Confirmed state."); } if (sdp == null) { throw new ArgumentNullException("sdp"); } lock (SyncRoot) { // TODO: SIP_Request reinvite = CreateRequest(SIP_Methods.INVITE); if (contact != null) { reinvite.Contact.RemoveAll(); reinvite.Contact.Add(contact.ToString()); } reinvite.ContentType = "application/sdp"; // reinvite.Data = sdp.ToStringData(); // TODO: Create request sender // TODO: Try to reuse existing data flow //SIP_RequestSender sender = this.Stack.CreateRequestSender(reinvite); //sender.Start(); } }
/// <summary> /// Creates new SIP request using this dialog info. /// </summary> /// <param name="method">SIP method.</param> /// <exception cref="ObjectDisposedException">Is raised when this class is Disposed and this method is accessed.</exception> /// <exception cref="ArgumentNullException">Is raised when <b>method</b> is null reference.</exception> /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception> /// <returns>Returns created request.</returns> public SIP_Request CreateRequest(string method) { if (State == SIP_DialogState.Disposed) { throw new ObjectDisposedException(GetType().Name); } if (method == null) { throw new ArgumentNullException("method"); } if (method == string.Empty) { throw new ArgumentException("Argument 'method' value must be specified."); } /* RFC 3261 12.2.1.1. * A request within a dialog is constructed by using many of the * components of the state stored as part of the dialog. * * The URI in the To field of the request MUST be set to the remote URI * from the dialog state. The tag in the To header field of the request * MUST be set to the remote tag of the dialog ID. The From URI of the * request MUST be set to the local URI from the dialog state. The tag * in the From header field of the request MUST be set to the local tag * of the dialog ID. If the value of the remote or local tags is null, * the tag parameter MUST be omitted from the To or From header fields, * respectively. * * The Call-ID of the request MUST be set to the Call-ID of the dialog. * Requests within a dialog MUST contain strictly monotonically * increasing and contiguous CSeq sequence numbers (increasing-by-one) * in each direction (excepting ACK and CANCEL of course, whose numbers * equal the requests being acknowledged or cancelled). Therefore, if * the local sequence number is not empty, the value of the local * sequence number MUST be incremented by one, and this value MUST be * placed into the CSeq header field. If the local sequence number is * empty, an initial value MUST be chosen using the guidelines of * Section 8.1.1.5. The method field in the CSeq header field value * MUST match the method of the request. * * With a length of 32 bits, a client could generate, within a single * call, one request a second for about 136 years before needing to * wrap around. The initial value of the sequence number is chosen * so that subsequent requests within the same call will not wrap * around. A non-zero initial value allows clients to use a time- * based initial sequence number. A client could, for example, * choose the 31 most significant bits of a 32-bit second clock as an * initial sequence number. * * The UAC uses the remote target and route set to build the Request-URI * and Route header field of the request. * * If the route set is empty, the UAC MUST place the remote target URI * into the Request-URI. The UAC MUST NOT add a Route header field to * the request. * * If the route set is not empty, and the first URI in the route set * contains the lr parameter (see Section 19.1.1), the UAC MUST place * the remote target URI into the Request-URI and MUST include a Route * header field containing the route set values in order, including all * parameters. * * If the route set is not empty, and its first URI does not contain the * lr parameter, the UAC MUST place the first URI from the route set * into the Request-URI, stripping any parameters that are not allowed * in a Request-URI. The UAC MUST add a Route header field containing * the remainder of the route set values in order, including all * parameters. The UAC MUST then place the remote target URI into the * Route header field as the last value. * * For example, if the remote target is sip:user@remoteua and the route * set contains: * <sip:proxy1>,<sip:proxy2>,<sip:proxy3;lr>,<sip:proxy4> * * The request will be formed with the following Request-URI and Route * header field: * METHOD sip:proxy1 * Route: <sip:proxy2>,<sip:proxy3;lr>,<sip:proxy4>,<sip:user@remoteua> * * If the first URI of the route set does not contain the lr * parameter, the proxy indicated does not understand the routing * mechanisms described in this document and will act as specified in * RFC 2543, replacing the Request-URI with the first Route header * field value it receives while forwarding the message. Placing the * Request-URI at the end of the Route header field preserves the * information in that Request-URI across the strict router (it will * be returned to the Request-URI when the request reaches a loose- * router). * * A UAC SHOULD include a Contact header field in any target refresh * requests within a dialog, and unless there is a need to change it, * the URI SHOULD be the same as used in previous requests within the * dialog. If the "secure" flag is true, that URI MUST be a SIPS URI. * As discussed in Section 12.2.2, a Contact header field in a target * refresh request updates the remote target URI. This allows a UA to * provide a new contact address, should its address change during the * duration of the dialog. * * However, requests that are not target refresh requests do not affect * the remote target URI for the dialog. * * The rest of the request is formed as described in Section 8.1.1. */ lock (m_pLock) { SIP_Request request = m_pStack.CreateRequest(method, new SIP_t_NameAddress("", m_pRemoteUri), new SIP_t_NameAddress("", m_pLocalUri)); if (m_pRouteSet.Length == 0) { request.RequestLine.Uri = m_pRemoteTarget; } else { SIP_Uri topmostRoute = ((SIP_Uri)m_pRouteSet[0].Address.Uri); if (topmostRoute.Param_Lr) { request.RequestLine.Uri = m_pRemoteTarget; for (int i = 0; i < m_pRouteSet.Length; i++) { request.Route.Add(m_pRouteSet[i].ToStringValue()); } } else { request.RequestLine.Uri = SIP_Utils.UriToRequestUri(topmostRoute); for (int i = 1; i < m_pRouteSet.Length; i++) { request.Route.Add(m_pRouteSet[i].ToStringValue()); } } } request.To.Tag = m_RemoteTag; request.From.Tag = m_LocalTag; request.CallID = m_CallID; request.CSeq.SequenceNumber = ++m_LocalSeqNo; request.Contact.Add(m_pLocalContact.ToString()); return(request); } }