/// <summary> /// Parses this from address param string. /// </summary> /// <param name="reader">Reader what contains address param string.</param> /// <exception cref="ArgumentNullException">Raised when <b>reader</b> is null.</exception> /// <exception cref="SIP_ParseException">Raised when invalid SIP message.</exception> public override void Parse(StringReader reader) { if(reader == null){ throw new ArgumentNullException("reader"); } // Parse address SIP_t_NameAddress address = new SIP_t_NameAddress(); address.Parse(reader); m_pAddress = address; // Parse parameters. ParseParameters(reader); }
/// <summary> /// Parses this from address param string. /// </summary> /// <param name="reader">Reader what contains address param string.</param> /// <exception cref="ArgumentNullException">Raised when <b>reader</b> is null.</exception> /// <exception cref="SIP_ParseException">Raised when invalid SIP message.</exception> public override void Parse(StringReader reader) { if (reader == null) { throw new ArgumentNullException("reader"); } // Parse address SIP_t_NameAddress address = new SIP_t_NameAddress(); address.Parse(reader); m_pAddress = address; // Parse parameters. ParseParameters(reader); }
/// <summary> /// Parses "hi-entry" from specified reader. /// </summary> /// <param name="reader">Reader from where to parse.</param> /// <exception cref="ArgumentNullException">Raised when <b>reader</b> is null.</exception> /// <exception cref="SIP_ParseException">Raised when invalid SIP message.</exception> public override void Parse(StringReader reader) { /* hi-entry = hi-targeted-to-uri *( SEMI hi-param ) hi-targeted-to-uri= name-addr hi-param = hi-index / hi-extension hi-index = "index" EQUAL 1*DIGIT *(DOT 1*DIGIT) hi-extension = generic-param */ if(reader == null){ throw new ArgumentNullException("reader"); } // name-addr m_pAddress = new SIP_t_NameAddress(); m_pAddress.Parse(reader); // Parse parameters ParseParameters(reader); }
/// <summary> /// Parses "hi-entry" from specified reader. /// </summary> /// <param name="reader">Reader from where to parse.</param> /// <exception cref="ArgumentNullException">Raised when <b>reader</b> is null.</exception> /// <exception cref="SIP_ParseException">Raised when invalid SIP message.</exception> public override void Parse(StringReader reader) { /* * hi-entry = hi-targeted-to-uri *( SEMI hi-param ) * hi-targeted-to-uri= name-addr * hi-param = hi-index / hi-extension * hi-index = "index" EQUAL 1*DIGIT *(DOT 1*DIGIT) * hi-extension = generic-param */ if (reader == null) { throw new ArgumentNullException("reader"); } // name-addr m_pAddress = new SIP_t_NameAddress(); m_pAddress.Parse(reader); // Parse parameters ParseParameters(reader); }
/// <summary> /// Parses "contact-param" from specified reader. /// </summary> /// <param name="reader">Reader from where to parse.</param> /// <exception cref="ArgumentNullException">Raised when <b>reader</b> is null.</exception> /// <exception cref="SIP_ParseException">Raised when invalid SIP message.</exception> public override void Parse(StringReader reader) { /* * Contact = ("Contact" / "m" ) HCOLON * ( STAR / (contact-param *(COMMA contact-param))) * contact-param = (name-addr / addr-spec) *(SEMI contact-params) * name-addr = [ display-name ] LAQUOT addr-spec RAQUOT * addr-spec = SIP-URI / SIPS-URI / absoluteURI * display-name = *(token LWS)/ quoted-string * * contact-params = c-p-q / c-p-expires / contact-extension * c-p-q = "q" EQUAL qvalue * c-p-expires = "expires" EQUAL delta-seconds * contact-extension = generic-param * delta-seconds = 1*DIGIT * * When the header field value contains a display name, the URI including all URI * parameters is enclosed in "<" and ">". If no "<" and ">" are present, all * parameters after the URI are header parameters, not URI parameters. * * Even if the "display-name" is empty, the "name-addr" form MUST be * used if the "addr-spec" contains a comma, semicolon, or question * mark. There may or may not be LWS between the display-name and the "<". */ if (reader == null) { throw new ArgumentNullException("reader"); } // Parse address SIP_t_NameAddress address = new SIP_t_NameAddress(); address.Parse(reader); m_pAddress = address; // Parse parameters ParseParameters(reader); }
/// <summary> /// Default constructor. /// </summary> /// <param name="value">To: header field value.</param> public SIP_t_To(string value) { m_pAddress = new SIP_t_NameAddress(); Parse(new StringReader(value)); }
private void Call(SIP_t_NameAddress from, SIP_t_NameAddress to) { if (from == null) { throw new ArgumentNullException("from"); } if (to == null) { throw new ArgumentNullException("to"); } #region Setup RTP session RTP_MultimediaSession rtpMultimediaSession = new RTP_MultimediaSession(RTP_Utils.GenerateCNAME()); RTP_Session rtpSession = CreateRtpSession(rtpMultimediaSession); // Port search failed. if (rtpSession == null) { throw new Exception("Calling not possible, RTP session failed to allocate IP end points."); } if (m_IsDebug) { wfrm_RTP_Debug rtpDebug = new wfrm_RTP_Debug(rtpMultimediaSession); rtpDebug.Show(); } #endregion #region Create SDP offer SDP_Message sdpOffer = new SDP_Message(); sdpOffer.Version = "0"; sdpOffer.Origin = new SDP_Origin("-", sdpOffer.GetHashCode(), 1, "IN", "IP4", System.Net.Dns.GetHostAddresses("")[0].ToString()); sdpOffer.SessionName = "SIP Call"; sdpOffer.Times.Add(new SDP_Time(0, 0)); #region Add 1 audio stream SDP_MediaDescription mediaStream = new SDP_MediaDescription(SDP_MediaTypes.audio, 0, 1, "RTP/AVP", null); rtpSession.NewReceiveStream += delegate(object s, RTP_ReceiveStreamEventArgs e) { AudioOut_RTP audioOut = new AudioOut_RTP(m_pAudioOutDevice, e.Stream, m_pAudioCodecs); audioOut.Start(); mediaStream.Tags["rtp_audio_out"] = audioOut; }; if (!HandleNAT(mediaStream, rtpSession)) { throw new Exception("Calling not possible, because of NAT or firewall restrictions."); } foreach (KeyValuePair<int, AudioCodec> entry in m_pAudioCodecs) { mediaStream.Attributes.Add(new SDP_Attribute("rtpmap", entry.Key + " " + entry.Value.Name + "/" + entry.Value.CompressedAudioFormat.SamplesPerSecond)); mediaStream.MediaFormats.Add(entry.Key.ToString()); } mediaStream.Attributes.Add(new SDP_Attribute("ptime", "20")); mediaStream.Attributes.Add(new SDP_Attribute("sendrecv", "")); mediaStream.Tags["rtp_session"] = rtpSession; mediaStream.Tags["audio_codecs"] = m_pAudioCodecs; sdpOffer.MediaDescriptions.Add(mediaStream); #endregion #endregion // Create INVITE request. SIP_Request invite = m_pStack.CreateRequest(SIP_Methods.INVITE, to, from); invite.ContentType = "application/sdp"; invite.Data = sdpOffer.ToByte(); SIP_RequestSender sender = m_pStack.CreateRequestSender(invite); // Create call. m_pCall = new SIP_Call(m_pStack, sender, rtpMultimediaSession); m_pCall.LocalSDP = sdpOffer; m_pCall.StateChanged += new EventHandler(m_pCall_StateChanged); bool finalResponseSeen = false; List<SIP_Dialog_Invite> earlyDialogs = new List<SIP_Dialog_Invite>(); sender.ResponseReceived += delegate(object s, SIP_ResponseReceivedEventArgs e) { // Skip 2xx retransmited response. if (finalResponseSeen) { return; } if (e.Response.StatusCode >= 200) { finalResponseSeen = true; } try { #region Provisional if (e.Response.StatusCodeType == SIP_StatusCodeType.Provisional) { /* RFC 3261 13.2.2.1. Zero, one or multiple provisional responses may arrive before one or more final responses are received. Provisional responses for an INVITE request can create "early dialogs". If a provisional response has a tag in the To field, and if the dialog ID of the response does not match an existing dialog, one is constructed using the procedures defined in Section 12.1.2. */ if (e.Response.StatusCode > 100 && e.Response.To.Tag != null) { earlyDialogs.Add((SIP_Dialog_Invite)e.GetOrCreateDialog); } // 180_Ringing. if (e.Response.StatusCode == 180) { //m_pPlayer.Play(ResManager.GetStream("ringing.wav"), 10); // We need BeginInvoke here, otherwise we block client transaction. m_pStatusBar.BeginInvoke(new MethodInvoker(delegate() { m_pStatusBar.Items[0].Text = "Ringing"; })); } } #endregion #region Success else if (e.Response.StatusCodeType == SIP_StatusCodeType.Success) { SIP_Dialog dialog = e.GetOrCreateDialog; /* Exit all all other dialogs created by this call (due to forking). That is not defined in RFC but, since UAC can send BYE to early and confirmed dialogs, all this is 100% valid. */ foreach (SIP_Dialog_Invite d in earlyDialogs.ToArray()) { if (!d.Equals(dialog)) { d.Terminate("Another forking leg accepted.", true); } } m_pCall.InitCalling(dialog, sdpOffer); // Remote-party provided SDP. if (e.Response.ContentType != null && e.Response.ContentType.ToLower().IndexOf("application/sdp") > -1) { try { // SDP offer. We sent offerless INVITE, we need to send SDP answer in ACK request.' if (e.ClientTransaction.Request.ContentType == null || e.ClientTransaction.Request.ContentType.ToLower().IndexOf("application/sdp") == -1) { // Currently we never do it, so it never happens. This is place holder, if we ever support it. } // SDP answer to our offer. else { // This method takes care of ACK sending and 2xx response retransmission ACK sending. HandleAck(m_pCall.Dialog, e.ClientTransaction); ProcessMediaAnswer(m_pCall, m_pCall.LocalSDP, SDP_Message.Parse(Encoding.UTF8.GetString(e.Response.Data))); } } catch { m_pCall.Terminate("SDP answer parsing/processing failed."); } } else { // If we provided SDP offer, there must be SDP answer. if (e.ClientTransaction.Request.ContentType != null && e.ClientTransaction.Request.ContentType.ToLower().IndexOf("application/sdp") > -1) { m_pCall.Terminate("Invalid 2xx response, required SDP answer is missing."); } } // Stop ringing. m_pPlayer.Stop(); } #endregion #region Failure else { /* RFC 3261 13.2.2.3. All early dialogs are considered terminated upon reception of the non-2xx final response. */ foreach (SIP_Dialog_Invite dialog in earlyDialogs.ToArray()) { dialog.Terminate("All early dialogs are considered terminated upon reception of the non-2xx final response. (RFC 3261 13.2.2.3)", false); } // We need BeginInvoke here, otherwise we block client transaction while message box open. if (m_pCall.State != SIP_CallState.Terminating) { this.BeginInvoke(new MethodInvoker(delegate() { m_pConnect.Image = global::PowerSDR.Properties.Resources.call; connected = false; MessageBox.Show("Calling failed: " + e.Response.StatusCode_ReasonPhrase, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error); })); } // We need BeginInvoke here, otherwise we block client transaction. m_pStatusBar.BeginInvoke(new MethodInvoker(delegate() { m_pStatusBar.Items[0].Text = ""; })); // Stop calling or ringing. m_pPlayer.Stop(); // Terminate call. m_pCall.Terminate("Remote party rejected a call.", false); } #endregion } catch (Exception x) { // We need BeginInvoke here, otherwise we block client transaction while message box open. this.BeginInvoke(new MethodInvoker(delegate() { MessageBox.Show("Error: " + x.Message, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error); })); } }; m_pStatusBar.Items[0].Text = "Calling"; m_pStatusBar.Items[1].Text = "00:00:00"; //m_pPlayer.Play(ResManager.GetStream("calling.wav"), 10); // Start calling. sender.Start(); }
private void m_pConnect_Click(object sender, EventArgs e) { this.Cursor = Cursors.WaitCursor; try { if (m_pCall != null) { m_pCall.Terminate("Hang up."); connected = false; m_pConnect.Image = global::PowerSDR.Properties.Resources.call; } else { #region Validate From:/To: SIP_t_NameAddress to = null; try { to = new SIP_t_NameAddress(m_pRemoteIP.Text); if (!to.IsSipOrSipsUri) { throw new ArgumentException("To: is not SIP URI."); } } catch { MessageBox.Show("To: is not SIP URI.", "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } SIP_t_NameAddress from = null; try { from = new SIP_t_NameAddress(m_pLocalIP.Text); if (!to.IsSipOrSipsUri) { throw new ArgumentException("From: is not SIP URI."); } } catch { MessageBox.Show("From: is not SIP URI.", "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } #endregion Call(from, to); } } catch (Exception x) { MessageBox.Show("Error: " + x.Message, "Error:", MessageBoxButtons.OK, MessageBoxIcon.Error); m_pCall = null; } this.Cursor = Cursors.Default; }
/// <summary> /// Parses "contact-param" from specified reader. /// </summary> /// <param name="reader">Reader from where to parse.</param> /// <exception cref="ArgumentNullException">Raised when <b>reader</b> is null.</exception> /// <exception cref="SIP_ParseException">Raised when invalid SIP message.</exception> public override void Parse(StringReader reader) { /* Contact = ("Contact" / "m" ) HCOLON ( STAR / (contact-param *(COMMA contact-param))) contact-param = (name-addr / addr-spec) *(SEMI contact-params) name-addr = [ display-name ] LAQUOT addr-spec RAQUOT addr-spec = SIP-URI / SIPS-URI / absoluteURI display-name = *(token LWS)/ quoted-string contact-params = c-p-q / c-p-expires / contact-extension c-p-q = "q" EQUAL qvalue c-p-expires = "expires" EQUAL delta-seconds contact-extension = generic-param delta-seconds = 1*DIGIT When the header field value contains a display name, the URI including all URI parameters is enclosed in "<" and ">". If no "<" and ">" are present, all parameters after the URI are header parameters, not URI parameters. Even if the "display-name" is empty, the "name-addr" form MUST be used if the "addr-spec" contains a comma, semicolon, or question mark. There may or may not be LWS between the display-name and the "<". */ if(reader == null){ throw new ArgumentNullException("reader"); } // Parse address SIP_t_NameAddress address = new SIP_t_NameAddress(); address.Parse(reader); m_pAddress = address; // Parse parameters ParseParameters(reader); }
/// <summary> /// Default constructor. /// </summary> /// <param name="value">From: header field value.</param> public SIP_t_From(string value) { m_pAddress = new SIP_t_NameAddress(); Parse(new StringReader(value)); }
/// <summary> /// Is called when Call/HangUp button has clicked. /// </summary> /// <param name="sender">Sender.</param> /// <param name="e">Event data.</param> private void m_pCall_HangUp_Click(object sender,EventArgs e) { this.Cursor = Cursors.WaitCursor; try{ if(m_pCall != null){ m_pCall.Terminate("Hang up."); } else{ #region Validate From:/To: SIP_t_NameAddress to = null; try{ to = new SIP_t_NameAddress(m_pTo.Text); if(!to.IsSipOrSipsUri){ throw new ArgumentException("To: is not SIP URI."); } } catch{ MessageBox.Show("To: is not SIP URI.","Error:",MessageBoxButtons.OK,MessageBoxIcon.Error); return; } SIP_t_NameAddress from = null; try{ from = new SIP_t_NameAddress(m_pFrom.Text); if(!to.IsSipOrSipsUri){ throw new ArgumentException("From: is not SIP URI."); } } catch{ MessageBox.Show("From: is not SIP URI.","Error:",MessageBoxButtons.OK,MessageBoxIcon.Error); return; } #endregion Call(from,to); m_pCall_HangUp.Image = ResManager.GetIcon("call_hangup.ico",new Size(24,24)).ToBitmap(); } } catch(Exception x){ MessageBox.Show("Error: " + x.Message,"Error:",MessageBoxButtons.OK,MessageBoxIcon.Error); } this.Cursor = Cursors.Default; }
/// <summary> /// Default constructor. /// </summary> public SIP_t_ContactParam() { m_pAddress = new SIP_t_NameAddress(); }
/// <summary> /// Default constructor. /// </summary> /// <param name="address">From address.</param> public SIP_t_From(SIP_t_NameAddress address) { m_pAddress = address; }
/// <summary> /// Creates new out-off dialog SIP request. /// </summary> /// <param name="method">SIP request-method.</param> /// <param name="to">Recipient address. For example: sip:[email protected]</param> /// <param name="from">Senders address. For example: sip:[email protected]</param> /// <returns>Returns created request.</returns> /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and and this method is accessed.</exception> /// <exception cref="ArgumentNullException">Is raised when <b>method</b>,<b>to</b> or <b>from</b> is null.</exception> /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception> public SIP_Request CreateRequest(string method,SIP_t_NameAddress to,SIP_t_NameAddress from) { if(m_State == SIP_StackState.Disposed){ throw new ObjectDisposedException(this.GetType().Name); } if(method == null){ throw new ArgumentNullException("method"); } if(method == ""){ throw new ArgumentException("Argument 'method' value must be specified."); } if(to == null){ throw new ArgumentNullException("to"); } if(from == null){ throw new ArgumentNullException("from"); } method = method.ToUpper(); /* RFC 3261 8.1.1 Generating the Request A valid SIP request formulated by a UAC MUST, at a minimum, contain the following header fields: To, From, CSeq, Call-ID, Max-Forwards, and Via; all of these header fields are mandatory in all SIP requests. These six header fields are the fundamental building blocks of a SIP message, as they jointly provide for most of the critical message routing services including the addressing of messages, the routing of responses, limiting message propagation, ordering of messages, and the unique identification of transactions. These header fields are in addition to the mandatory request line, which contains the method, Request-URI, and SIP version. */ SIP_Request request = new SIP_Request(method); #region Request-URI (section 8.1.1.1) /* The initial Request-URI of the message SHOULD be set to the value of the URI in the To field. One notable exception is the REGISTER method; behavior for setting the Request-URI of REGISTER is given in Section 10. */ request.RequestLine.Uri = to.Uri; #endregion #region To (section 8.1.1.2) /* The To header field first and foremost specifies the desired "logical" recipient of the request, or the address-of-record of the user or resource that is the target of this request. This may or may not be the ultimate recipient of the request. The To header field MAY contain a SIP or SIPS URI, but it may also make use of other URI schemes (the tel URL (RFC 2806 [9]), for example) when appropriate. */ SIP_t_To t = new SIP_t_To(to); request.To = t; #endregion #region From (section 8.1.1.3) /* The From header field indicates the logical identity of the initiator of the request, possibly the user's address-of-record. Like the To header field, it contains a URI and optionally a display name. It is used by SIP elements to determine which processing rules to apply to a request (for example, automatic call rejection). As such, it is very important that the From URI not contain IP addresses or the FQDN of the host on which the UA is running, since these are not logical names. The From header field allows for a display name. A UAC SHOULD use the display name "Anonymous", along with a syntactically correct, but otherwise meaningless URI (like sip:[email protected]), if the identity of the client is to remain hidden. The From field MUST contain a new "tag" parameter, chosen by the UAC. See Section 19.3 for details on choosing a tag. */ SIP_t_From f = new SIP_t_From(from); f.Tag = SIP_Utils.CreateTag(); request.From = f; #endregion #region CallID (section 8.1.1.4) /* The Call-ID header field acts as a unique identifier to group together a series of messages. It MUST be the same for all requests and responses sent by either UA in a dialog. It SHOULD be the same in each registration from a UA. */ if(method == SIP_Methods.REGISTER){ request.CallID = m_RegisterCallID.ToStringValue(); } else{ request.CallID = SIP_t_CallID.CreateCallID().ToStringValue(); } #endregion #region CSeq (section 8.1.1.5) /* The CSeq header field serves as a way to identify and order transactions. It consists of a sequence number and a method. The method MUST match that of the request. For non-REGISTER requests outside of a dialog, the sequence number value is arbitrary. The sequence number value MUST be expressible as a 32-bit unsigned integer and MUST be less than 2**31. As long as it follows the above guidelines, a client may use any mechanism it would like to select CSeq header field values. */ request.CSeq = new SIP_t_CSeq(ConsumeCSeq(),method); #endregion #region Max-Forwards (section 8.1.1.6) request.MaxForwards = m_MaxForwards; #endregion #region Allow,Supported (section 13.2.1) // RFC requires these headers for dialog establishing requests only. // We just add these to every request - this is won't violate RFC. request.Allow.Add(SIP_Utils.ListToString(m_pAllow)); if(m_pSupported.Count > 0){ request.Supported.Add(SIP_Utils.ListToString(m_pAllow)); } #endregion #region Pre-configured route (proxy server) // section 8.1.2 suggests to use pre-configured route for proxy. foreach(SIP_Uri proxy in m_pProxyServers){ request.Route.Add(proxy.ToString()); } #endregion #region User-Agent if(!string.IsNullOrEmpty(m_UserAgent)){ request.UserAgent = m_UserAgent; } #endregion return request; }
/// <summary> /// Default constructor. /// </summary> /// <param name="address">To address.</param> public SIP_t_To(SIP_t_NameAddress address) { m_pAddress = address; }
/// <summary> /// Default constructor. /// </summary> /// <param name="value">SIP 'Referred-By' value.</param> public SIP_t_ReferredBy(string value) { m_pAddress = new SIP_t_NameAddress(); Parse(value); }