/// <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); }
/// <summary> /// Cleans up any resources being used. /// </summary> public virtual void Dispose() { lock(m_pLock){ if(this.State == SIP_DialogState.Disposed){ return; } SetState(SIP_DialogState.Disposed,true); this.RequestReceived = null; m_pStack = null; m_CallID = null; m_LocalTag = null; m_RemoteTag = null; m_pLocalUri = null; m_pRemoteUri = null; m_pLocalContact = null; m_pRemoteTarget = null; m_pRouteSet = null; m_pFlow = null; } }
// TODO: Early timer. #region method ProcessRequest /// <summary> /// Processes specified request through this dialog. /// </summary> /// <param name="e">Method arguments.</param> /// <returns>Returns true if this dialog processed specified response, otherwise false.</returns> /// <exception cref="ArgumentNullException">Is raised when <b>e</b> is null reference.</exception> internal protected virtual bool ProcessRequest(SIP_RequestReceivedEventArgs e) { if(e == null){ throw new ArgumentNullException("e"); } /* RFC 3261 12.2.2. If the remote sequence number is empty, it MUST be set to the value of the sequence number in the CSeq header field value in the request. If the remote sequence number was not empty, but the sequence number of the request is lower than the remote sequence number, the request is out of order and MUST be rejected with a 500 (Server Internal Error) response. If the remote sequence number was not empty, and the sequence number of the request is greater than the remote sequence number, the request is in order. It is possible for the CSeq sequence number to be higher than the remote sequence number by more than one. This is not an error condition, and a UAS SHOULD be prepared to receive and process requests with CSeq values more than one higher than the previous received request. The UAS MUST then set the remote sequence number to the value of the sequence number in the CSeq header field value in the request. */ if(m_RemoteSeqNo == 0){ m_RemoteSeqNo = e.Request.CSeq.SequenceNumber; } else if(e.Request.CSeq.SequenceNumber < m_RemoteSeqNo){ e.ServerTransaction.SendResponse(this.Stack.CreateResponse(SIP_ResponseCodes.x500_Server_Internal_Error + ": The mid-dialog request is out of order(late arriving request).",e.Request)); return true; } else{ m_RemoteSeqNo = e.Request.CSeq.SequenceNumber; } /* RFC 3261 12.2.2. When a UAS receives a target refresh request, it MUST replace the dialog's remote target URI with the URI from the Contact header field in that request, if present. Per RFC we must do it after 2xx response. There are some drwabacks, like old contact not accessible any more due to NAT, so only valid contact new contact. Because of it currently we always change contact. */ if(IsTargetRefresh(e.Request.RequestLine.Method) && e.Request.Contact.Count != 0){ m_pRemoteTarget = (SIP_Uri)e.Request.Contact.GetTopMostValue().Address.Uri; } OnRequestReceived(e); return e.IsHandled; }
/// <summary> /// Parses SIP_Uri from SIP-URI string. /// </summary> /// <param name="value">SIP-URI string.</param> /// <returns>Returns parsed SIP_Uri object.</returns> /// <exception cref="ArgumentNullException">Raised when <b>reader</b> is null.</exception> /// <exception cref="SIP_ParseException">Raised when invalid SIP message.</exception> public static SIP_Uri Parse(string value) { // Syntax: sip:/sips: username@host:port *[;parameter] [?header *[&header]] if(value == null){ throw new ArgumentNullException("value"); } value = Uri.UnescapeDataString(value); if(!(value.ToLower().StartsWith("sip:") || value.ToLower().StartsWith("sips:"))){ throw new SIP_ParseException("Specified value is invalid SIP-URI !"); } SIP_Uri retVal = new SIP_Uri(); StringReader r = new StringReader(value); // IsSecure retVal.IsSecure = r.QuotedReadToDelimiter(':').ToLower() == "sips"; // Get username if(r.SourceString.IndexOf('@') > -1){ retVal.User = r.QuotedReadToDelimiter('@'); } // Gets host[:port] string[] host_port = r.QuotedReadToDelimiter(new char[]{';','?'},false).Split(':'); retVal.Host = host_port[0]; // Optional port specified if(host_port.Length == 2){ retVal.Port = Convert.ToInt32(host_port[1]); } // We have parameters and/or header if(r.Available > 0){ // Get parameters string[] parameters = TextUtils.SplitQuotedString(r.QuotedReadToDelimiter('?'),';'); foreach(string parameter in parameters){ if(parameter.Trim() != ""){ string[] name_value = parameter.Trim().Split(new char[]{'='},2); if(name_value.Length == 2){ retVal.Parameters.Add(name_value[0],name_value[1]); } else{ retVal.Parameters.Add(name_value[0],null); } } } // We have header if(r.Available > 0){ retVal.m_Header = r.ReadToEnd(); } } return retVal; }