Logo Search packages:      
Sourcecode: galaxium version File versions  Download package

SBConnection.cs

/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2007 Ben Motmans <ben.motmans@gmail.com>
 * Copyright (C) 2008 Philippe Durand <draekz@gmail.com>
 * Copyright (C) 2007-2008 Paul Burton <paulburton89@gmail.com>
 * 
 * License: GNU General Public License (GPL)
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Timers;
using System.Web;

using Anculus.Core;

using Galaxium.Core;
using Galaxium.Gui;

namespace Galaxium.Protocol.Msn
{
      public enum XFRRequestState { Incomplete, Requested, Completed };
      
      public class SBConnection : CommandConnection, IMsnP2PBridge
      {
            public event EventHandler<ContactEventArgs> ContactJoined;
            public event EventHandler<ContactEventArgs> ContactLeft;
            public event EventHandler<ContactEventArgs> InviteContactFailed;
            
            public event EventHandler BridgeClosed;
            public event EventHandler BridgeReady;
            
            string _auth;
            List<MsnContact> _contacts = new List<MsnContact> ();
            string _id;
            bool _localRequest = false;
            uint _authTimeout;
            Dictionary<MsnContact, uint> _inviteTimeouts = new Dictionary<MsnContact, uint> ();
            
            internal bool _authenticated = false;
            internal XFRRequestState _requestState = XFRRequestState.Incomplete;
            
            internal MsnContact[] _inviteContacts = new MsnContact[0];
            
            public XFRRequestState RequestState
            {
                  get { return _requestState; }
                  set { _requestState = value; }
            }
            
            public List<MsnContact> Contacts
            {
                  get { return _contacts; }
            }
            
            public string ID
            {
                  get { return _id; }
            }
            
            // The maximum amount of P2P data that can be sent per MSG via the switchboard
            public uint MaxDataSize
            {
                  get { return 1150; }
            }
            
            // Return true if the connection is ready to send P2P data
            public bool Ready
            {
                  get
                  {
                        return CanSend && (_contacts.Count == 1) && (_outQueue.Count == 0);
                  }
            }
            
            public override bool CanSend
            {
                  get { return base.CanSend && _authenticated && (Contacts.Count > 0); }
            }
            
            internal SBConnection (MsnSession session, bool priority, params MsnContact[] contacts)
                  : base (session, new MsnSBConnectionInfo (session.Connection.Connection.ConnectionInfo.HostName, 1, session.Connection.Connection.ConnectionInfo.UseHTTP, string.Empty), MsnConnectionType.Switchboard)
            {
                  _id = Guid.NewGuid ().ToString ();
                  _localRequest = true;
                  _inviteContacts = contacts;
                  
                  _requestState = XFRRequestState.Incomplete;
                  
                  Session.RequestSwitchboard (this, priority);
            }
            
            internal SBConnection (MsnSession session, MsnSBConnectionInfo info, string auth, string id)
                  : base (session, info, MsnConnectionType.Switchboard)
            {
                  _auth = auth;
                  _id = id;
                  
                  _requestState = XFRRequestState.Completed;
                  
                  Connect ();
            }
            
            public void Connect (MsnSBConnectionInfo info, string auth, string id)
            {
                  Connection.ConnectionInfo = info;
                  
                  _auth = auth;
                  _id = id;
                  
                  Connect ();
            }
            
            internal void OnXFRResponseReceived (IMsnCommand cmd)
            {
                  Log.Debug ("XFR response received, applying to switchboard connection...");
                  
                  _localRequest = true;
                  Session._lastXfrCompleted = DateTime.Now;
                  
                  if (cmd is XFRCommand)
                  {
                        _requestState = XFRRequestState.Completed;
                        
                        XFRCommand xfr = cmd as XFRCommand;
                        
                        _auth = xfr.SwitchboardAuthentication;
                        Connection.ConnectionInfo = new MsnSBConnectionInfo (xfr.HostName, xfr.Port, Connection.ConnectionInfo.UseHTTP, _auth);
                        
                        Connect ();
                  }
                  else
                        Log.Warn ("Error requesting switchboard\n{0}", cmd);
            }
            
            protected override void OnAfterConnect (object sender, ConnectionEventArgs args)
            {
                  base.OnAfterConnect (sender, args);
                  
                  Log.Debug ("SB {0} Connected (Initiated {1})", _id, _localRequest ? "Locally" : "Remotely");
                  
                  _authTimeout = TimerUtility.RequestCallback (AuthTimeout, 30000);
                  
                  if (_localRequest)
                        Send (new SBUSRCommand (Session, _auth), true);
                  else
                        Send (new ANSCommand (Session, _auth, _id), true);
            }
            
            protected override void OnClosed (object sender, ConnectionEventArgs args)
            {
                  _requestState = XFRRequestState.Incomplete;
                  _authenticated = false;
                  
                  OnBridgeClosed ();

                  while (_contacts.Count > 0)
                  {
                        MsnContact contact = _contacts[0];
                        
                        _contacts.Remove (contact);
                        OnContactLeft (new ContactEventArgs (contact));
                  }
                  
                  base.OnClosed (sender, args);
            }
            
            protected override void OnErrorOccurred (object sender, ConnectionErrorEventArgs args)
            {
                  _requestState = XFRRequestState.Incomplete;
                  _authenticated = false;
                  
                  OnBridgeClosed ();
                  base.OnErrorOccurred (sender, args);
            }
            
#region Command Handlers
#pragma warning disable 169
            [CommandHandler]
            private void OnErrorReceived (ErrorCommand msg)
            {
                  Log.Error ("Error {0} Occurred", msg.ErrorCode);
                  
                  if (!_authenticated)
                  {
                        // We got an error before we've finished authenticating
                        // kill the connection...
                        
                        Disconnect ();
                        return;
                  }
            }
            
            [CommandHandler]
            void OnIROReceived (IROCommand cmd)
            {
                  MsnContact contact = cmd.Contact;
                  contact.ClientIdentifier = cmd.ClientIdentifier;
                  
                  _contacts.Add (contact);
                  
                  if (ContactJoined != null)
                        ContactJoined (this, new ContactEventArgs (cmd.Contact));
                  
                  ProcessOutQueue ();
            }
            
            [CommandHandler]
            void OnJOIReceived (JOICommand cmd)
            {
                  MsnContact contact = cmd.Contact;
                  contact.ClientIdentifier = cmd.ClientIdentifier;
                  
                  if (_inviteTimeouts.ContainsKey (contact))
                  {
                        TimerUtility.RemoveCallback (_inviteTimeouts[contact]);
                        _inviteTimeouts.Remove (contact);
                  }
                  
                  _contacts.Add (contact);
                  
                  OnContactJoined (new ContactEventArgs (cmd.Contact));
                  
                  if (_contacts.Count > 1)
                        OnBridgeClosed ();
                  
                  ProcessOutQueue ();
            }
            
            [CommandHandler]
            void OnUSRReceived (SBUSRCommand cmd)
            {
                  foreach (MsnContact contact in _inviteContacts)
                        Invite (contact, true);
                  
                  _inviteContacts = new MsnContact[0];
                  
                  _authenticated = true;

                  if (_authTimeout != 0)
                  {
                        TimerUtility.RemoveCallback (_authTimeout);
                        _authTimeout = 0;
                  }
                  
                  ProcessOutQueue ();
            }
            
            [CommandHandler]
            void OnANSReceived (ANSCommand cmd)
            {
                  _authenticated = true;
                  
                  if (_authTimeout != 0)
                  {
                        TimerUtility.RemoveCallback (_authTimeout);
                        _authTimeout = 0;
                  }
                  
                  ProcessOutQueue ();
            }
            
            [CommandHandler]
            void OnBYEReceived (BYECommand cmd)
            {
                  if (_contacts.Contains (cmd.Contact))
                  {
                        _contacts.Remove (cmd.Contact);
                        
                        OnContactLeft (new ContactEventArgs (cmd.Contact));
                        
                        if (_contacts.Count == 0)
                              Disconnect ();
                  }
                  else
                        Log.Warn ("Received BYE for a contact not in the switchboard");
            }
            
            [CommandHandler]
            void OnCALReceived (CALCommand cmd)
            {
            }
            
            [CommandHandler]
            void OnACKReceived (ACKCommand cmd)
            {
            }
            
            [ContentHandler]
            void OnClientCapsReceived (ClientCapsContent content)
            {
                  Log.Info ("Contact is using {0} ({1})", content.Client, content.Logging ? "Logging" : "Not Logging");
            }
            
            [ContentHandler]
            void OnP2PContentReceived (P2PContent content)
            {
                  MsnP2PUtility.ProcessMessage (this, content.P2PMessage);
            }
            
            [ContentHandler]
            void OnKeepAliveContentReceived (KeepAliveContent content)
            {
                  // Do nothing, this is just sent to keep the connection alive
            }
            
            [CommandHandler]
            protected override void OnContentReceived (ContentCommand cmd)
            {
                  if ((cmd.Content != null) && (!_contentDelegates.ContainsKey (cmd.Content.GetType ())))
                  {
                        // No handler is registered for this content
                        
                        MsnConversation conv = (Session.Conversations as MsnConversationManager).GetConversation (this);
                        
                        if (conv == null)
                        {
                              if (_contacts.Count == 1)
                                    conv = (Session.Conversations as MsnConversationManager).GetConversation (_contacts.ToArray ()) as MsnConversation;
                              
                              if (conv == null)
                              {
                                    // There's also no conversation listening for content from this switchboard
                                    // We now create one so that it can register content handlers and receive this content
                                    
                                    Log.Debug ("Creating conversation for switchboard {0}", _id);
                                    
                                    conv = new MsnConversation (this);
                                    Session.Conversations.Add (conv);
                              }
                              else
                              {
                                    // There's a conversation with the correct contact which is inactive
                                    // We can use this instead of creating a new conversation
                                    
                                    Log.Debug ("Reactivating conversation {0} for switchboard {1}", conv.ID, _id);
                                    
                                    conv.Activate (this);
                              }
                        }
                  }
                  
                  base.OnContentReceived (cmd);
            }
#pragma warning restore 169
#endregion
            
            public void Invite (MsnContact contact)
            {
                  Invite (contact, false);
            }
            
            void Invite (MsnContact contact, bool force)
            {
                  _inviteTimeouts[contact] = TimerUtility.RequestCallback (delegate
                  {
                        if (_inviteTimeouts.ContainsKey (contact))
                        {
                              TimerUtility.RemoveCallback (_inviteTimeouts[contact]);
                              _inviteTimeouts.Remove (contact);
                        }
                        
                        OnInviteContactFailed (new ContactEventArgs (contact));
                  }, 30000);
                  
                  Send (new CALCommand (Session, contact), delegate (IMsnCommand response)
                  {
                        if (response is CALCommand)
                              return; // Invite was ok
                        
                        // An error occurred
                        
                        OnInviteContactFailed (new ContactEventArgs (contact));
                  }, force);
            }

            protected void OnInviteContactFailed (ContactEventArgs args)
            {
                  if (InviteContactFailed != null)
                        InviteContactFailed (this, args);
            }
            
            public void Send (P2PMessage msg)
            {
                  P2PContent content = new P2PContent (Session);
                  content.Data = msg.ToByteArray ();
                  
                  MSGCommand cmd = content.ToCommand<MSGCommand> ();
                  cmd.MIMEHeader["P2P-Dest"] = _contacts[0].UniqueIdentifier;
                  
                  Send (cmd);
            }
            
            protected override void OnReady (object sender, EventArgs args)
            {
                  base.OnReady (sender, args);
                  
                  if (_contacts.Count == 1)
                        OnBridgeReady ();
            }

            protected void OnBridgeReady ()
            {
                  if (BridgeReady != null)
                        BridgeReady (this, EventArgs.Empty);
            }
            
            protected void OnBridgeClosed ()
            {
                  if (BridgeClosed != null)
                        BridgeClosed (this, EventArgs.Empty);
            }
            
            protected void OnContactJoined (ContactEventArgs args)
            {
                  if (ContactJoined != null)
                        ContactJoined (this, args);
            }

            protected void OnContactLeft (ContactEventArgs args)
            {
                  if (ContactLeft != null)
                        ContactLeft (this, args);
            }
            
            void AuthTimeout ()
            {
                  Log.Debug ("Unable to authenticate within 30 seconds");
                  Disconnect ();
            }
            
            public override string ToString ()
            {
                  string contactStr = string.Empty;
                  
                  foreach (MsnContact contact in _contacts)
                        contactStr += contact.UniqueIdentifier + ", ";
                  
                  if (contactStr.Length > 2)
                        contactStr = contactStr.Substring (0, contactStr.Length - 2);
                  
                  return string.Format ("SBConnection {0} ({1})", _id, contactStr);
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index