using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Security.Cryptography;
using IMLibrary.NetProtocol;

namespace IMLibrary.IMAPLibrary
{
	/// <summary>
	/// IMAP client.
	/// </summary>
	/// <example>
	/// <code>
	/// using(IMAP_Client c = new IMAP_Client()){
	///		c.Connect("ivx",143);
	///		c.Authenticate("test","test");
	///				
	///		c.SelectFolder("Inbox");
	///		
	///		// Get messages header here
	///		IMAP_FetchItem msgsInfo = c.FetchMessages(1,-1,false,true,true);
	///		
	///		// Do your suff
	///	}
	/// </code>
	/// </example>
	public class IMAP_Client : IDisposable
	{
		private Socket m_pSocket        = null;
		private bool   m_Connected      = false;
		private bool   m_Authenticated  = false;
		private string m_SelectedFolder = "";
		private int    m_MsgCount       = 0;
		private int    m_NewMsgCount    = 0;

		/// <summary>
		/// Default constructor.
		/// </summary>
		public IMAP_Client()
		{			
		}

		#region method Dispose

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		public void Dispose()
		{
			Disconnect();
		}

		#endregion


		#region method Connect

		/// <summary>
		/// Connects to IMAP server.
		/// </summary>		
		/// <param name="host">Host name.</param>
		/// <param name="port">Port number.</param>
		public void Connect(string host,int port)
		{
			if(!m_Connected){
				m_pSocket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
				//IPEndPoint ipdest = new IPEndPoint(System.Net.Dns.Resolve(host).AddressList[0],port);
				IPEndPoint ipdest = new IPEndPoint(System.Net.Dns.GetHostEntry(host).AddressList[0],port);
				m_pSocket.Connect(ipdest);

				string reply = Core.ReadLine(m_pSocket);
				reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

				if(!reply.ToUpper().StartsWith("OK")){
					m_pSocket.Close();
					m_pSocket = null;
					throw new Exception("Server returned:" + reply);
				}

				m_Connected = true;
			}
		}

		#endregion

		#region method Disconnect

		/// <summary>
		/// Disconnects from IMAP server.
		/// </summary>
		public void Disconnect()
		{
			if(m_pSocket != null && m_pSocket.Connected){
				// Send QUIT
				Core.SendLine(m_pSocket,"a1 LOGOUT");

			//	m_pSocket.Close();
				m_pSocket = null;
			}

			m_Connected     = false;
			m_Authenticated = false;
		}

		#endregion

		#region method Authenticate

		/// <summary>
		/// Authenticates user.
		/// </summary>
		/// <param name="userName">User name.</param>
		/// <param name="password">Password.</param>
		public void Authenticate(string userName,string password)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(m_Authenticated){
				throw new Exception("You are already authenticated !");
			}

			Core.SendLine(m_pSocket,"a1 LOGIN " + userName +  " " + password);

			string reply = Core.ReadLine(m_pSocket);
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(reply.ToUpper().StartsWith("OK")){
				m_Authenticated = true;
			}
			else{
				throw new Exception("Server returned:" + reply);
			}
		}

		#endregion


		#region method CreateFolder

		/// <summary>
		/// Creates specified folder.
		/// </summary>
		/// <param name="folderName">Folder name. Eg. test, Inbox/SomeSubFolder. NOTE: use GetFolderSeparator() to get right folder separator.</param>
		public void CreateFolder(string folderName)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

			Core.SendLine(m_pSocket,"a1 CREATE \"" + folderName + "\"");

			string reply = Core.ReadLine(m_pSocket);
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}
		}

		#endregion

		#region method DeleteFolder

		/// <summary>
		/// Deletes specified folder.
		/// </summary>
		/// <param name="folderName">Folder name.</param>
		public void DeleteFolder(string folderName)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

			Core.SendLine(m_pSocket,"a1 DELETE \"" + folderName + "\"");

			string reply = Core.ReadLine(m_pSocket);
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}
		}

		#endregion

		#region method RenameFolder

		/// <summary>
		/// Renames specified folder.
		/// </summary>
		/// <param name="sourceFolderName">Source folder name.</param>
		/// <param name="destinationFolderName">Destination folder name.</param>
		public void RenameFolder(string sourceFolderName,string destinationFolderName)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

			Core.SendLine(m_pSocket,"a1 RENAME \"" + sourceFolderName + "\" \"" + destinationFolderName + "\"");

			string reply = Core.ReadLine(m_pSocket);
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}
		}

		#endregion

		#region method GetFolders

		/// <summary>
		///  Gets all available folders.
		/// </summary>
		/// <returns></returns>
		public string[] GetFolders()
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

			ArrayList list = new ArrayList();

			Core.SendLine(m_pSocket,"a1 LIST \"\" \"*\"");

			// Must get lines with * and cmdTag + OK or cmdTag BAD/NO
			string reply = Core.ReadLine(m_pSocket);			
			if(reply.StartsWith("*")){
				// Read multiline response
				while(reply.StartsWith("*")){
					reply = reply.Substring(reply.IndexOf(")") + 1).Trim(); // Remove * LIST(..)
					reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Folder separator

					if(reply.IndexOf("\"") > -1){
						list.Add(reply.Substring(reply.IndexOf("\"") + 1,reply.Length - reply.IndexOf("\"") - 2));
					}
					else{
						list.Add(reply.Substring(reply.LastIndexOf(" ")).Trim());
					}

					reply = Core.ReadLine(m_pSocket);
				}
			}
			
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}

			string[] retVal = new string[list.Count];
			list.CopyTo(retVal);

            return retVal;
		}

		#endregion

		#region method GetSubscribedFolders

		/// <summary>
		/// Gets all subscribed folders.
		/// </summary>
		/// <returns></returns>
		public string[] GetSubscribedFolders()
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

			ArrayList list = new ArrayList();

			Core.SendLine(m_pSocket,"a1 LSUB \"\" \"*\"");

			// Must get lines with * and cmdTag + OK or cmdTag BAD/NO
			string reply = Core.ReadLine(m_pSocket);			
			if(reply.StartsWith("*")){
				// Read multiline response
				while(reply.StartsWith("*")){
					//
					string folder = reply.Substring(reply.LastIndexOf(" ")).Trim().Replace("\"","");
					list.Add(folder);

					reply = Core.ReadLine(m_pSocket);
				}
			}
			
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}

			string[] retVal = new string[list.Count];
			list.CopyTo(retVal);

            return retVal;
		}

		#endregion

		#region method SubscribeFolder

		/// <summary>
		/// Subscribes specified folder.
		/// </summary>
		/// <param name="folderName">Folder name.</param>
		public void SubscribeFolder(string folderName)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

			Core.SendLine(m_pSocket,"a1 SUBSCRIBE \"" + folderName + "\"");

			string reply = Core.ReadLine(m_pSocket);
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}
		}

		#endregion

		#region method UnSubscribeFolder

		/// <summary>
		/// UnSubscribes specified folder.
		/// </summary>
		/// <param name="folderName">Folder name,</param>
		public void UnSubscribeFolder(string folderName)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

			Core.SendLine(m_pSocket,"a1 UNSUBSCRIBE \"" + folderName + "\"");

			string reply = Core.ReadLine(m_pSocket);
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}
		}

		#endregion

		#region method SelectFolder

		/// <summary>
		/// Selects specified folder.
		/// </summary>
		/// <param name="folderName">Folder name.</param>
		public void SelectFolder(string folderName)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

			Core.SendLine(m_pSocket,"a1 SELECT \"" + folderName + "\"");

			// Must get lines with * and cmdTag + OK or cmdTag BAD/NO
			string reply = Core.ReadLine(m_pSocket);			
			if(reply.StartsWith("*")){
				// Read multiline response
				while(reply.StartsWith("*")){
					// Get rid of *
					reply = reply.Substring(1).Trim();

					if(reply.ToUpper().IndexOf("EXISTS") > -1){		
						m_MsgCount = Convert.ToInt32(reply.Substring(0,reply.IndexOf(" ")).Trim());
					}
					if(reply.ToUpper().IndexOf("RECENT") > -1){
						m_NewMsgCount = Convert.ToInt32(reply.Substring(0,reply.IndexOf(" ")).Trim());
					}
					
					reply = Core.ReadLine(m_pSocket);
				}
			}
			
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}
	
			m_SelectedFolder = folderName;
		}

		#endregion

		
		#region method CopyMessages

		/// <summary>
		/// Makes copy of messages to specified folder.
		/// </summary>
		/// <param name="startMsgNo">Start message number.</param>
		/// <param name="endMsgNo">End message number. -1 = last.</param>
		/// <param name="destFolder">Folder where to cpoy messages.</param>
		/// <param name="uidCopy">Specifies if startMsgNo and endMsgNo is message UIDs.</param>
		public void CopyMessages(int startMsgNo,int endMsgNo,string destFolder,bool uidCopy)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}
			if(m_SelectedFolder.Length == 0){
				throw new Exception("You must select folder first !");
			}
			
			string endMsg = endMsgNo.ToString();
			if(endMsgNo < 1){
				endMsg = "*";
			}
			string uidC = "";
			if(uidCopy){
				uidC = "UID ";
			}

			Core.SendLine(m_pSocket,"a1 " + uidC + "COPY " + startMsgNo + ":" + endMsg  + " \"" + destFolder + "\"");

			string reply = Core.ReadLine(m_pSocket);
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}
		}

		#endregion

		#region method MoveMessages

		/// <summary>
		/// Moves messages to specified folder.
		/// </summary>
		/// <param name="startMsgNo">Start message number.</param>
		/// <param name="endMsgNo">End message number. -1 = last.</param>
		/// <param name="destFolder">Folder where to cpoy messages.</param>
		/// <param name="uidMove">Specifies if startMsgNo and endMsgNo is message UIDs.</param>
		public void MoveMessages(int startMsgNo,int endMsgNo,string destFolder,bool uidMove)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}
			if(m_SelectedFolder.Length == 0){
				throw new Exception("You must select folder first !");
			}

			CopyMessages(startMsgNo,endMsgNo,destFolder,uidMove);
			DeleteMessages(startMsgNo,endMsgNo,uidMove);
		}

		#endregion

		#region method DeleteMessages

		/// <summary>
		/// Deletes specified messages.
		/// </summary>
		/// <param name="startMsgNo">Start message number.</param>
		/// <param name="endMsgNo">End message number. -1 = last.</param>
		/// <param name="uidDelete">Specifies if startMsgNo and endMsgNo is message UIDs.</param>
		public void DeleteMessages(int startMsgNo,int endMsgNo,bool uidDelete)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}
			if(m_SelectedFolder.Length == 0){
				throw new Exception("You must select folder first !");
			}

			string endMsg = endMsgNo.ToString();
			if(endMsgNo < 1){
				endMsg = "*";
			}
			string uidD = "";
			if(uidDelete){
				uidD = "UID ";
			}

			// 1) Set deleted flag
			// 2) Delete messages with EXPUNGE command

			Core.SendLine(m_pSocket,"a1 " + uidD + "STORE " + startMsgNo + ":" + endMsg  + " +FLAGS.SILENT (\\Deleted)");

			string reply = Core.ReadLine(m_pSocket);
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}

			Core.SendLine(m_pSocket,"a1 EXPUNGE");

			reply = Core.ReadLine(m_pSocket);

			// Read multiline response, just skip these lines
			while(reply.StartsWith("*")){
				reply = Core.ReadLine(m_pSocket);
			}

			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}
		}

		#endregion

		#region method StoreMessage

		/// <summary>
		/// Stores message to specified folder.
		/// </summary>
		/// <param name="folderName">Folder where to store message.</param>
		/// <param name="data">Message data which to store.</param>
		public void StoreMessage(string folderName,byte[] data)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

			Core.SendLine(m_pSocket,"a1 APPEND \"" + folderName + "\" (\\Seen) {" + data.Length + "}");

			// must get reply with starting +
			string reply = Core.ReadLine(m_pSocket);
			if(reply.StartsWith("+")){
				// Send message
                m_pSocket.Send(data);

				// Why must send this ??? 
				m_pSocket.Send(new byte[]{(byte)'\r',(byte)'\n'});

				// Read store result
				reply = Core.ReadLine(m_pSocket);
				reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove command tag

				if(!reply.ToUpper().StartsWith("OK")){
					throw new Exception("Server returned:" + reply);
				}
			}
			else{
				throw new Exception("Server returned:" + reply);
			}			
		}

		#endregion

		#region method FetchMessages

		/// <summary>
		/// Fetches messages headers or full messages data.
		/// </summary>
		/// <param name="startMsgNo">Start message number.</param>
		/// <param name="endMsgNo">End message number. -1 = last.</param>
		/// <param name="uidFetch">Specifies if startMsgNo and endMsgNo is message UIDs.</param>
		/// <param name="headersOnly">If true message headers are retrieved, otherwise full message retrieved.</param>
		/// <param name="setSeenFlag">If true message seen flag is setted.</param>
		/// <returns></returns>
		public IMAP_FetchItem[] FetchMessages(int startMsgNo,int endMsgNo,bool uidFetch,bool headersOnly,bool setSeenFlag)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}
			if(m_SelectedFolder.Length == 0){
				throw new Exception("You must select folder first !");
			}

			ArrayList fetchItems = new ArrayList();
			
			string endMsg = endMsgNo.ToString();
			if(endMsgNo < 1){
				endMsg = "*";
			}
			string headers = "";
			if(headersOnly){
				headers = "HEADER";
			}
			string uidF = "";
			if(uidFetch){
				uidF = "UID ";
			}
			string seenFl = "";
			if(!setSeenFlag){
				seenFl = ".PEEK";
			}

			Core.SendLine(m_pSocket,"a1 " + uidF + "FETCH " + startMsgNo + ":" + endMsg  + " (UID RFC822.SIZE FLAGS BODY" + seenFl + "[" + headers + "])");

			// Must get lines with * and cmdTag + OK or cmdTag BAD/NO
			string reply = Core.ReadLine(m_pSocket);			
			if(reply.StartsWith("*")){
				// Read multiline response
				while(reply.StartsWith("*")){
					// Get rid of * 1 FETCH  and parse params. Reply:* 1 FETCH (UID 12 BODY[] ...)
					reply = reply.Substring(reply.IndexOf("FETCH (") + 7);

					int    uid      = 0;
					int    size     = 0;
					byte[] data     = null;
					bool   isNewMsg = true;

					// Loop fetch result fields
					for(int i=0;i<4;i++){						
						// UID field
						if(reply.ToUpper().StartsWith("UID")){
							reply = reply.Substring(3).Trim(); // Remove UID word from reply
							if(reply.IndexOf(" ") > -1){
								uid   = Convert.ToInt32(reply.Substring(0,reply.IndexOf(" ")));
								reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove UID value from reply
							}
							else{ // Last param, no ending ' '
								uid   = Convert.ToInt32(reply.Substring(0));
								reply = "";
							}
						}
						// RFC822.SIZE field
						else if(reply.ToUpper().StartsWith("RFC822.SIZE")){
							reply = reply.Substring(11).Trim(); // Remove RFC822.SIZE word from reply
							if(reply.IndexOf(" ") > -1){
								size  = Convert.ToInt32(reply.Substring(0,reply.IndexOf(" ")));
								reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove RFC822.SIZE value from reply
							}
							else{
								// Last param, no ending ' '
								size  = Convert.ToInt32(reply.Substring(0));
								reply = "";
							}
						}
						// BODY.PEEK field
						else if(reply.ToUpper().StartsWith("BODY")){
							// Get data. Find {dataLen}
							int dataLen = Convert.ToInt32(reply.Substring(reply.IndexOf("{") + 1,reply.IndexOf("}") - reply.IndexOf("{") - 1));

							MemoryStream storeStrm = new MemoryStream(dataLen);
							Core.ReadData(m_pSocket,dataLen,storeStrm,true,30000);

							data = storeStrm.ToArray();

							// Read last fetch line, can be ')' or some params')'.
							reply = Core.ReadLine(m_pSocket).Trim();
							if(!reply.EndsWith(")")){
								throw new Exception("UnExpected fetch end !");
							}
							else{
								reply = reply.Substring(0,reply.Length - 1).Trim(); // Remove ')' from reply
							}
						}
						// FLAGS field
						else if(reply.ToUpper().StartsWith("FLAGS")){
							if(reply.ToUpper().IndexOf("\\SEEN") > -1){
								isNewMsg = false;
							}

							reply = reply.Substring(reply.IndexOf(")") + 1).Trim(); // Remove FLAGS value from reply
						}
					}

					fetchItems.Add(new IMAP_FetchItem(uid,size,data,headersOnly,isNewMsg));

					reply = Core.ReadLine(m_pSocket);
				}
			}
			
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}

			IMAP_FetchItem[] retVal = new IMAP_FetchItem[fetchItems.Count];
			fetchItems.CopyTo(retVal);

			return retVal;
		}

		#endregion

		#region method GetMessagesTotalSize

		/// <summary>
		/// Gets messages total size in selected folder.
		/// </summary>
		/// <returns></returns>
		public int GetMessagesTotalSize()
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}
			if(m_SelectedFolder.Length == 0){
				throw new Exception("You must select folder first !");
			}

			int totalSize = 0;
			
			Core.SendLine(m_pSocket,"a1 FETCH 1:*" + " (RFC822.SIZE)");

			// Must get lines with * and cmdTag + OK or cmdTag BAD/NO
			string reply = Core.ReadLine(m_pSocket);			
			if(reply.StartsWith("*")){
				// Read multiline response
				while(reply.StartsWith("*")){
					// Get rid of * 1 FETCH  and parse params. Reply:* 1 FETCH (UID 12 BODY[] ...)
					reply = reply.Substring(reply.IndexOf("FETCH (") + 7);
					
					// RFC822.SIZE field
					if(reply.ToUpper().StartsWith("RFC822.SIZE")){
						reply = reply.Substring(11).Trim(); // Remove RFC822.SIZE word from reply
						
						totalSize += Convert.ToInt32(reply.Substring(0,reply.Length - 1).Trim()); // Remove ending ')'						
					}					

					reply = Core.ReadLine(m_pSocket);
				}
			}
			
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}

			return totalSize;
		}

		#endregion

		#region method GetUnseenMessagesCount

		/// <summary>
		/// Gets unseen messages count in selected folder.
		/// </summary>
		/// <returns></returns>
		public int GetUnseenMessagesCount()
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}
			if(m_SelectedFolder.Length == 0){
				throw new Exception("You must select folder first !");
			}

			int count = 0;
			
			Core.SendLine(m_pSocket,"a1 FETCH 1:*" + " (FLAGS)");

			// Must get lines with * and cmdTag + OK or cmdTag BAD/NO
			string reply = Core.ReadLine(m_pSocket);			
			if(reply.StartsWith("*")){
				// Read multiline response
				while(reply.StartsWith("*")){
					// Get rid of * 1 FETCH  and parse params. Reply:* 1 FETCH (UID 12 BODY[] ...)
					reply = reply.Substring(reply.IndexOf("FETCH (") + 7);
					
					if(reply.ToUpper().IndexOf("\\SEEN") == -1){
						count++;
					}

					reply = Core.ReadLine(m_pSocket);
				}
			}
			
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}

			return count;
		}

		#endregion

		#region method GetFolderSeparator

		/// <summary>
		/// Gets IMAP server folder separator char.
		/// </summary>
		/// <returns></returns>
		public string GetFolderSeparator()
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

			string folderSeparator = "";

			Core.SendLine(m_pSocket,"a1 LIST \"\" \"\"");

			// Must get lines with * and cmdTag + OK or cmdTag BAD/NO
			string reply = Core.ReadLine(m_pSocket);			
			if(reply.StartsWith("*")){
				// Read multiline response
				while(reply.StartsWith("*")){
					reply = reply.Substring(reply.IndexOf(")") + 1).Trim(); // Remove * LIST(..)

					// get folder separator
					folderSeparator = reply.Substring(0,reply.IndexOf(" ")).Trim();

					reply = Core.ReadLine(m_pSocket);
				}
			}
			
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}

			reply = reply.Substring(reply.IndexOf(")") + 1).Trim(); // Remove * LIST(..)


			return folderSeparator.Replace("\"","");
		}

		#endregion


		#region Properties Implementation

		/// <summary>
		/// Gets selected folder.
		/// </summary>
		public string SelectedFolder
		{
			get{ return m_SelectedFolder; }
		}

		/// <summary>
		/// Gets numbers of recent(not accessed messages) in selected folder.
		/// </summary>
		public int RecentMessagesCount
		{
			get{ return m_NewMsgCount; }
		}

		/// <summary>
		/// Gets numbers of messages in selected folder.
		/// </summary>
		public int MessagesCount
		{
			get{ return m_MsgCount; }
		}

		#endregion

	}
}
