//
// Copyright (c) 2002
// Ronald Kevin Burton
//
// Z poniższym kodem nie jest związana żadna gwarancja poprawności działania.
// Program został dołączony do książki ".NET CLR. Księga eksperta" w celu
// ilustracji koncepcji i zasad przedstawionych w tej książce. Program może być 
// używany na własne ryzyko.
//
// Przyznaje się prawo do użycia lub kopiowania tego oprogramowania do dowolnego celu
// bez konieczności ponoszenia żadnych opłat pod warunkiem, że powyższe uwagi zostaną 
// zachowane we wszystkich kopiach. Przyznaje się także prawo do modyfikacji kodu
// i dystrybucji zmodyfikowanego kodu pod warunkiem zachowania powyższych uwag
// oraz dołączenia informacji mówiącej o modyfikacji kodu.
//
// 
// Wszystkie komentarze pochodzą z pliku
// www.ietf.org/internet-drafts/draft-movva-msn-messenger-protocol-00.txt
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Security.Cryptography;
using System.Diagnostics;

namespace Messenger
{
	/// <summary>
	///	The Notification Server is the primary server component. The client
	///	and the Notification Server authenticate, synchronize user
	///	properties, and exchange asynchronous event notifications. The
	///	client's connection to the Notification Server occurs after the
	///	referral from the Dispatch Server is completed, and persists without
	///	interruption during the user's MSN Messenger Service session.
	///
	///	Some of the events transmitted between a client and a Notification
	///	Server are:  State changes (e.g. client is on-line, client is
	///	offline, client is idle), Switchboard Server invitation requests
	///	(see below), and application-specific notifications that are beyond
	///	the scope of this document. (E.g. new e-mail has arrived)
	/// </summary>

	public class NS
	{
		public event Status StatusUpdate;
		public event List ListUpdate;
		public Ring RingCallback;
		public event Change ChangeCallback;
		public event ClientOffline OfflineCallback;
		public event ClientInitial InitialCallback;
		public event ClientOnline OnlineCallback;

		private string _MSNHostServer;
		private string _MSNPassportEmail;
		private string _MSNPassportPassword;
		private string _MSNNickname;
		private MessengerForm parent;

		const int _port = 1863;
		private Thread _thread = null;
		private Socket _NSSocket;

//		CHG 7 NLN
		//   After the client is authenticated and synchronized, the client
		//   establishes its initial state with the server with the CHG command.
		//   The syntax of the command is:
		//
		//        C: CHG TrID State
		//        S: CHG TrID State
		//
		//   When the state is changed, the server will echo the settings back to
		//   client. The state shall not be considered changed until the response
		//   is received from the server.
		//
		//   Note that the server can send a state change message to the client
		//   at any time. If the server changes the state without a request from
		//   the client, the TrID parameter will be 0.
		//
		//   States are denoted by a string of three characters. The predefined
		//   states that the server recognizes are:
		//
		//   NLN - Make the client Online (after logging in) and send and receive
		//   notifications about buddies.
		//   FLN - Make the client Offline. If the client is already online,
		//   offline notifications will be sent to users on the RL. No message
		//   activity is allowed. In this state, the client can only synchronize
		//   the lists as described above.
		//   HDN - Make the client Hidden/Invisible. If the client is already
		//   online, offline notifications will be sent to users on the RL. The
		//   client will appear as Offline to others but can receive
		//   online/offline notifications from other users, and can also
		//   synchronize the lists. Clients cannot receive any instant messages
		//   in this state.
		//
		//   All other States are treated as sub-states of NLN (online). The
		//   other States currently supported are:
		//   BSY - Busy.
		//   IDL - Idle.
		//   BRB - Be Right Back.
		//   AWY - Away From Computer.
		//   PHN - On The Phone.
		//   LUN - Out To Lunch.
//		ILN 7 IDL brian.cassel@inewsroom.com brian.cassel@inewsroom.com
//	    ILN 7 NLN rmb7141@hotmail.com ({)(})%20how%20cute
		//   The client receives asynchronous notifications whenever a contact on
		//   the user's Forward List changes its state. The notifications are of
		//   the form:
		//
		//        S: NLN Substate UserHandle FriendlyName
		//        S: ILN TrID Substate UserHandle FriendlyName
		//        S: FLN UserHandle
		//
		//   NLN indicates that a user has come online.
		//   - Substate can be any three-letter code (see "Client States" above).
		//   - UserHandle and FriendlyName are the handle and names associated
		//     with the user coming online.
		//
		//   ILN is similar to the NLN message, and is received from the server
		//   in response to an CHG or ADD command from the client:
		//   1.  Immediately after the client logon and sends its first CHG
		//       command to the NS. In this case several ILNs may be received -
		//       one for each Forward List contact that is currently online.
		//   2.  After the client sends an "ADD TrID FL UserHandle
		//       CustomUserName" to the NS. (e.g. ILN for the new contact if that
		//       contact is currently online)
		//
		//   In both cases, TrID in the ILN is the same as the one sent by the
		//   client in the CHG or ADD command.
		//
		//   FLN means that the specified user is now offline.
//		SYN 8 33
		//   The SYN command is:
		//
		//        C: SYN TrID Ser#
		//        S: SYN TrID Ser#
		//
		//   The Ser# parameter sent by the client is the version of the
		//   properties currently cached on the client. The server responds with
		//   the current server version of the properties. If the server has a
		//   newer version, the server will immediately follow the SYN reply by
		//   updating the client with the latest version of the user properties.
		//   These updates are done as described below, and are done without the
		//   client explicitly initiating a LST, GTC or BLP command. Note that
		//   the server will update all server-stored properties to the client,
		//   regardless of how many entries have been changed.
		//
		//   The following "List Retrieval and Property Management" section
		//   describes the format of the user properties sent by the server.
		//   After the SYN reply from the server, the user property updates will
		//   be sent from the server in this sequence: GTC, BLP, LST FL, LST AL,
		//   LST BL, LST RL.
		//
		//   All the user property updates will share the same TrID as the SYN
		//   command and reply.
//		GTC 8 33 A
		//   Reverse List Prompting
		//
		//   The client can change its persistent setting for when to prompt the
		//   user in reaction to an Reverse List change. This is accomplished via
		//   the GTC command:
		//
		//        C: GTC TrID [A | N]
		//        S: GTC TrID Ser# [A | N]
		//
		//   The value of the A/N parameter determines how the client should
		//   behave when it discovers that a user is in its RL, but is not in its
		//   AL or BL. (Note that this occurs when a user has been added to
		//   another user's list, but has not been explicitly allowed or
		//   blocked):
		//
		//   A - Prompt the user as to whether the new user in the RL should be
		//   added to the AL or the BL
		//   N - Automatically add the new user in the RL to the AL
		//
		//   The A/N parameter is not interpreted by the server, merely stored.
		//
		//   The server will respond with the current setting if the change was
		//   successful. Otherwise, it will return an error with the matching
		//   TrID. If the client tries to change the setting to the same value as
		//   the current setting, the server will respond with an error message.
		//
		//   The default setting is A when a new user connects to the server for
		//   the first time.
//		BLP 8 33 AL
		//   Privacy Mode
		//
		//   The client can change how the server handles instant messages from
		//   users via the BLP command:
		//
		//        C: BLP TrID [AL | BL]
		//        S: BLP TrID Ser# [AL | BL]
		//
		//   The AL/BL parameter determines how the server should treat messages
		//   (MSG and RNG) from users. If the current setting is AL, messages
		//   from users who are not in BL will be delivered. If the current
		//   setting is BL, only messages from people who are in the AL will be
		//   delivered.
		//
		//   The server will respond with the current setting if the change was
		//   successful. Otherwise, it will return an error with the matching
		//   TrID. If the client tries to change the setting to the same value as
		//   the current setting, the server will respond with an error message.
		//
		//   The default setting is AL when a new user connects to the server for
		//   the first time.
//		PRP 8 33 PHH 
//		PRP 8 33 PHW 1%20608%20288-5170
//		PRP 8 33 PHM 
//		PRP 8 33 MOB N
//		PRP 8 33 MBE N
		//
//		LST 8 FL 33 1 3 rmb7141@hotmail.com ({)(})%20how%20cute
		//   List Command
		//
		//   By issuing the LST command, the client can explicitly request that a
		//   list be sent. The server will respond with a series of LST
		//   responses, one LST response for each item in the requested list.
		//
		//        C: LST TrID LIST
		//        S: LST TrID LIST Ser# Item# TtlItems UserHandle CustomUserName
		//
		//   - LIST is FL/RL/AL/BL for Forward List, Reverse List, Allow List,
		//     and Block List, respectively.
		//   - The Item# parameter contains the index of the item described in
		//     this command message. (E.g. item 1 of N, 2 of N, etc.)
		//   - The TtlItems parameter contains the total number of items in this
		//     list.
		//   - UserHandle is the user handle for this list item.
		//   - CustomUserName is the friendly name for this list item.
		//
		//   If the list is empty, the response will be:
		//
		//        S: LST TrID LIST Ser# 0 0
		//
//		BPR 33 rmb7141@hotmail.com PHH 
//		BPR 33 rmb7141@hotmail.com PHW 
//		BPR 33 rmb7141@hotmail.com PHM 
//		BPR 33 rmb7141@hotmail.com MOB N
		//
//		LST 8 FL 33 2 3 beckyburton1@home.com beckyburton1@home.com
//		BPR 33 beckyburton1@home.com PHH 
//		BPR 33 beckyburton1@home.com PHW 
//		BPR 33 beckyburton1@home.com PHM 
//		BPR 33 beckyburton1@home.com MOB N
//		LST 8 FL 33 3 3 brian.cassel@inewsroom.com brian.cassel@inewsroom.com
//		BPR 33 brian.cassel@inewsroom.com PHH 
//		BPR 33 brian.cassel@inewsroom.com PHW 
//		BPR 33 brian.cassel@inewsroom.com PHM 
//		BPR 33 brian.cassel@inewsroom.com MOB N
//		LST 8 AL 33 1 3 rmb7141@hotmail.com ({)(})%20how%20cute
//		LST 8 AL 33 2 3 beckyburton1@home.com beckyburton1@home.com
//		LST 8 AL 33 3 3 brian.cassel@inewsroom.com brian.cassel@inewsroom.com
//		LST 8 BL 33 0 0
//		LST 8 RL 33 1 2 rmb7141@hotmail.com ({)(})%20how%20cute
//		LST 8 RL 33 2 2 beckyburton1@home.com beckyburton1@home.com
//		RNG 16821974 64.4.12.183:1863 CKI 1012089677.20036 rmb7141@hotmail.com ({)(})%20how%20cute
		//   The other side of the session establishment is the behavior of the
		//   called client. The called client receives a RNG from its
		//   Notification Server and is expected to connect to the Switchboard
		//   Server and respond with an ANS.
		//
		//   The client receives a RNG from the Notification Server as follows:
		//
		//        S: RNG SessionID SwitchboardServerAddress SP AuthChallengeInfo
		//           CallingUserHandle CallingUserFriendlyName
		//
		//   - SessionID is a numeric ASCII session ID.
		//   - SwitchboardServerAddress is a DNS name or IP Address
		//   - SP is the security package in use. In this implementation only
		//     "CKI" is supported.
		//   - AuthChallengeInfo is the cookie to be passed back to the
		//     switchboard to gain entrance to the session.
		//   - CallingUserHandle is the user handle of the caller.
		//   - CallingUserFriendlyName is the custom user name of the caller.
		//
		//   To join the session, the called client connects to the Switchboard
		//   Server and carries out the following exchange to join the session:
		//
		//        C: ANS TrID LocalUserHandle AuthResponseInfo SessionID
		//        S: IRO TrID Participant# TotalParticipants UserHandle
		//           FriendlyName
		//        S: ANS TrID OK
		//
		//   The IRO commands relay to the newly joined client roster information
		//   about the current session. Each IRO command message from the
		//   Switchboard contains one participant in the session.
		//   - Participant# contains the index of the participant described in
		//     this IRO command (e.g. 1 of N, 2 of N).
		//   - TotalParticipants contains the total number of participants
		//     currently in the session.
		//
		//   The entire session roster will be sent to the new client joining the
		//   session before any JOI or BYE commands described below.
		//
		//   If no one is in the session when the user joins (an unexpected error
		//   condition), the server skips directly to "ANS TrID OK" command. All
		//   the responses from the server related to the issued ANS command will
		//   contain the same TrID as the original client ANS request.
        private void NSSocketDataMain()
		{
			try
			{
				string strData;
				byte[] buff;
				while (_NSSocket.Connected)
				{
					if (_NSSocket.Available > 0)
					{
						buff = new byte[_NSSocket.Available];
						_NSSocket.Receive(buff, _NSSocket.Available, SocketFlags.None);
						strData = UTF8Encoding.UTF8.GetString(buff);
						if(StatusUpdate != null)
							StatusUpdate(StatusMessageType.Response, strData);
						ProcessNSData(strData);
					}
					Thread.Sleep(100);
				}
				if(StatusUpdate != null)
					StatusUpdate(StatusMessageType.Status, "NS Connection Disconnected!");
			}
			catch (ThreadAbortException)
			{
			}
			catch (Exception ec)
			{
				Console.WriteLine(ec.Message + ec.StackTrace);
			}
		}

		private void HandleRNG(string request)
		{
			// RNG Request
			// RNG 11742066 64.4.13.74:1863 CKI 989495494.750408580 deaxxxx@hotmail.com Venkatesh
			
			Regex rngregex = new Regex(@"(?<RNG>RNG) (?<tid>[0-9]+) (?<ipaddress>[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+):(?<port>[0-9]+) (?<auth>CKI) (?<code>[0-9]+\.[0-9]+) (?<user>[^ ]+) (?<custom>.+)");
			Match mc = rngregex.Match(request);
			if ( !mc.Success )
			{
				return;
			}

			// We need to reply to the SB Server with:
			// ANS 1 venky_dude@hotmail.com 989495494.750408580 11742066
             				
			if(StatusUpdate != null)
				StatusUpdate(StatusMessageType.Request, "Received message request from " + mc.Groups["user"].Value + " (" + mc.Groups["custom"].Value + ") - Server: " + mc.Groups["ipaddress"].Value);

			// First we need to connect to the SB Server

			Debug.WriteLine("Connecting to SB Server...");	
			if(RingCallback != null)
			{
				IPEndPoint ep = new IPEndPoint(IPAddress.Parse(mc.Groups["ipaddress"].Value),
					                           Convert.ToInt32(mc.Groups["port"].Value));

				parent.BeginInvoke(RingCallback,
					               new object[] { Convert.ToInt32(mc.Groups["tid"].Value),
								                  ep,
										          mc.Groups["code"].Value,
										          mc.Groups["user"].Value,
										          mc.Groups["custom"].Value
										        });
			}
		}

		private void HandleCHL(string request)
		{
			// CHL will be in this format:
			// CHL 0 12653629621666346957
			// Response needs to be:
			// (1) Calculate MD5 Hash of the Challenge string prepended to Q1P7W2E4J9R8U3S5
			// (2) Respond with QRY + trid + " msmsgs@msnmsgr.com 32\r\n" + MD5Hash

			int i1 = request.IndexOf("CHL");
			int i2 = request.IndexOf(" ", i1);
			i2 = i2 + 1;
			i2 = request.IndexOf(" ", i2);
			i2 = i2 + 1;
			string str2 = request.Substring(i2);
			str2 = str2.Trim();
			str2 = str2.Replace("\n", "");
			str2 = str2.Replace("\r", "");
			str2 = "Q1P7W2E4J9R8U3S5" + str2;

			MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
			byte[] md5hash = md5.ComputeHash(Encoding.UTF8.GetBytes(str2));
			string str3 = GetByteString(md5hash);

			string tid = Convert.ToString(MessengerForm.GetNextTrialID());

			string str4 = "QRY " + tid + " msmsgs@msnmsgr.com 32\r\n" + str3;

			SendNSData(str4);
		}

		private void HandleADD(string request)
		{
			// Add will be in this format:
			// ADD 0 RL 10 atyourservice@fired-up.com AtYourService

			if (request.Substring(6, 2).ToUpper().Trim() == "RL")
			{
				int i2 = 10;
				i2 = request.IndexOf(" ", i2);
				i2 = i2 + 1;
				int i3 = request.IndexOf(" ", i2);
				string str2 = request.Substring(i2, i3 - i2);

				// SendNSData("ADD " + GetNextNSTrialID() + " AL " + str2 + " " + str2);			
			}
		}

		private void HandleLST(string request)
		{
			//	 LST 8 AL 33 1 3 rmb7141@hotmail.com ({)(})%20how%20cute
			//   List Command
			//
			//   By issuing the LST command, the client can explicitly request that a
			//   list be sent. The server will respond with a series of LST
			//   responses, one LST response for each item in the requested list.
			//
			//        C: LST TrID LIST
			//        S: LST TrID LIST Ser# Item# TtlItems UserHandle CustomUserName
			//
			//   - LIST is FL/RL/AL/BL for Forward List, Reverse List, Allow List,
			//     and Block List, respectively.
			//   - The Item# parameter contains the index of the item described in
			//     this command message. (E.g. item 1 of N, 2 of N, etc.)
			//   - The TtlItems parameter contains the total number of items in this
			//     list.
			//   - UserHandle is the user handle for this list item.
			//   - CustomUserName is the friendly name for this list item.
			//   If the list is empty, the response will be:
			//
			//        S: LST TrID LIST Ser# 0 0
			//
			Regex lstregex = new Regex(@"(?<LST>LST) (?<tid>[0-9]+) (?<list>FL|RL|AL|BL) (?<serial>[0-9]+) (?<count>[0-9]+) (?<total>[0-9]+)( (?<user>[^ ]+) (?<custom>.+))?");
			Match mc = lstregex.Match(request);
			if ( mc.Success )
			{
				if(Convert.ToInt32(mc.Groups["total"].Value) != 0)
				{
					if(ListUpdate != null)
					{
						if(mc.Groups["list"].Value == "FL")
						{
							ListUpdate(ListType.FL,
								       Convert.ToInt32(mc.Groups["count"].Value),
								       Convert.ToInt32(mc.Groups["total"].Value),
									   mc.Groups["user"].Value,
									   mc.Groups["custom"].Value);
						}
						if(mc.Groups["list"].Value == "RL")
						{
							ListUpdate(ListType.RL,
								Convert.ToInt32(mc.Groups["count"].Value),
								Convert.ToInt32(mc.Groups["total"].Value),
								mc.Groups["user"].Value,
								mc.Groups["custom"].Value);
						}
						if(mc.Groups["list"].Value == "AL")
						{
							ListUpdate(ListType.AL,
								Convert.ToInt32(mc.Groups["count"].Value),
								Convert.ToInt32(mc.Groups["total"].Value),
								mc.Groups["user"].Value,
								mc.Groups["custom"].Value);
						}
						if(mc.Groups["list"].Value == "BL")
						{
							ListUpdate(ListType.BL,
								Convert.ToInt32(mc.Groups["count"].Value),
								Convert.ToInt32(mc.Groups["total"].Value),
								mc.Groups["user"].Value,
								mc.Groups["custom"].Value);
						}
					}
				}
			}
		}

		private void HandleNLN(string request)
		{
			// NLN NLN brian.cassel@inewsroom.com brian.cassel@inewsroom.com
			Regex nlnregex = new Regex(@"(?<NLN>NLN) (?<substate>NLN|BSY|IDL|BRB|AWY|PHN|LUN) (?<user>[^ ]+) (?<alias>.+)");
			Match mc = nlnregex.Match(request);
			if(mc.Success)
			{
				if(OnlineCallback != null)
				{
					OnlineCallback(mc.Groups["substate"].Value == "NLN" ? SubStateType.NLN :
						mc.Groups["substate"].Value == "BSY" ? SubStateType.BSY :
						mc.Groups["substate"].Value == "IDL" ? SubStateType.IDL :
						mc.Groups["substate"].Value == "BRB" ? SubStateType.BRB :
						mc.Groups["substate"].Value == "AWY" ? SubStateType.AWY :
						mc.Groups["substate"].Value == "PHN" ? SubStateType.PHN :
						mc.Groups["substate"].Value == "LUN" ? SubStateType.LUN : SubStateType.NLN,
						mc.Groups["user"].Value,
						mc.Groups["alias"].Value);
				}
			}
		}

		private void HandleFLN(string request)
		{
			// FLN brian.cassel@inewsroom.com
			Regex flnregex = new Regex(@"(?<FLN>FLN) (?<user>.+)");
			Match mc = flnregex.Match(request);
			if(mc.Success)
			{
				if(OfflineCallback != null)
				{
					OfflineCallback(mc.Groups["user"].Value);
				}
			}
		}

		private void HandleILN(string request)
		{
			// ILN 12 NLN brian.cassel@inewsroom.com brian.cassel@inewsroom.com
			Regex ilnregex = new Regex(@"(?<ILN>ILN) (?<tid>[0-9]+) (?<substate>NLN|BSY|IDL|BRB|AWY|PHN|LUN) (?<user>[^ ]+) (?<alias>.+)");
			Match mc = ilnregex.Match(request);
			if(mc.Success)
			{
				if(InitialCallback != null)
				{
					InitialCallback(Convert.ToInt32(mc.Groups["tid"].Value),
						            mc.Groups["substate"].Value == "NLN" ? SubStateType.NLN :
									mc.Groups["substate"].Value == "BSY" ? SubStateType.BSY :
									mc.Groups["substate"].Value == "IDL" ? SubStateType.IDL :
									mc.Groups["substate"].Value == "BRB" ? SubStateType.BRB :
									mc.Groups["substate"].Value == "AWY" ? SubStateType.AWY :
									mc.Groups["substate"].Value == "PHN" ? SubStateType.PHN :
									mc.Groups["substate"].Value == "LUN" ? SubStateType.LUN : SubStateType.NLN,
						            mc.Groups["user"].Value,
		                            mc.Groups["alias"].Value);
				}
			}
		}

		private void HandleCHG(string request)
		{
			// CHG 7 NLN
			Regex chgregex = new Regex(@"(?<CHG>CHG) (?<tid>[0-9]+) (?<state>NLN|FLN|HDN)");
			Match mc = chgregex.Match(request);
			if(mc.Success)
			{
				if(ChangeCallback != null)
				{
					ChangeCallback(mc.Groups["state"].Value == "NLN" ? StateType.NLN :
						           mc.Groups["state"].Value == "FLN" ? StateType.FLN :
						           mc.Groups["state"].Value == "HDN" ? StateType.HDN : StateType.NLN);
				}
			}
		}
		private void ProcessNSData(string strData)
		{
			// MSN Messenger Protocol command syntax is ASCII and single line-
			// based. Commands begin with a case-sensitive, three-letter command
			// type, followed by zero or more parameters, and terminated by CRLF.
			// Parameters are separated by one or more whitespace characters and
			// cannot contain whitespace characters. Parameters that contain spaces
			// or extended (non 7-bit ASCII) characters should be encoded using
			// URL-style encoding (e.g. "%20" for space). Some commands accept un-
			// encoded binary data. In these cases, the length of the data is
			// transmitted as part of the command, and the data is transmitted
			// immediately following a CRLF of the command.

			//   5.7 User List Types
			//
			//   Some of the protocol commands are used to the manipulate lists of
			//   users. The following types of user lists are supported by the
			//   protocol:
			//
			//   Forward List (FL) - The list of users for whom a given user wants to
			//   receive state change notifications. The Forward List is what is most
			//   commonly referred to as the user's "contact list."
			//
			//   Reverse List (RL) - The list of users who have registered an
			//   interest in seeing this user's state change notifications.
			//
			//   Allow List (AL) - The list of users who the user has explicitly
			//   allowed to see state change notifications and establish client-to-
			//   client sessions via a Switchboard Server.
			//
			//   Block List (BL) - The list of users who the user has explicitly
			//   prevented from seeing state change notifications and establishing
			//   client-to-client sessions via a Switchboard Server.

			//6. Command Summary Table
			//
			//     Command  From             To           Description
			//   ==================================================================
			//     ACK      Switchboard      Client       Sends a positive message
			//                                            delivery acknowledgement.
			//   -------------------------------------------------------------------
			//     ADD      Client           Notification Adds to the user's FL, AL,
			//              Notification     Client       and BL. Notifies the client
			//                                            of asynchronous additions
			//                                            to a user's list.
			//   -------------------------------------------------------------------
			//     ANS      Client           Switchboard  Accepts a request for a
			//                                            switchboard server session.
			//   -------------------------------------------------------------------
			//     BLP      Client           Notification Changes the user's message
			//              Notification     Client       privacy setting, which
			//                                            determines how to treat
			//                                            messages from users not
			//                                            already in the BL or AL.
			//   -------------------------------------------------------------------
			//     BYE      Switchboard      Client       Notifies a client that a
			//                                            user is no longer in the
			//                                            session.
			//   -------------------------------------------------------------------
			//     CAL      Client           Switchboard  Initiates a switchboard
			//                                            server session.
			//   -------------------------------------------------------------------
			//     CHG      Client           Notification Sends a client state change
			//              Notification     Client       to the server.
			//                                            Echoes the success of
			//                                            client's state change
			//                                            request.
			//   -------------------------------------------------------------------
			//     FLN      Notification     Client       Notifies the client when
			//                                            users in the FL go off-
			//                                            line.
			//   -------------------------------------------------------------------
			//     GTC      Client           Notification Changes the user's prompt
			//              Notification     Client       setting, which determines
			//                                            how the client reacts to
			//                                            certain RL changes.
			//   -------------------------------------------------------------------
			//     INF      Client           Dispatch,    Requests set of support
			//                               Notification authentication protocol
			//              Dispatch,        Client       from the server.
			//              Notification                  Provides the set of
			//                                            supported authentication
			//                                            protocols to the client.
			//   -------------------------------------------------------------------
			//     ILN      Notification     Client       Notifies the client of the
			//                                            initial online state of a
			//                                            user in the FL, while
			//                                            either logging on or adding
			//                                            a user to the FL.
			//   -------------------------------------------------------------------
			//     IRO      Switchboard      Client       Provides the initial roster
			//                                            information for new users
			//                                            joining the session.
			//   -------------------------------------------------------------------
			//     JOI      Switchboard      Client       Notifies a client that a
			//                                            user is now in the session.
			//   -------------------------------------------------------------------
			//     LST      Client           Notification Retrieves the server's
			//              Notification     Client       version of the user's FL,
			//                                            RL, AL, or BL.
			//   -------------------------------------------------------------------
			//     MSG      Client           Switchboard  Sends a message to the
			//                                            members of the current
			//                                            session.
			//   -------------------------------------------------------------------
			//     MSG      Notification,    Client       Delivers a message from
			//              Switchboard                   another client or from a
			//                                            server-side component.
			//   -------------------------------------------------------------------
			//     NAK      Switchboard      Client       Sends a negative message
			//                                            delivery acknowledgement.
			//   -------------------------------------------------------------------
			//     NLN      Notification     Client       Notifies the client when
			//                                            users in the FL go on-line
			//                                            or when their on-line state
			//                                            changes.
			//   -------------------------------------------------------------------
			//     OUT      All              All          Ends a client-server
			//                                            Session.
			//   -------------------------------------------------------------------
			//     REM      Client           Notification Removes from the user's FL,
			//              Notification     Client       AL, and BL.
			//                                            Notifies the client of
			//                                            asynchronous removals from
			//                                            a user's list.
			//   -------------------------------------------------------------------
			//     RNG      Notification     Client       Notifies the client of a
			//                                            request by another client
			//                                            to establish a session via
			//                                            a switchboard server.
			//   -------------------------------------------------------------------
			//     SYN      Client           Notification Initiates client-server
			//              Notification     Client       property synchronization.
			//   -------------------------------------------------------------------
			//     USR      All              All          Authenticates client with
			//                                            server, possibly in
			//                                            multiple passes.
			//   ------------------------------------------------------------------
			//     VER      Client           Dispatch     Negotiates common protocol
			//              Dispatch         Client       dialect between client and
			//                                            Server.
			//   ------------------------------------------------------------------
			//     XFR      Client           Notification Requests a Switchboard
			//              Notification     Client       server for use in
			//                                            establishing a session.
			//   -------------------------------------------------------------------
			//     XFR      Dispatch         Client       Notification of login-NS to
			//              Notification     Client       the client or notification
			//                                            to move to a different NS.
			//=======================================================================
			Regex mlregex = new Regex(@"(?<string>.+)");
			MatchCollection mcoll = mlregex.Matches(strData);
			if(mcoll.Count > 1)
			{
				foreach (Match m in mcoll)
				{
					ProcessNSData(m.Value.TrimEnd());
				}
				return;
			}
			else
				strData = strData.TrimEnd();

			Debug.WriteLine(String.Format("Received data {0}: {1}", strData.Length, strData));
			
			if (strData.IndexOf("RNG") != -1)
			{		
				HandleRNG(strData);
			}
			if (strData.IndexOf("CHL") != -1)
			{
				HandleCHL(strData);
			}
			if (strData.IndexOf("ADD") != -1)
			{
				HandleADD(strData);
			}
			if (strData.IndexOf("LST") != -1)
			{
				HandleLST(strData);
			}
			if (strData.IndexOf("NLN") != -1)
			{
				HandleNLN(strData);
			}
			if (strData.IndexOf("FLN") != -1)
			{
				HandleFLN(strData);
			}
			if (strData.IndexOf("ILN") != -1)
			{
				HandleILN(strData);
			}
			if (strData.IndexOf("CHG") != -1)
			{
				HandleCHG(strData);
			}

			//   Privacy Mode
			//
			//   The client can change how the server handles instant messages from
			//   users via the BLP command:
			//
			//        C: BLP TrID [AL | BL]
			//        S: BLP TrID Ser# [AL | BL]
			//
			//   The AL/BL parameter determines how the server should treat messages
			//   (MSG and RNG) from users. If the current setting is AL, messages
			//   from users who are not in BL will be delivered. If the current
			//   setting is BL, only messages from people who are in the AL will be
			//   delivered.
			//
			//   The server will respond with the current setting if the change was
			//   successful. Otherwise, it will return an error with the matching
			//   TrID. If the client tries to change the setting to the same value as
			//   the current setting, the server will respond with an error message.
			//   The default setting is AL when a new user connects to the server for
			//   the first time.

			//   Reverse List Prompting
			//
			//   The client can change its persistent setting for when to prompt the
			//   user in reaction to an Reverse List change. This is accomplished via
			//   the GTC command:
			//
			//        C: GTC TrID [A | N]
			//        S: GTC TrID Ser# [A | N]
			//
			//   The value of the A/N parameter determines how the client should
			//   behave when it discovers that a user is in its RL, but is not in its
			//   AL or BL. (Note that this occurs when a user has been added to
			//   another user's list, but has not been explicitly allowed or
			//   blocked):
			//
			//   A - Prompt the user as to whether the new user in the RL should be
			//   added to the AL or the BL
			//   N - Automatically add the new user in the RL to the AL
			//
			//   The A/N parameter is not interpreted by the server, merely stored.
			//
			//   The server will respond with the current setting if the change was
			//   successful. Otherwise, it will return an error with the matching
			//   TrID. If the client tries to change the setting to the same value as
			//   the current setting, the server will respond with an error message.
			//
			//   The default setting is A when a new user connects to the server for
			//   the first time.

			// 7.7 Client States
			//   After the client is authenticated and synchronized, the client
			//   establishes its initial state with the server with the CHG command.
			//   The syntax of the command is:
			//
			//        C: CHG TrID State
			//        S: CHG TrID State
			//
			//   When the state is changed, the server will echo the settings back to
			//   client. The state shall not be considered changed until the response
			//   is received from the server.
			//
			//   Note that the server can send a state change message to the client
			//   at any time. If the server changes the state without a request from
			//   the client, the TrID parameter will be 0.
			//
			//   States are denoted by a string of three characters. The predefined
			//   states that the server recognizes are:
			//
			//   NLN - Make the client Online (after logging in) and send and receive
			//   notifications about buddies.
			//   FLN - Make the client Offline. If the client is already online,
			//   offline notifications will be sent to users on the RL. No message
			//   activity is allowed. In this state, the client can only synchronize
			//   the lists as described above.
			//   HDN - Make the client Hidden/Invisible. If the client is already
			//   online, offline notifications will be sent to users on the RL. The
			//   client will appear as Offline to others but can receive
			//   online/offline notifications from other users, and can also
			//   synchronize the lists. Clients cannot receive any instant messages
			//   in this state.
			//
			//   All other States are treated as sub-states of NLN (online). The
			//   other States currently supported are:
			//   BSY - Busy.
			//   IDL - Idle.
			//   BRB - Be Right Back.
			//   AWY - Away From Computer.
			//   PHN - On The Phone.
			//   LUN - Out To Lunch.

			// 7.8 List Modifications
			//   The protocol supports generic commands to add and remove users from
			//   various lists. This is used by clients to enable "Adding" contacts
			//   to the list of folks being watched, or for the "Block" and "Allow"
			//   features that define how users chooses to interact with one another.
			//   However, these generic commands have different semantics based on
			//   the list being modified. For example, only the server can add or
			//   remove entries from the Reverse List - since it is an indirect
			//   consequence of the user having been added to another user's Forward
			//   List.
			//
			//   The add and remove commands:
			//
			//        C: ADD TrID LIST UserHandle CustomUserName
			//        S: ADD TrID LIST ser# UserHandle CustomUserName
			//
			//        C: REM TrID LIST UserHandle
			//        S: REM TrID LIST ser# UserHandle
			//
			//   Valid values for LIST in Client initiated adds and removes are
			//   FL/AL/BL.
			//
			//   All client initiated adds and removes will be echoed by the server
			//   with a new serial number that should be persisted by the client
			//   along with the list modification. If not successful, an error will
			//   result.
			//
			//   The protocol also supports the concept of an ADD or REM that the
			//   client did not initiate. Server generated ADDs and REMs can have
			//   LIST values of FL/AL/BL/RL. This is common with RL changes, which
			//   are never initiated by the client, but is an indirect consequence of
			//   this user having been added to someone's Forward List. If the RL
			//   change happens while the user is online, it will trigger an
			//   asynchronous ADD or REM command from the server.
			//
			//   Asynchronous ADDs and REMs to the FL, AL, and BL can happen when the
			//   server allows an authenticated user to make list changes from
			//   another environment, such as a web site. In all of these cases, the
			//   server will send the ADD or REM command with the TrID parameter
			//   equal to 0.

			// 7.9 Notification Messages
			//
			//   The client receives asynchronous notifications whenever a contact on
			//   the user's Forward List changes its state. The notifications are of
			//   the form:
			//
			//        S: NLN Substate UserHandle FriendlyName
			//        S: ILN TrID Substate UserHandle FriendlyName
			//        S: FLN UserHandle
			//
			//   NLN indicates that a user has come online.
			//   - Substate can be any three-letter code (see "Client States" above).
			//   - UserHandle and FriendlyName are the handle and names associated
			//     with the user coming online.
			//
			//   ILN is similar to the NLN message, and is received from the server
			//   in response to an CHG or ADD command from the client:
			//
			//   1.  Immediately after the client logon and sends its first CHG
			//       command to the NS. In this case several ILNs may be received -
			//       one for each Forward List contact that is currently online.
			//   2.  After the client sends an "ADD TrID FL UserHandle
			//       CustomUserName" to the NS. (e.g. ILN for the new contact if that
			//       contact is currently online)
			//
			//   In both cases, TrID in the ILN is the same as the one sent by the
			//   client in the CHG or ADD command.
			//
			//   FLN means that the specified user is now offline.

			// 7.10 Connection Close
			//   The client issues the following command to logoff from the NS:
			//
			//        C: OUT
			//        S: OUT {StatusCode}
			//
			//   The server will reply with an OUT to the client before it initiates
			//   a disconnect, with an optional StatusCode.
			//
			//   The StatusCode can be "OTH", which indicates that a client with the
			//   same user handle and password has logged on to the server from
			//   another location, or "SSD" meaning the server is being shut down for
			//   maintenance.
			//
			//   The server will drop the connection after sending the OUT.

		}

		public NS()
		{
			_NSSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
			_thread = new Thread(new ThreadStart(NSSocketDataMain));
			_thread.Name = "NS Socket Data Thread";
		}

		public void Close()
		{
			if(_NSSocket != null)
			{
				if(_thread != null && _thread.ThreadState != System.Threading.ThreadState.Unstarted)
				{
					_thread.Abort();
					_thread.Join();
				}

				string t = "OUT";
				if(StatusUpdate != null)
					StatusUpdate(StatusMessageType.Request, t);
				SendNSData(t);

				_NSSocket.Close();
				_NSSocket = null;
			}
		}

		public string Host
		{
			get
			{
				return _MSNHostServer;
			}
			set
			{
				_MSNHostServer = value;
			}
		}
		public string Email
		{
			get
			{
				return _MSNPassportEmail;
			}
			set
			{
				_MSNPassportEmail = value;
			}
		}
		public string Password
		{
			get
			{
				return _MSNPassportPassword;
			}
			set
			{
				_MSNPassportPassword = value;
			}
		}
		public string Nickname
		{
			get
			{
				return _MSNNickname;
			}
			set
			{
				_MSNNickname = value;
			}
		}

		public string GetByteString(byte[] data)
		{
			StringBuilder sb = new StringBuilder();
			foreach (byte b1 in data)
			{
				sb.Append(b1.ToString("x"));
			}

			return sb.ToString();
		}

		public MessengerForm ParentForm
		{
			get
			{
				return parent;
			}
			set
			{
				parent = value;
			}
		}

		public string ReceiveNSData()
		{
			string strData = "";
			byte[] buff;
			int timeout = 300;
			int timepos = 0;

			// Block until data
			while (_NSSocket.Available == 0) 
			{
				if (timepos >= timeout)
				{
					break;
				}
				timepos++;
				Thread.Sleep(50);
			}

			if (_NSSocket.Available > 0)
			{
				buff = new byte[_NSSocket.Available];
				_NSSocket.Receive(buff, _NSSocket.Available, SocketFlags.None);
				strData = UTF8Encoding.UTF8.GetString(buff);
			}
			return strData;
		}

		private void SendNSData(string strData)
		{
			// Console.WriteLine("Sent NS Data: " + strData);
			_NSSocket.Send(Encoding.UTF8.GetBytes(strData + "\n"));
		}

		public void Authenticate(string _user, string _password)
		{
			string str1 = "", str2 = "", str3 = "", str4 = "", str5 = "";
			string tid = "0", srvport = "";
			string t;
			bool b1, b2;
			int i1, i2, i3, i4, i5;

			// Suspend NS Data Thread -- we'll take it manually from here

			//   After the client connects to a dispatch server by opening a TCP
			//   socket to port 1863, the client and server agree on a particular
			//   protocol version before they proceed. The Client-Server protocol
			//   version handshake involves the following command exchange:
			//
			//        C: VER TrID dialect-name{ dialect-name...}
			//        S: VER TrID dialect-name
			//
			//   The client can provide multiple dialect names in preferred order.
			//   The dialect-name parameter returned by the server is the version
			//   server is designating for this connection
			//
			//   The current protocol dialect-name supported by Messenger servers is
			//   "MSNP2". The dialect names are not case-sensitive.
			//
			//   The string "0" is a reserved dialect name and is used to indicate a
			//   failure response. E.g.:
			//
			//        S: VER TrID 0{ dialect-name ... }

			t = "VER " + tid + " MSNP5 MSNP4 CVRO" ;
			if(StatusUpdate != null)
				StatusUpdate(StatusMessageType.Request, t);
			SendNSData(t);
			str1 = ReceiveNSData();
			if(StatusUpdate != null)
				StatusUpdate(StatusMessageType.Response, str1);
 
			// Verify Proper VER Response

			if (str1.Trim().ToUpper() != "VER " + tid + " MSNP5 MSNP4")
			{
				throw new ApplicationException("After sending VER " + tid + " MSNP5 MSNP4 CVRO command, did not receive expected VER " + tid + " MSNP5 MSNP4 response.");
			}

			//   The client next queries the server for variable "policy"
			//   information. In this version of the protocol, the only policy
			//   information returned by the server is the authentication package in
			//   use.
			//
			//        C: INF TrID
			//        S: INF TrID SP{,SP...}
			//
			//   SP identifies a security package - the name of the SASL mechanism to
			//   use for authentication. "MD5" is used by the Notification Server,
			//   "CKI" by the Switchboard Server.

			// Request Security Package

			tid = Convert.ToString(MessengerForm.GetNextTrialID());
			t = "INF " + tid;
			if(StatusUpdate != null)
				StatusUpdate(StatusMessageType.Request, t);
			SendNSData(t);
			str1 = ReceiveNSData();
			if(StatusUpdate != null)
				StatusUpdate(StatusMessageType.Response, str1);

			// Verify Proper Security Package Response
			// Supported SSPs are: MD5

			if (str1.Trim().ToUpper() != "INF " + tid + " MD5")
			{
				throw new ApplicationException("After sending INF " + tid + " command, did not receive expected INF "  + tid + " MD5 response.");
			}

			// The client needs to authenticate itself after protocol version
			// handshake and identifying the security packages supported on the
			// server. The following are the client server interactions involved.
			//
			//        C: USR TrID SP I{ AuthInitiateInfo}
			//        S: USR TrID SP S{ AuthChallengeInfo}
			//        C: USR TrID SP S{ AuthResponseInfo }
			//        S: USR TrID OK UserHandle FriendlyName
			//
			// The SP parameter is the name of the security package("MD5"). The
			// next parameter is a sequence value, which must be I to (I)nitiate
			// the authentication process and S for all (S)ubsequent messages. If
			// authentication fails on the server, the client can start the
			// authentication process again.
			//
			// For the MD5 security package:
			// - The AuthInitiateInfo parameter provided by the client must be the
			//   User handle.
			// - The AuthChallengeInfo parameter returned by the server contains a
			//   challenge string.
			// - The AuthResponseInfo contains the binary response as a hexadecimal
			//   string, which the MD5 hash of the challenge and the User password
			//   strings concatenated together.
			//
			// The final response from the server contains, in addition to the user
			// handle, the current "Friendly Name" associated with the user handle.
			// This is a "Custom User Name" as described above.

			// Send Initial Authentication Userid

			tid = Convert.ToString(MessengerForm.GetNextTrialID());
			t = "USR " + tid + " MD5 I " + _MSNPassportEmail;
			if(StatusUpdate != null)
				StatusUpdate(StatusMessageType.Request, t);
			SendNSData(t);
			str1 = ReceiveNSData();
			if(StatusUpdate != null)
				StatusUpdate(StatusMessageType.Response, str1);

			//   There are three cases in which clients are referred from one server
			//   to another:
			//
			//   1.  The initial "Dispatch Server" refers the client to the
			//       Notification Server to which it is assigned.
			//   2.  Asynchronous referral by the Notification Server to reassign the
			//       client to a different Notification Server if that server is
			//       overloaded or undergoing maintenance.
			//   3.  During Switchboard Session establishment, the assigned
			//       Notification Server refers the client to a particular
			//       switchboard server for use. This is discussed below.
			//
			//   In the current implementation the Dispatch Server uses the user
			//   handle provided in the initial USR command above to assign the user
			//   in question to a Notification Server. Alternate implementations
			//   might not require referral at this stage.
			//
			//   If received, referral is of the form:
			//
			//        S: XFR TrID ReferralType Address[:PortNo]
			//
			//   ReferralType is either "NS" or "SB" and defines the type of referral
			//   to a Notification Server or Switchboard Server.
			//   Address is a valid DNS name or IP address to a referred server, with
			//   optional port# suffixed as ":PortNo".
			//
			//   If this command is received from the server, the client should
			//   attempt to log in to the server provided.
			//
			//   In the case of "NS" referrals during logon, the Server automatically
			//   closes the client connection after sending this XFR response so that
			//   the client can connect to the new IP Address.
			//
			//   If sent asynchronously, the client is responsible for closing the
			//   connection.
			//
			//   After a "NS" referral, the client will not receive any more messages
			//   from the "old" NS, and also must not send any commands to the "old"
			//   NS after receiving an XFR.

			// Parse NS Server Response

			b1 = false; // if all conditions are met, this will be true

			str1 = str1.Trim().ToUpper();

			str2 = "XFR " + tid + " NS ";

			i1 = str1.IndexOf(str2);
			if (i1 != -1)
			{
				i2 = i1 + str2.Length;
				i3 = str1.IndexOf(" ", i2);
				str3 = str1.Substring(i2, i3 - i2);
				if (str3 != "")
				{
					b1 = true;
				}
			}

			if (!b1)
			{
				throw new ApplicationException("Parsing the NS Server failed.");
			}

			srvport = str3;

			b2 = false;
			i5 = 1; // attempts

			while (!b2)
			{

				// Connect to NS Server
				if(StatusUpdate != null)
					StatusUpdate(StatusMessageType.Status, "Connecting to NS Server...");

				//// Step 1: Close down NSSocketDataMain
				// _NSSocketDataThread.Abort();
				// _NSSocketDataThread.Join();

				//// Step 2: Shutdown the Socket
				if(StatusUpdate != null)
					StatusUpdate(StatusMessageType.Status, "Shutdown the Socket...");
				_NSSocket.Close();

				//// Step 3: Create the Socket and Connect to Server (str3)
				if(StatusUpdate != null)
					StatusUpdate(StatusMessageType.Status, "Create the Socket and Connect to Server");
				_NSSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
				str3 = srvport;
				i4 = str3.IndexOf(":");
				if (i4 == -1)
				{
					throw new ApplicationException("Unable to parse server and port.");
				}

				str4 = str3.Substring(0, i4);
				str5 = str3.Substring(i4, str3.Length - i4);
				str5 = str5.Replace(":", "");
				
				if(StatusUpdate != null)
					StatusUpdate(StatusMessageType.Status, "Endpoint " + str4 + " port " + str5);
				IPEndPoint ep = new IPEndPoint(IPAddress.Parse(str4), Int32.Parse(str5));
				if(StatusUpdate != null)
					StatusUpdate(StatusMessageType.Status, "Connect . . .");
				_NSSocket.Connect(ep);

				if (!_NSSocket.Connected)
				{
					throw new ApplicationException("Unable to connect to real NS Server!");
				}

				//// Step 4: Recreate NSSocketDataMain

				// SuspendNSDataThread();
				// _NSSocketDataThread = new Thread(new ThreadStart(NSSocketDataMain));
				// NSSocketDataThread.Name = "NS Socket Data Thread";
				// NSSocketDataThread.Start();

				// Repeat Above Handshake Process, except this time with NS Server
 
				tid = Convert.ToString(MessengerForm.GetNextTrialID());
				t = "VER " + tid + " MSNP5 MSNP4 CVRO";
				if(StatusUpdate != null)
					StatusUpdate(StatusMessageType.Request, t);
				SendNSData(t);
				str1 = ReceiveNSData();
				if(StatusUpdate != null)
					StatusUpdate(StatusMessageType.Response, str1);

				// Verify Proper VER Response

				if (str1.Trim().ToUpper() != "VER " + tid + " MSNP5 MSNP4")
				{
					throw new ApplicationException("After sending VER " + tid + " MSNP5 MSNP4 CVRO command, did not receive expected VER " + tid + " MSNP5 MSNP4 response.");
				}

				// Request Security Package

				tid = Convert.ToString(MessengerForm.GetNextTrialID());
				t = "INF " + tid;
				if(StatusUpdate != null)
					StatusUpdate(StatusMessageType.Request, t);
				SendNSData(t);
				str1 = ReceiveNSData();
				if(StatusUpdate != null)
					StatusUpdate(StatusMessageType.Response, str1);

				// Verify Proper Security Package Response
				// Supported SSPs are: MD5

				if (str1.Trim().ToUpper() != "INF " + tid + " MD5")
				{
					throw new ApplicationException("After sending INF " + tid + " command, did not receive expected INF "  + tid + " MD5 response.");
				}

				// Send Initial Authentication Userid

				tid = Convert.ToString(MessengerForm.GetNextTrialID());
				t = "USR " + tid + " MD5 I " + _MSNPassportEmail;
				if(StatusUpdate != null)
					StatusUpdate(StatusMessageType.Request, t);
				SendNSData(t);
				str1 = ReceiveNSData();
				if(StatusUpdate != null)
					StatusUpdate(StatusMessageType.Response, str1);

				// Parse MD5 Hash Check Response
				// Format: USR 5 MD5 S 989048851.1851137130

				b1 = false; // if all conditions are met, this will be true

				str1 = str1.Trim().ToUpper();

				str2 = "USR " + tid + " MD5 S ";

				i1 = str1.IndexOf(str2);
				if (i1 != -1)
				{
					i2 = i1 + str2.Length;
					i3 = str1.Length; // Length because there is no 0 at the end of the MD5 hash
					str3 = str1.Substring(i2, i3 - i2);
					if (str3 != "")
					{
						b1 = true;
					}
				}

				if (!b1)
				{
					throw new ApplicationException("Parsing the MD5 Hash failed.");
				}

				// str3 = MD5 Hash Check
			
				str3 = str3.Trim();
			
				str2 = str3 + _password;

				// Obtain MD5 Hash

				MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();
				byte[] md5hash = md5.ComputeHash(Encoding.UTF8.GetBytes(str2));
				str1 = GetByteString(md5hash);

				tid = Convert.ToString(MessengerForm.GetNextTrialID());
				str2 = "USR " + tid + " MD5 S " + str1;
				if(StatusUpdate != null)
					StatusUpdate(StatusMessageType.Request, str2);
				SendNSData(str2);
				str1 = ReceiveNSData();
				if(StatusUpdate != null)
					StatusUpdate(StatusMessageType.Response, str1);
			
				// Verify Auth Response is OK

				str2 = "USR " + tid + " OK";
				if (str1.IndexOf(str2) == -1)
				{
					if (i5 < 5)
					{
						str1 = str1.Replace("\n", "");
						str1 = str1.Replace("\r", "");
						t = "Auth Response: " + str1 + ". Trying again...";
						if(StatusUpdate != null)
							StatusUpdate(StatusMessageType.Request, t);
						i5++;
						tid = Convert.ToString(MessengerForm.GetNextTrialID());
					}
					else
					{
						// 7.11 Error Information
						//
						//   Error messages from the server are of the format:
						//
						//        S: eee {TrID} {(error-info) {param...}}
						//
						//   eee is a 3 digit decimal number indicating the error code. Error-
						//   info contains the description of the error in a text string
						//   localized to the server's locale. The optional parameters provide
						//   indication of the client command causing the error. TrID is the
						//   Transaction ID of the client command that caused this error. Any
						//   server generated errors will not have Transaction IDs.
						//
						//
						//             ERR_SYNTAX_ERROR                 200
						//             ERR_INVALID_PARAMETER            201
						//             ERR_INVALID_USER                 205
						//             ERR_FQDN_MISSING                 206
						//             ERR_ALREADY_LOGIN                207
						//             ERR_INVALID_USERNAME             208
						//             ERR_INVALID_FRIENDLY_NAME        209
						//             ERR_LIST_FULL                    210
						//             ERR_ALREADY_THERE                215
						//             ERR_NOT_ON_LIST                  216
						//             ERR_ALREADY_IN_THE_MODE          218
						//             ERR_ALREADY_IN_OPPOSITE_LIST     219
						//             ERR_SWITCHBOARD_FAILED           280
						//             ERR_NOTIFY_XFR_FAILED            281
						//
						//             ERR_REQUIRED_FIELDS_MISSING      300
						//             ERR_NOT_LOGGED_IN                302
						//             ERR_INTERNAL_SERVER              500
						//             ERR_DB_SERVER                    501
						//             ERR_FILE_OPERATION               510
						//             ERR_MEMORY_ALLOC                 520
						//             ERR_SERVER_BUSY                  600
						//             ERR_SERVER_UNAVAILABLE           601
						//             ERR_PEER_NS_DOWN                 602
						//             ERR_DB_CONNECT                   603
						//             ERR_SERVER_GOING_DOWN            604
						//             ERR_CREATE_CONNECTION            707
						//             ERR_BLOCKING_WRITE               711
						//             ERR_SESSION_OVERLOAD             712
						//             ERR_USER_TOO_ACTIVE              713
						//             ERR_TOO_MANY_SESSIONS            714
						//             ERR_NOT_EXPECTED                 715
						//             ERR_BAD_FRIEND_FILE              717
						//             ERR_AUTHENTICATION_FAILED        911
						//             ERR_NOT_ALLOWED_WHEN_OFFLINE     913
						//             ERR_NOT_ACCEPTING_NEW_USERS      920
						throw new ApplicationException("Authentication Failed! Server Response: " + str1);
					}
				}
				else
				{
					b2 = true;
				}				
			}

			// Resume NSDataThread

			// Change Online
			tid = Convert.ToString(MessengerForm.GetNextTrialID());
			t = "CHG " + tid + " NLN";
			if(StatusUpdate != null)
				StatusUpdate(StatusMessageType.Request, t);
			SendNSData(t);

			// Send List Request
			// LST 8 RL

			//   Several of the user properties used by the Messenger application are
			//   stored on the server. This is done for two reasons:
			//
			//   1) So that users can "roam", i.e. log in from different locations
			//   and still have the appropriate data, such as their contact lists and
			//   privacy settings.
			//   2) If changes occur to a user's Reverse List while that user was
			//   offline (the user was added to another user's list), the client can
			//   be updated with this information.
			//
			//   For performance reasons it is useful to cache these properties on
			//   the client, so that bandwidth usage is minimized in the typical case
			//   where the user is not roaming and there were no Reverse List
			//   changes.
			//
			//   These requirements are met by the SYN command - synchronization.
			//
			//   Once a client logs in successfully, it uses the SYN command to
			//   ensure it has the latest version of the server-stored properties.
			//   These properties include: Forward List, Reverse List, Block List,
			//   Allow List, GTC setting (privacy setting when someone adds this user
			//   to their Forward List), and BLP setting (the user's privacy mode).
			//   The SYN command is:
			//
			//        C: SYN TrID Ser#
			//        S: SYN TrID Ser#
			//
			//   The Ser# parameter sent by the client is the version of the
			//   properties currently cached on the client. The server responds with
			//   the current server version of the properties. If the server has a
			//   newer version, the server will immediately follow the SYN reply by
			//   updating the client with the latest version of the user properties.
			//   These updates are done as described below, and are done without the
			//   client explicitly initiating a LST, GTC or BLP command. Note that
			//   the server will update all server-stored properties to the client,
			//   regardless of how many entries have been changed.
			//
			//   The following "List Retrieval and Property Management" section
			//   describes the format of the user properties sent by the server.
			//   After the SYN reply from the server, the user property updates will
			//   be sent from the server in this sequence: GTC, BLP, LST FL, LST AL,
			//   LST BL, LST RL.
			//
			//   All the user property updates will share the same TrID as the SYN
			//   command and reply.
			tid = Convert.ToString(MessengerForm.GetNextTrialID());
			t = "SYN " + tid + " 0";
			if(StatusUpdate != null)
				StatusUpdate(StatusMessageType.Request, t);
			SendNSData(t);

			str1 = ReceiveNSData();
			if(StatusUpdate != null)
				StatusUpdate(StatusMessageType.Response, str1);

			// Mission Accomplished!
			if(StatusUpdate != null)
				StatusUpdate(StatusMessageType.Status, "Authentication Successful!");
		}

		public void Connect(string _server)
		{
			// The MSN Messenger Protocol currently works over TCP/IP. The MSN
			// Messenger server components support connections over port numbers
			// 1863, which is the registered port number assigned by the IANA
			// (http://www.isi.edu/in-notes/iana/assignments/port-numbers).
			IPEndPoint ep = new IPEndPoint(IPAddress.Parse(_server), _port);
			_NSSocket.Connect(ep);
			if (!_NSSocket.Connected)
			{
				throw new SocketException(System.Runtime.InteropServices.Marshal.GetLastWin32Error());
			}
		}
		public void Start()
		{
			_thread.Start();
		}
		public void Stop()
		{
			if(_thread != null && _thread.ThreadState != System.Threading.ThreadState.Unstarted)
			{
				_thread.Abort();
				_thread.Join();
			}
		}
	}
}
