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

MsnSession.cs

/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2005-2007 Philippe Durand <draekz@gmail.com>
 * Copyright (C) 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.IO;
using System.Text;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Xml.Serialization;

using Anculus.Core;

using Galaxium.Core;
using Galaxium.Protocol.Msn.Soap;
using Galaxium.Protocol.Msn.Soap.BodyParts;

namespace Galaxium.Protocol.Msn
{
      public sealed class MsnSession : AbstractSession
      {
            public event EventHandler<SessionEventArgs> Usurped;
            
            const int _xfrInterval = 8;
            
            NSConnection _connection;
            bool _disposed;
            
            ContactCollection _contacts;
            GroupCollection _groups;
            
            OIMService _oimService;
            OIMStoreService _oimStoreService;
            PassportService _ppService;
            SharingService _sharingService;
            ABService _abService;
            StoreService _sstoreService;
            
            internal FindMembershipResult _memberships;
            internal ABFindAllResult _addressBook;
            
            MsnClientConfig _clientConfig = new MsnClientConfig ();
            
            internal List<SBConnection> _switchboards = new List<SBConnection> ();
            Dictionary<string, RequestSecurityTokenResponse> _securityTokens = new Dictionary<string, RequestSecurityTokenResponse> ();
            
            static Dictionary<MsnSession, List<SBConnection>> _awaitingXfr = new Dictionary<MsnSession, List<SBConnection>> ();
            private uint _xfrTimerId = 0;
            internal DateTime _lastXfrCompleted = DateTime.Now;

            public new MsnAccount Account
            {
                  get { return base.Account as MsnAccount; }
            }
            
            internal OIMService OIMService
            {
                  get { return _oimService; }
            }
            
            internal OIMStoreService OIMStoreService
            {
                  get { return _oimStoreService; }
            }
            
            internal PassportService PassportService
            {
                  get { return _ppService; }
            }
            
            internal SharingService SharingService
            {
                  get { return _sharingService; }
            }
            
            internal ABService ABService
            {
                  get { return _abService; }
            }
            
            internal StoreService StoreService
            {
                  get { return _sstoreService; }
            }
            
            internal Dictionary<string, RequestSecurityTokenResponse> SecurityTokens
            {
                  get { return _securityTokens; }
            }
            
            public MsnClientConfig ClientConfig
            {
                  get { return _clientConfig; }
            }
            
            public MsnSession (MsnAccount account)
                  : base (account)
            {
                  _lastXfrCompleted = _lastXfrCompleted.Subtract(new TimeSpan(0, 0, _xfrInterval));
                  
                  _contacts = new ContactCollection ();
                  _groups = new GroupCollection ();
                  
                  Conversations = new MsnConversationManager ();
                  
                  IGroup emptyGroup = new MsnGroup (this, "0", "Other Contacts", true);
                  
                  _groups.Add (emptyGroup);
                  
                  account.Presence = account.InitialPresence;
                  
                  _oimService = new OIMService (this);
                  _oimStoreService = new OIMStoreService (this);
                  _ppService = new PassportService (this);
                  _sharingService = new SharingService (this);
                  _abService = new ABService (this);
                  _sstoreService = new StoreService (this);
                  
                  foreach (RequestSecurityTokenResponse token in (Account.Cache as MsnAccountCache).GetSecurityTokens ())
                        _securityTokens.Add (token.AppliesTo.EndpointReference.Address, token);
                  
                  // If we have a stored notification server address/port, use it
                  // NSConnection will fall back to the dispatch server if necessary
                  
                  //TODO: get the proxy settings from the account
                  
                  if (!string.IsNullOrEmpty (account.NotificationServerHostname))
                        _connection = new NSConnection (this, new MsnNSConnectionInfo (account.NotificationServerHostname, account.NotificationServerPort, account.UseHTTP));
                  else
                        _connection = new NSConnection (this, new MsnNSConnectionInfo (account.UseHTTP));
                  
                  _connection.Closed += ConnectionClosed;
                  _connection.ErrorOccurred += ConnectionErrorOccurred;
            }

            protected override void Dispose (bool disposing)
            {
                  if (!_disposed)
                  {
                        _connection.Closed -= ConnectionClosed;
                        _connection.ErrorOccurred -= ConnectionErrorOccurred;
                        
                        if (disposing && _connection != null)
                              _connection.Dispose ();
                        
                        _oimService.Dispose ();
                        _oimStoreService.Dispose ();
                        _ppService.Dispose ();
                        _sharingService.Dispose ();
                        _abService.Dispose ();
                        _sstoreService.Dispose ();
                  }
                  
                  _disposed = true;
            }
            
            void ConnectionErrorOccurred (object sender, ConnectionErrorEventArgs args)
            {
                  OnErrorOccurred (new ErrorEventArgs (this, args.ErrorIdentifier, args.Description));
            }
            
            void ConnectionClosed (object sender, ConnectionEventArgs args)
            {
                  if (!_connection.Reconnecting)
                  {
                        CloseSwitchboards ();
                        OnErrorOccurred (new ErrorEventArgs (this, "Closed", "The connection was lost"));
                  }
            }
            
            void CloseSwitchboards ()
            {
                  while (_switchboards.Count > 0)
                        _switchboards[0].Disconnect();
            }

            public NSConnection Connection
            {
                  get { return _connection; }
            }
            
            public override GroupCollection GroupCollection
            {
                  get { return this._groups; }
            }

            public override ContactCollection ContactCollection
            {
                  get { return this._contacts; }
            }

            public IEnumerable<IGroup> Groups
            {
                  get { return this._groups; }
            }
            
            public MsnProtocolVersion Protocol
            {
                  get { return _connection.Protocol; }
            }

            public override void Connect ()
            {
                  _connection.Connect ();
                  OnConnected (new SessionEventArgs (this));
            }

            public override void Disconnect ()
            {
                  if (_xfrTimerId != 0)
                  {
                        TimerUtility.RemoveCallback(_xfrTimerId);
                        _xfrTimerId = 0;
                  }
                  
                  foreach (IConversation conversation in Conversations)
                        conversation.Close();
                  
                  _oimService.Abort ();
                  _oimStoreService.Abort ();
                  _ppService.Abort ();
                  _sharingService.Abort ();
                  _abService.Abort ();
                  _sstoreService.Abort ();
                  
                  _connection.Disconnect ();
                  
                  OnDisconnected (new SessionEventArgs (this));
            }
            
            public bool AddContactToGroups (out string error, string passport, params string [] guids)
            {
                  return _connection.AddContactToGroups (out error, passport, guids);
            }
            
            public bool AddContactWithGroups (out string error, string passport, string alias, bool block, params string [] guids)
            {
                  return _connection.AddContactWithGroups (out error, passport, alias, block, guids);
            }

            public bool AddContact (out string error, string passport, string alias, bool block)
            {
                  return _connection.AddContact (out error, passport, alias, block);
            }
            
            public bool MoveContactToGroup (out string error, string passport, string fromGuid, string toGuid)
            {
                  return _connection.MoveContactToGroup (out error, passport, fromGuid, toGuid);
            }
            
            public bool RemoveContactFromGroups (out string error, string passport, params string [] guids)
            {
                  return _connection.RemoveContactFromGroups (out error, passport, guids);
            }
            
            public bool RemoveContact (out string error, string uid)
            {
                  return _connection.RemoveContact (out error, uid);
            }

            public bool AddGroup (out string error, string name)
            {
                  return _connection.AddGroup (out error, name);
            }
            
            public bool RemoveGroup (out string error, string guid, bool clear)
            {
                  return _connection.RemoveGroup (out error, guid, clear);
            }
            
            public bool RenameGroup (out string error, string guid, string name)
            {
                  return _connection.RenameGroup (out error, guid, name);
            }
            
            public override IFileTransfer SendFile (IContact contact, string fileName)
            {
                  ThrowUtility.ThrowIfNull ("contact", contact);
                  ThrowUtility.ThrowIfEmpty ("fileName", fileName);
                  
                  return new MsnFileTransfer (this, contact, fileName);
            }
            
            public IEnumerable<MsnGroup> GetGroupsForContact (MsnContact contact)
            {
                  lock (_groups)
                  {
                        foreach (MsnGroup group in _groups)
                        {
                              if (group.Contains (contact))
                                    yield return group;
                        }
                  }
            }
            
            public void EmitUsurped (SessionEventArgs args)
            {
                  if (Usurped != null)
                        Usurped (this, args);
            }
            
            public override void SetPresence (BasePresence presence)
            {
                  IPresence msnpresence = MsnPresence.Get (presence);
                  
                  if (msnpresence != null)
                        Account.Presence = msnpresence;
                  else
                        Log.Warn("Cannot handle BasePresence {0}", presence);
            }
            
            public override IEntity FindEntity (string uid)
            {
                  string networkName = string.Empty;
                  
                  if (uid.StartsWith ("<"))
                  {
                        int colon = uid.IndexOf (":");
                        
                        networkName = uid.Substring (1, colon - 1);
                        uid = uid.Substring (colon + 1, uid.Length - colon - 2);
                  }
                  
                  return FindEntity (uid, MsnNetworkUtility.FromName (networkName));
            }
            
            public IMsnEntity FindEntity (string uid, Network network)
            {
                  if ((_account.UniqueIdentifier == uid) && ((network & Network.WindowsLive) == network))
                        return _account as MsnAccount;
                  
                  return FindContact (uid, network);
            }
            
            public MsnContact FindContact (string uid)
            {
                  string networkName = string.Empty;
                  
                  if (uid.StartsWith ("<"))
                  {
                        int colon = uid.IndexOf (":");
                        
                        networkName = uid.Substring (1, colon - 1);
                        uid = uid.Substring (colon + 1, uid.Length - colon - 2);
                  }
                  
                  return FindContact (uid, MsnNetworkUtility.FromName (networkName));
            }
            
            public MsnContact FindContact (string uid, Network network)
            {
                  lock (_contacts)
                  {
                        foreach (MsnContact contact in _contacts)
                        {
                              if (!contact.UniqueIdentifier.Equals (uid, StringComparison.InvariantCultureIgnoreCase))
                                    continue;
                              
                              if ((contact.Network & network) != network)
                                    continue;
                              
                              return contact;
                        }
                        
                        MsnContact newContact = new MsnContact (this, uid, network);
                        
                        try
                        {
                              _contacts.Add (newContact);
                        }
                        catch (Exception ex)
                        {
                              Log.Error (ex, "Error adding contact {0} ({1} Network)", uid, network);
                        }
                        
                        return newContact;
                  }
            }
            
            internal void UpdateMemberships (FindMembershipResult result)
            {
                  if (_memberships == null)
                        _memberships = result;
                  else
                  {
                        _memberships.OwnerNamespace = result.OwnerNamespace.Clone ();
                        
                        foreach (Service svc in result.Services)
                        {
                              if (_memberships.Services[svc.Info.Handle.Type.Name] != null)
                              {
                                    Service s = _memberships.Services[svc.Info.Handle.Type.Name];
                                    
                                    s.Info = svc.Info.Clone ();
                                    s.LastChange = svc.LastChange;
                                    s.LastChangeSpecified = svc.LastChangeSpecified;
                                    
                                    foreach (Membership mship in svc.Memberships)
                                    {
                                          if (s.Memberships[mship.MemberRole] != null)
                                          {
                                                foreach (Member mbr in mship.Members)
                                                      s.Memberships[mship.MemberRole].Members[mbr.MembershipId] = mbr.Clone ();
                                          }
                                          else
                                                s.Memberships[mship.MemberRole] = mship.Clone ();
                                    }
                              }
                              else
                                    _memberships.Services.Add (svc.Clone ());
                        }
                  }
                  
                  List<KeyValuePair<Membership, Member>> removals = new List<KeyValuePair<Membership, Member>> ();
                  
                  foreach (Service svc in _memberships.Services)
                  {
                        foreach (Membership mship in svc.Memberships)
                        {
                              foreach (Member mbr in mship.Members)
                              {
                                    if (mbr.Deleted)
                                          removals.Add (new KeyValuePair<Membership, Member> (mship, mbr));
                              }
                        }
                  }
                  
                  foreach (KeyValuePair<Membership, Member> pair in removals)
                  {
                        Log.Debug ("Member {0} {1} deleted", pair.Value.MembershipId, (pair.Value is PassportMember) ? (pair.Value as PassportMember).PassportName : pair.Value.ToString ());
                        
                        pair.Key.Members.Remove (pair.Value);
                  }
                  
                  (Account.Cache as MsnAccountCache).SaveCachedMembership (_memberships, _memberships.OwnerNamespace.LastChangeString);
            }
            
            internal void UpdateAddressBook (ABFindAllResult result)
            {
                  if (_addressBook == null)
                        _addressBook = result;
                  else
                  {
                        _addressBook.AB.Info = result.AB.Info;
                        
                        foreach (ABContact contact in result.Contacts)
                        {
                              if (_addressBook.Contacts[contact.Id] != null)
                              {
                                    ABContact c = _addressBook.Contacts[contact.Id];
                                    
                                    if (contact.InfoSpecified)
                                          c.Info = contact.Info.Clone ();
                                    if (contact.PropertiesChangedSpecified)
                                          c.PropertiesChanged = contact.PropertiesChanged;
                              }
                              else
                                    _addressBook.Contacts.Add (contact.Clone ());
                        }
                        
                        foreach (ABGroup group in result.Groups)
                        {
                              if (_addressBook.Groups[group.Id] != null)
                              {
                                    ABGroup g = _addressBook.Groups[group.Id];
                                    
                                    if (group.InfoSpecified)
                                          g.Info = group.Info.Clone ();
                                    if (group.PropertiesChangedSpecified)
                                          g.PropertiesChanged = group.PropertiesChanged;
                              }
                              else
                                    _addressBook.Groups.Add (group.Clone ());
                        }
                  }
                  
                  (Account.Cache as MsnAccountCache).SaveCachedAddressBook (_addressBook, _addressBook.AB.LastChangeString);
            }
            
            internal void UpdateContacts ()
            {
                  lock (_groups)
                  {
                        // First, add all groups to the GroupCollection
                        foreach (ABGroup group in _addressBook.Groups)
                        {
                              MsnGroup existing = null;
                              
                              foreach (MsnGroup g in _groups)
                              {
                                    if (g.UniqueIdentifier == group.Id.ToString ())
                                    {
                                          existing = g;
                                          break;
                                    }
                              }
                              
                              if (existing == null)
                              {
                                    existing = new MsnGroup (this, group.Id.ToString (), group.Info.Name);
                                    _groups.Add (existing);
                              }
                              else
                                    existing.Name = group.Info.Name;
                        }
                  }
                  
                  lock (_contacts)
                  {
                        // Next, add contacts from the Messenger membership list
                        
                        if (_memberships.Services["Messenger"] != null)
                        {
                              foreach (Membership membership in _memberships.Services["Messenger"].Memberships)
                              {
                                    MsnListType listType = GetListType (membership.MemberRole);
                                    
                                    if (listType == MsnListType.Unknown)
                                          continue;
                                    
                                    //Log.Debug ("Membership: {0}", membership.MemberRole);
                                    
                                    foreach (Member member in membership.Members)
                                    {
                                          string email = string.Empty;
                                          Network network = Network.Unknown;
                                          
                                          try
                                          {
                                                switch (member.Type)
                                                {
                                                case MemberType.Passport:
                                                      email = ((PassportMember)member).PassportName;
                                                      network = Network.WindowsLive;
                                                      break;
                                                case MemberType.Email:
                                                      email = ((EmailMember)member).Email;
                                                      network = GetNetwork ((EmailMember)member);
                                                      break;
                                                case MemberType.Phone:
                                                      // FIXME: Is this really how you parse a phone type member? or do we?
                                                      //email = ((PhoneMember)member).Phone;
                                                      //network = Network.Telephone;
                                                      break;
                                                case MemberType.Role:
                                                      // How is this parsed, and what is a role member anyway?
                                                      break;
                                                      
                                                default:
                                                      Log.Debug ("  Unknown type: "+member.Type);
                                                      break;
                                                }
                                                
                                                if (network == Network.Unknown)
                                                      throw new ApplicationException ("Unknown network");
                                                
                                                //Log.Debug ("  Member {0}: {1}, {2}", network, email, member.Type);
                                                
                                                MsnContact contact = FindContact (email, network) as MsnContact;
                                                
                                                if (contact == null)
                                                      throw new ApplicationException ("Member could not be found");
                                                
                                                contact.ListType |= listType;
                                                contact.MembershipId = member.MembershipId;
                                          }
                                          catch (Exception ex)
                                          {
                                                Log.Error (ex, "Error loading member");
                                                
                                                try
                                                {
                                                      XmlSerializer serializer = new XmlSerializer (typeof (Member));
                                                      StringWriter writer = new StringWriter ();
                                                      
                                                      serializer.Serialize (writer, member);
                                                      
                                                      Log.Debug ("Member:\n{0}", writer.ToString ());
                                                      
                                                      writer.Close ();
                                                }
                                                catch (Exception ex2)
                                                {
                                                      Log.Error (ex2, "Unable to serialize member");
                                                }
                                          }
                                    }
                              }
                        }
                        
                        // Finally, add contacts from the address book to the forward list
                        // and get information such as names from address book
                        foreach (ABContact contact in _addressBook.Contacts)
                        {
                              try
                              {
                                    if (contact.Info.Type == ContactType.Me)
                                    {
                                          (_account as MsnAccount).SetDisplayName (contact.Info.DisplayName);
                                          (_account as MsnAccount)._contactID = contact.Info.CID;
                                          
                                          continue;
                                    }
                                    
                                    string email = contact.Info.PassportName;
                                    Network network = Network.Unknown;
                                    
                                    if (string.IsNullOrEmpty (email))
                                    {
                                          // The contact does not have a passport account
                                          // Check if they have a messenger enabled email address
                                          // eg. Yahoo users
                                          
                                          foreach (ContactEmail cEmail in contact.Info.Emails)
                                          {
                                                if (cEmail.IsMessengerEnabled)
                                                {
                                                      email = cEmail.Email;
                                                      network = (Network)cEmail.Capability;
                                                      break;
                                                }
                                          }
                                    }
                                    else
                                          network = Network.WindowsLive;
                                    
                                    if (network == Network.Unknown)
                                    {
                                          Log.Warn ("Unknown network, ignoring contact '{0}'", email);
                                          continue;
                                    }
                                    
                                    MsnContact msnContact = FindContact (email, network) as MsnContact;
                                    
                                    if (msnContact == null)
                                          throw new ApplicationException ("Unable to find contact");
                                    
                                    msnContact.SetDisplayName (contact.Info.DisplayName, false);
                                    msnContact.Guid = contact.Id.ToString ();
                                    msnContact.ListType |= MsnListType.Forward;
                                    
                                    // Add the contact to the correct groups
                                    // Add to the default group if none are given in the address book
                                    if (contact.Info.GroupIds.Count < 1)
                                    {
                                          if (!_groups.GetGroup ("0").Contains (msnContact))
                                                _groups.GetGroup ("0").Add (msnContact);
                                    }
                                    else
                                    {
                                          foreach (Guid groupId in contact.Info.GroupIds)
                                          {
                                                if (!_groups.GetGroup (groupId.ToString ()).Contains (msnContact))
                                                      _groups.GetGroup (groupId.ToString ()).Add (msnContact);
                                          }
                                    }
                              }
                              catch (Exception ex)
                              {
                                    Log.Error (ex, "Error loading contact");
                                    
                                    try
                                    {
                                          XmlSerializer serializer = new XmlSerializer (typeof (ABContact));
                                          StringWriter writer = new StringWriter ();
                                          
                                          serializer.Serialize (writer, contact);
                                          
                                          Log.Debug ("Contact:\n{0}", writer.ToString ());
                                          
                                          writer.Close ();
                                    }
                                    catch (Exception ex2)
                                    {
                                          Log.Error (ex2, "Unable to serialize contact");
                                    }
                              }
                        }
                        
                        // Somehow contacts can sometimes be on both the allow & block lists
                        // In this case we remove them from allow
                        foreach (MsnContact contact in ContactCollection)
                        {
                              if (((contact.ListType & MsnListType.Allowed) == MsnListType.Allowed) && 
                                  ((contact.ListType & MsnListType.Blocked)) == MsnListType.Blocked)
                              {
                                    Log.Debug ("Contact {0} in {1} lists!", contact.UniqueIdentifier, contact.ListType);
                                    contact.ListType &= ~MsnListType.Allowed;
                              }
                        }
                  }
            }
            
            Network GetNetwork (EmailMember member)
            {
                  string buddyType = member.Annotations["MSN.IM.BuddyType"];
                  int val;
                  
                  if (buddyType.Contains (":") && int.TryParse (buddyType.Substring (0, buddyType.IndexOf (":")), out val))
                        return (Network)val;
                  
                  return Network.Unknown;
            }
            
            MsnListType GetListType (MemberRole role)
            {
                  switch (role)
                  {
                  case MemberRole.Allow:
                        return MsnListType.Allowed;
                  case MemberRole.Block:
                        return MsnListType.Blocked;
                  case MemberRole.Pending:
                        return MsnListType.Pending;
                  case MemberRole.Reverse:
                        return MsnListType.Reverse;
                  default:
                        return MsnListType.Unknown;
                  }
            }
            
            internal void UpdateSecurityTokens (RequestSecurityTokenResponseCollection tokens)
            {
                  foreach (RequestSecurityTokenResponse token in tokens)
                        UpdateSecurityToken (token);
            }
            
            internal void UpdateSecurityToken (RequestSecurityTokenResponse token)
            {
                  _securityTokens[token.AppliesTo.EndpointReference.Address] = token;
                  (Account.Cache as MsnAccountCache).SaveSecurityToken (token);
            }
            
            internal void RequireSecurityTokens (ExceptionDelegate callback, params string[] domains)
            {
                  bool allOk = true;
                  
                  // How long should be require the token to last before we request a new one?
                  foreach (string domain in domains)
                        allOk &= _securityTokens.ContainsKey (domain) && _securityTokens[domain].LifeTime.Expires.ToLocalTime() > DateTime.Now.AddMinutes (2);
                  
                  if (allOk)
                  {
                        Log.Debug ("Required security tokens all OK, no request needed");
                        
                        if (callback != null)
                              callback (null);
                        
                        return;
                  }
                  
                  Log.Debug ("Security tokens expired or not present, requesting new ones");
                  
                  RequestMultipleSecurityTokens requests = new RequestMultipleSecurityTokens ();
                  requests.Add (SecurityToken.PassportTb, SecurityToken.GetPolicy (SecurityToken.PassportTb));
                  
                  foreach (string domain in domains)
                        requests.Add (domain, SecurityToken.GetPolicy (domain));
                  
                  if (callback == null)
                  {
                        RequestSecurityTokenResponseCollection responses = PassportService.RequestMultipleSecurityTokens (requests);
                        UpdateSecurityTokens (responses);
                  }
                  else
                  {
                        PassportService.BeginRequestMultipleSecurityTokens (requests, delegate (IAsyncResult asyncResult)
                        {
                              Exception ex = null;
                              
                              try
                              {
                                    RequestSecurityTokenResponseCollection responses = PassportService.EndRequestMultipleSecurityTokens (asyncResult);
                                    UpdateSecurityTokens (responses);
                              }
                              catch (Exception e)
                              {
                                    ex = e;
                              }
                              
                              callback (ex);
                        }, null);
                  }
            }
            
            internal void RequireSecurityTokens (params string[] tokens)
            {
                  RequireSecurityTokens (null, tokens);
            }
            
            // Find a switchboard with the given contacts
            public SBConnection FindSwitchboard (params MsnContact[] contacts)
            {
                  StringBuilder uids = new StringBuilder ();
                  
                  if (CoreUtility.Debug)
                  {
                        bool first = true;
                        foreach (MsnContact contact in contacts) {
                              if (first)
                                    first = false;
                              else
                                    uids.Append (", " );

                              uids.Append (contact.UniqueIdentifier);
                        }
                  }
                  
                  lock (_switchboards)
                  {
                        foreach (SBConnection sb in _switchboards)
                        {
                              // Check if the switchboard has the contacts we want
                              bool match = false;
                              
                              // If the switchboard doesn't have the same amount of contacts
                              // we know it's not what we want & can skip checking each contact
                              if (sb.Contacts.Count == contacts.Length)
                              {
                                    match = true;
                                    
                                    foreach (MsnContact contact in contacts)
                                          match &= sb.Contacts.Contains (contact);
                              }
                              
                              // It doesn't, but maybe it's not yet established & it will have the
                              // contacts we want when it is, so check if it will be inviting them
                              if ((!match) && (!sb._authenticated) && (sb._inviteContacts.Length == contacts.Length))
                              {
                                    match = true;
                                    
                                    foreach (MsnContact contact in contacts)
                                    {
                                          bool sbHasContact = false;
                                          
                                          foreach (MsnContact sbContact in sb._inviteContacts)
                                          {
                                                // Check if the switchboards contact is the same as the contact we want
                                                if (sbContact == contact)
                                                {
                                                      sbHasContact = true;
                                                      break;
                                                }
                                          }
                                          
                                          // If the switchboard has all the contacts we want, it's a match
                                          // otherwise, match will be false
                                          match &= sbHasContact;
                                    }
                              }
                        
                              // We found a switchboard that matches what we're looking for!
                              if (match)
                              {
                                    Log.Debug ("Found switchboard with {0}", uids.ToString ());
                                    
                                    return sb;
                              }
                        }
                  }
                  
                  Log.Debug ("No switchboard found with {0}", uids.ToString ());
                  
                  return null;
            }
            
            public SBConnection FindSwitchboard (string id)
            {
                  lock (_switchboards)
                  {
                        foreach (SBConnection sb in _switchboards)
                        {
                              if (sb.ID == id)
                                    return sb;
                        }
                  }
                  
                  return null;
            }
            
            internal void RequestSwitchboard (SBConnection connection, bool priority)
            {
                  if (connection.RequestState == XFRRequestState.Incomplete ||
                        connection.RequestState == XFRRequestState.Requested)
                  {
                        lock (_awaitingXfr)
                        {
                              if (!_awaitingXfr.ContainsKey (this))
                                    _awaitingXfr.Add (this, new List<SBConnection> ());
                              
                              TimeSpan span = DateTime.Now.Subtract (_lastXfrCompleted);
                              
                              if (_awaitingXfr[this].Count == 0 && span.TotalSeconds > _xfrInterval)
                              {
                                    _lastXfrCompleted = DateTime.Now;
                                    connection.RequestState = XFRRequestState.Requested;
                                    Connection.Send (new XFRCommand (this), connection.OnXFRResponseReceived);
                                    return;
                              }
                              else
                              {
                                    // Now for safety, lets make sure we dont insert the same connection more than once.
                                    if (_awaitingXfr[this].Contains(connection))
                                    {
                                          // We already have this connection in the queue, if its priority, move it up.
                                          if (priority)
                                          {
                                                _awaitingXfr[this].Remove(connection);
                                                _awaitingXfr[this].Insert(0, connection);
                                          }
                                          else
                                                return;
                                    }
                                    else
                                    {
                                          // Now we have to worry about priority queueing. if the priority is set to true
                                          // We need to put this specific switchboard request ahead of the others.
                                          if (priority)
                                          {
                                                // We have to put this connection as priority.
                                                _awaitingXfr[this].Insert (0, connection);
                                          }
                                          else
                                          {
                                                // We have to put this at the end of the line.
                                                _awaitingXfr[this].Add (connection);
                                          }
                                          
                                          connection.RequestState = XFRRequestState.Requested;
                                    }
                                    
                                    if (_xfrTimerId == 0)
                                    {
                                          int time = (int)(_xfrInterval - span.TotalSeconds) * 1000;
                                          
                                          if (time < 1)
                                          {
                                                Log.Debug ("Time to delay the XFR callback is less than 1!");
                                                time = 1000;
                                          }
                                          
                                          _xfrTimerId = TimerUtility.RequestInfiniteCallback(OnXFRTimerElapsed, time);
                                    }
                              }
                        }
                  }
            }
            
            private void OnXFRTimerElapsed ()
            {
                  lock (_awaitingXfr)
                  {
                        if (_awaitingXfr.ContainsKey (this))
                        {
                              SBConnection connection = _awaitingXfr[this][0];
                              _awaitingXfr[this].Remove (connection);
                              
                              if (connection != null)
                                    Connection.Send (new XFRCommand (this), connection.OnXFRResponseReceived);
                              
                              if (_awaitingXfr[this].Count < 1)
                              {
                                    if (_xfrTimerId != 0)
                                    {
                                          TimerUtility.RemoveCallback(_xfrTimerId);
                                          _xfrTimerId = 0;
                                    }
                              }
                        }
                  }
            }
            
            // Find a switchboard with the given contacts, and create one if we don't already have one
            public SBConnection GetSwitchboard (bool priority, params MsnContact[] contacts)
            {
                  SBConnection sb = FindSwitchboard (contacts);
                  
                  if (sb == null)
                  {
                        // There's no switchboard already created with the contacts we want
                        // So we need to create a new one
                        
                        lock (_switchboards)
                        {
                              sb = new SBConnection (this, priority, contacts);
                              
                              sb.Closed += delegate
                              {
                                    if (sb.Reconnecting)
                                          return;
                                    
                                    Log.Debug ("Switchboard {0} closed", sb.ID);
                                    
                                    lock (_switchboards)
                                          _switchboards.Remove (sb);
                              };
                              
                              Log.Debug ("Switchboard {0} created", sb.ID);
                              _switchboards.Add (sb);
                        }
                  }
                  else
                  {
                        // We have a swithcboard, but it may not have completed its request.
                        if (sb.RequestState == XFRRequestState.Incomplete)
                              RequestSwitchboard (sb, priority);
                  }
                  
                  return sb;
            }
            
            public override bool InContactList (IContact contact)
            {
                  return base.InContactList (contact) && (contact as MsnContact).IsInList (MsnListType.Forward);
            }
            
            
      }
}

Generated by  Doxygen 1.6.0   Back to index