Click here to Skip to main content
15,891,136 members
Articles / Programming Languages / C#

SIP Stack with SIP Proxy - (VOIP)

Rate me:
Please Sign up or sign in to vote.
4.86/5 (45 votes)
11 Jun 2007CPOL2 min read 1.6M   28.1K   162  
C# implementation of SIP
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Collections.Generic;

using LumiSoft.Net.IMAP.Server;

namespace LumiSoft.Net.IMAP.Client
{
	/// <summary>
	/// IMAP client.
	/// </summary>
	/// <example>
	/// <code>
	/// using(IMAP_Client c = new IMAP_Client()){
	///		c.Connect("ivx",143);
	///		c.Authenticate("test","test");
	///				
	///		c.SelectFolder("Inbox");
	///				
	///		IMAP_SequenceSet sequence_set = new IMAP_SequenceSet();
	///		// First message
	///		sequence_set.Parse("1");
	///		// All messages
	///	//  sequence_set.Parse("1:*");
	///		// Messages 1,3,6 and 100 to last
	///	//  sequence_set.Parse("1,3,6,100:*");
	///	
	///		// Get messages flags and header
	///		IMAP_FetchItem msgsInfo = c.FetchMessages(sequence_set,IMAP_FetchItem_Flags.MessageFlags | IMAP_FetchItem_Flags.Header,true,false);
	///		
	///		// Do your suff
	///	}
	/// </code>
	/// </example>
	public class IMAP_Client : IDisposable
	{
		private SocketEx m_pSocket        = null;
		private bool     m_Connected      = false;
		private bool     m_Authenticated  = false;
        private char     m_PathSeparator  = '\0';
		private string   m_SelectedFolder = "";
		private int      m_MsgCount       = 0;
		private int      m_NewMsgCount    = 0;
        private long     m_UIDNext        = 0;
        private long     m_UIDValidity    = 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. Default IMAP port is 143.</param>
        public void Connect(string host,int port)
		{
            Connect(host,port,false);
        }

		/// <summary>
		/// Connects to IMAP server.
		/// </summary>		
		/// <param name="host">Host name.</param>
		/// <param name="port">Port number. Default IMAP port is 143 and SSL port is 993.</param>
        /// <param name="ssl">Specifies if to connected via SSL.</param>
		public void Connect(string host,int port,bool ssl)
		{
			if(!m_Connected){
				m_pSocket = new SocketEx();
                m_pSocket.Connect(host,port,ssl);

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

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

				m_Connected = true;
                // Clear path separator, so next access will get it.
                m_PathSeparator  = '\0';
			}
		}

		#endregion

		#region method Disconnect

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

				m_pSocket = null;
			}

			m_Connected     = false;
			m_Authenticated = false;
		}

		#endregion

        #region method StartTLS

        /// <summary>
        /// Switches IMAP connection to SSL.
        /// </summary>
        public void StartTLS()
        {
            /* RFC 2595 3. IMAP STARTTLS extension.
             
                Example:    C: a001 CAPABILITY
                            S: * CAPABILITY IMAP4rev1 STARTTLS LOGINDISABLED
                            S: a001 OK CAPABILITY completed
                            C: a002 STARTTLS
                            S: a002 OK Begin TLS negotiation now
                            <TLS negotiation, further commands are under TLS layer>
            */

            if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(m_Authenticated){
				throw new Exception("The STARTTLS command is only valid in non-authenticated state !");
			}
            if(m_pSocket.SSL){
                throw new Exception("Connection is already secure !");
            }

            m_pSocket.WriteLine("a1 STARTTLS");

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

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

            m_pSocket.SwitchToSSL_AsClient();
        }

        #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 !");
			}

			m_pSocket.WriteLine("a1 LOGIN \"" + userName +  "\" \"" + password + "\"");

			string reply = m_pSocket.ReadLine();
			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 !");
			}

            // Ensure that we send right separator to server, we accept both \ and /.
            folderName = folderName.Replace('\\',this.PathSeparator).Replace('/',this.PathSeparator);

			m_pSocket.WriteLine("a1 CREATE \"" + Core.Encode_IMAP_UTF7_String(folderName) + "\"");

			string reply = m_pSocket.ReadLine();
			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 !");
			}

			m_pSocket.WriteLine("a1 DELETE \"" + Core.Encode_IMAP_UTF7_String(folderName) + "\"");

			string reply = m_pSocket.ReadLine();
			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 !");
			}

			m_pSocket.WriteLine("a1 RENAME \"" + Core.Encode_IMAP_UTF7_String(sourceFolderName) + "\" \"" + Core.Encode_IMAP_UTF7_String(destinationFolderName) + "\"");

			string reply = m_pSocket.ReadLine();
			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();

			m_pSocket.WriteLine("a1 LIST \"\" \"*\"");

			// Must get lines with * and cmdTag + OK or cmdTag BAD/NO
			string reply = m_pSocket.ReadLine();			
			if(reply.StartsWith("*")){
				// Read multiline response
				while(reply.StartsWith("*")){
					// don't show not selectable folders
					if(reply.ToLower().IndexOf("\\noselect") == -1){
						reply = reply.Substring(reply.IndexOf(")") + 1).Trim(); // Remove * LIST(..)
						reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Folder separator

						// Folder name between ""
						if(reply.IndexOf("\"") > -1){
							list.Add(Core.Decode_IMAP_UTF7_String(reply.Substring(reply.IndexOf("\"") + 1,reply.Length - reply.IndexOf("\"") - 2)));
						}
						else{
							list.Add(Core.Decode_IMAP_UTF7_String(reply.Trim()));
						}
					}

					reply = m_pSocket.ReadLine();
				}
			}
			
			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();

			m_pSocket.WriteLine("a1 LSUB \"\" \"*\"");

			// Must get lines with * and cmdTag + OK or cmdTag BAD/NO
			string reply = m_pSocket.ReadLine();			
			if(reply.StartsWith("*")){
				// Read multiline response
				while(reply.StartsWith("*")){
					// don't show not selectable folders
					if(reply.ToLower().IndexOf("\\noselect") == -1){
						reply = reply.Substring(reply.IndexOf(")") + 1).Trim(); // Remove * LSUB(..)
						reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Folder separator

						// Folder name between ""
						if(reply.IndexOf("\"") > -1){
							list.Add(Core.Decode_IMAP_UTF7_String(reply.Substring(reply.IndexOf("\"") + 1,reply.Length - reply.IndexOf("\"") - 2)));
						}
						else{
							list.Add(Core.Decode_IMAP_UTF7_String(reply.Trim()));
						}
					}

					reply = m_pSocket.ReadLine();
				}
			}
			
			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;

            // REMOVE ME:
	/*		ArrayList list = new ArrayList();

			m_pSocket.WriteLine("a1 LSUB \"\" \"*\"");

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

					reply = m_pSocket.ReadLine();
				}
			}
			
			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 !");
			}

			m_pSocket.WriteLine("a1 SUBSCRIBE \"" + Core.Encode_IMAP_UTF7_String(folderName) + "\"");

			string reply = m_pSocket.ReadLine();
			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 !");
			}

			m_pSocket.WriteLine("a1 UNSUBSCRIBE \"" + Core.Encode_IMAP_UTF7_String(folderName) + "\"");

			string reply = m_pSocket.ReadLine();
			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 !");
			}

			m_pSocket.WriteLine("a1 SELECT \"" + Core.Encode_IMAP_UTF7_String(folderName) + "\"");

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

					if(reply.ToUpper().IndexOf("EXISTS") > -1 && reply.ToUpper().IndexOf("FLAGS") == -1){
						m_MsgCount = Convert.ToInt32(reply.Substring(0,reply.IndexOf(" ")).Trim());
					}
					else if(reply.ToUpper().IndexOf("RECENT") > -1 && reply.ToUpper().IndexOf("FLAGS") == -1){
						m_NewMsgCount = Convert.ToInt32(reply.Substring(0,reply.IndexOf(" ")).Trim());
					}
                    else if(reply.ToUpper().IndexOf("UIDNEXT") > -1){
                        m_UIDNext = Convert.ToInt64(reply.Substring(reply.ToUpper().IndexOf("UIDNEXT") + 8,reply.IndexOf(']') - reply.ToUpper().IndexOf("UIDNEXT") - 8));
                    }
                    else if(reply.ToUpper().IndexOf("UIDVALIDITY") > -1){
                        m_UIDValidity = Convert.ToInt64(reply.Substring(reply.ToUpper().IndexOf("UIDVALIDITY") + 12,reply.IndexOf(']') - reply.ToUpper().IndexOf("UIDVALIDITY") - 12));
                    }
					
					reply = m_pSocket.ReadLine();
				}
			}
			
			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 GetNamespacesInfo

        /// <summary>
        /// Gets IMAP server namespaces info.
        /// </summary>
        /// <returns></returns>
        public IMAP_NamespacesInfo GetNamespacesInfo()
        {
            /* RFC 2342 5. NAMESPACE Command.
               Arguments: none

               Response:  an untagged NAMESPACE response that contains the prefix
                          and hierarchy delimiter to the server's Personal
                          Namespace(s), Other Users' Namespace(s), and Shared
                          Namespace(s) that the server wishes to expose. The
                          response will contain a NIL for any namespace class
                          that is not available. Namespace_Response_Extensions
                          MAY be included in the response.
                          Namespace_Response_Extensions which are not on the IETF
                          standards track, MUST be prefixed with an "X-".

               Result:    OK - Command completed
                          NO - Error: Can't complete command
                          BAD - argument invalid
             
             
                Example:
                    < A server that supports a single personal namespace.  No leading
                    prefix is used on personal mailboxes and "/" is the hierarchy
                    delimiter.>

                    C: A001 NAMESPACE
                    S: * NAMESPACE (("" "/")) NIL NIL
                    S: A001 OK NAMESPACE command completed             
            */

            if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

            m_pSocket.WriteLine("a1 NAMESPACE");

            IMAP_NamespacesInfo namespacesInfo = new IMAP_NamespacesInfo(null,null,null);
            string reply = m_pSocket.ReadLine();
            while(reply.StartsWith("*")){
                reply = RemoveCmdTag(reply);

                if(reply.ToUpper().StartsWith("NAMESPACE")){
                    namespacesInfo = IMAP_NamespacesInfo.Parse(reply);
                }

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

            return namespacesInfo;
        }

        #endregion


        #region method GetFolderACL

        /// <summary>
		/// Gets specified folder ACL entries.
		/// </summary>
		/// <param name="folderName">Folder which ACL entries to get.</param>
        /// <returns></returns>
		public IMAP_Acl[] GetFolderACL(string folderName)
		{
            /* RFC 2086 4.3. GETACL
				Arguments:  mailbox name

				Data:       untagged responses: ACL

				Result:     OK - getacl completed
							NO - getacl failure: can't get acl
							BAD - command unknown or arguments invalid

					The GETACL command returns the access control list for mailbox in
					an untagged ACL reply.

				Example:    C: A002 GETACL INBOX
							S: * ACL INBOX Fred rwipslda
							S: A002 OK Getacl complete
							
							.... Multiple users
							S: * ACL INBOX Fred rwipslda test rwipslda
							
							.... No acl flags for Fred
							S: * ACL INBOX Fred "" test rwipslda
									
			*/
            			
            if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

            m_pSocket.WriteLine("a1 GETACL " + TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(folderName)));

            List<IMAP_Acl> retVal = new List<IMAP_Acl>();
            string reply = m_pSocket.ReadLine();
            while(reply.StartsWith("*")){
                reply = RemoveCmdTag(reply);

                if(reply.ToUpper().StartsWith("ACL")){
                    retVal.Add(IMAP_Acl.Parse(reply));
                }

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

            return retVal.ToArray();
        }

        #endregion

        #region method SetFolderACL

        /// <summary>
		/// Sets specified user ACL permissions for specified folder.
		/// </summary>
		/// <param name="folderName">Folder name which ACL to set.</param>
		/// <param name="userName">User name who's ACL to set.</param>
		/// <param name="acl">ACL permissions to set.</param>
		public void SetFolderACL(string folderName,string userName,IMAP_ACL_Flags acl)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

			m_pSocket.WriteLine("a1 SETACL \"" + Core.Encode_IMAP_UTF7_String(folderName) + "\" \"" + userName + "\" " + IMAP_Utils.ACL_to_String(acl));

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

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

		#endregion

		#region method DeleteFolderACL

		/// <summary>
		/// Deletes specified user access to specified folder.
		/// </summary>
		/// <param name="folderName">Folder which ACL to remove.</param>
		/// <param name="userName">User name who's ACL to remove.</param>
		public void DeleteFolderACL(string folderName,string userName)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

			m_pSocket.WriteLine("a1 DELETEACL \"" + Core.Encode_IMAP_UTF7_String(folderName) + "\" \"" + userName + "\"");

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

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

		#endregion

		#region method GetFolderMyrights

		/// <summary>
		/// Gets myrights to specified folder.
		/// </summary>
		/// <param name="folderName"></param>
		/// <returns></returns>
		public IMAP_ACL_Flags GetFolderMyrights(string folderName)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

			IMAP_ACL_Flags aclFlags = IMAP_ACL_Flags.None;

			m_pSocket.WriteLine("a1 MYRIGHTS \"" + Core.Encode_IMAP_UTF7_String(folderName) + "\"");

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

					if(reply.ToUpper().IndexOf("MYRIGHTS") > -1){
						aclFlags = IMAP_Utils.ACL_From_String(reply.Substring(0,reply.IndexOf(" ")).Trim());
					}
					
					reply = m_pSocket.ReadLine();
				}
			}
			
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

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

			return aclFlags;
		}

		#endregion

		
		#region method CopyMessages
        		
		/// <summary>
		/// Copies specified messages to specified folder.
		/// </summary>
		/// <param name="sequence_set">IMAP sequence-set.</param>
		/// <param name="destFolder">Destination folder name.</param>
		/// <param name="uidCopy">Specifies if UID COPY or COPY. 
		/// For UID COPY all sequence_set numers must be message UID values and for normal COPY message numbers.</param>
		public void CopyMessages(IMAP_SequenceSet sequence_set,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 !");
			}
			

			if(uidCopy){
				m_pSocket.WriteLine("a1 UID COPY " + sequence_set.ToSequenceSetString() + " \"" + Core.Encode_IMAP_UTF7_String(destFolder) + "\"");
			}
			else{
				m_pSocket.WriteLine("a1 COPY " + sequence_set.ToSequenceSetString() + " \"" + Core.Encode_IMAP_UTF7_String(destFolder) + "\"");
			}

			// Read server resposnse
			string reply = m_pSocket.ReadLine();

			// There is STATUS reponse, read and process it
			while(reply.StartsWith("*")){
				ProcessStatusResponse(reply);

				reply = m_pSocket.ReadLine();
			}

			// We must get OK or otherwise there is error
			if(!RemoveCmdTag(reply).ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}
		}
		
		#endregion

		#region method MoveMessages
        				
		/// <summary>
		/// Moves specified messages to specified folder.
		/// </summary>
		/// <param name="sequence_set">IMAP sequence-set.</param>
		/// <param name="destFolder">Folder where to copy messages.</param>
		/// <param name="uidMove">Specifies if sequence-set contains message UIDs or message numbers.</param>
		public void MoveMessages(IMAP_SequenceSet sequence_set,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(sequence_set,destFolder,uidMove);
			DeleteMessages(sequence_set,uidMove);
		}

		#endregion

		#region method DeleteMessages
        		
		/// <summary>
		/// Deletes specified messages.
		/// </summary>
		/// <param name="sequence_set">IMAP sequence-set.</param>
		/// <param name="uidDelete">Specifies if sequence-set contains message UIDs or message numbers.</param>
		public void DeleteMessages(IMAP_SequenceSet sequence_set,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 !");
			}

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

			if(uidDelete){
				m_pSocket.WriteLine("a1 UID STORE " + sequence_set.ToSequenceSetString()  + " +FLAGS.SILENT (\\Deleted)");
			}
			else{
				m_pSocket.WriteLine("a1 STORE " + sequence_set.ToSequenceSetString()  + " +FLAGS.SILENT (\\Deleted)");
			}

			// Read server resposnse
			string reply = m_pSocket.ReadLine();

			// There is STATUS reponse, read and process it
			while(reply.StartsWith("*")){
				ProcessStatusResponse(reply);

				reply = m_pSocket.ReadLine();
			}

			// We must get OK or otherwise there is error
			if(!RemoveCmdTag(reply).ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}
			
			m_pSocket.WriteLine("a1 EXPUNGE");

			// Read server resposnse
			reply = m_pSocket.ReadLine();

			// There is STATUS reponse, read and process it
			while(reply.StartsWith("*")){
				ProcessStatusResponse(reply);

				reply = m_pSocket.ReadLine();
			}

			// We must get OK or otherwise there is error
			if(!RemoveCmdTag(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)
		{
			StoreMessage(folderName,IMAP_MessageFlags.Seen,DateTime.Now,data);
		}

		/// <summary>
		/// Stores message to specified folder.
		/// </summary>
		/// <param name="folderName">Folder where to store message.</param>
		/// <param name="messageFlags">Message flags what are stored for message.</param>
		/// <param name="inernalDate">Internal date value what are stored for message.</param>
		/// <param name="data">Message data which to store.</param>
		public void StoreMessage(string folderName,IMAP_MessageFlags messageFlags,DateTime inernalDate,byte[] data)
		{
			if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

            // Ensure that we send right separator to server, we accept both \ and /.
            folderName = folderName.Replace('\\',this.PathSeparator).Replace('/',this.PathSeparator);

			m_pSocket.WriteLine("a1 APPEND \"" + Core.Encode_IMAP_UTF7_String(folderName) + "\" (" + IMAP_Utils.MessageFlagsToString(messageFlags) + ") \"" + IMAP_Utils.DateTimeToString(inernalDate) + "\" {" + data.Length + "}");

			// Read server resposnse
			string reply = m_pSocket.ReadLine();

			// There is STATUS reponse, read and process it
			while(reply.StartsWith("*")){
				ProcessStatusResponse(reply);

				reply = m_pSocket.ReadLine();
			}

			// We must get reply with starting +			
			if(reply.StartsWith("+")){
				// Send message
                m_pSocket.Write(data);

				// Send CRLF, ends splitted command line
				m_pSocket.Write(new byte[]{(byte)'\r',(byte)'\n'});

				// Read server resposnse
				reply = m_pSocket.ReadLine();

				// There is STATUS reponse, read and process it
				while(reply.StartsWith("*")){
					ProcessStatusResponse(reply);

					reply = m_pSocket.ReadLine();
				}

				// We must get OK or otherwise there is error
				if(!RemoveCmdTag(reply).ToUpper().StartsWith("OK")){
					throw new Exception("Server returned:" + reply);
				}
			}
			else{
				throw new Exception("Server returned:" + reply);
			}
		}

		#endregion

		#region method Search
        
		private int[] Search()
		{
			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 !");
			}

			// TODO:

			return null;
		}

		#endregion

		#region method FetchMessages
        		
		/// <summary>
		/// Fetches specifes messages specified fetch items.
		/// </summary>
		/// <param name="sequence_set">IMAP sequence-set.</param>
		/// <param name="fetchFlags">Specifies what data to fetch from IMAP server.</param>
		/// <param name="setSeenFlag">If true message seen flag is setted.</param>
		/// <param name="uidFetch">Specifies if sequence-set contains message UIDs or message numbers.</param>
		/// <returns></returns>
		public IMAP_FetchItem[] FetchMessages(IMAP_SequenceSet sequence_set,IMAP_FetchItem_Flags fetchFlags,bool setSeenFlag,bool uidFetch)
		{
			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 !");
			}

			List<IMAP_FetchItem> fetchItems = new List<IMAP_FetchItem>();

			//--- Construct FETCH command line -----------------------------------------------------------------------//
			string fetchCmdLine = "a1";
			if(uidFetch){
				fetchCmdLine += " UID";
			}
			fetchCmdLine += " FETCH " + sequence_set.ToSequenceSetString() + " (UID";
			
			// FLAGS
			if((fetchFlags & IMAP_FetchItem_Flags.MessageFlags) != 0){
				fetchCmdLine += " FLAGS";
			}
			// RFC822.SIZE
			if((fetchFlags & IMAP_FetchItem_Flags.Size) != 0){
				fetchCmdLine += " RFC822.SIZE";
			}
			// INTERNALDATE
			if((fetchFlags & IMAP_FetchItem_Flags.InternalDate) != 0){
				fetchCmdLine += " INTERNALDATE";
			}
			// ENVELOPE
			if((fetchFlags & IMAP_FetchItem_Flags.Envelope) != 0){
				fetchCmdLine += " ENVELOPE";
			}
            // BODYSTRUCTURE
			if((fetchFlags & IMAP_FetchItem_Flags.BodyStructure) != 0){
				fetchCmdLine += " BODYSTRUCTURE";
			}
			// BODY[] or BODY.PEEK[]
			if((fetchFlags & IMAP_FetchItem_Flags.Message) != 0){
				if(setSeenFlag){
					fetchCmdLine += " BODY[]";
				}
				else{
					fetchCmdLine += " BODY.PEEK[]";
				}
			}
			// BODY[HEADER] or BODY.PEEK[HEADER] ---> This needed only if full message isn't requested.
			if((fetchFlags & IMAP_FetchItem_Flags.Message) == 0 && (fetchFlags & IMAP_FetchItem_Flags.Header) != 0){
				if(setSeenFlag){
					fetchCmdLine += " BODY[HEADER]";
				}
				else{
					fetchCmdLine += " BODY.PEEK[HEADER]";
				}
			}
			//--------------------------------------------------------------------------------------------------------//
            
			fetchCmdLine += ")";

			// Send fetch command line to server
			m_pSocket.WriteLine(fetchCmdLine);
	
			// Must get lines with * and cmdTag + OK or cmdTag BAD/NO
			string reply = m_pSocket.ReadLine(50000);
			// Read multiline response
			while(reply.StartsWith("*")){
				// Fetch may return status response there, skip them					
				if(IsStatusResponse(reply)){
					// Read next fetch item or server response
					reply = m_pSocket.ReadLine(50000);
					continue;
				}
				
				int               no            = 0;
				int               uid           = 0;
				int               size          = 0;
				byte[]            data          = null;
				IMAP_MessageFlags flags         = IMAP_MessageFlags.Recent;
				string            envelope      = "";
                string            bodystructure = "";
				string            internalDate  = "";

				// Remove *
				reply = reply.Substring(1).TrimStart();
				
				// Get message number
				no = Convert.ToInt32(reply.Substring(0,reply.IndexOf(" ")));

				// Get rid of FETCH  and parse params. Reply:* 1 FETCH (UID 12 BODY[] ...)
				reply = reply.Substring(reply.IndexOf("FETCH (") + 7);

				StringReader r = new StringReader(reply);
				// Loop fetch result fields
				while(r.Available > 0){
					r.ReadToFirstChar();

					// Fetch command closing ) parenthesis
					if(r.SourceString == ")"){
						break;
					}

					#region UID <value>

					// UID <value>
					else if(r.StartsWith("UID",false)){
						// Remove UID word from reply
						r.ReadSpecifiedLength("UID".Length);
						r.ReadToFirstChar();

						// Read <value>
						string word = r.ReadWord();
						if(word == null){
							throw new Exception("IMAP server didn't return UID <value> !");
						}
						else{
							uid = Convert.ToInt32(word);
						}
					}

					#endregion

					#region RFC822.SIZE <value>

					// RFC822.SIZE <value>
					else if(r.StartsWith("RFC822.SIZE",false)){
						// Remove RFC822.SIZE word from reply
						r.ReadSpecifiedLength("RFC822.SIZE".Length);
						r.ReadToFirstChar();

						// Read <value>
						string word = r.ReadWord();
						if(word == null){
							throw new Exception("IMAP server didn't return RFC822.SIZE <value> !");
						}
						else{
							try{
								size = Convert.ToInt32(word);
							}
							catch{
								throw new Exception("IMAP server returned invalid RFC822.SIZE <value> '" + word + "' !");
							}
						}
					}

					#endregion

					#region INTERNALDATE <value>

					// INTERNALDATE <value>
					else if(r.StartsWith("INTERNALDATE",false)){
						// Remove INTERNALDATE word from reply
						r.ReadSpecifiedLength("INTERNALDATE".Length);
						r.ReadToFirstChar();

						// Read <value>
						string word = r.ReadWord();
						if(word == null){
							throw new Exception("IMAP server didn't return INTERNALDATE <value> !");
						}
						else{
							internalDate = word;
						}
					}

					#endregion

					#region ENVELOPE (<envelope-string>)

					else if(r.StartsWith("ENVELOPE",false)){
						// Remove ENVELOPE word from reply
						r.ReadSpecifiedLength("ENVELOPE".Length);
						r.ReadToFirstChar();
                        
                        /* 
                            Handle string literals {count-to-read}<CRLF>data(length = count-to-read).
                            (string can be quoted string or literal)
                            Loop while get envelope,invalid response or timeout.
                        */

                        while(true){
                            try{
						        envelope = r.ReadParenthesized();
                                break;
                            }
                            catch(Exception x){
                                string s = r.ReadToEnd();

                                /* partial_envelope {count-to-read}
                                   Example: ENVELOPE ("Mon, 03 Apr 2006 10:10:10 GMT" {35}
                                */
                                if(s.EndsWith("}")){                                
                                    // Get partial envelope and append it back to reader
                                    r.AppenString(s.Substring(0,s.LastIndexOf('{')));

                                    // Read remaining envelope and append it to reader
                                    int countToRead = Convert.ToInt32(s.Substring(s.LastIndexOf('{') + 1,s.LastIndexOf('}') - s.LastIndexOf('{') - 1));
                                    MemoryStream strm = new MemoryStream();
                                    m_pSocket.ReadSpecifiedLength(countToRead,strm);
                                    r.AppenString(TextUtils.QuoteString(System.Text.Encoding.Default.GetString(strm.ToArray())));

                                    // Read fetch continuing line
						            r.AppenString(m_pSocket.ReadLine(50000));
                                }
                                // Unexpected response
                                else{
                                    throw x;
                                }
                            }
                        }
					}

					#endregion

                    #region BODYSTRUCTURE (<bodystructure-string>)

                    else if(r.StartsWith("BODYSTRUCTURE",false)){
                        // Remove BODYSTRUCTURE word from reply
						r.ReadSpecifiedLength("BODYSTRUCTURE".Length);
						r.ReadToFirstChar();

						bodystructure = r.ReadParenthesized();
                    }

                    #endregion

                    #region BODY[] or BODY[HEADER]

                    // BODY[] or BODY[HEADER]
					else if(r.StartsWith("BODY",false)){
						if(r.StartsWith("BODY[]",false)){
							// Remove BODY[]
							r.ReadSpecifiedLength("BODY[]".Length);
						}
						else if(r.StartsWith("BODY[HEADER]",false)){
							// Remove BODY[HEADER]
							r.ReadSpecifiedLength("BODY[HEADER]".Length);
						}
						else{
							throw new Exception("Invalid FETCH response: " + r.SourceString);
						}
						r.ReadToFirstChar();

						// We must now have {<size-to-read>}, or there is error
						if(!r.StartsWith("{")){
							throw new Exception("Invalid FETCH BODY[] or BODY[HEADER] response: " + r.SourceString);
						}
						// Read <size-to-read>
						int dataLength = Convert.ToInt32(r.ReadParenthesized());

						// Read data
						MemoryStream storeStrm = new MemoryStream(dataLength);
						m_pSocket.ReadSpecifiedLength(dataLength,storeStrm);
						data = storeStrm.ToArray();
				
						// Read fetch continuing line
						r.AppenString(m_pSocket.ReadLine(50000).Trim());
					}

					#endregion

					#region FLAGS (<flags-list>)

					// FLAGS (<flags-list>)
					else if(r.StartsWith("FLAGS",false)){
						// Remove FLAGS word from reply
						r.ReadSpecifiedLength("FLAGS".Length);
						r.ReadToFirstChar();

						// Read (<flags-list>)
						string flagsList = r.ReadParenthesized();
						if(flagsList == null){
							throw new Exception("IMAP server didn't return FLAGS (<flags-list>) !");
						}
						else{
							flags = IMAP_Utils.ParseMessageFlags(flagsList);
						}
					}

					#endregion

					else{
						throw new Exception("Not supported fetch reply: " + r.SourceString);
					}
				}

				fetchItems.Add(new IMAP_FetchItem(no,uid,size,data,flags,internalDate,envelope,bodystructure,fetchFlags));

				// Read next fetch item or server response
				reply = m_pSocket.ReadLine(50000);
			}
			
			// We must get OK or otherwise there is error
			if(!RemoveCmdTag(reply).ToUpper().StartsWith("OK")){
				if(!reply.ToUpper().StartsWith("NO")){
					throw new Exception("Server returned:" + reply);
				}
			}

			return fetchItems.ToArray();
		}

		#endregion

        #region method FetchMessage

        /// <summary>
        /// Gets specified message from server and stores to specified stream.
        /// </summary>
        /// <param name="uid">Message UID which to get.</param>
        /// <param name="storeStream">Stream where to store message.</param>
        public void FetchMessage(int uid,Stream storeStream)
        {
            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 !");
			}
                        
            m_pSocket.WriteLine("a1 UID FETCH " + uid + " BODY[]");

            string reply = m_pSocket.ReadLine(50000);
			// Read multiline response
			while(reply.StartsWith("*")){
                // Fetch may return status response there, skip them					
				if(IsStatusResponse(reply)){
					// Read next server response
					reply = m_pSocket.ReadLine(50000);
					continue;
				}

                reply = RemoveCmdTag(reply);

                // We must get here: BODY[] {sizeOfData}
                if(reply.ToUpper().ToString().IndexOf("BODY[") > - 1){
                    if(reply.IndexOf('{') > -1){
                        StringReader r = new StringReader(reply);
                        while(r.Available > 0 && !r.StartsWith("{")){
                            r.ReadSpecifiedLength(1);
                        }
                        int sizeOfData = Convert.ToInt32(r.ReadParenthesized());
                        m_pSocket.ReadSpecifiedLength(sizeOfData,storeStream);                        
                        m_pSocket.ReadLine();
                    }
                }

                // Read next server response
				reply = m_pSocket.ReadLine(50000);
            }

            // We must get OK or otherwise there is error
			if(!RemoveCmdTag(reply).ToUpper().StartsWith("OK")){
				if(!reply.ToUpper().StartsWith("NO")){
					throw new Exception("Server returned:" + reply);
				}
			}
        }

        #endregion

        #region method StoreMessageFlags

        /// <summary>
		/// Stores specified message flags to specified messages.
		/// </summary>
		/// <param name="sequence_set">IMAP sequence-set.</param>
		/// <param name="msgFlags">Message flags.</param>
		/// <param name="uidStore">Specifies if UID STORE or STORE. 
		/// For UID STORE all sequence_set numers must be message UID values and for normal STORE message numbers.</param>
		public void StoreMessageFlags(IMAP_SequenceSet sequence_set,IMAP_MessageFlags msgFlags,bool uidStore)
		{
			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 !");
			}

			if(uidStore){
				m_pSocket.WriteLine("a1 UID STORE " + sequence_set.ToSequenceSetString() + " FLAGS (" + IMAP_Utils.MessageFlagsToString(msgFlags) + ")");
			}
			else{
				m_pSocket.WriteLine("a1 STORE " + sequence_set.ToSequenceSetString() + " FLAGS (" + IMAP_Utils.MessageFlagsToString(msgFlags) + ")");
			}

			// Read server resposnse
			string reply = m_pSocket.ReadLine();

			// There is STATUS reponse, read and process it
			while(reply.StartsWith("*")){
				ProcessStatusResponse(reply);

				reply = m_pSocket.ReadLine();
			}

			// We must get OK or otherwise there is error
			if(!RemoveCmdTag(reply).ToUpper().StartsWith("OK")){
				throw new Exception("Server returned:" + reply);
			}
		}

		#endregion


        #region method GetFolderQuota

        /// <summary>
        /// Gets specified folder quota info. Throws Exception if server doesn't support QUOTA.
        /// </summary>
        /// <param name="folder">Folder name.</param>
        /// <returns></returns>
        public IMAP_Quota GetFolderQuota(string folder)
        {
            /* RFC 2087 4.3. GETQUOTAROOT Command

                    Arguments:  mailbox name

                    Data:       untagged responses: QUOTAROOT, QUOTA

                    Result:     OK - getquota completed
                                NO - getquota error: no such mailbox, permission denied
                                BAD - command unknown or arguments invalid

               The GETQUOTAROOT command takes the name of a mailbox and returns the
               list of quota roots for the mailbox in an untagged QUOTAROOT
               response.  For each listed quota root, it also returns the quota
               root's resource usage and limits in an untagged QUOTA response.

                   Example:    C: A003 GETQUOTAROOT INBOX
                               S: * QUOTAROOT INBOX ""
                               S: * QUOTA "" (STORAGE 10 512)
                               S: A003 OK Getquota completed
            */

            if(!m_Connected){
				throw new Exception("You must connect first !");
			}
			if(!m_Authenticated){
				throw new Exception("You must authenticate first !");
			}

            IMAP_Quota retVal = null;

            m_pSocket.WriteLine("a1 GETQUOTAROOT \"" + Core.Encode_IMAP_UTF7_String(folder) + "\"");

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

					if(reply.ToUpper().StartsWith("QUOTAROOT")){
						// Skip QUOTAROOT
					}
					else if(reply.ToUpper().StartsWith("QUOTA")){
						StringReader r = new StringReader(reply);
                        // Skip QUOTA word
                        r.ReadWord();

                        string qoutaRootName = r.ReadWord();
                        long   storage       = -1;
                        long   maxStorage    = -1;
                        long   messages      = -1;
                        long   maxMessages   = -1;

                        string limits = r.ReadParenthesized();
                        r = new StringReader(limits);
                        while(r.Available > 0){
                            string limitName = r.ReadWord();
                            // STORAGE usedBytes maximumAllowedBytes
                            if(limitName.ToUpper() == "STORAGE"){
                                storage    = Convert.ToInt64(r.ReadWord());
                                maxStorage = Convert.ToInt64(r.ReadWord());
                            }
                            // STORAGE messagesCount maximumAllowedMessages
                            else if(limitName.ToUpper() == "MESSAGE"){
                                messages    = Convert.ToInt64(r.ReadWord());
                                maxMessages = Convert.ToInt64(r.ReadWord());
                            }
                        }

                        retVal = new IMAP_Quota(qoutaRootName,messages,maxMessages,storage,maxStorage);
					}
					
					reply = m_pSocket.ReadLine();
				}
			}
			
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

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

            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;
			
			m_pSocket.WriteLine("a1 FETCH 1:* (RFC822.SIZE)");

			// Must get lines with * and cmdTag + OK or cmdTag BAD/NO
			string reply = m_pSocket.ReadLine();			
			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 = m_pSocket.ReadLine();
				}
			}
			
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				if(!reply.ToUpper().StartsWith("NO")){
					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;
			
			m_pSocket.WriteLine("a1 FETCH 1:* (FLAGS)");

			// Must get lines with * and cmdTag + OK or cmdTag BAD/NO
			string reply = m_pSocket.ReadLine();			
			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 = m_pSocket.ReadLine();
				}
			}
			
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

			if(!reply.ToUpper().StartsWith("OK")){
				if(!reply.ToUpper().StartsWith("NO")){
					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 = "";

			m_pSocket.WriteLine("a1 LIST \"\" \"\"");

			// Must get lines with * and cmdTag + OK or cmdTag BAD/NO
			string reply = m_pSocket.ReadLine();			
			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 = m_pSocket.ReadLine();
				}
			}
			
			reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove Cmd tag

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

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


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

		#endregion


		#region method RemoveCmdTag

		/// <summary>
		/// Removes command tag from response line.
		/// </summary>
		/// <param name="responseLine">Response line with command tag.</param>
		/// <returns></returns>
		private string RemoveCmdTag(string responseLine)
		{
			return responseLine.Substring(responseLine.IndexOf(" ")).Trim();
		}

		#endregion

		#region method ProcessStatusResponse

		/// <summary>
		/// Processes IMAP STATUS response and updates this class status info.
		/// </summary>
		/// <param name="statusResponse">IMAP STATUS response line.</param>
		private void ProcessStatusResponse(string statusResponse)
		{
			/* RFC 3501 7.3.1.  EXISTS Response
				Example:    S: * 23 EXISTS
			*/

			/* RFC 3501 7.3.2.  RECENT Response
				Example:    S: * 5 RECENT
			*/

			statusResponse = statusResponse.ToUpper();

			// Get rid of *
			statusResponse = statusResponse.Substring(1).Trim();

			if(statusResponse.IndexOf("EXISTS") > -1 && statusResponse.IndexOf("FLAGS") == -1){
				m_MsgCount = Convert.ToInt32(statusResponse.Substring(0,statusResponse.IndexOf(" ")).Trim());
			}
			else if(statusResponse.IndexOf("RECENT") > -1 && statusResponse.IndexOf("FLAGS") == -1){
				m_NewMsgCount = Convert.ToInt32(statusResponse.Substring(0,statusResponse.IndexOf(" ")).Trim());
			}
		}

		#endregion

		#region method IsStatusResponse

		/// <summary>
		/// Gets if specified line is STATUS response.
		/// </summary>
		/// <param name="line"></param>
		/// <returns></returns>
		private bool IsStatusResponse(string line)
		{
			if(!line.StartsWith("*")){
				return false;
			}

			// Remove *
			line = line.Substring(1).TrimStart();

			// RFC 3951 7.1.2.  NO Response (untagged)
			if(line.ToUpper().StartsWith("NO")){
				return true;
			}
			// RFC 3951 7.1.3.  BAD Response (untagged)
			else if(line.ToUpper().StartsWith("BAD")){
				return true;
			}

			// * 1 EXISTS
			// * 1 RECENT
            // * 1 EXPUNGE
			if(line.ToLower().IndexOf("exists") > -1){
				return true;
			}
			if(line.ToLower().IndexOf("recent") > -1 && line.ToLower().IndexOf("flags") == -1){
				return true;
			}
            else if(line.ToLower().IndexOf("expunge") > -1){
                return true;
            }

			return false;
		}

		#endregion


		#region Properties Implementation

		/// <summary>
		/// Gets if pop3 client is connected.
		/// </summary>
		public bool Connected
		{
			get{ return m_Connected; }
		}

		/// <summary>
		/// Gets if pop3 client is authenticated.
		/// </summary>
		public bool Authenticated
		{
			get{ return m_Authenticated; }
		}

        /// <summary>
        /// Gets IMAP server path separator char.
        /// </summary>
        public char PathSeparator
        {
            get{
                // Get separator
                if(m_PathSeparator == '\0'){
                    m_PathSeparator = GetFolderSeparator()[0];
                }

                return m_PathSeparator; 
            }
        }

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

        /// <summary>
        /// Gets folder UID.
        /// </summary>
        public long UIDValidity
        {
            get{ return m_UIDValidity; }
        }

        /// <summary>
        /// Gets next predicted message UID.
        /// </summary>
        public long UIDNext
        {
            get{ return m_UIDNext; }
        }

		/// <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; }
		}
        
        /// <summary>
        /// Gets if the connection is an SSL connection.
        /// </summary>
        public bool IsSecureConnection
        {
            get{ 
                if(!m_Connected){
				    throw new Exception("You must connect first");
			    }

                return m_pSocket.SSL; 
            }
        }

		#endregion

	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Estonia Estonia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions