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 agsXMPP.Net;
25 using agsXMPP.Xml.Dom;
26 using agsXMPP.protocol;
27 using agsXMPP.protocol.component;
28 using agsXMPP.Xml;
29 
30 
31 namespace agsXMPP
32 {
33 	/// <summary>
34     /// <para>
35 	/// use this class to write components that connect to a Jabebr/XMPP server
36     /// </para>
37     /// <para>
38     /// http://www.xmpp.org/extensions/xep-0114.html
39     /// </para>
40 	/// </summary>
41 	public class XmppComponentConnection : XmppConnection
42 	{
43         // This route stuff is old undocumented jabberd(2) stuff. hopefully we can get rid of this one day
44         // or somebody writes up and XEP
RouteHandler(object sender, Route r)45 		public delegate void RouteHandler	(object sender, Route r);
46 
47 		private bool						m_CleanUpDone;
48         private bool						m_StreamStarted;
49 
50 		#region << Constructors >>
51 		/// <summary>
52 		/// Creates a new Component Connection to a given server and port
53 		/// </summary>
XmppComponentConnection()54 		public XmppComponentConnection()
55 		{
56             m_IqGrabber = new IqGrabber(this);
57 		}
58 
59 		/// <summary>
60 		/// Creates a new Component Connection to a given server and port
61 		/// </summary>
62 		/// <param name="server">host/ip of the listening server</param>
63 		/// <param name="port">port the server listens for the connection</param>
XmppComponentConnection(string server, int port)64 		public XmppComponentConnection(string server, int port) : this()
65 		{
66 			base.Server		= server;
67 			base.Port		= port;
68 		}
69 
70 		/// <summary>
71 		/// Creates a new Component Connection to a given server, port and password (secret)
72 		/// </summary>
73 		/// <param name="server">host/ip of the listening server</param>
74 		/// <param name="port">port the server listens for the connection</param>
75 		/// <param name="password">password</param>
XmppComponentConnection(string server, int port, string password)76 		public XmppComponentConnection(string server, int port, string password) : this(server,port)
77 		{
78 			this.Password	= password;
79 		}
80 		#endregion
81 
82 		#region << Properties and Member Variables >>
83 
84 		private		string			m_Password			= null;
85 		private		bool			m_Authenticated		= false;
86 		private		Jid				m_ComponentJid		= null;
87         private     IqGrabber       m_IqGrabber         = null;
88 
89 		public string Password
90 		{
91 			get { return m_Password; }
92 			set { m_Password = value; }
93 		}
94 
95 		/// <summary>
96 		/// Are we Authenticated to the server? This is readonly and set by the library
97 		/// </summary>
98 		public bool Authenticated
99 		{
100 			get { return m_Authenticated; }
101 		}
102 
103 		/// <summary>
104 		/// The Domain of the component.
105 		/// <para>
106 		/// eg: <c>jabber.ag-software.de</c>
107 		/// </para>
108 		/// </summary>
109 		public Jid ComponentJid
110 		{
111 			get { return m_ComponentJid; }
112 			set { m_ComponentJid = value; }
113 		}
114 
115         public IqGrabber IqGrabber
116         {
117             get { return m_IqGrabber; }
118             set { m_IqGrabber = value; }
119         }
120 		#endregion
121 
122 		#region << Events >>
123 		// public event ErrorHandler			OnError;
124 
125 		/// <summary>
126 		/// connection is authenticated now and ready for receiving Route, Log and Xdb Packets
127 		/// </summary>
128 		public event ObjectHandler			OnLogin;
129 
130 		public event ObjectHandler			OnClose;
131 
132 		/// <summary>
133 		/// handler for incoming routet packtes from the server
134 		/// </summary>
135 		public event RouteHandler			OnRoute;
136 
137         /// <summary>
138         /// Event that occurs on authentication errors
139         /// e.g. wrong password, user doesnt exist etc...
140         /// </summary>
141         public event XmppElementHandler     OnAuthError;
142 
143         /// <summary>
144         /// Stream errors &lt;stream:error/&gt;
145         /// </summary>
146         public event XmppElementHandler     OnStreamError;
147 
148         /// <summary>
149         /// Event occurs on Socket Errors
150         /// </summary>
151         public event ErrorHandler           OnSocketError;
152 
153         /// <summary>
154         ///
155         /// </summary>
156         public event IqHandler              OnIq;
157 
158         /// <summary>
159         /// We received a message. This could be a chat message, headline, normal message or a groupchat message.
160         /// There are also XMPP extension which are embedded in messages.
161         /// e.g. X-Data forms.
162         /// </summary>
163         public event MessageHandler         OnMessage;
164 
165         /// <summary>
166         /// We received a presence from a contact or chatroom.
167         /// Also subscriptions is handles in this event.
168         /// </summary>
169         public event PresenceHandler        OnPresence;
170 
171 		#endregion
172 
Open()173 		public void Open()
174 		{
175 			_Open();
176 		}
177 
178 		/// <summary>
179 		///
180 		/// </summary>
181 		/// <param name="server"></param>
182 		/// <param name="port"></param>
Open(string server, int port)183 		public void Open(string server, int port)
184 		{
185 			this.Server	= server;
186 			this.Port	= port;
187 			_Open();
188 		}
189 
_Open()190 		private void _Open()
191 		{
192             m_CleanUpDone   = false;
193             m_StreamStarted = false;
194 
195             if (ConnectServer == null)
196                 SocketConnect(base.Server, base.Port);
197             else
198                 SocketConnect(this.ConnectServer, base.Port);
199 		}
200 
SendOpenStream()201 		private void SendOpenStream()
202 		{
203 			// <stream:stream
204 			// xmlns='jabber:component:accept'
205 			// xmlns:stream='http://etherx.jabber.org/streams'
206 			// to='shakespeare.lit'>
207 			StringBuilder sb = new StringBuilder();
208 
209 			//sb.Append("<?xml version='1.0'?>");
210 			sb.Append("<stream:stream");
211 
212 			if (m_ComponentJid!=null)
213 				sb.Append(" to='" + m_ComponentJid.ToString() + "'");
214 
215 			sb.Append(" xmlns='" + Uri.ACCEPT + "'");
216 			sb.Append(" xmlns:stream='" + Uri.STREAM + "'");
217 
218 			sb.Append(">");
219 
220 			Open(sb.ToString());
221 		}
222 
Login()223 		private void Login()
224 		{
225 			// Send Handshake
226 			Send( new Handshake(this.m_Password, this.StreamId) );
227 		}
228 
229 		#region << Stream Parser events >>
StreamParserOnStreamStart(object sender, Node e)230 		public override void StreamParserOnStreamStart(object sender, Node e)
231 		{
232 			base.StreamParserOnStreamStart (sender, e);
233 
234             m_StreamStarted = true;
235 
236             Login();
237 		}
238 
StreamParserOnStreamEnd(object sender, Node e)239 		public override void StreamParserOnStreamEnd(object sender, Node e)
240 		{
241 			base.StreamParserOnStreamEnd (sender, e);
242 
243 			if(!m_CleanUpDone)
244 				CleanupSession();
245 		}
246 
StreamParserOnStreamElement(object sender, ElementEventArgs eventArgs)247         public override void StreamParserOnStreamElement(object sender, ElementEventArgs eventArgs)
248 		{
249             base.StreamParserOnStreamElement (sender, eventArgs);
250             var e = eventArgs.Element;
251 
252 			if (e is Handshake)
253 			{
254 				m_Authenticated = true;
255 
256 				if (OnLogin != null)
257 					OnLogin(this);
258 
259                 if (KeepAlive)
260                     CreateKeepAliveTimer();
261                 eventArgs.Handled  = true;
262 			}
263 			else if (e is Route)
264 			{
265 				if (OnRoute != null)
266 					OnRoute(this, e as Route);
267                 eventArgs.Handled = true;
268 			}
269             else if (e is protocol.Error)
270             {
271                 protocol.Error streamErr = e as protocol.Error;
272                 switch (streamErr.Condition)
273                 {
274                     // Auth errors are important for the users here, so throw catch auth errors
275                     // in a separate event here
276                     case agsXMPP.protocol.StreamErrorCondition.NotAuthorized:
277                         // Authentication Error
278                         if (OnAuthError != null)
279                             OnAuthError(this, e as Element);
280                         break;
281                     default:
282                         if (OnStreamError != null)
283                             OnStreamError(this, e as Element);
284                         break;
285                 }
286                 eventArgs.Handled = true;
287             }
288             else if (e is Message)
289             {
290                 if (OnMessage != null) {
291                     OnMessage(this, e as Message);
292                     eventArgs.Handled = true;
293                 }
294             }
295             else if (e is Presence)
296             {
297                 if (OnPresence != null) {
298                     OnPresence(this, e as Presence);
299                     eventArgs.Handled = true;
300                 }
301             }
302             else if (e is IQ)
303             {
304                 if (OnIq != null) {
305                     var iqEventArgs = new protocol.client.IQEventArgs((IQ)e);
306                     OnIq(this, iqEventArgs);
307                     if (iqEventArgs.Handled) {
308                         eventArgs.Handled = true;
309                     }
310                 }
311             }
312 
313 		}
314 
m_StreamParser_OnStreamError(object sender, Exception ex)315 		private void m_StreamParser_OnStreamError(object sender, Exception ex)
316 		{
317 			if(!m_CleanUpDone)
318 				CleanupSession();
319 		}
320 		#endregion
321 
322 		#region << ClientSocket Events >>
SocketOnConnect(object sender)323 		public override void SocketOnConnect(object sender)
324 		{
325 			base.SocketOnConnect (sender);
326 
327 			SendOpenStream();
328 		}
329 
SocketOnDisconnect(object sender)330 		public override void SocketOnDisconnect(object sender)
331 		{
332 			base.SocketOnDisconnect (sender);
333 
334 			if(!m_CleanUpDone)
335 				CleanupSession();
336 		}
337 
SocketOnError(object sender, Exception ex)338         public override void SocketOnError(object sender, Exception ex)
339         {
340             base.SocketOnError(sender, ex);
341 
342             if (m_StreamStarted && !m_CleanUpDone)
343                 CleanupSession();
344 
345             if (OnSocketError != null)
346                 OnSocketError(this, ex);
347 
348         }
349 		#endregion
350 
Send(Element e)351         public override void Send(Element e)
352         {
353             // this is a hack to not send the xmlns="jabber:component:accept" with all packets
354             Element dummyEl = new Element("a");
355             dummyEl.Namespace = Uri.ACCEPT;
356 
357             dummyEl.AddChild(e);
358             string toSend = dummyEl.ToString();
359 
360             Send(toSend.Substring(35, toSend.Length - 35 - 4));
361         }
362 
CleanupSession()363 		private void CleanupSession()
364 		{
365             // This cleanup has only to be done if we were able to connect and teh XMPP Stream was started
366             DestroyKeepAliveTimer();
367 			m_CleanUpDone = true;
368 			StreamParser.Reset();
369 
370             m_IqGrabber.Clear();
371 
372 			if (OnClose!=null)
373 				OnClose(this);
374 		}
375 	}
376 }
377