PageRenderTime 37ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

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

#
C# | 292 lines | 196 code | 41 blank | 55 comment | 18 complexity | 2fef776b9a4af2e5afc3e7a52a1950ac MD5 | raw file
  1. //-----------------------------------------------------------------------------
  2. // Filename: XMPPStream.cs
  3. //
  4. // Description: Represents the XML stream to read and write XML stanzas.
  5. //
  6. // History:
  7. // 14 Nov 2010 Aaron Clauson Created.
  8. //
  9. // License:
  10. // This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
  11. //
  12. // Copyright (c) 2010 Aaron Clauson (aaron@sipsorcery.com), Hobart, Tasmanian, Australia (www.sipsorcery.com)
  13. // All rights reserved.
  14. //
  15. // Redistribution and use in source and binary forms, with or without modification, are permitted provided that
  16. // the following conditions are met:
  17. //
  18. // Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  19. // Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
  20. // disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of SIP Sorcery.
  21. // nor the names of its contributors may be used to endorse or promote products derived from this software without specific
  22. // prior written permission.
  23. //
  24. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
  25. // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  26. // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  27. // OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
  28. // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  29. // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  30. // POSSIBILITY OF SUCH DAMAGE.
  31. //-----------------------------------------------------------------------------
  32. using System;
  33. using System.Collections.Generic;
  34. using System.IO;
  35. using System.Linq;
  36. using System.Text;
  37. using System.Threading;
  38. using System.Xml;
  39. using System.Xml.Linq;
  40. using System.Xml.Schema;
  41. using SIPSorcery.Sys;
  42. using log4net;
  43. namespace SIPSorcery.XMPP
  44. {
  45. public class XMPPStream
  46. {
  47. public const string STREAM_ELEMENT_NAME = "stream";
  48. public const string SASL_AUTH_ELEMENT_NAME = "auth";
  49. public const string BIND_ELEMENT_NAME = "bind";
  50. public const string IQ_ELEMENT_NAME = "iq";
  51. public const string RESOURCE_ELEMENT_NAME = "resource";
  52. public const string MECHANISMS_ELEMENT_NAME = "mechanisms";
  53. public const string STREAM_PREFIX = "stream";
  54. public const string STREAM_NAMESPACE = "http://etherx.jabber.org/streams";
  55. public const string STREAM_PARAMS_NAMESPACE = "urn:ietf:params:xml:ns:xmpp-streams";
  56. public const string SASL_NAMESPACE = "urn:ietf:params:xml:ns:xmpp-sasl";
  57. public const string BIND_NAMESPACE = "urn:ietf:params:xml:ns:xmpp-bind";
  58. public const string GOOGLE_SESSION_NAMESPACE = "http://www.google.com/session";
  59. public const string GOOGLE_PHONE_SESSION_NAMESPACE = "http://www.google.com/session/phone";
  60. public const string TRANSPORT_NAMESPACE = "http://www.google.com/transport/p2p";
  61. public const int MAJOR_VERSION = 1;
  62. public const int MINOR_VERSION = 0;
  63. #if !SILVERLIGHT
  64. private static XmlSchemaSet m_xmppSchemaSet = new XmlSchemaSet();
  65. #endif
  66. private static ILog logger = AppState.logger;
  67. protected static XNamespace m_streamNS = STREAM_NAMESPACE;
  68. protected static XNamespace StreamParamsNS = STREAM_PARAMS_NAMESPACE;
  69. protected static XNamespace JabberClientNS = XMPPConstants.JABBER_NAMESPACE;
  70. protected Stream NetworkStream;
  71. protected XmlWriter XmlWriter;
  72. //private XmlReader m_xmlReader;
  73. protected bool IsTLS;
  74. protected bool IsAuthenticated;
  75. public string JID;
  76. private string m_toDomain;
  77. private string m_fromUsername;
  78. protected string StreamError;
  79. protected List<XMPPStreamFeature> Features;
  80. protected bool Exit;
  81. protected string SASLToken;
  82. // Stream attributes from server.
  83. protected string StreamID;
  84. private decimal m_serverVersion;
  85. private string m_serverLanguage;
  86. private string m_serverNamespace;
  87. private string m_serverFrom;
  88. private string m_serverTo;
  89. public event Action<XElement> ElementReceived;
  90. static XMPPStream()
  91. {
  92. //m_xmppSchemaSet.Add(JABBER_NAMESPACE, "jabber.xsd");
  93. }
  94. public XMPPStream(Stream stream)
  95. {
  96. NetworkStream = stream;
  97. }
  98. public void Start(string toDomain, string saslToken, string fromUsername)
  99. {
  100. m_toDomain = toDomain;
  101. SASLToken = saslToken;
  102. m_fromUsername = fromUsername;
  103. XmlWriterSettings xws = new XmlWriterSettings();
  104. if (IsTLS)
  105. {
  106. xws.OmitXmlDeclaration = true;
  107. }
  108. xws.Indent = true;
  109. xws.NewLineHandling = NewLineHandling.None;
  110. xws.Encoding = new UTF8Encoding(false);
  111. xws.CloseOutput = false;
  112. XmlWriter = XmlWriter.Create(NetworkStream, xws);
  113. SendStreamHeader(XmlWriter);
  114. Listen();
  115. }
  116. protected void SendStreamHeader(XmlWriter xmlWriter)
  117. {
  118. xmlWriter.WriteStartElement(STREAM_PREFIX, STREAM_ELEMENT_NAME, STREAM_NAMESPACE);
  119. if (IsAuthenticated)
  120. {
  121. xmlWriter.WriteAttributeString("from", m_fromUsername);
  122. }
  123. xmlWriter.WriteAttributeString("to", m_toDomain);
  124. xmlWriter.WriteAttributeString("version", MAJOR_VERSION + "." + MINOR_VERSION);
  125. xmlWriter.WriteAttributeString("xml", "lang", null, "en");
  126. xmlWriter.WriteAttributeString("xmlns", XMPPConstants.JABBER_NAMESPACE);
  127. xmlWriter.WriteWhitespace("\n");
  128. xmlWriter.Flush();
  129. //Console.WriteLine("stream initialisation element sent (from=" + m_fromUsername + ".");
  130. }
  131. private void Listen()
  132. {
  133. //Console.WriteLine("Staring listener...");
  134. XmlReaderSettings xrs = new XmlReaderSettings();
  135. //xrs.ConformanceLevel = ConformanceLevel.Fragment;
  136. xrs.CloseInput = false;
  137. //xrs.IgnoreWhitespace = true;
  138. //xrs.IgnoreComments = true;
  139. //xrs.XmlResolver = null;
  140. #if !SILVERLIGHT
  141. xrs.ValidationType = ValidationType.None;
  142. xrs.Schemas = m_xmppSchemaSet;
  143. xrs.ValidationEventHandler += new ValidationEventHandler(ValidationCallBack);
  144. #endif
  145. using (XmlReader xmlReader = XmlReader.Create(NetworkStream, xrs))
  146. {
  147. //Console.WriteLine("XML reader ready.");
  148. while (!Exit && xmlReader.Read())
  149. {
  150. //Console.WriteLine("Read " + xmlReader.NodeType + " " + xmlReader.Name);
  151. if (xmlReader.NodeType == XmlNodeType.Element)
  152. {
  153. if (xmlReader.Name == STREAM_PREFIX + ":" + STREAM_ELEMENT_NAME)
  154. {
  155. //Console.WriteLine("stream initialisation node received.");
  156. LoadStreamAttributes(xmlReader);
  157. }
  158. else
  159. {
  160. XElement streamDocElement = XElement.Load(xmlReader.ReadSubtree());
  161. //Console.WriteLine(streamDocElement.Name.LocalName);
  162. //Console.WriteLine(streamDocElement.ToString());
  163. switch (streamDocElement.Name.LocalName)
  164. {
  165. case "error":
  166. StreamError = streamDocElement.Element(StreamParamsNS + "text").Value;
  167. //throw new ApplicationException(m_streamError);
  168. logger.Warn("XMPPStream error: " + StreamError);
  169. break;
  170. case "features":
  171. //streamDocElement.Nodes().ToList().ForEach(x => Console.WriteLine(x.ToString()));
  172. Features = (from feature in streamDocElement.Nodes() select new XMPPStreamFeature((XElement)feature)).ToList();
  173. //Features.ForEach(x => Console.WriteLine("feature received " + x.Name + ", is required " + x.IsRequired + "."));
  174. break;
  175. default:
  176. break;
  177. }
  178. ElementReceived(streamDocElement);
  179. }
  180. }
  181. else if (xmlReader.NodeType == XmlNodeType.EndElement && xmlReader.Name == STREAM_PREFIX + ":" + STREAM_ELEMENT_NAME)
  182. {
  183. //Console.WriteLine("Server closed the stream.");
  184. Close();
  185. break;
  186. }
  187. if (!Exit)
  188. {
  189. //Console.WriteLine("xml reader next read...");
  190. }
  191. }
  192. //m_xmlWriter.Close();
  193. }
  194. //Console.WriteLine("Listen exiting.");
  195. }
  196. public void Close()
  197. {
  198. logger.Debug("Closing XMPPStream.");
  199. XmlWriter.WriteEndDocument();
  200. XmlWriter.Flush();
  201. }
  202. protected void WriteElement(XElement element)
  203. {
  204. element.WriteTo(XmlWriter);
  205. XmlWriter.Flush();
  206. }
  207. private void LoadStreamAttributes(XmlReader xmlReader)
  208. {
  209. if (xmlReader.HasAttributes)
  210. {
  211. //Console.WriteLine("Attributes of <" + xmlReader.Name + ">");
  212. while (xmlReader.MoveToNextAttribute())
  213. {
  214. string attributeName = xmlReader.Name;
  215. string attributeValue = xmlReader.Value;
  216. //Console.WriteLine(" {0}={1}", attributeName, attributeValue);
  217. switch (attributeName.ToLower())
  218. {
  219. case "id":
  220. StreamID = attributeValue;
  221. break;
  222. case "from":
  223. m_serverFrom = attributeValue;
  224. break;
  225. case "to":
  226. m_serverTo = attributeValue;
  227. break;
  228. case "version":
  229. Decimal.TryParse(attributeValue, out m_serverVersion);
  230. break;
  231. case "xmlns":
  232. m_serverNamespace = attributeValue;
  233. break;
  234. case "xmlns:stream":
  235. break;
  236. default:
  237. logger.Warn("Stream attribute " + attributeName + " was not recognised.");
  238. break;
  239. }
  240. }
  241. }
  242. }
  243. #if !SILVERLIGHT
  244. // Display any validation errors.
  245. private static void ValidationCallBack(object sender, ValidationEventArgs e)
  246. {
  247. logger.Warn(String.Format("Validation Error: {0}", e.Message));
  248. }
  249. #endif
  250. }
  251. }