public async Task LoginAsync(UserStatus initialStatus = UserStatus.Online) { await @lock.WriterLockAsync(); try { if (IsLoggedIn) return; IConnection connection = null; ConnectionStream stream = null; CommandReader reader = null; CommandWriter writer = null; ResponseTracker responseTracker = null; IConnectableObservable<Command> commands = null; IDisposable commandsDisposable = null; int transferCount = 0; SocketEndPoint endPoint = SocketEndPoint.Parse("messenger.hotmail.com:1863"); string authTicket = null; while (authTicket == null) { connection = new SocketConnection(); await connection.ConnectAsync(endPoint); stream = new ConnectionStream(connection); writer = new CommandWriter(stream); reader = new CommandReader(stream, new Dictionary<string, Type> { { "VER", typeof(VersionCommand) }, { "CVR", typeof(ClientVersionCommand) }, { "USR", typeof(AuthenticateCommand) }, { "XFR", typeof(TransferCommand) }, { "SYN", typeof(SynchronizeCommand) }, { "SBS", typeof(SbsCommand) }, { "MSG", typeof(MessageCommand) }, { "LST", typeof(UserCommand) }, { "LSG", typeof(GroupCommand) }, { "BPR", typeof(UserPropertyCommand) }, { "BLP", typeof(PrivacySettingCommand) }, { "GTC", typeof(PrivacySettingCommand) }, { "CHG", typeof(ChangeStatusCommand) }, { "UBX", typeof(BroadcastCommand) }, { "PRP", typeof(LocalPropertyCommand) }, { "NLN", typeof(UserOnlineCommand) }, { "ILN", typeof(InitialUserOnlineCommand) }, { "FLN", typeof(UserOfflineCommand) }, { "UUX", typeof(SendBroadcastCommand) }, { "NOT", typeof(NotificationCommand) }, { "QNG", typeof(PingCommand) }, { "CHL", typeof(ChallengeCommand) }, { "ADC", typeof(AddContactCommand) }, { "REM", typeof(RemoveContactCommand) }, { "ADG", typeof(AddGroupCommand) }, { "RMG", typeof(RemoveGroupCommand) }, { "REG", typeof(RenameGroupCommand) }, { "QRY", typeof(AcceptChallengeCommand) }, { "RNG", typeof(RingCommand) }, { "SBP", typeof(ChangeUserPropertyCommand) }, { "IMS", typeof(EnableIMCommand) }, }); commands = reader.GetReadObservable().Publish(); responseTracker = new ResponseTracker(writer, commands); commandsDisposable = commands.Connect(); var versionCommand = new VersionCommand("MSNP12"); var versionResponse = await responseTracker.GetResponseAsync<VersionCommand>(versionCommand, defaultTimeout); if (versionResponse.Versions.Length == 0) throw new ProtocolNotAcceptedException(); var clientVersionCommand = new ClientVersionCommand { LocaleId = "0x0409", OsType = "winnt", OsVersion = "5.0", Architecture = "1386", LibraryName = "MSMSGS", ClientVersion = "5.0.0482", ClientName = "WindowsMessenger", LoginName = credentials.LoginName, }; await responseTracker.GetResponseAsync<ClientVersionCommand>(clientVersionCommand, defaultTimeout); var userCommand = new AuthenticateCommand("TWN", "I", credentials.LoginName); var userResponse = await responseTracker.GetResponseAsync(userCommand, new Type[] { typeof(AuthenticateCommand), typeof(TransferCommand) }, defaultTimeout); if (userResponse is AuthenticateCommand) { authTicket = (userResponse as AuthenticateCommand).Argument; } else if (userResponse is TransferCommand) { TransferCommand transferResponse = userResponse as TransferCommand; if (transferCount > 3) throw new InvalidOperationException("The maximum number of redirects has been reached."); transferCount++; endPoint = SocketEndPoint.Parse(transferResponse.Host); commandsDisposable.Dispose(); reader.Close(); writer.Close(); connection.Dispose(); } } PassportAuthentication auth = new PassportAuthentication(); string authToken = await auth.GetToken(credentials.LoginName, credentials.Password, authTicket); var authCommand = new AuthenticateCommand("TWN", "S", authToken); var authResponse = await responseTracker.GetResponseAsync<AuthenticateCommand>(authCommand, defaultTimeout); var synCommand = new SynchronizeCommand(syncTimeStamp1 ?? "0", syncTimeStamp2 ?? "0"); var synResponse = await responseTracker.GetResponseAsync<SynchronizeCommand>(synCommand, defaultTimeout); IDisposable syncCommandsSubscription = null; List<Command> syncCommands = null; if (synResponse.TimeStamp1 != syncTimeStamp1 || synResponse.TimeStamp2 != syncTimeStamp2) { syncCommands = new List<Command>(); Type[] syncTypes = new Type[] { typeof(MessageCommand), typeof(UserCommand), typeof(GroupCommand), typeof(LocalPropertyCommand), typeof(PrivacySettingCommand), }; syncCommandsSubscription = commands .Where(c => syncTypes.Contains(c.GetType())) .Catch(Observable.Empty<Command>()) .Subscribe(c => syncCommands.Add(c)); //if we're expecting users/groups, wait for them before we proceed if (synResponse.UserCount + synResponse.GroupCount > 0) { await commands .Where(c => c is UserCommand || c is GroupCommand) .Take(synResponse.UserCount + synResponse.GroupCount) .Timeout(defaultTimeout); } } UserCapabilities capabilities = 0; MSNObject displayPicture = MSNObject.Empty; if (LocalUser != null) { capabilities = LocalUser.Capabilities; displayPicture = LocalUser.DisplayPicture; } Command changeStatusCommand = new ChangeStatusCommand(User.StatusToString(UserStatus.Online), (uint)capabilities, displayPicture != MSNObject.Empty ? displayPicture.ToString() : "0"); await responseTracker.GetResponseAsync(changeStatusCommand, defaultTimeout); if (syncCommandsSubscription != null) syncCommandsSubscription.Dispose(); this.writer = writer; this.reader = reader; this.stream = stream; this.connection = connection; this.responseTracker = responseTracker; this.commands = commands; this.commandsDisposable = commandsDisposable; if (LocalUser == null) { LocalUser = new LocalUser(this, credentials.LoginName); userCache.Add(credentials.LoginName, new WeakReference(LocalUser)); } LocalUser.Status = initialStatus; SyncEvents syncEvents = null; if (syncCommands != null) { syncTimeStamp1 = synResponse.TimeStamp1; syncTimeStamp2 = synResponse.TimeStamp2; syncEvents = ProcessSyncCommands(syncCommands); } var commandsSafe = commands .Catch<Command, ConnectionErrorException>(tx => Observable.Empty<Command>()); commandsSafe.OfType<MessageCommand>().Subscribe(cmd => HandleMessages(cmd, connection)); commandsSafe.OfType<RingCommand>().Subscribe(cmd => HandleRings(cmd, connection)); commandsSafe.OfType<BroadcastCommand>().Subscribe(cmd => HandleBroadcasts(cmd, connection)); commandsSafe.OfType<NotificationCommand>().Subscribe(cmd => HandleNotifications(cmd, connection)); commandsSafe.OfType<AddContactCommand>().Subscribe(cmd => HandleNewUsers(cmd, connection)); commandsSafe.OfType<OutCommand>().Subscribe(cmd => HandleOut(cmd, connection)); commandsSafe.OfType<ChallengeCommand>().Subscribe(cmd => HandleChallenges(cmd, connection)); commandsSafe.OfType<UserOnlineCommand>().Subscribe(cmd => HandleOnlineUsers(cmd, connection)); commandsSafe.OfType<InitialUserOnlineCommand>().Subscribe(cmd => HandleOnlineUsers(cmd, connection)); commandsSafe.OfType<UserOfflineCommand>().Subscribe(cmd => HandleOfflineUsers(cmd, connection)); connection.Error += connection_Error; IsLoggedIn = true; OnLoggedIn(); OnUserStatusChanged(new UserStatusEventArgs(LocalUser, initialStatus, UserStatus.Offline, true)); if (syncEvents != null) RaiseSyncEvents(syncEvents); } finally { @lock.WriterRelease(); } }
void LogoutInner(LogoutReason reason, ConnectionErrorException connectionError) { if (!IsLoggedIn) return; connection.Error -= connection_Error; commandsDisposable.Dispose(); connection.Close(); reader = null; writer = null; connection = null; responseTracker = null; var statusChangeEvents = new List<UserStatusEventArgs>(); foreach (User user in UserLists.ForwardList.Users.Where(u => u.Status != UserStatus.Offline)) { UserStatus prevStatus = user.Status; user.Status = UserStatus.Offline; statusChangeEvents.Add(new UserStatusEventArgs(user, UserStatus.Offline, prevStatus, false)); } IsLoggedIn = false; OnLoggedOut(new LoggedOutEventArgs(reason, connectionError)); foreach (UserStatusEventArgs e in statusChangeEvents) OnUserStatusChanged(e); }