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

NSConnection.cs

/*
 * Galaxium Messenger
 * 
 * Copyright (C) 2005-2007 Ben Motmans <ben.motmans@gmail.com>
 * Copyright (C) 2005-2007 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.Timers;
using System.Web;
using System.Web.Services.Protocols;
using System.Xml;

using Anculus.Core;

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

namespace Galaxium.Protocol.Msn
{
      public sealed class NSConnection : CommandConnection
      {
            // Maximum OIMs to delete in one request
            const int _maxOIMDelete = 5;
            
            bool _isSynchronized;
            bool _initialDone = false;
            
            MsnProtocolVersion _ver = MsnProtocolVersion.CVR0;
            
            bool _allowDispatchConnect;
            
            Timer _pngTimer;
            uint _missedQNGs;
            
            int _adlWaiting = 0;
            
            Dictionary<Guid, OrderedDictionary<int, MsnOfflineTextMessage>> _offlineMessages = new Dictionary<Guid, OrderedDictionary<int, MsnOfflineTextMessage>> ();
            int _offlineMessagesToRetrieve;
            
            IConfigurationSection _accountConfig;
            
            public new MsnSession Session
            {
                  get { return base.Session as MsnSession; }
            }
            
            public MsnProtocolVersion Protocol
            {
                  get { return _ver; }
            }
            
            internal NSConnection (MsnSession session, MsnNSConnectionInfo info)
                  : base (session, info, MsnConnectionType.Notification)
            {
                  // Setup the certificate policy to accept all.
                  ServicePointManager.CertificatePolicy = new PassportCertificatePolicy ();
                  //ServicePointManager.ServerCertificateValidationCallback += delegate { return true; };
                  
                  _allowDispatchConnect = (info.HostName != MsnConstants.DefaultNotificationServerHostname);
                  
                  _pngTimer = new Timer ();
                  _pngTimer.Elapsed += PNGTimerElapsed;
                  
                  _accountConfig = ConfigurationUtility.Accounts[Session.Account.Protocol.Name][Session.Account.UniqueIdentifier];
            }
            
            public override void Dispose ()
            {
                  _pngTimer.Dispose ();
                  
                  base.Dispose ();
            }
            
            protected override void OnErrorOccurred (object sender, ConnectionErrorEventArgs args)
            {
                  if (_allowDispatchConnect)
                  {
                        // We only want to fall back to the dispatch server once
                        _allowDispatchConnect = false;
                        
                        // Reset stored server to avoid using it again
                        Session.Account.NotificationServerHostname = string.Empty;
                        
                        Connection.ConnectionInfo.HostName = MsnConstants.DefaultNotificationServerHostname;
                        Connection.ConnectionInfo.Port = MsnConstants.DefaultNotificationServerPort;
                        
                        Reconnect();
                  }
                  else
                        base.OnErrorOccurred (sender, args);
            }
            
            protected override void OnClosed (object sender, ConnectionEventArgs args)
            {
                  lock (_pngTimer)
                  {
                        if (_pngTimer.Enabled)
                              _pngTimer.Stop ();
                  }
                  
                  base.OnClosed (sender, args);
                  
                  _ver = MsnProtocolVersion.CVR0;
            }
            
            protected override void OnAfterConnect (object sender, ConnectionEventArgs args)
            {
                  base.OnAfterConnect (sender, args);
                  
                  string versions = string.Empty;
                  
                  foreach (string ver in Enum.GetNames (typeof (MsnProtocolVersion)))
                        versions = ver + " " + versions;
                  
                  Send (new VERCommand (Session, versions.Trim ()));
            }
            
            private void ProcessDisplayImage (MsnContact contact, MsnDisplayImage obj)
            {
                  if (obj != null)
                  {
                        if ((contact.DisplayImage == null) || ((contact.DisplayImage as MsnDisplayImage).Sha != obj.Sha))
                        {
                              //IDisplayImage oldImage = contact.DisplayImage;
                              contact.SetUserDisplay (obj);
                        }
                  }
            }
            
#region Command Handlers
#pragma warning disable 169
            [CommandHandler]
            private void OnErrorReceived (ErrorCommand cmd)
            {
                  Log.Error ("Error Occurred: {0}", cmd);
                  
                  if (MsnError.RequiresUserNotification (cmd.ErrorCode))
                  {
                        string description = MsnError.GetErrorDescription (cmd.ErrorCode);

                        Session.EmitErrorOccurred (new ErrorEventArgs (Session, cmd.ErrorCode.ToString (), description));
                  } //TODO: else: handle internally
            }
            
            [CommandHandler]
            void OnVERReceived (VERCommand cmd)
            {
                  foreach (string ver in cmd.Versions)
                  {
                        try
                        {
                              _ver = (MsnProtocolVersion)Enum.Parse (typeof (MsnProtocolVersion), ver);
                              break;
                        }
                        catch
                        {
                              _ver = MsnProtocolVersion.CVR0;
                        }
                  }
                  
                  if (_ver == MsnProtocolVersion.CVR0)
                        throw new ApplicationException ("Notification server doesn't support any protocol versions we do");
                  
                  Log.Info ("Using MSN Protocol Version {0}", _ver);
                  
                  _allowDispatchConnect = false;
                  
                  Send (new CVRCommand (Session));
            }

            [CommandHandler]
            private void OnUBXReceived (UBXCommand msg)
            {
                  MsnContact contact = msg.Contact as MsnContact;
                  
                  if (contact != null)
                  {
                        if (contact.CurrentMedia.ToString () != msg.CurrentMedia)
                              contact.CurrentMedia = MsnCurrentMedia.FromString (msg.CurrentMedia);
                        
                        //string oldMessage = contact.DisplayMessage;
                        
                        if (contact.DisplayMessage != msg.PersonalMessage)
                        {
                              contact.DisplayMessage = msg.PersonalMessage;
                        }
                        
                        Session.EmitContactChanged (new ContactEventArgs (contact));
                  }
                  else
                  {
                        Log.Warn("Contact is missing from collection!");
                  }
            }
            
            [CommandHandler]
            private void OnILNReceived (ILNCommand cmd)
            {
                  MsnContact contact = cmd.Contact;
                  
                  if (contact == null)
                        return;
                  
                  ProcessDisplayImage (contact, cmd.DisplayImage);
                  
                  contact.ClientIdentifier = cmd.ClientIdentifier;
                  
                  if (contact.Presence != cmd.Presence)
                        contact.Presence = cmd.Presence;
                  
                  if (string.IsNullOrEmpty (contact.DisplayName) || (!contact.DisplayName.Equals (cmd.DisplayName)))
                        contact.SetDisplayName (cmd.DisplayName, true);
            }
            
            [CommandHandler]
            private void OnFLNReceived (FLNCommand cmd)
            {
                  MsnContact contact = cmd.Contact as MsnContact;
                  
                  if (contact.Presence != MsnPresence.Offline)
                  {
                        IPresence oldpresence = contact.Presence;
                        contact.Presence = MsnPresence.Offline;
                        
                        ActivityUtility.EmitActivity (this, new EntityPresenceChangeActivity (contact, oldpresence));
                        
                        Session.EmitContactPresenceChanged (new EntityChangeEventArgs<IPresence>(contact, MsnPresence.Offline, oldpresence));
                        Session.EmitContactChanged (new ContactEventArgs (contact));
                  }
            }
            
            [CommandHandler]
            private void OnGCFReceived (GCFCommand cmd)
            {
            }

            [CommandHandler]
            private void OnNLNReceived (NLNCommand cmd)
            {
                  MsnContact contact = cmd.Contact;
                  
                  ProcessDisplayImage(contact, cmd.DisplayImage);
                  
                  contact.ClientIdentifier = cmd.ClientIdentifier;
                  
                  if(contact.Presence != cmd.Presence)
                  {
                        IPresence oldPresence = contact.Presence;
                        contact.Presence = cmd.Presence;
                        
                        ActivityUtility.EmitActivity (this, new EntityPresenceChangeActivity (contact, oldPresence));
                        
                        Session.EmitContactPresenceChanged (new EntityChangeEventArgs<IPresence>(contact, contact.Presence, oldPresence));
                  }
                  
                  if (!contact.DisplayName.Equals(cmd.DisplayName))
                  {
                        string oldName = contact.DisplayName;
                        contact.DisplayName = cmd.DisplayName;
                        
                        ActivityUtility.EmitActivity (this, new EntityNameChangeActivity (contact, oldName));
                        
                        Session.EmitContactNameChanged (new EntityChangeEventArgs<string>(contact, contact.DisplayName, oldName));
                  }
                  
                  Session.EmitContactChanged (new ContactEventArgs (contact));
            }

            [CommandHandler]
            private void OnRMLReceived (RMLCommand msg)
            {
                  foreach (string key in msg.Items.Keys)
                  {
                        List<ListCommand.ListItem> list;
                        
                        if (msg.Items.TryGetValue(key, out list))
                        {
                              foreach (ListCommand.ListItem item in list)
                              {
                                    MsnContact contact = Session.ContactCollection.GetContact (item.Contact+"@"+key) as MsnContact;
                                    
                                    if (contact != null)
                                    {
                                          contact.ListType &= ~item.ListType;
                                          
                                          Session.EmitContactChanged (new ContactEventArgs (contact));
                                    }
                              }
                        }
                  }
            }
            
            [CommandHandler]
            private void OnADLReceived (ADLCommand cmd)
            {
                  if (!_initialDone && cmd.IsOK)
                  {
                        _adlWaiting--;
                        
                        if (_adlWaiting == 0)
                        {
                              Log.Debug ("All ADL OKs received");
                              
                              _initialDone = true;
                              
                              CompleteLogin();
                        }
                        else
                              Log.Debug ("Still waiting for {0} more ADL OKs", _adlWaiting);
                  }
                  
                  foreach (string key in cmd.Items.Keys)
                  {
                        List<ListCommand.ListItem> list;
                        
                        if (cmd.Items.TryGetValue(key, out list))
                        {
                              foreach (ListCommand.ListItem item in list)
                              {
                                    MsnContact contact = Session.ContactCollection.GetContact (item.Contact.UniqueIdentifier) as MsnContact;
                                    
                                    if (contact != null)
                                    {
                                          contact.ListType |= item.ListType;
                                          
                                          switch (item.ListType)
                                          {
                                                case MsnListType.Pending:
                                                case MsnListType.Reverse:
                                                      if (contact.IsInList(MsnListType.Forward))
                                                            Session.EmitContactChanged (new ContactEventArgs (contact));
                                                      else if (!contact.IsInList(MsnListType.Allowed) && !contact.IsInList (MsnListType.Blocked))
                                                      {
                                                            Session.EmitContactReverse (new ContactEventArgs (contact));
                                                            ActivityUtility.EmitActivity (this, new NewContactActivity(this.Session, contact));
                                                      }
                                                      break;
                                                
                                                default:
                                                      Session.EmitContactChanged (new ContactEventArgs (contact));
                                                      break;
                                          }
                                    }
                                    else
                                    {
                                          Session.EmitContactReverse (new ContactEventArgs (item.Contact.UniqueIdentifier));
                                          ActivityUtility.EmitActivity (this, new NewContactActivity(this.Session, item.Contact.UniqueIdentifier));
                                    }
                              }
                        }
                  }
            }
            
            [CommandHandler]
            private void OnXFRReceived (XFRCommand cmd)
            {
                  if (!cmd.IsSwitchboard)
                  {
                        //reconnect notification server
                        Session.Account.NotificationServerHostname = cmd.HostName;
                        Session.Account.NotificationServerPort = cmd.Port;
                        
                        Connection.ConnectionInfo.HostName = cmd.HostName;
                        Connection.ConnectionInfo.Port = cmd.Port;
                        Reconnect();
                  }
            }
            
            [CommandHandler]
            private void OnOUTReceived (OUTCommand cmd)
            {
                  if (cmd.OTH)
                  {
                        // We have been booted off MSN from another client.
                        Session.EmitUsurped (new SessionEventArgs (Session));
                  }
            }
            
            [CommandHandler]
            private void OnRNGReceived (RNGCommand cmd)
            {
                  SBConnection sb = Session.FindSwitchboard (cmd.SwitchboardIdentifier);
                  
                  if (sb == null)
                  {
                        sb = new SBConnection (Session, new MsnSBConnectionInfo (cmd.HostName, cmd.Port, Connection.ConnectionInfo.UseHTTP, cmd.AuthenticationString), cmd.AuthenticationString, cmd.SwitchboardIdentifier);
                        Session._switchboards.Add (sb);
                  }
                  else
                        sb.Connect (new MsnSBConnectionInfo (cmd.HostName, cmd.Port, Connection.ConnectionInfo.UseHTTP, cmd.AuthenticationString), cmd.AuthenticationString, cmd.SwitchboardIdentifier);
            }
            
            [CommandHandler]
            private void OnCVRReceived (CVRCommand cmd)
            {
                  Session.EmitLoginProgress (new SessionProgressEventArgs (Session, "Authenticating...", 0.33));
                  
                  Send (new NSUSRCommand (Session as MsnSession, true, Session.Account.UniqueIdentifier));
            }
            
            [ContentHandler]
            private void OnProfileReceived (ProfileContent content)
            {
                  Session.EmitLoginProgress (new SessionProgressEventArgs (Session, "Synchronizing...", 0.66));
                  
                  try
                  {
                        SyncMembershipsAndAddressBook (new ExceptionDelegate (delegate (Exception ex)
                        {
                              if (ex != null)
                              {
                                    Session.EmitErrorOccurred (new ErrorEventArgs (Session, "Service Unavailable", "Service Unavailable"));
                              }
                              else
                              {
                                    Session.UpdateContacts ();
                                    
                                    // Set privacy settings
                                    MsnAccount account = Session.Account as MsnAccount;
                                    SetPrivacy (account.AllowUnknownContacts);
                              }
                        }));
                  }
                  catch (Exception ex)
                  {
                        Log.Warn (ex, "Error Requesting Lists");
                        
                        Session.EmitErrorOccurred (new ErrorEventArgs (Session, "Service Unavailable", "Service Unavailable"));
                        return;
                  }
            }
            
            [CommandHandler]
            private void OnBLPReceived (BLPCommand cmd)
            {
                  if (!_isSynchronized)
                  {
                        _isSynchronized = true;
                        
                        HandleContacts();
                  }
            }

            [CommandHandler]
            private void OnUSRReceived (NSUSRCommand cmd)
            {
                  if (cmd.IsOK)
                  {
                        Session.EmitLoginProgress (new SessionProgressEventArgs (Session, "Synchronizing...", 0.66));
                        return;
                  }
                  
                  BeginSSOAuthentication (cmd);
            }
            
            [CommandHandler]
            private void OnCHLReceived (CHLCommand cmd)
            {
                  Send (new QRYCommand (Session as MsnSession, MsnConstants.ProductID, new Challenge (MsnConstants.ProductID, MsnConstants.ProductKey).GetChallengeResponse (cmd.Challenge)));
            }
            
            [ContentHandler]
            void OIMNotificationContentReceived (OIMNotificationContent content)
            {
                  ProcessMailData (content.MailData);
            }
            
            [CommandHandler]
            void OnSBSReceived (SBSCommand cmd)
            {
                  
            }
            
            [ContentHandler]
            void OnInitialMailReceived (InitialEmailContent content)
            {
                  ProcessMailData (content.MailData);
            }
            
            [CommandHandler]
            void OnCHGReceived (CHGCommand cmd)
            {
                  IPresence old_presence = Session.Account.Presence;
                  Session.Account.SetPresence (cmd.Presence);
                  Session.Account.EmitPresenceChange (new EntityChangeEventArgs<IPresence> (Session.Account, cmd.Presence, old_presence));
            }
            
            [CommandHandler]
            void OnUUXReceived (UUXCommand cmd)
            {
            }
            
            [CommandHandler]
            void OnQRYReceived (QRYCommand cmd)
            {
                  
            }
            
            [CommandHandler]
            void OnUBNCommand (UBNCommand cmd)
            {
                  Log.Info ("Received UBN Command\n{0}", Encoding.UTF8.GetString (cmd.ToByteArray ()));
            }
            
            [CommandHandler]
            void OnUUNReceived (UUNCommand cmd)
            {
                  
            }
            
            [CommandHandler]
            void OnUBMCommand (UBMCommand cmd)
            {
                  if ((cmd.Type == UUMType.TextMessage) || (cmd.Type == UUMType.TypingUser) || (cmd.Type == UUMType.Nudge))
                  {
                        MsnConversation conv = Session.Conversations.GetConversation (cmd.Source as MsnContact) as MsnConversation;
                        
                        if (conv == null)
                        {
                              conv = new MsnConversation (cmd.Source as MsnContact);
                              Session.Conversations.Add (conv);
                        }
                        
                        conv.ProcessContent (cmd.Content);
                  }
                  else
                        Log.Info ("Received Unknown UBM Command\n{0}", Encoding.UTF8.GetString (cmd.ToByteArray ()));
            }
            
            [CommandHandler]
            void OnUUMReceived (UUMCommand cmd)
            {
                  
            }
            
            [CommandHandler]
            void OnQNGReceived (QNGCommand cmd)
            {
                  // Reset _missedQNGs (number of consecutively missed QNGs) to 0
                  _missedQNGs = 0;
                  
                  // Reset _pngTimer with our new timeout
                  lock (_pngTimer)
                  {
                        if (_pngTimer.Enabled)
                              _pngTimer.Stop ();
                        
                        _pngTimer.Interval = cmd.Delay * 1000;
                        
                        _pngTimer.Start ();
                  }
            }
            
            
            [CommandHandler]
            void OnNOTReceived (NOTCommand cmd)
            {
            }
#pragma warning restore 169
#endregion Command Handlers
            
            ServiceFilter GetMembershipServiceFilter ()
            {
                  // TODO: Which services can we actually make use of?
                  
                  ServiceFilter filter = new ServiceFilter ();
                  filter.Types.Add (new ServiceType ("Messenger"));
                  filter.Types.Add (new ServiceType ("Invitation"));
                  filter.Types.Add (new ServiceType ("SocialNetwork"));
                  filter.Types.Add (new ServiceType ("Space"));
                  filter.Types.Add (new ServiceType ("Profile"));
                  
                  return filter;
            }
            
            internal void SyncMembershipsAndAddressBook (params ExceptionDelegate[] callbacks)
            {
                  bool membershipsDone = false;
                  bool addressBookDone = false;
                  bool cancel = false;
                  
                  SyncMemberships (new ExceptionDelegate (delegate (Exception ex)
                  {
                        if (cancel)
                              return;
                        
                        if (ex != null)
                        {
                              cancel = true;
                              
                              foreach (ExceptionDelegate cb in callbacks)
                                    cb (ex);
                        }
                        else
                        {
                              membershipsDone = true;
                              
                              if (membershipsDone && addressBookDone)
                              {
                                    foreach (ExceptionDelegate cb in callbacks)
                                          cb (null);
                              }
                        }
                  }));
                  SyncAddressBook (ABPartnerScenario.Initial, new ExceptionDelegate (delegate (Exception ex)
                  {
                        if (cancel)
                              return;
                        
                        if (ex != null)
                        {
                              cancel = true;
                              
                              foreach (ExceptionDelegate cb in callbacks)
                                    cb (ex);
                        }
                        else
                        {
                              addressBookDone = true;
                              
                              if (membershipsDone && addressBookDone)
                              {
                                    foreach (ExceptionDelegate cb in callbacks)
                                          cb (null);
                              }
                        }
                  }));
            }
            
            internal void SyncMemberships (params ExceptionDelegate[] callbacks)
            {
                  ServiceFilter filter = GetMembershipServiceFilter ();
                  string lastModified = _accountConfig.GetString ("MembershipsLastModified", null);
                  
                  if ((!string.IsNullOrEmpty (lastModified)) && ((Session._memberships != null) || (Session.Account.Cache as MsnAccountCache).LoadCachedMembership (lastModified)))
                  {
                        Log.Debug ("Requesting membership delta since {0}", lastModified);
                        
                        // For some reason FindMembership seems to need this...
                        string dt = DateTime.Parse (lastModified).ToUniversalTime ().ToString ("yyyy-MM-ddThh:mm:ss.fffzzz");
                        
                        Session.SharingService.BeginFindMembershipDelta (filter, ABView.Full, true, dt, MembershipsFound, new FindMembershipState (true, callbacks));
                  }
                  else
                  {
                        Log.Debug ("No valid cached membership data, requesting full membership");
                        
                        Session.SharingService.BeginFindMembership (filter, MembershipsFound, new FindMembershipState (false, callbacks));
                  }
            }
            
            internal void SyncAddressBook (ABPartnerScenario scenario, params ExceptionDelegate[] callbacks)
            {
                  string lastModified = _accountConfig.GetString ("AddressBookLastModified", null);
                  
                  bool delta = (!string.IsNullOrEmpty (lastModified)) && ((Session._addressBook != null) || (Session.Account.Cache as MsnAccountCache).LoadCachedAddressBook (lastModified));
                  
                  if (delta)
                        Log.Debug ("Requesting address book delta since {0} UTC", lastModified);
                  else
                        Log.Debug ("No valid cached address book, requesting it all");
                  
                  // Retrieve Address Book
                  
                  Session.ABService.appHeader.PartnerScenario = scenario;
                  Session.ABService.BeginABFindAll (new Guid (), ABView.Full, delta, delta ? lastModified : "0001-01-01T00:00:00.0000000-08:00", AddressBookFound,
                                                    new ABFindAllState (delta, callbacks, scenario));
            }
            
            void MembershipsFound (IAsyncResult asyncResult)
            {
                  ThreadUtility.Check ();
                  
                  FindMembershipState state = asyncResult.AsyncState as FindMembershipState;
                  
                  try
                  {
                        FindMembershipResult membershipResult = null;
                        
                        if (state.Delta)
                              membershipResult = Session.SharingService.EndFindMembershipDelta (asyncResult);
                        else
                              membershipResult = Session.SharingService.EndFindMembership (asyncResult);
                        
                        _accountConfig.SetString ("MembershipsLastModified", membershipResult.OwnerNamespace.LastChangeString);
                        
                        if (!state.Delta)
                              Session._memberships = null;
                        
                        Session.UpdateMemberships (membershipResult);
                        
                        foreach (ExceptionDelegate cb in state.Callbacks)
                              cb (null);
                  }
                  catch (SoapException ex)
                  {
                        if ((ex.Detail != null) && (ex.Detail["errorcode"] != null) && (ex.Detail["errorcode"].InnerText == "ABDoesNotExist"))
                        {
                              // This is a brand new account, it doesn't have an address book yet
                              
                              ABInfo info = new ABInfo ();
                              info.Name = null;
                              info.OwnerPuid = 0;
                              info.OwnerEmail = Session.Account.UniqueIdentifier;
                              info.FDefault = true;
                              
                              //TODO: make this async
                              Session.ABService.ABAdd (info);
                              
                              Session.UpdateMemberships (new FindMembershipResult ());
                              
                              foreach (ExceptionDelegate cb in state.Callbacks)
                                    cb (null);
                        }
                        else if (state.Delta)
                        {
                              Log.Error (ex, "Unable to retrieve membership delta, requesting whole membership list");
                              
                              Session.SharingService.BeginFindMembership (GetMembershipServiceFilter (), MembershipsFound, new FindMembershipState (false, state.Callbacks));
                        }
                        else
                        {
                              foreach (ExceptionDelegate cb in state.Callbacks)
                                    cb (ex);
                        }
                  }
                  catch (Exception ex)
                  {
                        foreach (ExceptionDelegate cb in state.Callbacks)
                              cb (ex);
                  }
            }
            
            void AddressBookFound (IAsyncResult asyncResult)
            {
                  ThreadUtility.Check ();
                  
                  ABFindAllState state = asyncResult.AsyncState as ABFindAllState;
                  
                  try
                  {
                        ABFindAllResult abResult = Session.ABService.EndABFindAll (asyncResult);
                        
                        _accountConfig.SetString ("AddressBookLastModified", abResult.AB.LastChangeString);
                        
                        if (!state.Delta)
                              Session._addressBook = null;
                        
                        Session.UpdateAddressBook (abResult);
                        
                        foreach (ExceptionDelegate cb in state.Callbacks)
                              cb (null);
                  }
                  catch (Exception ex)
                  {
                        if (state.Delta)
                        {
                              Log.Warn (ex, "Unable to retrieve address book delta, requesting whole address book");
                              
                              Session.ABService.appHeader.PartnerScenario = state.Scenario;
                              Session.ABService.BeginABFindAll (new Guid (), ABView.Full, false, "0001-01-01T00:00:00.0000000-08:00", AddressBookFound,
                                                                new ABFindAllState (false, state.Callbacks, state.Scenario));
                        }
                        else
                        {
                              foreach (ExceptionDelegate cb in state.Callbacks)
                                    cb (ex);
                        }
                  }
            }
            
            private void HandleContacts ()
            {
                  int count = 0;
                  int adlComplete = 0;
                  
                  lock (Session.ContactCollection)
                  {
                        foreach (MsnContact contact in Session.ContactCollection)
                              if (contact.IsInList (MsnListType.Forward))
                                    adlComplete++;
                        
                        if (adlComplete == 0)
                        {
                              // There's no contacts to handle, so we don't need the ADL
                              
                              _initialDone = true;
                              CompleteLogin();
                        }
                        else
                        {
                              ADLCommand adl = new ADLCommand (Session as MsnSession);
                              
                              foreach (MsnContact contact in Session.ContactCollection)
                              {
                                    contact.Load ();
                                    
                                    //only send contacts in FL
                                    if (contact.IsInList (MsnListType.Forward))
                                    {
                                          //contacts that are not in the list should not be sent (official client behavior)
                                          if (!adl.Add (contact))
                                          {
                                                Log.Warn ("Unable to add contact '{0}' to ADL command", contact.UniqueIdentifier);
                                                continue;
                                          }
                                          
                                          //Log.Debug ("Added contact {0} to ADL command", contact.UniqueIdentifier);
                                          
                                          count++;
                                          
                                          //ADL can contain max 150 items (~7500 bytes), we take a safer limit
                                          if ((count > 0 && (count % 125) == 0) || count >= adlComplete)
                                          {
                                                //Log.Debug ("Sending ADL:\n{0}", Encoding.UTF8.GetString (adl.ToByteArray ()));
                                                
                                                _adlWaiting++;
                                                Send (adl);
                                                adl = new ADLCommand (Session as MsnSession);
                                          }
                                          
                                          if (count >= adlComplete)
                                                break;
                                    }
                              }
                        }
                  }
            }
            
            private void CompleteLogin ()
            {
                  MsnAccount account = Session.Account as MsnAccount;
                  
                  // Setup the account using its own data.
                  Send (new PRPCommand (Session, account.DisplayName), delegate {});
                  Send (new CHGCommand (Session, account.Presence, MsnClientIdentifier.Default, account.DisplayImage as MsnDisplayImage), delegate {});
                  Send (new UUXCommand (Session, account.DisplayMessage, account.CurrentMedia), delegate {});
                  
                  // Obtain the roaming profile.
                  Session.RequireSecurityTokens (new ExceptionDelegate (delegate
                  {
                        Log.Debug ("Requesting roaming profile");
                        
                        ProfileHandle handle = new ProfileHandle ();
                        ProfileAttributes attributes = new ProfileAttributes ();
                        
                        handle.Alias = new ProfileAlias (Session._addressBook.AB.Info.OwnerCID.ToString (), "MyCidStuff");
                        handle.RelationshipName = "MyProfile";
                        
                        Session.StoreService.BeginGetProfile (handle, attributes, ProcessRoamingProfile, null);
                  }), SecurityToken.Storage);
                  
                  // Emit that the login sequence is completed.
                  Session.EmitLoginCompleted (new SessionEventArgs (Session));
                  
                  // Start the PNG timer if we are not using HTTP method.
                  lock (_pngTimer)
                  {
                        _pngTimer.Interval = 50000;
                        
                        if (!(Connection is HTTPConnection))
                              _pngTimer.Start ();
                  }
                  
                  // Check to see if we have any contacts that should be emitted the "new contact" activity.
                  foreach (MsnContact contact in this.Session.ContactCollection)
                  {
                        // Look for contacts that have ONLY reverse list.
                        
                        if (contact.IsInList (MsnListType.Reverse) || contact.IsInList(MsnListType.Pending))
                        {
                              if (!contact.IsInList (MsnListType.Allowed) && !contact.IsInList (MsnListType.Blocked) && !contact.IsInList (MsnListType.Forward))
                              {
                                    ActivityUtility.EmitActivity (this, new NewContactActivity(this.Session, contact));
                                    Session.EmitContactReverse (new ContactEventArgs (contact));
                              }
                        }
                  }
            }
            
            public void SetPrivacy (bool allowUnknownContacts)
            {
                  Send (new BLPCommand (Session as MsnSession, allowUnknownContacts));
            }
            
            public bool MoveContactToGroup (out string error, string passport, string fromGuid, string toGuid)
            {
                  if (!AddContactToGroups (out error, passport, toGuid))
                        return false;
                  
                  if (!RemoveContactFromGroups (out error, passport, fromGuid))
                        return false;
                  
                  return true;
            }
            
            public bool AddContactToGroups (out string error, string passport, params string [] guids)
            {
                  error = "None";
                  
                  MsnContact contact = Session.ContactCollection.GetContact (passport) as MsnContact;
                  
                  try
                  {
                        ABContact abContact = new ABContact ();
                        abContact.Id = new Guid (contact.Guid);
                        abContact.IdSpecified = true;
                        
                        ABContactCollection abContacts = new ABContactCollection ();
                        abContacts.Add (abContact);
                        
                        GroupFilter groupFilter = new GroupFilter ();
                        foreach (string guidStr in guids)
                              groupFilter.GroupIDs.Add (new Guid (guidStr));
                        
                        Session.ABService.ABGroupContactAdd (new Guid (), groupFilter, abContacts, null);
                  }
                  catch (Exception ex)
                  {
                        error = ex.Message;
                        return false;
                  }
                  
                  foreach (string guid in guids)
                  {
                        MsnGroup group = Session.GroupCollection.GetGroup(guid) as MsnGroup;
                        group.Add(contact);
                        
                        Session.EmitContactAdded(new ContactListEventArgs(contact, group));
                  }
                  
                  MsnGroup groupless = Session.GroupCollection.GetGroup ("0") as MsnGroup;
                  if (groupless.Contains (contact))
                        groupless.Remove (contact);
                  
                  Session.EmitContactRemoved (new ContactListEventArgs(contact, groupless));
                  
                  return true;
            }
            
            public bool AddContactWithGroups (out string error, string passport, string alias, bool block, params string [] guids)
            {
                  error = "None";
                  
                  ABGroupContactAddResult result;
                  
                  try
                  {
                        ABContact abContact = new ABContact ();
                        abContact.Info = new ContactInfo ();
                        abContact.Info.PassportName = passport;
                        abContact.Info.IsMessengerUser = true;
                        abContact.Info.IsSmtp = false;
                        abContact.Info.IsSmtpSpecified = true;
                        
                        if (!string.IsNullOrEmpty (alias))
                              abContact.Info.AddAnnotation ("AB.NickName", alias);
                        
                        ABContactCollection abContacts = new ABContactCollection ();
                        abContacts.Add (abContact);
                        
                        GroupFilter groupFilter = new GroupFilter ();
                        foreach (string guidStr in guids)
                              groupFilter.GroupIDs.Add (new Guid (guidStr));
                        
                        result = Session.ABService.ABGroupContactAdd (new Guid (), groupFilter, abContacts, new ABGroupContactAddOptions ());
                  }
                  catch (Exception ex)
                  {
                        error = ex.Message;
                        return false;
                  }
                  
                  MsnContact contact = Session.FindContact (passport);
                  
                  contact.AddToLists (MsnListType.Forward | (block ? MsnListType.Blocked : MsnListType.Allowed));
                  contact.Guid = result.Guid.ToString ();
                  
                  SyncAddressBook (ABPartnerScenario.ContactSave);
                  
                  foreach (string guid in guids)
                  {
                        MsnGroup group = Session.GroupCollection.GetGroup(guid) as MsnGroup;
                        group.Add(contact);
                        
                        Session.EmitContactAdded (new ContactListEventArgs(contact, group));
                  }
                  
                  MsnGroup groupless1 = Session.GroupCollection.GetGroup ("0") as MsnGroup;
                  
                  if (groupless1.Contains (contact))
                  {
                        groupless1.Remove (contact);
                        Session.EmitContactRemoved (new ContactListEventArgs(contact, groupless1));
                  }
                  
                  return true;
            }
            
            public bool AddContact (out string error, string passport, string alias, bool block)
            {
                  error = "None";
                  
                  ABContactAddResult result;
                  
                  try
                  {
                        ABContact abContact = new ABContact ();
                        
                        abContact.Info = new ContactInfo ();
                        abContact.Info.PassportName = passport;
                        abContact.Info.IsMessengerUser = true;
                        abContact.Info.Type = ContactType.Regular;
                        
                        if (!string.IsNullOrEmpty (alias))
                        {
                              abContact.Info.MemberInfo = new MessengerMemberInfo ();
                              abContact.Info.MemberInfo.DisplayName = alias;
                        }
                        
                        ABContactCollection abContacts = new ABContactCollection ();
                        abContacts.Add (abContact);
                        
                        result = Session.ABService.ABContactAdd (new Guid (), abContacts, new ABContactAddOptions ());
                  }
                  catch (Exception ex)
                  {
                        error = ex.Message;
                        return false;
                  }
                  
                  MsnContact contact = Session.FindContact (passport);
                  
                  contact.AddToLists (MsnListType.Forward | (block ? MsnListType.Blocked : MsnListType.Allowed));
                  contact.Guid = result.Guid;
                  
                  MsnGroup group1 = Session.GroupCollection.GetGroup ("0") as MsnGroup;
                  group1.Add (contact);
                  
                  SyncAddressBook (ABPartnerScenario.ContactSave);
                  
                  Session.EmitContactAdded (new ContactListEventArgs (contact, group1));
                  
                  return true;
            }
            
            public bool RemoveContactFromGroups (out string error, string passport, params string [] guids)
            {
                  error = "None";
                  MsnContact contact = Session.ContactCollection.GetContact (passport) as MsnContact;
                  
                  if (contact != null)
                  {
                        try
                        {
                              ABContact abContact = new ABContact ();
                              abContact.Id = new Guid (contact.Guid);
                              abContact.IdSpecified = true;
                              
                              ABContactCollection abContacts = new ABContactCollection ();
                              abContacts.Add (abContact);
                              
                              GroupFilter groupFilter = new GroupFilter ();
                              foreach (string guidStr in guids)
                                    if (!guidStr.Equals ("0"))
                                          groupFilter.GroupIDs.Add (new Guid (guidStr));
                              
                              if (groupFilter.GroupIDs.Count > 0)
                                    Session.ABService.ABGroupContactDelete (new Guid (), abContacts, groupFilter);
                        }
                        catch (Exception ex)
                        {
                              error = ex.Message;
                              return false;
                        }
                        
                        foreach (string guid in guids)
                        {
                              // If we try and remove the user from group 0, we
                              MsnGroup group = Session.GroupCollection.GetGroup (guid) as MsnGroup;
                              group.Remove (contact);
                              Session.EmitContactRemoved (new ContactListEventArgs (contact, group));
                        }
                        
                        // If the contact is no longer part of any group, we have to add him to the "0" group.
                        bool found = false;
                        foreach (MsnGroup group in Session.GroupCollection)
                              if (group.Contains(contact))
                                    found = true;
                        
                        if (!found)
                        {
                              MsnGroup groupless = Session.GroupCollection.GetGroup ("0") as MsnGroup;
                              groupless.Add (contact);
                              Session.EmitContactAdded (new ContactListEventArgs(contact, groupless));
                        }
                        
                        return true;
                  }
                  
                  return false;
            }
            
            public bool RemoveContact (out string error, string passport)
            {
                  error = "None";
                  MsnContact contact = Session.ContactCollection.GetContact (passport) as MsnContact;
                  
                  if (contact != null && contact.Guid != null && contact.Guid != String.Empty)
                  {
                        try
                        {
                              ABContact abContact = new ABContact ();
                              abContact.Id = new Guid (contact.Guid);
                              abContact.IdSpecified = true;
                              
                              ABContactCollection abContacts = new ABContactCollection ();
                              abContacts.Add (abContact);
                              
                              Session.ABService.ABContactDelete (new Guid (), abContacts);
                        }
                        catch (Exception ex)
                        {
                              error = ex.Message;
                              return false;
                        }
                        
                        contact.RemoveFromLists (MsnListType.Forward);
                        
                        foreach (MsnGroup group in Session.GroupCollection)
                        {
                              if (group.Contains (contact))
                              {
                                    group.Remove (contact);
                                    Session.EmitContactRemoved (new ContactListEventArgs (contact, group));
                              }
                        }
                        
                        return true;
                  }
                  else
                  {
                        Log.Fatal ("Contact does not have a GUID to remove them from your list! Bad Internal Data!");
                  }
                  
                  return false;
            }
            
            public bool AddGroup (out string error, string name)
            {
                  error = "";
                  
                  ABGroupAddResult result;
                  
                  try
                  {
                        ABGroupAddInfo addInfo = new ABGroupAddInfo ();
                        addInfo.GroupInfo.Name = name;
                        addInfo.GroupInfo.Type = new Guid ("C8529CE2-6EAD-434d-881F-341E17DB3FF8");
                        addInfo.GroupInfo.TypeSpecified = true;
                        addInfo.GroupInfo.Messenger = false;
                        addInfo.GroupInfo.MessengerSpecified = true;
                        addInfo.GroupInfo.Annotations["MSN.IM.Display"] = "1";
                        addInfo.GroupInfo.AnnotationsSpecified = true;
                        
                        result = Session.ABService.ABGroupAdd (new Guid (), new ABGroupAddOptions (), addInfo);
                  }
                  catch (Exception ex)
                  {
                        error = ex.Message;
                        return false;
                  }
                  
                  MsnGroup group = Session.GroupCollection.GetGroup (result.Guid.ToString ()) as MsnGroup;
                  
                  if (group == null)
                        group = new MsnGroup (Session, result.Guid.ToString (), name, false);
                  
                  Session.GroupCollection.Add (group);
                  
                  Session.EmitGroupAdded (new GroupEventArgs (group));
                  
                  return true;
            }
            
            public bool RemoveGroup (out string error, string guid, bool clear)
            {
                  error = "";
                  
                  MsnGroup group = Session.GroupCollection.GetGroup (guid) as MsnGroup;
                  
                  if (group != null)
                  {
                        List<string> uids = new List<string> ();
                        
                        foreach (MsnContact contact in group)
                              uids.Add(contact.UniqueIdentifier);
                        
                        foreach (string uid in uids)
                        {
                              Log.Debug ("Removing uid from group: "+uid);
                              
                              MsnContact contact = group.GetContact (uid) as MsnContact;
                              
                              if (clear)
                              {
                                    if (!RemoveContact (out error, contact.UniqueIdentifier))
                                          return false;
                              }
                              else
                              {
                                    if (!RemoveContactFromGroups (out error, contact.UniqueIdentifier, group.UniqueIdentifier))
                                          return false;
                              }
                        }
                        
                        try
                        {
                              GroupFilter groupFilter = new GroupFilter ();
                              groupFilter.GroupIDs.Add (new Guid (guid));
                              
                              Session.ABService.ABGroupDelete (new Guid (), groupFilter);
                        }
                        catch (Exception ex)
                        {
                              error = ex.Message;
                              return false;
                        }
                        
                        Session.GroupCollection.Remove (group);
                        
                        Session.EmitGroupRemoved (new GroupEventArgs (group));
                        
                        return true;
                  }
                  
                  return false;
            }
            
            public bool RenameGroup (out string error, string guid, string name)
            {
                  error = "";
                  
                  MsnGroup group = Session.GroupCollection.GetGroup(guid) as MsnGroup;
                  
                  if (group != null)
                  {
                        try
                        {
                              ABGroup abGroup = new ABGroup ();
                              abGroup.Id = new Guid (guid);
                              abGroup.IdSpecified = true;
                              abGroup.Info.Name = name;
                              abGroup.InfoSpecified = true;
                              abGroup.PropertiesChanged = "GroupName";
                              
                              ABGroupCollection abGroups = new ABGroupCollection ();
                              abGroups.Add (abGroup);
                              
                              Session.ABService.ABGroupUpdate (new Guid (), abGroups);
                        }
                        catch (Exception ex)
                        {
                              error = ex.Message;
                              return false;
                        }
                        
                        group.Name = name;
                                                
                        Session.EmitGroupRenamed (new GroupEventArgs (group));
                        
                        return true;
                  }
                  
                  return false;
            }
            
            void ProcessMailData (string data)
            {
                  if (data == "too-large")
                  {
                        // The mail data was too large to fit into a command payload, so
                        // we have to request it via SOAP
                        
                        RetrieveMailData ();
                        return;
                  }
                  else if (string.IsNullOrEmpty (data))
                        return;
                  
                  XmlDocument xml = new XmlDocument ();
                  xml.LoadXml (data);
                  
                  List<XmlElement> oimElements = MsnXmlUtility.FindChildren (xml.DocumentElement, "M");
                  
                  if (oimElements.Count > 0)
                  {
                        Session.RequireSecurityTokens (new ExceptionDelegate (delegate
                        {
                              lock (_offlineMessages)
                              {
                                    foreach (XmlElement oimElement in oimElements)
                                    {
                                          string id = MsnXmlUtility.FindChild (oimElement, "I").InnerText;
                                          string senderPassport = MsnXmlUtility.FindChild (oimElement, "E").InnerText;
                                          
                                          _offlineMessagesToRetrieve++;
                                          
                                          Session.OIMService.BeginGetMessage (id, false, OIMReceived, new KeyValuePair<string, string> (id, senderPassport));
                                    }
                              }
                        }), SecurityToken.Messenger);
                  }
            }
            
            void OIMReceived (IAsyncResult asyncResult)
            {
                  try
                  {
                        lock (_offlineMessages)
                        {
                              KeyValuePair<string, string> pair = (KeyValuePair<string, string>)asyncResult.AsyncState;
                              
                              string msgID = pair.Key;
                              IMsnEntity source = Session.FindContact (pair.Value);
                              
                              MsnOfflineTextMessage msg = MsnOfflineTextMessage.FromMime (Session.OIMService.EndGetMessage (asyncResult));
                              msg.Source = source;
                              msg.ID = msgID;
                              
                              if (!_offlineMessages.ContainsKey (msg.RunID))
                                    _offlineMessages.Add (msg.RunID, new OrderedDictionary<int, MsnOfflineTextMessage> (true));
                              
                              _offlineMessages[msg.RunID].Add (msg.SequenceNum, msg);
                              
                              _offlineMessagesToRetrieve--;
                              
                              Log.Debug ("RunID {0}, waiting for {1}", msg.RunID, _offlineMessagesToRetrieve);
                              
                              if (_offlineMessagesToRetrieve == 0)
                                    OIMsAllReceived ();
                        }
                  }
                  catch (Exception ex)
                  {
                        Log.Warn (ex, "Error retrieving OIM, retrying");
                        
                        // Try to retrieve the message again
                        Session.RequireSecurityTokens (new ExceptionDelegate (delegate
                        {
                              lock (_offlineMessages)
                              {
                                    KeyValuePair<string, string> pair = (KeyValuePair<string, string>)asyncResult.AsyncState;
                                    
                                    string id = pair.Key;
                                    string senderPassport = pair.Value;
                                    
                                    _offlineMessagesToRetrieve++;
                                    
                                    Session.OIMService.BeginGetMessage (id, false, OIMReceived, new KeyValuePair<string, string> (id, senderPassport));
                              }
                        }), SecurityToken.Messenger);
                  }
            }
            
            void OIMsAllReceived ()
            {
                  List<string> toDelete = new List<string> ();
                  
                  foreach (Guid runID in _offlineMessages.Keys)
                  {
                        MsnConversation conv = (Session.Conversations as MsnConversationManager).GetConversation (runID.ToString ());
                        
                        if (conv == null)
                        {
                              MsnContact src = null;
                              
                              //TODO: More efficient way to find the source?
                              foreach (MsnOfflineTextMessage msg in _offlineMessages[runID].Values)
                              {
                                    src = msg.Source as MsnContact;
                                    break;
                              }
                              
                              // Check if we have a conversation with the source contact already
                              conv = Session.Conversations.GetPrivateConversation (src) as MsnConversation;
                              
                              if (conv == null)
                              {
                                    // We have no conversation with the source, create one
                                    
                                    conv = new MsnConversation (src, runID.ToString ());
                                    Session.Conversations.Add (conv);
                              }
                        }
                        
                        _offlineMessages[runID].Sort (delegate (KeyValuePair<int, MsnOfflineTextMessage> item1, KeyValuePair<int, MsnOfflineTextMessage> item2)
                        {
                              return item1.Key - item2.Key;
                        });
                        
                        foreach (MsnOfflineTextMessage msg in _offlineMessages[runID].Values)
                        {
                              PlainTextContent content = new PlainTextContent (Session);
                              content.Message = msg;
                              
                              conv.ProcessContent (content);
                              
                              toDelete.Add (msg.ID);
                        }
                  }
                  
                  _offlineMessages.Clear ();
                  
                  Log.Debug ("Ready to delete {0} offline messages", toDelete.Count);
                  
                  Session.RequireSecurityTokens (new ExceptionDelegate (delegate
                  {
                        while (toDelete.Count > 0)
                        {
                              List<string> thisDelete = new List<string> ();
                              for (int i = 0; i < Math.Min (_maxOIMDelete, toDelete.Count); i++)
                                    thisDelete.Add (toDelete[i]);
                              
                              toDelete.RemoveRange (0, thisDelete.Count);
                              
                              Session.OIMService.BeginDeleteMessages (thisDelete.ToArray (), delegate (IAsyncResult asyncResult)
                              {
                                    try
                                    {
                                          Session.OIMService.EndDeleteMessages (asyncResult);
                                          Log.Debug ("{0} offline messages deleted", thisDelete.Count);
                                    }
                                    catch (Exception ex)
                                    {
                                          Log.Error (ex, "Error deleting OIMs");
                                    }
                              }, null);
                        }
                  }), SecurityToken.Messenger);
            }
            
            void RetrieveMailData ()
            {
                  Session.RequireSecurityTokens (new ExceptionDelegate (delegate
                  {
                        Session.OIMService.BeginGetMetadata (OIMMetadataRetrieved, null);
                  }), SecurityToken.Messenger);
            }
            
            void OIMMetadataRetrieved (IAsyncResult asyncResult)
            {
                  string mailData = Session.OIMService.EndGetMetadata (asyncResult);
                  ProcessMailData (mailData);
            }
            
            void PNGTimerElapsed (object sender, ElapsedEventArgs args)
            {
                  ThreadUtility.SyncDispatch (new VoidDelegate (delegate
                  {
                        if (_missedQNGs > 0)
                              Log.Debug ("Missed {0} QNGs", _missedQNGs);
                        
                        //TODO: how many should we allow to miss in a row?
                        if (_missedQNGs >= 3)
                        {
                              Log.Warn ("Too many QNGs missed, disconnecting");
                              
                              Connection.Disconnect ();
                              return;
                        }
                        
                        _missedQNGs++;
                        
                        Send (new PNGCommand (Session));
                  }));
            }
            
            void ProcessRoamingProfile (IAsyncResult asyncResult)
            {
                  GetProfileResult profile = Session.StoreService.EndGetProfile (asyncResult);
                  
                  Session.Account._profile = profile;
                  
                  if (!string.IsNullOrEmpty (profile.ExpressionProfile.DisplayName))
                        Session.Account.DisplayName = profile.ExpressionProfile.DisplayName;
                  
                  if (!string.IsNullOrEmpty (profile.ExpressionProfile.PersonalStatus))
                        Session.Account.DisplayMessage = profile.ExpressionProfile.PersonalStatus;
                  
                  if (profile.ExpressionProfile.Photo != null)
                  {
                        //Log.Debug ("A photo is stored in the roaming profile");
                        
                        Session.RequireSecurityTokens (new ExceptionDelegate (delegate
                        {
                              foreach (DocumentStream docStream in profile.ExpressionProfile.Photo.DocumentStreams)
                              {
                                    string uri = string.Format ("http://byfiles.storage.msn.com{0}?t={1}", docStream.PreAuthURL, Session.SecurityTokens[SecurityToken.Storage].RequestedSecurityToken.BinarySecurityToken.Ticket);

                                    Log.Debug ("Found photo document stream, uri {0}", uri);
                                    
                                    HttpWebRequest req = WebRequest.Create (uri) as HttpWebRequest;
                                    req.Timeout = 30000;
                                    
                                    req.Headers.Clear ();
                                    req.Accept = "*/*";
                                    req.Headers["Proxy-Connection"] = "Keep-Alive";
                                    req.KeepAlive = true;
                                    
                                    req.BeginGetResponse (delegate (IAsyncResult downloadAsyncResult)
                                    {
                                          try
                                          {
                                                WebResponse response = req.EndGetResponse (downloadAsyncResult);
                                                Stream stream  = response.GetResponseStream ();
                                                
                                                byte[] buf = new byte[256];
                                                byte[] data = new byte[response.ContentLength];
                                                int read;
                                                int written = 0;
                                                
                                                while ((read = stream.Read (buf, 0, buf.Length)) > 0)
                                                {
                                                      Array.Copy (buf, 0, data, written, read);
                                                      written += read;
                                                      
                                                      //Log.Debug ("Downloaded {0} of {1} bytes", written, data.Length);
                                                }
                                                
                                                ThreadUtility.SyncDispatch (new VoidDelegate (delegate
                                                {
                                                      MsnDisplayImage image = new MsnDisplayImage (Session);
                                                      image.Data = data;
                                                      Session.Account.DisplayImage = image;
                                                }));
                                          }
                                          catch (Exception ex)
                                          {
                                                Log.Error (ex, "Error downloading display image");
                                          }
                                    }, null);
                                    
                                    break;
                              }
                        }), SecurityToken.Storage);
                  }
                  //else
                  //    Log.Debug ("No photo stored in the roaming profile");
            }
            
            void BeginSSOAuthentication (NSUSRCommand cmd)
            {
                  try
                  {
                        string policy = cmd.GetAuthData (0);
                        string nonce = cmd.GetAuthData (1);
                        
                        SecurityToken.RegisterToken (SecurityToken.MessengerClear, policy);
                        
                        Session.RequireSecurityTokens (new ExceptionDelegate (delegate (Exception ex)
                        {
                              ThreadUtility.Check ();
                              
                              if (ex != null)
                              {
                                    Log.Error (ex, "Unable to authenticate");
                                    Session.EmitErrorOccurred (new ErrorEventArgs (Session, "Authentication error", "Unable to authenticate."));
                                    Disconnect ();
                                    return;
                              }
                              
                              string secToken = Session.SecurityTokens[SecurityToken.MessengerClear].RequestedSecurityToken.BinarySecurityToken.Token;
                              string binSecret = Session.SecurityTokens[SecurityToken.MessengerClear].RequestedProofToken.BinarySecret;
                              
                              Send (new NSUSRCommand (Session as MsnSession, false, secToken, CalculateMBIResponse (binSecret, nonce)));
                              
                        }), SecurityToken.MessengerClear, SecurityToken.MessengerSecure, SecurityToken.Contacts);
                  }
                  catch (Exception ex)
                  {
                        Log.Error (ex, "Unable to authenticate");
                        Session.EmitErrorOccurred (new ErrorEventArgs (Session, "Authentication error", "Unable to authenticate."));
                        Disconnect ();
                  }
            }
            
            byte[] Join (byte[] b1, byte[] b2)
            {
                  byte[] ret = new byte[b1.Length + b2.Length];
                  
                  Array.Copy (b1, ret, b1.Length);
                  Array.Copy (b2, 0, ret, b1.Length, b2.Length);
                  
                  return ret;
            }
            
            byte[] DeriveMBIKey (byte[] key, string magic)
            {
                  HMACSHA1 sha = new HMACSHA1 ();
                  sha.Key = key;
                  
                  byte[] magicb = Encoding.ASCII.GetBytes (magic);
                  
                  byte[] hash1 = sha.ComputeHash (magicb);
                  byte[] hash2 = sha.ComputeHash (Join (hash1, magicb));
                  byte[] hash3 = sha.ComputeHash (hash1);
                  byte[] hash4 = sha.ComputeHash (Join (hash3, magicb));
                  
                  byte[] ret = new byte[hash2.Length + 4];
                  Array.Copy (hash2, ret, hash2.Length);
                  Array.Copy (hash4, 0, ret, hash2.Length, 4);
                  
                  return ret;
            }
            
            string CalculateMBIResponse (string key, string nonce)
            {
                  byte[] key1 = Convert.FromBase64String (key);
                  byte[] key2 = DeriveMBIKey (key1, "WS-SecureConversationSESSION KEY HASH");
                  byte[] key3 = DeriveMBIKey (key1, "WS-SecureConversationSESSION KEY ENCRYPTION");
                  
                  HMACSHA1 sha = new HMACSHA1 ();
                  sha.Key = key2;
                  
                  byte[] hash = sha.ComputeHash (Encoding.ASCII.GetBytes (nonce));
                  
                  byte[] iv = new byte[8];
                  new Random ().NextBytes (iv);
                  
                  TripleDESCryptoServiceProvider des3 = new TripleDESCryptoServiceProvider ();
                  des3.Key = key3;
                  des3.Mode = CipherMode.CBC;
                  des3.IV = iv;
                  
                  ICryptoTransform akm = des3.CreateEncryptor ();
                  byte[] input = Encoding.ASCII.GetBytes (nonce + (char)8 + (char)8 + (char)8 + (char)8 + (char)8 + (char)8 + (char)8 + (char)8);
                  byte[] output = new byte[72];
                  akm.TransformBlock (input, 0, input.Length, output, 0);
                  
                  byte[] blob = new byte[28 + iv.Length + hash.Length + output.Length];
                  MemoryStream stream = new MemoryStream (blob);
                  BinaryWriter writer = new BinaryWriter (stream);
                  
                  writer.Write (BitUtility.FromInt32 (28, true));
                  writer.Write (BitUtility.FromInt32 (1, true));              //CRYPT_MODE_CBC
                  writer.Write (BitUtility.FromInt32 (26115, true));          //CALC_3DES
                  writer.Write (BitUtility.FromInt32 (32772, true));          //CALG_SHA1
                  writer.Write (BitUtility.FromInt32 (iv.Length, true));
                  writer.Write (BitUtility.FromInt32 (hash.Length, true));
                  writer.Write (BitUtility.FromInt32 (output.Length, true));
                  writer.Write (iv);
                  writer.Write (hash);
                  writer.Write (output);
                  
                  writer.Close ();
                  stream.Close ();
                  
                  return Convert.ToBase64String (blob);
            }
            
#region Async State Classes
            class FindMembershipState
            {
                  public bool Delta;
                  public ExceptionDelegate[] Callbacks;
                  
                  public FindMembershipState (bool delta, ExceptionDelegate[] callbacks)
                  {
                        Delta = delta;
                        Callbacks = callbacks;
                  }
            }
            
            class ABFindAllState
            {
                  public bool Delta;
                  public ExceptionDelegate[] Callbacks;
                  public ABPartnerScenario Scenario;
                  
                  public ABFindAllState (bool delta, ExceptionDelegate[] callbacks, ABPartnerScenario scenario)
                  {
                        Delta = delta;
                        Callbacks = callbacks;
                        Scenario = scenario;
                  }
            }
#endregion
      }
}

Generated by  Doxygen 1.6.0   Back to index