1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2  * Copyright (c) 2003-2012 by AG-Software 											 *
3  * All Rights Reserved.																 *
4  * Contact information for AG-Software is available at http://www.ag-software.de	 *
5  *																					 *
6  * Licence:																			 *
7  * The agsXMPP SDK is released under a dual licence									 *
8  * agsXMPP can be used under either of two licences									 *
9  * 																					 *
10  * A commercial licence which is probably the most appropriate for commercial 		 *
11  * corporate use and closed source projects. 										 *
12  *																					 *
13  * The GNU Public License (GPL) is probably most appropriate for inclusion in		 *
14  * other open source projects.														 *
15  *																					 *
16  * See README.html for details.														 *
17  *																					 *
18  * For general enquiries visit our website at:										 *
19  * http://www.ag-software.de														 *
20  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
21 
22 using System;
23 using System.Text;
24 using System.Threading;
25 
26 using agsXMPP.Net;
27 using agsXMPP.protocol.extensions.bosh;
28 
29 using agsXMPP.Xml;
30 using agsXMPP.Xml.Dom;
31 using agsXMPP.Idn;
32 
33 namespace agsXMPP
34 {
XmlHandler(object sender, string xml)35 	public delegate void XmlHandler			(object sender, string xml);
ErrorHandler(object sender, Exception ex)36     public delegate void ErrorHandler       (object sender, Exception ex);
37 
XmppConnectionStateHandler(object sender, XmppConnectionState state)38     public delegate void XmppConnectionStateHandler	(object sender, XmppConnectionState state);
39 	/// <summary>
40 	/// abstract base class XmppConnection.
41 	/// </summary>
42 	public abstract class XmppConnection
43 	{
44 
45         private Timer m_KeepaliveTimer = null;
46 
47 		#region << Events >>
48 		/// <summary>
49 		/// This event just informs about the current state of the XmppConnection
50 		/// </summary>
51 		public event XmppConnectionStateHandler OnXmppConnectionStateChanged;
52 
53 		/// <summary>
54 		/// a XML packet or text is received.
55 		/// This are no winsock events. The Events get generated from the XML parser
56 		/// </summary>
57 		public event XmlHandler					OnReadXml;
58 		/// <summary>
59 		/// XML or Text is written to the Socket this includes also the keep alive packages (a single space)
60 		/// </summary>
61 		public event XmlHandler					OnWriteXml;
62 
63         public event ErrorHandler               OnError;
64 
65         /// <summary>
66         /// Data received from the Socket
67         /// </summary>
68         public event BaseSocket.OnSocketDataHandler OnReadSocketData;
69 
70         /// <summary>
71         /// Data was sent to the socket for sending
72         /// </summary>
73         public event BaseSocket.OnSocketDataHandler OnWriteSocketData;
74 
75 		#endregion
76 
77 		#region << Constructors >>
XmppConnection()78 		public XmppConnection()
79 		{
80 			InitSocket();
81 			// Streamparser stuff
82 			m_StreamParser = new StreamParser();
83 
84             m_StreamParser.OnStreamStart		+= new StreamHandler(StreamParserOnStreamStart);
85 			m_StreamParser.OnStreamEnd			+= new StreamHandler(StreamParserOnStreamEnd);
86             m_StreamParser.OnStreamElement += StreamParserOnStreamElement;
87             m_StreamParser.StreamElementNotHandled += StreamParserStreamElementNotHandled;
88 			m_StreamParser.OnStreamError		+= new StreamError	(StreamParserOnStreamError);
89             m_StreamParser.OnError              += new ErrorHandler (StreamParserOnError);
90 		}
91 
XmppConnection(agsXMPP.Net.SocketConnectionType type)92 		public XmppConnection(agsXMPP.Net.SocketConnectionType type) : this()
93 		{
94 			m_SocketConnectionType = agsXMPP.Net.SocketConnectionType.Direct;
95 		}
96 		#endregion
97 
98 		#region << Properties and Member Variables >>
99 		private		int						m_Port					    = 5222;
100 		private		string					m_Server				    = null;
101 		private		string					m_ConnectServer			    = null;
102 		private		string					m_StreamId				    = "";
103 		private		string					m_StreamVersion			    = "1.0";
104 		private		XmppConnectionState		m_ConnectionState		    = XmppConnectionState.Disconnected;
105 		private		BaseSocket				m_ClientSocket			    = null;
106 		private		StreamParser			m_StreamParser			    = null;
107 		private		SocketConnectionType	m_SocketConnectionType	    = SocketConnectionType.Direct;
108         private     bool                    m_AutoResolveConnectServer  = true;
109         private     int                     m_KeepAliveInterval         = 120;
110         private     bool                    m_KeepAlive                 = true;
111 		/// <summary>
112 		/// The Port of the remote server for the connection
113 		/// </summary>
114 		public int Port
115 		{
116 			get	{ return m_Port; }
117 			set { m_Port = value; }
118 		}
119 
120 		/// <summary>
121 		/// domain or ip-address of the remote server for the connection
122 		/// </summary>
123 		public string Server
124 		{
125 			get { return m_Server; }
126             set
127             {
128 #if !STRINGPREP
129                 if (value != null)
130 				    m_Server = value.ToLower();
131                 else
132                     m_Server = null;
133 #else
134                 if (value != null)
135                     m_Server = Stringprep.NamePrep(value);
136                 else
137                     m_Server = null;
138 #endif
139             }
140 		}
141 
142 		/// <summary>
143 		///
144 		/// </summary>
145 		public string ConnectServer
146 		{
147 			get { return m_ConnectServer; }
148 			set { m_ConnectServer = value; }
149 		}
150 
151 		/// <summary>
152 		/// the id of the current xmpp xml-stream
153 		/// </summary>
154 		public string StreamId
155 		{
156 			get { return m_StreamId;  }
157 			set { m_StreamId = value; }
158 		}
159 
160 		/// <summary>
161 		/// Set to null for old Jabber Protocol without SASL andstream features
162 		/// </summary>
163 		public string StreamVersion
164 		{
165 			get { return m_StreamVersion; }
166 			set { m_StreamVersion = value; }
167 		}
168 
169 		public XmppConnectionState XmppConnectionState
170 		{
171 			get { return m_ConnectionState; }
172 		}
173 
174 		/// <summary>
175 		/// Read Online Property ClientSocket
176 		/// returns the SOcket object used for this connection
177 		/// </summary>
178 		public BaseSocket ClientSocket
179 		{
180 			get { return m_ClientSocket; }
181 		}
182 
183         /// <summary>
184         /// the underlaying XMPP StreamParser. Normally you don't need it, but we make it accessible for
185         /// low level access to the stream
186         /// </summary>
187 		public StreamParser StreamParser
188 		{
189 			get { return m_StreamParser; }
190 		}
191 
192 		public SocketConnectionType SocketConnectionType
193 		{
194 			get { return m_SocketConnectionType; }
195 			set
196 			{
197 				m_SocketConnectionType = value;
198 				InitSocket();
199 			}
200 		}
201 
202         public bool AutoResolveConnectServer
203         {
204             get { return m_AutoResolveConnectServer; }
205             set { m_AutoResolveConnectServer = value; }
206         }
207 
208         /// <summary>
209         /// <para>
210         /// the keep alive interval in seconds.
211         /// Default value is 120
212         /// </para>
213         /// <para>
214         /// Keep alive packets prevent disconenct on NAT and broadband connections which often
215         /// disconnect if they are idle.
216         /// </para>
217         /// </summary>
218         public int KeepAliveInterval
219         {
220             get
221             {
222                 return m_KeepAliveInterval;
223             }
224             set
225             {
226                 m_KeepAliveInterval = value;
227             }
228         }
229         /// <summary>
230         /// Send Keep Alives (for NAT)
231         /// </summary>
232         public bool KeepAlive
233         {
234             get
235             {
236                 return m_KeepAlive;
237             }
238             set
239             {
240                 m_KeepAlive = value;
241             }
242         }
243 		#endregion
244 
245 		#region << Socket handers >>
SocketOnConnect(object sender)246 		public virtual void SocketOnConnect(object sender)
247 		{
248 			DoChangeXmppConnectionState(XmppConnectionState.Connected);
249 		}
250 
SocketOnDisconnect(object sender)251 		public virtual void SocketOnDisconnect(object sender)
252 		{
253 
254 		}
255 
SocketOnReceive(object sender, byte[] data, int count)256 		public virtual void SocketOnReceive(object sender, byte[] data, int count)
257 		{
258 
259             if (OnReadSocketData != null)
260                 OnReadSocketData(sender, data, count);
261 
262             // put the received bytes to the parser
263             lock (this)
264             {
265                 StreamParser.Push(data, 0, count);
266 			}
267 		}
268 
SocketOnError(object sender, Exception ex)269         public virtual void SocketOnError(object sender, Exception ex)
270         {
271 
272         }
273 		#endregion
274 
275 		#region << StreamParser Events >>
StreamParserOnStreamStart(object sender, Node e)276 		public virtual void StreamParserOnStreamStart		(object sender, Node e)
277 		{
278             string xml = e.ToString().Trim();
279             xml = xml.Substring(0, xml.Length - 2) + ">";
280 
281             this.FireOnReadXml(this, xml);
282 
283             protocol.Stream st = (protocol.Stream)e;
284             if (st != null)
285             {
286                 m_StreamId = st.StreamId;
287                 m_StreamVersion = st.Version;
288             }
289 		}
290 
StreamParserOnStreamEnd(object sender, Node e)291 		public virtual void StreamParserOnStreamEnd			(object sender, Node e)
292 		{
293             Element tag = e as Element;
294 
295 			string qName;
296 			if (tag.Prefix == null)
297 				qName = tag.TagName;
298 			else
299 				qName = tag.Prefix + ":" + tag.TagName;
300 
301 			string xml = "</" + qName + ">";
302 
303 			this.FireOnReadXml(this, xml);
304 		}
305 
StreamParserStreamElementNotHandled(object sender, UnhandledElementEventArgs eventArgs)306         public virtual void StreamParserStreamElementNotHandled(object sender, UnhandledElementEventArgs eventArgs)
307         {
308         }
309 
StreamParserOnStreamElement(object sender, ElementEventArgs e)310         public virtual void StreamParserOnStreamElement(object sender, ElementEventArgs e)
311 		{
312             this.FireOnReadXml(this, e.Element.ToString());
313 		}
StreamParserOnStreamError(object sender, Exception ex)314 		public virtual void StreamParserOnStreamError		(object sender, Exception ex)
315 		{
316 		}
StreamParserOnError(object sender, Exception ex)317         public virtual void StreamParserOnError             (object sender, Exception ex)
318         {
319             FireOnError(sender, ex);
320         }
321 		#endregion
322 
DoChangeXmppConnectionState(XmppConnectionState state)323 		internal void DoChangeXmppConnectionState(XmppConnectionState state)
324 		{
325 			m_ConnectionState = state;
326 
327 			if (OnXmppConnectionStateChanged!=null)
328 				OnXmppConnectionStateChanged(this, state);
329 		}
330 
InitSocket()331 		private void InitSocket()
332 		{
333             if (m_ClientSocket != null) {
334                 m_ClientSocket.OnConnect    -= SocketOnConnect;
335                 m_ClientSocket.OnDisconnect -= SocketOnDisconnect;
336                 m_ClientSocket.OnReceive    -= SocketOnReceive;
337                 m_ClientSocket.OnError      -= SocketOnError;
338             }
339 			m_ClientSocket = null;
340 
341 			// Socket Stuff
342 			if (m_SocketConnectionType == agsXMPP.Net.SocketConnectionType.HttpPolling)
343 				m_ClientSocket= new PollClientSocket();
344             else if (m_SocketConnectionType == agsXMPP.Net.SocketConnectionType.Bosh)
345                 m_ClientSocket = new BoshClientSocket(this);
346             else
347                 m_ClientSocket = new ClientSocket();
348 
349             m_ClientSocket.OnConnect    += SocketOnConnect;
350             m_ClientSocket.OnDisconnect += SocketOnDisconnect;
351             m_ClientSocket.OnReceive    += SocketOnReceive;
352             m_ClientSocket.OnError      += SocketOnError;
353 		}
354 
355 		/// <summary>
356 		/// Starts connecting of the socket
357 		/// </summary>
SocketConnect()358 		public virtual void SocketConnect()
359 		{
360 			DoChangeXmppConnectionState(XmppConnectionState.Connecting);
361 			ClientSocket.Connect();
362 		}
363 
SocketConnect(string server, int port)364 		public void SocketConnect(string server, int port)
365 		{
366 			ClientSocket.Address	= server;
367 			ClientSocket.Port		= port;
368 			SocketConnect();
369 		}
370 
SocketDisconnect()371 		public void SocketDisconnect()
372 		{
373 			m_ClientSocket.Disconnect();
374 		}
375 
376 		/// <summary>
377 		/// Send a xml string over the XmppConnection
378 		/// </summary>
379 		/// <param name="xml"></param>
Send(string xml)380 		public void Send(string xml)
381 		{
382             FireOnWriteXml(this, xml);
383 			m_ClientSocket.Send(xml);
384 
385             if (OnWriteSocketData != null)
386                 OnWriteSocketData(this, Encoding.UTF8.GetBytes(xml), xml.Length);
387 
388             // reset keep alive timer if active to make sure the interval is always idle time from the last
389             // outgoing packet
390             if (m_KeepAlive && m_KeepaliveTimer != null)
391                 m_KeepaliveTimer.Change(m_KeepAliveInterval * 1000, m_KeepAliveInterval * 1000);
392 		}
393 
394 		/// <summary>
395 		/// Send a xml element over the XmppConnection
396 		/// </summary>
397 		/// <param name="e"></param>
Send(Element e)398 		public virtual void Send(Element e)
399 		{
400 			Send(e.ToString());
401 		}
402 
Open(string xml)403 		public void Open(string xml)
404 		{
405 			Send(xml);
406 		}
407 
408 		/// <summary>
409 		/// Send the end of stream
410 		/// </summary>
Close()411 		public virtual void Close()
412 		{
413 			Send("</stream:stream>");
414 		}
415 
FireOnReadXml(object sender, string xml)416 		protected void FireOnReadXml(object sender, string xml)
417 		{
418 			if (OnReadXml != null)
419 				OnReadXml(sender, xml);
420 		}
421 
FireOnWriteXml(object sender, string xml)422 		protected void FireOnWriteXml(object sender, string xml)
423 		{
424 			if (OnWriteXml != null)
425 				OnWriteXml(sender, xml);
426 		}
427 
FireOnError(object sender, Exception ex)428         protected void FireOnError(object sender, Exception ex)
429         {
430             if (OnError != null)
431                 OnError(sender, ex);
432         }
433 
434         #region << Keepalive Timer functions >>
CreateKeepAliveTimer()435         protected void CreateKeepAliveTimer()
436         {
437             // Create the delegate that invokes methods for the timer.
438             TimerCallback timerDelegate = new TimerCallback(KeepAliveTick);
439             int interval = m_KeepAliveInterval * 1000;
440             // Create a timer that waits x seconds, then invokes every x seconds.
441             m_KeepaliveTimer = new Timer(timerDelegate, null, interval, interval);
442         }
443 
DestroyKeepAliveTimer()444         protected void DestroyKeepAliveTimer()
445         {
446             if (m_KeepaliveTimer == null)
447                 return;
448 
449             m_KeepaliveTimer.Dispose();
450             m_KeepaliveTimer = null;
451         }
452 
KeepAliveTick(Object state)453         private void KeepAliveTick(Object state)
454         {
455             // Send a Space for Keep Alive
456             Send(" ");
457         }
458         #endregion
459 	}
460 }