PageRenderTime 49ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/sipsorcery-xmpp/SIPSorcery.XMPP/XMPPPhoneSession.cs

https://github.com/thecc4re/sipsorcery-mono
C# | 268 lines | 181 code | 39 blank | 48 comment | 22 complexity | 3b99c5693a7d3b7c6c0a3c36bc406982 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. //-----------------------------------------------------------------------------
  2. // Filename: XMPPPhoneSession.cs
  3. //
  4. // Description: Represents the an XMPP session that operates on top of an authenticated
  5. // XMPP stream and that negotiates a voice call.
  6. //
  7. // History:
  8. // 13 Dec 2010 Aaron Clauson Created.
  9. //
  10. // License:
  11. // This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
  12. //
  13. // Copyright (c) 2010 Aaron Clauson (aaron@sipsorcery.com), Hobart, Tasmania, Australia (www.sipsorcery.com)
  14. // All rights reserved.
  15. //
  16. // Redistribution and use in source and binary forms, with or without modification, are permitted provided that
  17. // the following conditions are met:
  18. //
  19. // Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  20. // Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
  21. // disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of SIP Sorcery.
  22. // nor the names of its contributors may be used to endorse or promote products derived from this software without specific
  23. // prior written permission.
  24. //
  25. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
  26. // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  27. // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  28. // OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  29. // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  30. // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  31. // POSSIBILITY OF SUCH DAMAGE.
  32. //-----------------------------------------------------------------------------
  33. using System;
  34. using System.Collections.Generic;
  35. using System.Linq;
  36. using System.Xml.Linq;
  37. using System.Text;
  38. using SIPSorcery.Net;
  39. using SIPSorcery.Sys;
  40. using log4net;
  41. namespace SIPSorcery.XMPP
  42. {
  43. public class XMPPPhoneSession
  44. {
  45. private static ILog logger = AppState.logger;
  46. private static XNamespace m_sessionNS = XMPPStream.GOOGLE_SESSION_NAMESPACE;
  47. private static XNamespace m_phoneNS = XMPPStream.GOOGLE_PHONE_SESSION_NAMESPACE;
  48. private static XNamespace m_transportNS = XMPPStream.TRANSPORT_NAMESPACE;
  49. protected static XNamespace JabberClientNS = XMPPStream.JABBER_NAMESPACE;
  50. private string m_destination;
  51. private SDP m_sdp;
  52. private string m_jid;
  53. private XMPPAuthenticatedStream m_xmppStream;
  54. private string m_sessionID;
  55. // IQ id's.
  56. private string m_descriptionID;
  57. // Call response.
  58. private string m_remoteIPAddress;
  59. private int m_remotePort;
  60. private string m_remoteUsername;
  61. private string m_remotePassword;
  62. private List<XElement> m_payloads;
  63. public Action<SDP> Accepted;
  64. public Action Rejected;
  65. public Action Hungup;
  66. public XMPPPhoneSession(string jid, XMPPAuthenticatedStream xmppStream)
  67. {
  68. m_sessionID = Crypto.GetRandomString(6);
  69. m_jid = jid;
  70. m_xmppStream = xmppStream;
  71. m_xmppStream.RegisterSession(m_sessionID, OnIQRequest);
  72. }
  73. public void PlaceCall(string destination, SDP sdp)
  74. {
  75. m_destination = destination;
  76. m_sdp = sdp;
  77. XElement descriptionElement = SDPToJingle.GetDescription(m_sdp);
  78. m_descriptionID = Crypto.GetRandomString(6);
  79. XElement callElement = new XElement(JabberClientNS + "iq",
  80. new XAttribute("from", m_jid),
  81. new XAttribute("id", m_descriptionID),
  82. new XAttribute("to", m_destination),
  83. new XAttribute("type", "set"),
  84. new XElement(m_sessionNS + "session",
  85. new XAttribute("type", "initiate"),
  86. new XAttribute("id", m_sessionID),
  87. new XAttribute("initiator", m_jid),
  88. descriptionElement
  89. //new XElement(m_transportNS + "transport")
  90. ));
  91. logger.Debug("XMPPPhoneSession sending iq with session description.");
  92. m_xmppStream.WriteElement(callElement, OnIQResponse);
  93. }
  94. private void SendCandidates()
  95. {
  96. XElement candidatesElement = SDPToJingle.GetCandidates(m_sdp);
  97. string candidateIqID = Crypto.GetRandomString(6);
  98. XElement candElement = new XElement(JabberClientNS + "iq",
  99. new XAttribute("from", m_jid),
  100. new XAttribute("id", candidateIqID),
  101. new XAttribute("to", m_destination),
  102. new XAttribute("type", "set"),
  103. new XElement(m_sessionNS + "session",
  104. new XAttribute("type", "candidates"),
  105. new XAttribute("id", m_sessionID),
  106. new XAttribute("initiator", m_jid),
  107. candidatesElement));
  108. logger.Debug("XMPPPhoneSession sending iq with session candidate.");
  109. m_xmppStream.WriteElement(candElement, OnIQResponse);
  110. }
  111. public void RetargetCallMedia(string ipAddress, int port)
  112. {
  113. XElement candidateElement = SDPToJingle.GetCandidate(ipAddress, port);
  114. string candidateIqID = Crypto.GetRandomString(6);
  115. XElement candElement = new XElement(JabberClientNS + "iq",
  116. new XAttribute("from", m_jid),
  117. new XAttribute("id", candidateIqID),
  118. new XAttribute("to", m_destination),
  119. new XAttribute("type", "set"),
  120. new XElement(m_sessionNS + "session",
  121. new XAttribute("type", "candidates"),
  122. new XAttribute("id", m_sessionID),
  123. new XAttribute("initiator", m_jid),
  124. candidateElement));
  125. m_xmppStream.WriteElement(candElement, OnIQResponse);
  126. }
  127. public void TerminateCall()
  128. {
  129. string iqID = Crypto.GetRandomString(6);
  130. XElement cancelElement = new XElement(JabberClientNS + "iq",
  131. new XAttribute("from", m_jid),
  132. new XAttribute("id", iqID),
  133. new XAttribute("to", m_destination),
  134. new XAttribute("type", "set"),
  135. new XElement(m_sessionNS + "session",
  136. new XAttribute("type", "terminate"),
  137. new XAttribute("id", m_sessionID),
  138. new XAttribute("initiator", m_jid)));
  139. logger.Debug("XMPPPhoneSession sending iq to terminate session.");
  140. m_xmppStream.WriteElement(cancelElement, OnIQResponse);
  141. }
  142. /// <summary>
  143. /// Handler method for receiving responses to iq stanzas initiated by us.
  144. /// </summary>
  145. /// <param name="id">The id of the original iq request.</param>
  146. /// <param name="iq">The iq response stanza.</param>
  147. public void OnIQResponse(string id, XElement iq)
  148. {
  149. //Console.WriteLine("IQ received result for ID " + id + " => " + iq.ToString());
  150. // Send ok.
  151. //XAttribute sessionType = iq.Element(m_sessionNS + "session").Attribute("type");
  152. XAttribute iqType = iq.Attribute("type");
  153. logger.Debug("XMPPPhoneSession iq response, type=" + iqType.Value + ".");
  154. if (iqType.Value == "result")
  155. {
  156. string iqID = iq.Attribute("id").Value;
  157. if (iqID == m_descriptionID)
  158. {
  159. SendCandidates();
  160. }
  161. }
  162. else if (iqType.Value == "error")
  163. {
  164. int errorCode = 0;
  165. Int32.TryParse(iq.Element(JabberClientNS + "error").Attribute("code").Value, out errorCode);
  166. if (errorCode >= 300 && errorCode <= 399)
  167. {
  168. // Redirect.
  169. string redirect = iq.Element(JabberClientNS + "error").Element(m_sessionNS + "redirect").Value;
  170. m_destination = redirect.Replace("xmpp:", String.Empty);
  171. PlaceCall(m_destination, m_sdp);
  172. }
  173. else
  174. {
  175. Rejected();
  176. }
  177. }
  178. }
  179. /// <summary>
  180. /// Handler method for receiving iq requests from the remote server.
  181. /// </summary>
  182. /// <param name="iq">The iq stanza request received.</param>
  183. public void OnIQRequest(XElement iq)
  184. {
  185. logger.Debug("XMPPPhoneSession iq request, type=" + iq.Attribute("type").Value + ".");
  186. if (iq.Element(m_sessionNS + "session") != null)
  187. {
  188. XAttribute sessionType = iq.Element(m_sessionNS + "session").Attribute("type");
  189. if (sessionType != null)
  190. {
  191. if (sessionType.Value == "candidates")
  192. {
  193. logger.Debug("XMPPPhoneSession session candidates stanza was received.");
  194. XElement candidate = (from cand in iq.Descendants(m_sessionNS + "session").Descendants(m_sessionNS + "candidate")
  195. where cand.Attribute("protocol").Value == "udp"
  196. select cand).First();
  197. m_remoteIPAddress = candidate.Attribute("address").Value;
  198. Int32.TryParse(candidate.Attribute("port").Value, out m_remotePort);
  199. m_remoteUsername = candidate.Attribute("username").Value;
  200. m_remotePassword = candidate.Attribute("password").Value;
  201. }
  202. else if (sessionType.Value == "accept")
  203. {
  204. logger.Debug("XMPPPhoneSession session accept stanza was received.");
  205. m_payloads = (from pl in iq.Descendants(m_sessionNS + "session").Descendants(m_phoneNS + "description")
  206. .Descendants(m_phoneNS + "payload-type")
  207. select pl).ToList();
  208. Accepted(SDPToJingle.GetSDP(m_remoteIPAddress, m_remotePort, m_remoteUsername, m_remotePassword, m_payloads));
  209. }
  210. else if (sessionType.Value == "terminate")
  211. {
  212. logger.Debug("XMPPPhoneSession session terminate stanza was received.");
  213. Hungup();
  214. }
  215. }
  216. else
  217. {
  218. logger.Warn("XMPPPhoneSession session element was received with a missing type attribute.");
  219. logger.Warn(iq.ToString());
  220. }
  221. }
  222. else
  223. {
  224. logger.Warn("XMPPPhoneSession an iq element was received with no session element.");
  225. logger.Warn(iq.ToString());
  226. }
  227. }
  228. }
  229. }