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.IO;
24 using System.Text;
25 using System.Collections;
26 using System.Collections.Specialized;
27 using System.ComponentModel;
28 using System.Threading;
29 using System.Net;
30 using System.Net.Sockets;
31 
32 using agsXMPP.Xml;
33 using agsXMPP.Xml.Dom;
34 
35 using agsXMPP.protocol;
36 using agsXMPP.protocol.iq;
37 using agsXMPP.protocol.iq.auth;
38 using agsXMPP.protocol.iq.agent;
39 using agsXMPP.protocol.iq.disco;
40 using agsXMPP.protocol.iq.roster;
41 using agsXMPP.protocol.iq.register;
42 using agsXMPP.protocol.iq.version;
43 using agsXMPP.protocol.stream;
44 using agsXMPP.protocol.stream.feature.compression;
45 using agsXMPP.protocol.client;
46 using agsXMPP.protocol.tls;
47 
48 using agsXMPP.protocol.extensions.caps;
49 using agsXMPP.protocol.extensions.compression;
50 
51 using agsXMPP.Exceptions;
52 
53 using agsXMPP.Sasl;
54 using agsXMPP.Net;
55 using agsXMPP.Net.Dns;
56 
57 
58 using agsXMPP.Idn;
59 
60 namespace agsXMPP
61 {
ObjectHandler(object sender)62 	public delegate void ObjectHandler		(object sender);
XmppElementHandler(object sender, Element e)63 	public delegate void XmppElementHandler	(object sender, Element e);
64 
65 	/// <summary>
66 	/// Summary description for XmppClient.
67 	/// </summary>
68 	public class XmppClientConnection : XmppConnection
69 	{
70 
71         const string SRV_RECORD_PREFIX = "_xmpp-client._tcp.";
72 
73 		// Delegates
RosterHandler(object sender, RosterItem item)74 		public delegate void RosterHandler				(object sender, RosterItem item);
AgentHandler(object sender, Agent agent)75 		public delegate void AgentHandler				(object sender, Agent agent);
76 
77 		private SaslHandler					m_SaslHandler		= null;
78 
79 		private bool						m_CleanUpDone;
80         private bool                        m_StreamStarted;
81 
82         private SRVRecord[]                 _SRVRecords;
83         private SRVRecord                   _currentSRVRecord;
84 
85 
86 		#region << Properties and Member Variables >>
87         private     string                  m_ClientLanguage    = "en";
88         private     string                  m_ServerLanguage    = null;
89 		private		string					m_Username			= "";
90 		private		string					m_Password			= "";
91 		private		string					m_Resource			= "agsXMPP";
92 		private		string					m_Status			= "";
93 		private		int						m_Priority			= 5;
94 		private		ShowType				m_Show				= ShowType.NONE;
95 		private		bool					m_AutoRoster		= true;
96 		private		bool					m_AutoAgents		= true;
97         private     bool                    m_AutoPresence      = true;
98 #if !(CF || CF_2)
99         private     bool                    m_UseSso            = false;
100         internal    string                  m_KerberosPrincipal;
101 #endif
102 
103 		private		bool					m_UseSSL			= false;
104 #if (CF || CF_2) && !BCCRYPTO
105         private     bool                    m_UseStartTLS       = false;
106         private bool f_ForceStartTLS = false;
107 #else
108         private		bool					m_UseStartTLS		= true;
109         private bool f_ForceStartTLS = true;
110 #endif
111         private     bool                    m_UseCompression    = false;
112 		internal	bool					m_Binded			= false;
113 		private		bool					m_Authenticated		= false;
114 
115 		private		IqGrabber				m_IqGrabber			= null;
116 		private		MessageGrabber			m_MessageGrabber	= null;
117         private     PresenceGrabber         m_PresenceGrabber   = null;
118 		private		bool					m_RegisterAccount	= false;
119 		private		PresenceManager			m_PresenceManager;
120 		private		RosterManager			m_RosterManager;
121 
122 
123         private     Capabilities            m_Capabilities          = new Capabilities();
124         private     string                  m_ClientVersion         = "1.0";
125         private     bool                    m_EnableCapabilities    = false;
126 
127         private     DiscoInfo               m_DiscoInfo             = new DiscoInfo();
128 
129 
130         /// <summary>
131         /// The prefered Client Language Attribute
132         /// </summary>
133         /// <seealso cref="agsXMPP.protocol.Base.XmppPacket.Language"/>
134         public string ClientLanguage
135         {
136             get { return m_ClientLanguage; }
137             set { m_ClientLanguage = value; }
138         }
139 
140         /// <summary>
141         /// The language which the server decided to use.
142         /// </summary>
143         /// <seealso cref="agsXMPP.protocol.Base.XmppPacket.Language"/>
144         public string ServerLanguage
145         {
146             get { return m_ServerLanguage; }
147         }
148 
149 		/// <summary>
150 		/// the username that is used to authenticate to the xmpp server
151 		/// </summary>
152 		public string Username
153 		{
154 			get { return m_Username; }
155 			set
156             {
157                 // first Encode the user/node
158                 m_Username = value;
159 
160                 string tmpUser = Jid.EscapeNode(value);
161 #if !STRINGPREP
162                 if (value != null)
163 				    m_Username = tmpUser.ToLower();
164                 else
165                     m_Username = null;
166 #else
167                 if (value != null)
168                     m_Username = Stringprep.NodePrep(tmpUser);
169                 else
170                     m_Username = null;
171 #endif
172 
173             }
174 		}
175 
176 		/// <summary>
177 		/// the password that is used to authenticate to the xmpp server
178 		/// </summary>
179 		public string Password
180 		{
181 			get { return m_Password; }
182 			set	{ m_Password = value; }
183 		}
184 
185 		/// <summary>
186 		/// the resource for this connection each connection to the server with the same jid needs a unique resource.
187         /// You can also set <code>Resource = null</code> and the server will assign a random Resource for you.
188 		/// </summary>
189 		public string Resource
190 		{
191 			get { return m_Resource;  }
192 			set { m_Resource = value; }
193 		}
194 
195 		/// <summary>
196 		/// our XMPP id build from Username, Server and Resource Property (user@server/resourcee)
197 		/// </summary>
198 		public Jid MyJID
199 		{
200 			get
201 			{
202 				return BuildMyJid();
203 			}
204 		}
205 
206         /// <summary>
207         /// The status message of this connection which is sent with the presence packets.
208         /// </summary>
209         /// <remarks>
210         /// you have to call the method <b>SendMyPresence</b> to send your updated presence to the server.
211         /// </remarks>
212 		public string Status
213 		{
214 			get
215 			{
216 				return m_Status;
217 			}
218 			set
219 			{
220 				m_Status = value;
221 			}
222 		}
223 
224 		/// <summary>
225 		/// The priority of this connection send with the presence packets.
226         /// The OPTIONAL priority element contains non-human-readable XML character data that specifies the priority level
227         /// of the resource. The value MUST be an integer between -128 and +127. If no priority is provided, a server
228         /// SHOULD consider the priority to be zero.
229 		/// </summary>
230         /// <remarks>you have to call the method <b>SendMyPresence</b> to send your updated presence to the server.</remarks>
231 		public int Priority
232 		{
233 			get { return m_Priority; }
234 			set
235             {
236                 if ((value < -127) || (value > 127))
237                     throw new ArgumentException("The value MUST be an integer between -128 and +127");
238 
239                 m_Priority = value;
240 			}
241 		}
242 
243         /// <summary>
244         /// change the showtype.
245         /// </summary>
246         /// <remarks>you have to call the method <b>SendMyPresence</b> to send your updated presence to the server.</remarks>
247 		public ShowType Show
248 		{
249 			get { return m_Show; }
250 			set { m_Show = value; }
251 		}
252 
253         /// <summary>
254         /// If set to true then the Roster (contact list) is requested automatically after sucessful login.
255         /// Set this property to false if you don't want to receive your contact list, or request it manual.
256         /// To save bandwidth is makes sense to cache the contact list and don't receive it on each login.
257         /// </summary>
258         /// <remarks>default value is <b>true</b></remarks>
259 		public bool AutoRoster
260 		{
261 			get	{ return m_AutoRoster; }
262 			set	{ m_AutoRoster = value;	}
263 		}
264 
265         /// <summary>
266         /// Sends the presence Automatically after successful login.
267         /// This property works only in combination with AutoRoster (AutoRoster = true).
268         /// </summary>
269         public bool AutoPresence
270         {
271             get { return m_AutoPresence; }
272             set { m_AutoPresence = value; }
273         }
274 
275 		/// <summary>
276         /// If set to true then the Agents are requested automatically after sucessful login.
277         /// Set this property to false if you don't use agents at all, or if you request them manual.
278 		/// </summary>
279         /// <remarks>default value is <b>true</b></remarks>
280 		public bool AutoAgents
281 		{
282 			get	{ return m_AutoAgents; }
283 			set	{ m_AutoAgents = value;	}
284         }
285 
286 #if !(CF || CF_2)
287         /// <summary>
288         /// Use Single sign on (GSSAPI/KERBEROS)
289         /// </summary>
290         public bool UseSso
291         {
292             get { return m_UseSso; }
293             set
294             {
295                 if (Util.Runtime.IsMono() && Util.Runtime.IsUnix())
296                     throw new NotImplementedException();
297 
298                 m_UseSso = value;
299             }
300         }
301 
302         /// <summary>
303         /// Gets the kerberos principal.
304         /// </summary>
305         /// <value>The kerberos principal.</value>
306         public string KerberosPrincipal
307         {
308             get { return m_KerberosPrincipal; }
309             set { m_KerberosPrincipal = value; }
310         }
311 #endif
312 
313 
314         /// <summary>
315 		/// use "old style" ssl for this connection (Port 5223).
316 		/// </summary>
317         [Obsolete("Try to use ForceStartTls instead")]
318 		public bool UseSSL
319 		{
320 			get	{ return m_UseSSL; }
321 
322 #if SSL
323 			set
324 			{
325                 // Only one of both can be true
326 				m_UseSSL = value;
327                 if (value == true)
328                     m_UseStartTLS = false;
329 			}
330 #endif
331 		}
332 
333         public bool ForceStartTls {
334             get {
335                 return f_ForceStartTLS;
336             }
337             set {
338                 UseStartTLS = UseStartTLS || value;
339                 f_ForceStartTLS = value;
340             }
341         }
342 
343 		/// <summary>
344 		/// use Start-TLS on this connection when the server supports it. Make sure UseSSL is false when
345 		/// you want to use this feature.
346 		/// </summary>
347 		public bool UseStartTLS
348 		{
349 			get { return m_UseStartTLS; }
350 
351 #if SSL || BCCRYPTO || CF_2
352 			set
353 			{
354                 // Only one of both can be true
355 				m_UseStartTLS = value;
356                 if (value == true)
357                     m_UseSSL = false;
358 			}
359 #endif
360 		}
361 
362         /// <summary>
363         /// Use Stream compression to save bandwidth?
364         /// This should not be used in combination with StartTLS,
365         /// because TLS has build in compression (see RFC 2246, http://www.ietf.org/rfc/rfc2246.txt)
366         /// </summary>
367         public bool UseCompression
368         {
369             get { return m_UseCompression; }
370 			set	{ m_UseCompression = value;	}
371         }
372 
373 		/// <summary>
374 		/// Are we Authenticated to the server? This is readonly and set by the library
375 		/// </summary>
376 		public bool Authenticated
377 		{
378 			get { return m_Authenticated; }
379 		}
380 
381 		/// <summary>
382 		/// is the resource binded? This is readonly and set by the library
383 		/// </summary>
384 		public bool Binded
385 		{
386 			get { return m_Binded; }
387 		}
388 
389 		/// <summary>
390 		/// Should the library register a new account on the server
391 		/// </summary>
392 		public bool RegisterAccount
393 		{
394 			get { return m_RegisterAccount; }
395 			set { m_RegisterAccount = value; }
396 		}
397 
398 		public IqGrabber IqGrabber
399 		{
400 			get { return m_IqGrabber; }
401 		}
402 
403         public MessageGrabber MessageGrabber
404 		{
405 			get { return m_MessageGrabber; }
406 		}
407 
408         public PresenceGrabber PresenceGrabber
409         {
410             get { return m_PresenceGrabber; }
411         }
412 
413 		public RosterManager RosterManager
414 		{
415 			get { return m_RosterManager; }
416 		}
417 
418 		public PresenceManager PresenceManager
419 		{
420 			get { return m_PresenceManager; }
421 		}
422 
423         public bool EnableCapabilities
424         {
425             get { return m_EnableCapabilities; }
426             set { m_EnableCapabilities = value; }
427         }
428 
429         public string ClientVersion
430         {
431             get { return m_ClientVersion; }
432             set { m_ClientVersion = value; }
433         }
434 
435         public Capabilities Capabilities
436         {
437             get { return m_Capabilities; }
438             set { m_Capabilities = value; }
439         }
440 
441         public Capabilities ServerCapabilities { get; set; }
442 
443         /// <summary>
444         /// The DiscoInfo object is used to respond to DiscoInfo request if AutoAnswerDiscoInfoRequests == true in DisoManager objects,
445         /// it's also used to build the Caps version when EnableCapabilities is set to true.
446         /// <remarks>
447         /// When EnableCapailities == true call UpdateCapsVersion after each update of the DiscoInfo object
448         /// </remarks>
449         /// </summary>
450         public DiscoInfo DiscoInfo
451         {
452             get { return m_DiscoInfo; }
453             set { m_DiscoInfo = value; }
454         }
455 		#endregion
456 
457 		#region << Events >>
458 
459 		/// <summary>
460 		/// We are authenticated to the server now.
461 		/// </summary>
462 		public event ObjectHandler				OnLogin;
463 		/// <summary>
464 		/// This event occurs after the resource was binded
465 		/// </summary>
466 		public event ObjectHandler				OnBinded;
467 
468         /// <summary>
469         /// Event that occurs on bind errors
470         /// </summary>
471         public event XmppElementHandler         OnBindError;
472 
473         /// <summary>
474         /// This event is fired when we get register information.
475         /// You ca use this event for custom registrations.
476         /// </summary>
477         public event RegisterEventHandler       OnRegisterInformation;
478 
479         /// <summary>
480 		/// This event gets fired after a new account is registered
481 		/// </summary>
482 		public event ObjectHandler				OnRegistered;
483 
484 		/// <summary>
485 		/// This event ets fired after a ChangePassword Request was successful
486 		/// </summary>
487 		public event ObjectHandler				OnPasswordChanged;
488 
489 		/*
490         was never used, comment ot until we need it
491 		public event XmppElementHandler			OnXmppError;
492 		*/
493 
494 		/// <summary>
495 		/// Event that occurs on registration errors
496 		/// </summary>
497 		public event XmppElementHandler			OnRegisterError;
498 
499         /// <summary>
500         /// Event occurs on Xmpp Stream error elements
501         /// </summary>
502         public event XmppElementHandler         OnStreamError;
503 
504 		/// <summary>
505 		/// Event that occurs on authentication errors
506 		/// e.g. wrong password, user doesnt exist etc...
507 		/// </summary>
508 		public event XmppElementHandler			OnAuthError;
509 
510         /// <summary>
511         /// Event occurs on Socket Errors
512         /// </summary>
513         public event ErrorHandler               OnSocketError;
514 
515 		public event ObjectHandler				OnClose;
516 
517         public event EventHandler<SendingServiceUnavailableEventArgs> SendingServiceUnavailable;
518 
519 
520         /// <summary>
521         /// This event is raised when a response to a roster query is received. The roster query contains the contact list.
522         /// This lost could be very large and could contain hundreds of contacts. The are all send in a single XML element from
523         /// the server. Normally you show the contact list in a GUI control in you application (treeview, listview).
524         /// When this event occurs you couls Suspend the GUI for faster drawing and show change the mousepointer to the hourglass
525         /// </summary>
526         /// <remarks>see also OnRosterItem and OnRosterEnd</remarks>
527         public event ObjectHandler				OnRosterStart;
528 
529         /// <summary>
530         /// This event is raised when a response to a roster query is received. It notifies you that all RosterItems (contacts) are
531         /// received now.
532         /// When this event occurs you could Resume the GUI and show the normal mousepointer again.
533         /// </summary>
534         /// <remarks>see also OnRosterStart and OnRosterItem</remarks>
535         public event ObjectHandler				OnRosterEnd;
536 
537         /// <summary>
538         /// This event is raised when a response to a roster query is received. This event always contains a single RosterItem.
539         /// e.g. you have 150 friends on your contact list, then this event is called 150 times.
540         /// </summary>
541         /// <remarks>see also OnRosterItem and OnRosterEnd</remarks>
542         public event RosterHandler              OnRosterItem;
543 
544         /// <summary>
545         /// This event is raised when a response to an agents query which could contain multiple agentitems.
546         /// Normally you show the items in a GUI. This event could be used to suspend the UI for faster drawing.
547         /// </summary>
548         /// <remarks>see also OnAgentItem and OnAgentEnd</remarks>
549 		public event ObjectHandler				OnAgentStart;
550 
551         /// <summary>
552         /// This event is raised when a response to an agents query which could contain multiple agentitems.
553         /// Normally you show the items in a GUI. This event could be used to resume the suspended userinterface.
554         /// </summary>
555         /// <remarks>see also OnAgentStart and OnAgentItem</remarks>
556         public event ObjectHandler				OnAgentEnd;
557 
558         /// <summary>
559         /// This event returns always a single AgentItem from a agents query result.
560         /// This is from the old jabber protocol. Instead of agents Disco (Service Discovery) should be used in modern
561         /// application. But still lots of servers use Agents.
562         /// <seealso cref=""/>
563         /// </summary>
564         /// <remarks>see also OnAgentStart and OnAgentEnd</remarks>
565         public event AgentHandler				OnAgentItem;
566 
567         /// <summary>
568         ///
569         /// </summary>
570         public event IqHandler                  OnIq;
571 
572 		/// <summary>
573 		/// We received a message. This could be a chat message, headline, normal message or a groupchat message.
574         /// There are also XMPP extension which are embedded in messages.
575         /// e.g. X-Data forms.
576 		/// </summary>
577 		public event MessageHandler				OnMessage;
578 
579         /// <summary>
580         /// We received a presence from a contact or chatroom.
581         /// Also subscriptions is handles in this event.
582         /// </summary>
583         public event PresenceHandler			OnPresence;
584 
585         //public event ErrorHandler				OnError;
586 
587 		public event SaslEventHandler			OnSaslStart;
588 		public event ObjectHandler				OnSaslEnd;
589 
590 
591 		#endregion
592 
593 		#region << Constructors >>
XmppClientConnection()594 		public XmppClientConnection() : base()
595 		{
596 			m_IqGrabber			= new IqGrabber(this);
597 			m_MessageGrabber	= new MessageGrabber(this);
598             m_PresenceGrabber   = new PresenceGrabber(this);
599 			m_PresenceManager	= new PresenceManager(this);
600 			m_RosterManager		= new RosterManager(this);
601 		}
602 
XmppClientConnection(SocketConnectionType type)603 		public XmppClientConnection(SocketConnectionType type) : this()
604 		{
605 			base.SocketConnectionType = type;
606 		}
607 
608         /// <summary>
609         /// create a new XmppClientConnection with the given JabberId and password
610         /// </summary>
611         /// <param name="jid">JabberId (user@example.com)</param>
612         /// <param name="pass">password</param>
XmppClientConnection(Jid jid, string pass)613         public XmppClientConnection(Jid jid, string pass)
614             : this()
615         {
616             base.Server     = jid.Server;
617             this.Username   = jid.User;
618             this.Password   = pass;
619         }
620 
621         /// <summary>
622         /// create a new XmppClientConnection with the given server
623         /// Username and Password gets set later
624         /// </summary>
625         /// <param name="server"></param>
XmppClientConnection(string server)626 		public XmppClientConnection(string server) : this()
627 		{
628 			base.Server = server;
629 		}
630 
631         /// <summary>
632         /// create a new XmppClientConnection with the given server and port number
633         /// Username and Password gets set later
634         /// </summary>
635         /// <param name="server"></param>
XmppClientConnection(string server, int port)636 		public XmppClientConnection(string server, int port) : this(server)
637 		{
638 			base.Port = port;
639 		}
640 		#endregion
641 
642         /// <summary>
643         /// This method open the connections to the xmpp server and authenticates you to ther server.
644         /// This method is async, don't assume you are already connected when it returns. You have to wait for the OnLogin Event
645         /// </summary>
Open()646 		public void Open()
647 		{
648 			_Open();
649 		}
650 
651         /// <summary>
652         /// This method open the connections to the xmpp server and authenticates you to ther server.
653         /// This method is async, don't assume you are already connected when it returns. You have to wait for the OnLogin Event
654         /// </summary>
655         /// <param name="username">your username</param>
656         /// <param name="password">your password</param>
Open(string username, string password)657 		public void Open(string username, string password)
658 		{
659             this.Username   = username;
660             this.Password   = password;
661 
662 			_Open();
663 		}
664 
665         /// <summary>
666         /// This method open the connections to the xmpp server and authenticates you to ther server.
667         /// This method is async, don't assume you are already connected when it returns. You have to wait for the OnLogin Event
668         /// </summary>
669         /// <param name="username">your username</param>
670         /// <param name="password">your passowrd</param>
671         /// <param name="resource">resource for this connection</param>
Open(string username, string password, string resource)672 		public void Open(string username, string password, string resource)
673 		{
674 			this.m_Username = username;
675 			this.m_Password	= password;
676 			this.m_Resource	= resource;
677 			_Open();
678 		}
679 
680         /// <summary>
681         /// This method open the connections to the xmpp server and authenticates you to ther server.
682         /// This method is async, don't assume you are already connected when it returns. You have to wait for the OnLogin Event
683         /// </summary>
684         /// <param name="username">your username</param>
685         /// <param name="password">your password</param>
686         /// <param name="resource">resource for this connection</param>
687         /// <param name="priority">priority which will be sent with presence packets</param>
Open(string username, string password, string resource, int priority)688 		public void Open(string username, string password, string resource, int priority)
689 		{
690 			this.m_Username = username;
691 			this.m_Password	= password;
692 			this.m_Resource	= resource;
693 			this.m_Priority	= priority;
694 			_Open();
695 		}
696 
697         /// <summary>
698         /// This method open the connections to the xmpp server and authenticates you to ther server.
699         /// This method is async, don't assume you are already connected when it returns. You have to wait for the OnLogin Event
700         /// </summary>
701         /// <param name="username">your username</param>
702         /// <param name="password">your password</param>
703         /// <param name="priority">priority which will be sent with presence packets</param>
Open(string username, string password, int priority)704 		public void Open(string username, string password, int priority)
705 		{
706 			this.m_Username = username;
707 			this.m_Password	= password;
708 			this.m_Priority	= priority;
709 			_Open();
710 		}
711 
712 		#region << Socket handers >>
SocketOnConnect(object sender)713 		public override void SocketOnConnect(object sender)
714 		{
715 			base.SocketOnConnect(sender);
716 
717             SendStreamHeader(true);
718 		}
719 
SocketOnDisconnect(object sender)720 		public override void SocketOnDisconnect(object sender)
721 		{
722 			base.SocketOnDisconnect(sender);
723 
724 			if(!m_CleanUpDone)
725 				CleanupSession();
726 		}
727 
OnSendingServiceUnavailable(SendingServiceUnavailableEventArgs e)728         protected virtual void OnSendingServiceUnavailable(SendingServiceUnavailableEventArgs e)
729         {
730             if (SendingServiceUnavailable != null)
731                 SendingServiceUnavailable(this, e);
732         }
733 
SocketOnError(object sender, Exception ex)734         public override void SocketOnError(object sender, Exception ex)
735         {
736             base.SocketOnError(sender, ex);
737 
738             if ((ex.GetType() == typeof(ConnectTimeoutException)
739                 || (ex.GetType() == typeof(SocketException) && ((SocketException)ex).ErrorCode == 10061))
740                 && _SRVRecords != null
741                 && _SRVRecords.Length > 1)
742             {
743                 // connect failed. We are using SRV records and have multiple results.
744                 // remove the current record
745                 RemoveSrvRecord(_currentSRVRecord);
746                 // find and set a new record
747                 SetConnectServerFromSRVRecords();
748                 // connect again
749                 OpenSocket();
750             }
751             else
752             {
753                 // Fires the socket error
754                 if (OnSocketError != null)
755                     OnSocketError(this, ex);
756 
757                 // Only cleaneUp Session and raise on close if the stream already has started
758                 // if teh stream gets closed because of a socket error we have to raise both errors fo course
759                 if (m_StreamStarted && !m_CleanUpDone)
760                     CleanupSession();
761             }
762         }
763 		#endregion
764 
_Open()765 		private void _Open()
766 		{
767             m_CleanUpDone   = false;
768             m_StreamStarted = false;
769 
770 			StreamParser.Reset();
771 #if SSL
772 			if (ClientSocket.GetType() == typeof(ClientSocket))
773 				((ClientSocket) ClientSocket).SSL = m_UseSSL;
774 #endif
775             // this should start later
776             //if (m_KeepAlive)
777             //    CreateKeepAliveTimer();
778 
779             if (SocketConnectionType == SocketConnectionType.Direct && AutoResolveConnectServer)
780                 ResolveSrv();
781 
782             OpenSocket();
783 		}
784 
OpenSocket()785         private void OpenSocket()
786         {
787             if (ConnectServer == null)
788                 SocketConnect(base.Server, base.Port);
789             else
790                 SocketConnect(this.ConnectServer, base.Port);
791         }
792 
793         #region << SRV functions >>
794         /// <summary>
795         /// Resolves the connection host of a xmpp domain when SRV records are set
796         /// </summary>
ResolveSrv()797         private void ResolveSrv()
798         {
799 #if !(CF || CF_2)
800             try
801             {
802                 // get the machine's default DNS servers
803                 var dnsServers = IPConfigurationInformation.DnsServers;
804 
805                 if (dnsServers.Count > 0)
806                 {
807                     // Take the 1st DNS Server for our query
808                     IPAddress dnsServer = dnsServers[0];
809 
810                     string queryDomain = SRV_RECORD_PREFIX + Server;
811 
812                     _SRVRecords = Resolver.SRVLookup(queryDomain, dnsServer);
813 
814                     SetConnectServerFromSRVRecords();
815                 }
816             }
817             catch (Exception ex)
818             {
819                 FireOnError(this, ex);
820             }
821 #endif
822         }
823 
SetConnectServerFromSRVRecords()824         private void SetConnectServerFromSRVRecords()
825         {
826             // check we have a response
827             if (_SRVRecords != null && _SRVRecords.Length > 0)
828             {
829                 //SRVRecord srv = _SRVRecords[0];
830                 _currentSRVRecord = PickSRVRecord();
831 
832                 this.Port           = _currentSRVRecord.Port;
833                 this.ConnectServer  = _currentSRVRecord.Target;
834             }
835             else
836             {
837                 // no SRV-Records set
838                 _currentSRVRecord = null;
839                 this.ConnectServer = null;
840             }
841         }
842 
RemoveSrvRecord(SRVRecord rec)843         private void RemoveSrvRecord(SRVRecord rec)
844         {
845             int i = 0;
846             SRVRecord[] recs = new SRVRecord[_SRVRecords.Length - 1];
847             foreach (SRVRecord srv in _SRVRecords)
848             {
849                 if (!srv.Equals(rec))
850                 {
851                     recs[i] = srv;
852                     i++;
853                 }
854             }
855             _SRVRecords = recs;
856         }
857 
858         /// <summary>
859         /// Picks one of the SRV records.
860         /// priority and weight are evaluated by the following algorithm.
861         /// </summary>
862         /// <returns>SRVRecord</returns>
PickSRVRecord()863         private SRVRecord PickSRVRecord()
864         {
865             SRVRecord ret = null;
866 
867             // total weight of all servers with the same priority
868             int totalWeight = 0;
869 
870             // ArrayList for the servers with the lowest priority
871             ArrayList lowServers = new ArrayList();
872             // check we have a response
873             if (_SRVRecords != null && _SRVRecords.Length > 0)
874             {
875                 // Find server(s) with the highest priority (could be multiple)
876                 foreach (SRVRecord srv in _SRVRecords)
877                 {
878                     if (ret == null)
879                     {
880                         ret = srv;
881                         lowServers.Add(ret);
882                         totalWeight = ret.Weight;
883                     }
884                     else
885                     {
886                         if (srv.Priority == ret.Priority)
887                         {
888                             lowServers.Add(srv);
889                             totalWeight += srv.Weight;
890                         }
891                         else if (srv.Priority < ret.Priority)
892                         {
893                             // found a servr with a lower priority
894                             // clear the lowServers Array and start with this server
895                             lowServers.Clear();
896                             lowServers.Add(ret);
897                             ret = srv;
898                             totalWeight = ret.Weight;
899                         }
900                         else if (srv.Priority > ret.Priority)
901                         {
902                             // exit the loop, because servers are already sorted by priority
903                             break;
904                         }
905                     }
906                 }
907             }
908 
909             // if we have multiple lowServers then we have to pick a random one
910             // BUT we have too involve the weight which can be used for "Load Balancing" here
911             if (lowServers.Count > 1)
912             {
913                 if (totalWeight > 0)
914                 {
915                     // Create a random value between 1 - total Weight
916                     int rnd = new Random().Next(1, totalWeight);
917                     int i = 0;
918                     foreach (SRVRecord sr in lowServers)
919                     {
920                         if (rnd > i && rnd <= (i + sr.Weight))
921                         {
922                             ret = sr;
923                             break;
924                         }
925                         else
926                         {
927                             i += sr.Weight;
928                         }
929                     }
930                 }
931                 else
932                 {
933                     // Servers have no weight, they are all equal, pick a random server
934                     int rnd = new Random().Next(lowServers.Count);
935                     ret = (SRVRecord) lowServers[rnd];
936                 }
937             }
938 
939             return ret;
940         }
941 
942         #endregion
943 
SendStreamHeader(bool startParser)944         private void SendStreamHeader(bool startParser)
945         {
946             StringBuilder sb = new StringBuilder();
947 
948 
949             sb.Append("<stream:stream");
950             sb.Append(" to='" + base.Server + "'");
951             sb.Append(" xmlns='jabber:client'");
952             sb.Append(" xmlns:stream='http://etherx.jabber.org/streams'");
953 
954             if (StreamVersion != null)
955                 sb.Append(" version='" + StreamVersion + "'");
956 
957             if (m_ClientLanguage != null)
958                 sb.Append(" xml:lang='" + m_ClientLanguage + "'");
959 
960             // xml:lang="en"<stream:stream to="coversant.net" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams"  xml:lang="en" version="1.0" >
961             // sb.Append(" xml:lang='" + "en" + "' ");
962 
963 
964             sb.Append(">");
965 
966             Open(sb.ToString());
967         }
968 
969 
970 		/// <summary>
971 		/// Sends our Presence, the packet is built of Status, Show and Priority
972 		/// </summary>
SendMyPresence()973 		public void SendMyPresence()
974 		{
975             Presence pres = new Presence(m_Show, m_Status, m_Priority);
976 
977             // Add client caps when enabled
978             if (m_EnableCapabilities)
979             {
980                 if (m_Capabilities.Version == null)
981                     UpdateCapsVersion();
982 
983                 pres.AddChild(m_Capabilities);
984             }
985 
986             this.Send(pres);
987 		}
988 
989         /// <summary>
990         /// Sets the caps version automatically from the DiscoInfo object.
991         /// Call this member after each change of the DiscoInfo object
992         /// </summary>
UpdateCapsVersion()993         public void UpdateCapsVersion()
994         {
995             m_Capabilities.SetVersion(m_DiscoInfo);
996         }
997 
RequestLoginInfo()998 		internal void RequestLoginInfo()
999 		{
1000 			AuthIq iq = new AuthIq(IqType.get, new Jid(base.Server));
1001 			iq.Query.Username = this.m_Username;
1002 
1003             IqGrabber.SendIq(iq, OnGetAuthInfo);
1004 		}
1005 
1006 		/// <summary>
1007 		/// Changing the Password. You should use this function only when connected with SSL or TLS
1008 		/// because the password is sent in plain text over the connection.
1009 		/// </summary>
1010 		/// /// <remarks>
1011 		///		<para>
1012 		///			After this request was successful the new password is set automatically in the Username Property
1013 		///		</para>
1014 		/// </remarks>
1015 		/// <param name="newPass">value of the new password</param>
ChangePassword(string newPass)1016 		public void ChangePassword(string newPass)
1017 		{
1018 			/*
1019 
1020 			Example 10. Password Change
1021 			<iq type='set' to='somehost' id='change1'>
1022 			<query xmlns='jabber:iq:register'>
1023 				<username>bill</username>
1024 				<password>newpass</password>
1025 			</query>
1026 			</iq>
1027 
1028 			Because the password change request contains the password in plain text,
1029 			a client SHOULD NOT send such a request unless the underlying stream is encrypted
1030 			(using SSL or TLS) and the client has verified that the server certificate is signed
1031 			by a trusted certificate authority. A given domain MAY choose to disable password
1032 			changes if the stream is not properly encrypted, or to disable in-band password
1033 			changes entirely.
1034 
1035 			If the user provides an empty password element or a password element that contains
1036 			no XML character data (i.e., either <password/> or <password></password>),
1037 			the server or service MUST NOT change the password to a null value,
1038 			but instead MUST maintain the existing password.
1039 
1040 			Example 11. Host Informs Client of Successful Password Change
1041 
1042 			<iq type='result' id='change1'/>
1043 			*/
1044 
1045 			RegisterIq regIq  = new RegisterIq(IqType.set, new Jid(base.Server));
1046 			regIq.Query.Username = this.m_Username;
1047 			regIq.Query.Password = newPass;
1048 
1049             IqGrabber.SendIq(regIq,
1050                 (object sender, IQEventArgs e) => OnChangePasswordResult(e, newPass)
1051             );
1052 		}
1053 
1054 		/// <summary>
1055 		///
1056 		/// </summary>
1057 		/// <param name="sender"></param>
1058 		/// <param name="iq"></param>
1059 		/// <param name="data">contains the new password</param>
OnChangePasswordResult(IQEventArgs e, string newPass)1060         private void OnChangePasswordResult(IQEventArgs e, string newPass)
1061 		{
1062             if (e.IQ.Type == IqType.result)
1063 			{
1064 				if(OnPasswordChanged!=null)
1065 					OnPasswordChanged(this);
1066 
1067 				// Set the new password in the Password property on sucess
1068                 m_Password = newPass;
1069                 e.Handled = true;
1070 			}
1071             else if (e.IQ.Type == IqType.error)
1072 			{
1073 				/*
1074 				The server or service SHOULD NOT return the original XML sent in
1075 				IQ error stanzas related to password changes.
1076 
1077 				Example 12. Host Informs Client of Failed Password Change (Bad Request)
1078 
1079 				<iq type='error' from='somehost' to='user@host/resource' id='change1'>
1080 				<error code='400' type='modify'>
1081 					<conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
1082 				</error>
1083 				</iq>
1084 
1085 
1086 				Example 13. Host Informs Client of Failed Password Change (Not Authorized)
1087 
1088 				<iq type='error' from='somehost' to='user@host/resource' id='change1'>
1089 				<error code='401' type='cancel'>
1090 					<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
1091 				</error>
1092 				</iq>
1093 
1094 
1095 				Example 14. Server Informs Client of Failed Password Change (Not Allowed)
1096 
1097 				<iq type='error' from='somehost' to='user@host/resource' id='change1'>
1098 				<error code='405' type='cancel'>
1099 					<not-allowed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
1100 				</error>
1101 				</iq>
1102 
1103 				*/
1104 
1105 				if(OnRegisterError!=null)
1106                     OnRegisterError(this, e.IQ);
1107 			}
1108         }
1109 
1110         #region << Register new Account >>
1111 
1112 
1113         /// <summary>
1114         /// requests the registration fields
1115         /// </summary>
1116         /// <param name="obj">object which contains the features node which we need later for login again</param>
GetRegistrationFields(Element elem)1117         void GetRegistrationFields(Element elem)
1118         {
1119             // <iq type='get' id='reg1'>
1120             //  <query xmlns='jabber:iq:register'/>
1121             // </iq>
1122 
1123             RegisterIq regIq = new RegisterIq(IqType.get, new Jid(base.Server));
1124             IqGrabber.SendIq(regIq,
1125                 (object sender, IQEventArgs e) =>
1126                     OnRegistrationFieldsResult(e, elem)
1127             );
1128         }
1129 
OnRegistrationFieldsResult(IQEventArgs e, Element data)1130         void OnRegistrationFieldsResult(IQEventArgs e, Element data)
1131         {
1132             if (e.IQ.Type != IqType.error)
1133             {
1134                 if (e.IQ.Query is Register)
1135                 {
1136                     RegisterEventArgs args = new RegisterEventArgs(e.IQ.Query as Register);
1137                     if (OnRegisterInformation != null)
1138                         OnRegisterInformation(this, args);
1139 
1140                     DoChangeXmppConnectionState(XmppConnectionState.Registering);
1141 
1142                     IQ regIq = new IQ(IqType.set);
1143                     regIq.GenerateId();
1144                     regIq.To = new Jid(base.Server);
1145 
1146                     //RegisterIq regIq = new RegisterIq(IqType.set, new Jid(base.Server));
1147                     if (args.Auto)
1148                     {
1149                         Register reg = new Register(this.m_Username, this.m_Password);
1150                         regIq.Query = reg;
1151                     }
1152                     else
1153                     {
1154                         regIq.Query = args.Register;
1155                     }
1156                     IqGrabber.SendIq(regIq,
1157                         (object sender, IQEventArgs ev) => OnRegisterResult(ev, data)
1158                     );
1159                     e.Handled = true;
1160                 }
1161             }
1162             else
1163             {
1164                 if (OnRegisterError != null)
1165                     OnRegisterError(this, e.IQ);
1166                 e.Handled = true; // not really
1167             }
1168         }
1169 
OnRegisterResult(IQEventArgs e, Element data)1170         private void OnRegisterResult(IQEventArgs e, Element data)
1171 		{
1172 			/*
1173 			Example 6. Host Informs Entity of Failed Registration (Username Conflict)
1174 
1175 			<iq type='error' id='reg2'>
1176 			<query xmlns='jabber:iq:register'>
1177 				<username>bill</username>
1178 				<password>m1cro$oft</password>
1179 				<email>billg@bigcompany.com</email>
1180 			</query>
1181 			<error code='409' type='cancel'>
1182 				<conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
1183 			</error>
1184 			</iq>
1185 
1186 
1187 			Example 7. Host Informs Entity of Failed Registration (Some Required Information Not Provided)
1188 
1189 			<iq type='error' id='reg2'>
1190 			<query xmlns='jabber:iq:register'>
1191 				<username>bill</username>
1192 				<password>Calliope</password>
1193 			</query>
1194 			<error code='406' type='modify'>
1195 				<not-acceptable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
1196 			</error>
1197 			</iq>
1198 			*/
1199             if (e.IQ.Type == IqType.result)
1200             {
1201                 DoChangeXmppConnectionState(XmppConnectionState.Registered);
1202                 if (OnRegistered != null)
1203                     OnRegistered(this);
1204 
1205                 if (this.StreamVersion != null && this.StreamVersion.StartsWith("1."))
1206                 {
1207                     // init sasl login
1208                     InitSaslHandler();
1209                     var eventArgs = new ElementEventArgs(data);
1210                     m_SaslHandler.OnStreamElement(this, eventArgs);
1211                     if (eventArgs.Handled) {
1212                         e.Handled = true;
1213                     }
1214                 }
1215                 else
1216                 {
1217                     // old jabber style login
1218                     RequestLoginInfo();
1219                     e.Handled = true;
1220                 }
1221             }
1222             else if (e.IQ.Type == IqType.error)
1223             {
1224                 if (OnRegisterError != null)
1225                     OnRegisterError(this, e.IQ);
1226             }
1227         }
1228         #endregion
1229 
OnGetAuthInfo(object sender, IQEventArgs e)1230         private void OnGetAuthInfo(object sender, IQEventArgs e)
1231 		{
1232 			// We get smth like this and should add password (digest) and ressource
1233 			// Recv:<iq type="result" id="MX_7"><query xmlns="jabber:iq:auth"><username>gnauck</username><password/><digest/><resource/></query></iq>
1234 			// Send:<iq type='set' id='mx_login'>
1235 			//			<query xmlns='jabber:iq:auth'><username>gnauck</username><digest>27c05d464e3f908db3b2ca1729674bfddb28daf2</digest><resource>Office</resource></query>
1236 			//		</iq>
1237 			// Recv:<iq id="mx_login" type="result"/>
1238 
1239             e.Handled = true;
1240 
1241             var iq = e.IQ;
1242             if (iq.Error != null) {
1243                 FireOnAuthError(iq);
1244                 return;
1245             }
1246 
1247 			iq.GenerateId();
1248 			iq.SwitchDirection();
1249 			iq.Type = IqType.set;
1250 
1251 			Auth auth = (Auth) iq.Query;
1252 
1253 			auth.Resource = this.m_Resource;
1254 			auth.SetAuth(this.m_Username, this.m_Password, this.StreamId);
1255 
1256             IqGrabber.SendIq(iq, OnAuthenticate);
1257 		}
1258 
1259 		/// <summary>
1260 		/// Refreshes the myJid Member Variable
1261 		/// </summary>
BuildMyJid()1262 		private Jid BuildMyJid()
1263 		{
1264             Jid jid = new Jid(null);
1265 
1266             jid.m_User = m_Username;
1267             jid.m_Server = Server;
1268             jid.m_Resource = m_Resource;
1269 
1270             jid.BuildJid();
1271 
1272             return jid;
1273 		}
1274 
1275 		#region << RequestAgents >>
RequestAgents()1276 		public void RequestAgents()
1277 		{
1278 			AgentsIq iq = new AgentsIq(IqType.get, new Jid(base.Server));
1279             IqGrabber.SendIq(iq, OnAgents);
1280 		}
1281 
OnAgents(object sender, IQEventArgs e)1282         private void OnAgents(object sender, IQEventArgs e)
1283 		{
1284             e.Handled = true;
1285 			if (OnAgentStart != null)
1286 				OnAgentStart(this);
1287 
1288             Agents agents = e.IQ.Query as Agents;
1289 			if (agents != null)
1290 			{
1291 				foreach (Agent a in agents.GetAgents())
1292 				{
1293 					if (OnAgentItem != null)
1294 						OnAgentItem(this, a);
1295 				}
1296 			}
1297 
1298 			if (OnAgentEnd != null)
1299 				OnAgentEnd(this);
1300             e.Handled = true;
1301 		}
1302 		#endregion
1303 
1304 		#region << RequestRoster >>
RequestRoster()1305 		public void RequestRoster()
1306 		{
1307 			RosterIq iq = new RosterIq(IqType.get);
1308 			Send(iq);
1309 		}
1310 
OnRosterIQ(IQ iq)1311 		private void OnRosterIQ(IQ iq)
1312 		{
1313 			// if type == result then it must be the "FullRoster" we requested
1314 			// in this case we raise OnRosterStart and OnRosterEnd
1315 			//
1316 			// if type == set its a new added r updated rosteritem. Here we dont raise
1317 			// OnRosterStart and OnRosterEnd
1318 			if (iq.Type == IqType.result && OnRosterStart != null)
1319 				OnRosterStart(this);
1320 
1321 			Roster r = iq.Query as Roster;
1322 			if (r != null)
1323 			{
1324 				foreach (RosterItem i in r.GetRoster())
1325 				{
1326 					if (OnRosterItem != null)
1327 						OnRosterItem(this, i);
1328 				}
1329 			}
1330 
1331             if (iq.Type == IqType.result && OnRosterEnd != null)
1332                 OnRosterEnd(this);
1333 
1334             if (m_AutoPresence && iq.Type == IqType.result)
1335                 SendMyPresence();
1336 		}
1337 		#endregion
1338 
OnAuthenticate(object sender, IQEventArgs e)1339         private void OnAuthenticate(object sender, IQEventArgs e)
1340 		{
1341             if (e.IQ.Type == IqType.result)
1342 			{
1343                 m_Authenticated = true;
1344                 RaiseOnLogin();
1345                 e.Handled = true;
1346 			}
1347             else if(e.IQ.Type == IqType.error)
1348 			{
1349 				/*
1350 				 * <iq xmlns="jabber:client" id="agsXMPP_2" type="error">
1351 				 *		<query xmlns="jabber:iq:auth">
1352 				 *			<username>test</username>
1353 				 *			<digest sid="842070264">dc7e472abb95b65c2b75129ade607170be478b16</digest>
1354 				 *			<resource>MiniClient</resource>
1355 				 *		</query>
1356 				 *		<error code="401">Unauthorized</error>
1357 				 * </iq>
1358 				 *
1359 				 */
1360                 if (OnAuthError!=null)
1361                     OnAuthError(this, e.IQ);
1362 			}
1363 
1364 		}
1365 
FireOnAuthError(Element e)1366 		internal void FireOnAuthError(Element e)
1367 		{
1368 			if (OnAuthError!=null)
1369 				OnAuthError(this, e);
1370 		}
1371 
1372 		#region << StreamParser Events >>
StreamParserOnStreamStart(object sender, Node e)1373 		public override void StreamParserOnStreamStart(object sender, Node e)
1374 		{
1375 			base.StreamParserOnStreamStart(this, e);
1376 
1377             m_StreamStarted = true;
1378 
1379 			//m_CleanUpDone = false; moved that to _Open();
1380 
1381             protocol.Stream st = (protocol.Stream)e;
1382             if (st == null)
1383                 return;
1384 
1385             // Read the server language string
1386             m_ServerLanguage = st.Language;
1387 
1388 
1389 			// Auth stuff
1390 			if (!RegisterAccount)
1391 			{
1392 				if (this.StreamVersion != null && this.StreamVersion.StartsWith("1."))
1393 				{
1394 					if (!Authenticated)
1395 					{
1396 						// we assume server supports SASL here, because it advertised a StreamVersion 1.X
1397 						// and wait for the stream features and initialize the SASL Handler
1398                         InitSaslHandler();
1399 					}
1400 				}
1401 				else
1402 				{
1403 					// old auth stuff
1404 					RequestLoginInfo();
1405 				}
1406 			}
1407 			else
1408 			{
1409                 // Register on "old" jabber servers without stream features
1410                 if (this.StreamVersion == null)
1411                     GetRegistrationFields(null);
1412 			}
1413 
1414 		}
1415 
InitSaslHandler()1416         private void InitSaslHandler()
1417         {
1418             if (m_SaslHandler == null)
1419             {
1420                 m_SaslHandler = new SaslHandler(this);
1421                 m_SaslHandler.OnSaslStart += new SaslEventHandler(m_SaslHandler_OnSaslStart);
1422                 m_SaslHandler.OnSaslEnd += new ObjectHandler(m_SaslHandler_OnSaslEnd);
1423             }
1424         }
1425 
StreamParserOnStreamEnd(object sender, Node e)1426 		public override void StreamParserOnStreamEnd(object sender, Node e)
1427 		{
1428 			base.StreamParserOnStreamEnd(sender, e);
1429 
1430 			if (!m_CleanUpDone)
1431 				CleanupSession();
1432 		}
1433 
StreamParserOnStreamElement(object sender, ElementEventArgs eventArgs)1434         public override void StreamParserOnStreamElement(object sender, ElementEventArgs eventArgs)
1435 		{
1436             base.StreamParserOnStreamElement(sender, eventArgs);
1437             bool handled = false;
1438             var e = eventArgs.Element;
1439 
1440 			if (e is IQ)
1441 			{
1442                 IQ iq = e as IQ;
1443 
1444                 if (OnIq != null) {
1445                     var ev = new IQEventArgs(iq);
1446                     OnIq(this, ev);
1447                     handled = handled || ev.Handled;
1448                 }
1449 
1450 				if ( iq != null && iq.Query != null)
1451 				{
1452 					// Roster
1453                     if (iq.Query is Roster) {
1454                         OnRosterIQ(iq);
1455                         handled = true;
1456                     }
1457 				}
1458 			}
1459 			else if (e is Message)
1460 			{
1461                 if (OnMessage != null) {
1462 					OnMessage(this, e as Message);
1463                     handled = true;
1464                 }
1465 			}
1466 			else if (e is Presence)
1467 			{
1468                 if (OnPresence != null) {
1469 					OnPresence(this, e as Presence);
1470                     handled = true;
1471                 }
1472 			}
1473 			else if (e is protocol.stream.Features)
1474 			{
1475 				// Stream Features
1476 				// StartTLS stuff
1477 				protocol.stream.Features f = e as protocol.stream.Features;
1478 #if SSL || BCCRYPTO || CF_2
1479 				if (f.SupportsStartTls && m_UseStartTLS)
1480 				{
1481 					DoChangeXmppConnectionState(XmppConnectionState.Securing);
1482 					Send(new StartTls());
1483 				}
1484                 // connection is not encrypted, doesn't support starttls but tls is forced
1485                 else if (!ClientSocket.IsEncrypted && !f.SupportsStartTls && ForceStartTls) {
1486                     FireOnError(this, new StartTlsException("StartTls is not supported on this server"));
1487                     Close();
1488                 }
1489                 else
1490 #endif
1491                 if (m_UseCompression &&
1492                     f.SupportsCompression &&
1493                     f.Compression.SupportsMethod(CompressionMethod.zlib))
1494                 {
1495                     // Check for Stream Compression
1496                     // we support only ZLIB because its a free algorithm without patents
1497                     // yes ePatents suck
1498                     DoChangeXmppConnectionState(XmppConnectionState.StartCompression);
1499                     Send(new Compress(CompressionMethod.zlib));
1500                 }
1501 
1502                 else if (m_RegisterAccount)
1503                 {
1504                     // Do registration after TLS when possible
1505                     if (f.SupportsRegistration)
1506                         GetRegistrationFields(e);
1507                     else
1508                     {
1509                         // registration is not enabled on this server
1510                         FireOnError(this, new RegisterException("Registration is not allowed on this server"));
1511                         Close();
1512                         // Close the stream
1513                     }
1514                 }
1515                 ServerCapabilities = f.Capabilities;
1516             }
1517 #if SSL || BCCRYPTO || CF_2
1518             else if (e is Proceed)
1519 			{
1520 				StreamParser.Reset();
1521 		        if (ClientSocket.StartTls())
1522                 {
1523 				    SendStreamHeader(false);
1524 				    DoChangeXmppConnectionState(XmppConnectionState.Authenticating);
1525                     handled = true;
1526                 }
1527             }
1528 #endif
1529             else if (e is Compressed)
1530 			{
1531                 //DoChangeXmppConnectionState(XmppConnectionState.StartCompression);
1532 				StreamParser.Reset();
1533                 ClientSocket.StartCompression();
1534                 // Start new Stream Header compressed.
1535                 SendStreamHeader(false);
1536 
1537                 DoChangeXmppConnectionState(XmppConnectionState.Compressed);
1538                 handled = true;
1539 			}
1540             else if (e is agsXMPP.protocol.Error)
1541             {
1542                 if (OnStreamError != null) {
1543                     OnStreamError(this, e as Element);
1544                     handled = true;
1545                 }
1546             }
1547 
1548             if (handled) {
1549                 eventArgs.Handled = true;
1550             }
1551 		}
1552 
StreamParserStreamElementNotHandled(object sender, UnhandledElementEventArgs eventArgs)1553         public override void StreamParserStreamElementNotHandled(object sender, UnhandledElementEventArgs eventArgs)
1554         {
1555             var stanza = eventArgs.Element as protocol.Base.StanzaWithError;
1556             if (stanza == null) {
1557                 // what should we do here?
1558                 return;
1559             }
1560             if (stanza.Error != null) {
1561                 // don't respond to error messages with service unavailable
1562                 return;
1563             }
1564             stanza.Error = new protocol.client.Error(ErrorCondition.ServiceUnavailable);
1565             stanza.SwitchDirection();
1566 
1567             // allow the client to prevent this message (privacy/security reasons)
1568             var ev = new SendingServiceUnavailableEventArgs(stanza);
1569             OnSendingServiceUnavailable(ev);
1570             if (ev.Cancel) {
1571                 // the client has cancelled this
1572                 return;
1573             }
1574             Send(eventArgs.Element);
1575         }
1576 
StreamParserOnStreamError(object sender, Exception ex)1577 		public override void StreamParserOnStreamError(object sender, Exception ex)
1578 		{
1579 			base.StreamParserOnStreamError(sender, ex);
1580 
1581 			SocketDisconnect();
1582 			CleanupSession();
1583 
1584 			//this._NetworkStream.Close();
1585 
1586 			FireOnError(this, ex);
1587 
1588             if (!m_CleanUpDone)
1589                 CleanupSession();
1590 		}
1591         #endregion
1592 
1593 
1594 
Send(Element e)1595         public override void Send(Element e)
1596         {
1597             if (!(ClientSocket is BoshClientSocket))
1598             {
1599                 // this is a hack to not send the xmlns="jabber:client" with all packets
1600                 Element dummyEl = new Element("a");
1601                 dummyEl.Namespace = Uri.CLIENT;
1602 
1603                 dummyEl.AddChild(e);
1604                 string toSend = dummyEl.ToString();
1605 
1606                 Send(toSend.Substring(25, toSend.Length - 25 - 4));
1607             }
1608             else
1609                 base.Send(e);
1610         }
1611 
1612 		/// <summary>
1613 		/// Does the Clieanup of the Session and sends the OnClose Event
1614 		/// </summary>
CleanupSession()1615 		private void CleanupSession()
1616 		{
1617 			m_CleanUpDone = true;
1618 
1619             // TODO, check if this is always OK
1620             if (ClientSocket.Connected)
1621 			    ClientSocket.Disconnect();
1622 
1623             DoChangeXmppConnectionState(XmppConnectionState.Disconnected);
1624 
1625 			StreamParser.Reset();
1626 
1627 			m_IqGrabber.Clear();
1628 			m_MessageGrabber.Clear();
1629 
1630 			if (m_SaslHandler != null)
1631 			{
1632 				m_SaslHandler.Dispose();
1633 				m_SaslHandler = null;
1634 			}
1635 
1636 			m_Authenticated		= false;
1637 			m_Binded			= false;
1638 
1639 			DestroyKeepAliveTimer();
1640 
1641 			if (OnClose!=null)
1642 				OnClose(this);
1643 		}
1644 
Reset()1645 		internal void Reset()
1646 		{
1647             // tell also the socket that we need to reset the stream, this is needed for BOSH
1648             ClientSocket.Reset();
1649 
1650             StreamParser.Reset();
1651             SendStreamHeader(false);
1652 		}
1653 
DoRaiseEventBinded()1654 		internal void DoRaiseEventBinded()
1655 		{
1656 			if (OnBinded!=null)
1657 				OnBinded(this);
1658 		}
1659 
DoRaiseEventBindError(Element iq)1660         internal void DoRaiseEventBindError(Element iq)
1661         {
1662             if (OnBindError != null)
1663                 OnBindError(this, iq);
1664         }
1665 
1666 		#region << SASL Handler Events >>
m_SaslHandler_OnSaslStart(object sender, SaslEventArgs args)1667 		private void m_SaslHandler_OnSaslStart(object sender, SaslEventArgs args)
1668 		{
1669 			// This acts only as a tunnel to the client
1670 			if (OnSaslStart!=null)
1671 				OnSaslStart(this, args);
1672 		}
1673 
RaiseOnLogin()1674 		internal void RaiseOnLogin()
1675 		{
1676             if (KeepAlive)
1677                 CreateKeepAliveTimer();
1678 
1679 			if (OnLogin!=null)
1680 				OnLogin(this);
1681 
1682 			if(m_AutoAgents)
1683 				RequestAgents();
1684 
1685 			if (m_AutoRoster)
1686 				RequestRoster();
1687 		}
1688 
m_SaslHandler_OnSaslEnd(object sender)1689 		private void m_SaslHandler_OnSaslEnd(object sender)
1690 		{
1691 			if (OnSaslEnd!=null)
1692 				OnSaslEnd(this);
1693 
1694 			m_Authenticated = true;
1695 		}
1696 		#endregion
1697 	}
1698 
1699     public class SendingServiceUnavailableEventArgs : CancelEventArgs
1700     {
1701         public protocol.Base.StanzaWithError Stanza { get; protected set; }
1702 
SendingServiceUnavailableEventArgs(protocol.Base.StanzaWithError stanza)1703         public SendingServiceUnavailableEventArgs(protocol.Base.StanzaWithError stanza)
1704             :base()
1705         {
1706             Stanza = stanza;
1707         }
1708     }
1709 }