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 }