// Uncomment the define below to write tlsssl log to the console screen.
//#define LOGTLSSSL
using System;
using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using ComponentPro.Net;
using ComponentPro.Net.Mail;
using ComponentPro.Security.Certificates;

namespace Pop3Samples
{
    static class ConsoleUtil
    {
        public const ConsoleColor TextColorCommandResponse = ConsoleColor.DarkGray; // Text color for response logs.
        public const ConsoleColor TextColorError = ConsoleColor.Red; // Text color for error logs.
        public const ConsoleColor TextColorInfo = ConsoleColor.Green; // Text color for information logs.

        /// <summary>
        /// Writes line a string to the console screen with the specified color.
        /// </summary>
        /// <param name="color">The text color.</param>
        /// <param name="msg">The string.</param>
        public static void WriteLine(ConsoleColor color, string msg)
        {
            Console.ForegroundColor = color;
            Console.WriteLine(msg);
            Console.ResetColor();
        }

        /// <summary>
        /// Writes line a string to the console with the specified color.
        /// </summary>
        /// <param name="color">The text color.</param>
        /// <param name="msg">The string format.</param>
        /// <param name="args">An array of objects to write using format.</param>
        public static void WriteLine(ConsoleColor color, string msg, params object[] args)
        {
            Console.ForegroundColor = color;
            Console.WriteLine(msg, args);
            Console.ResetColor();
        }

        /// <summary>
        /// Writes line an error message to the console.
        /// </summary>
        /// <param name="msg">The error message.</param>
        public static void WriteError(string msg)
        {
            Console.ForegroundColor = TextColorError;
            Console.WriteLine("ERROR: " + msg);
            Console.ResetColor();
        }

        /// <summary>
        /// Writes line an information to the console.
        /// </summary>
        /// <param name="msg">The information to write.</param>
        public static void WriteInfo(string msg)
        {
            Console.ForegroundColor = TextColorInfo;
            Console.WriteLine(msg);
            Console.ResetColor();
        }

        /// <summary>
        /// Writes line a string to the console with the specified color.
        /// </summary>
        /// <param name="msg">The string format.</param>
        /// <param name="args">An array of objects to write using format.</param>
        public static void WriteInfo(string msg, params object[] args)
        {
            Console.ForegroundColor = TextColorInfo;
            Console.WriteLine(msg, args);
            Console.ResetColor();
        }

        /// <summary>
        /// Parses a string into an array of parameters.
        /// </summary>
        /// <param name="param">The string to parse.</param>
        /// <returns>Parsed parameters.</returns>
        public static string[] ParseParams(string param)
        {
            List<string> arr = new List<string>();

            int start = 0;
            bool instr = false;
            char pc = ' ';

            for (int i = 0; i < param.Length; i++)
            {
                char c = param[i];

                // Start a string?
                if (c == '"')
                    instr = !instr;
                else if (c == ' ' && pc != ' ' && !instr)
                {
                    // Removes all leading and trailing occurrences of a set of "space, tab and qoute".
                    string p = param.Substring(start, i - start).Trim(new char[]{'\t', ' ', '"'});
                    // Add to the parameters list.
                    arr.Add(p);
                    start = i + 1;
                }
                pc = c;
            }

            string s = param.Substring(start).Trim(new char[] { '\t', ' ', '"' });
            arr.Add(s);

            return arr.ToArray();
        }
    }

    internal class Pop3ConsoleClient
    {
        private readonly Pop3 _client; // Pop3 object.
        private DateTime _operationTime = DateTime.MinValue; // Start time of an operation.

        public Pop3ConsoleClient()
        {
            // Create Pop3 object and set event handlers
            _client = new Pop3();
            _client.Timeout = 25000;
            _client.CommandResponse += CommandResponse;
            _client.Progress += Progress;
            _client.CertificateReceived += _client_CertificateReceived;
            _client.CertificateRequired += _client_CertificateRequired;
        }

        /// <summary>
        /// Returns all issues of the given certificate.
        /// </summary>
        /// <param name="status">The certificate verification result.</param>
        /// <param name="code">The error code.</param>
        /// <returns>Certificate problems.</returns>
        private static string GetCertProblem(CertificateVerificationStatus status, int code)
        {
            switch (status)
            {
                case CertificateVerificationStatus.TimeNotValid:
                    return "Server's certificate has expired or is not valid yet.";

                case CertificateVerificationStatus.Revoked:
                    return "Server's certificate has been revoked.";

                case CertificateVerificationStatus.UnknownCA:
                    return "Server's certificate was issued by an unknown authority.";

                case CertificateVerificationStatus.RootNotTrusted:
                    return "Server's certificate was issued by an untrusted authority.";

                case CertificateVerificationStatus.IncompleteChain:
                    return "Server's certificate does not chain up to a trusted root authority.";

                case CertificateVerificationStatus.Malformed:
                    return "Server's certificate is malformed.";

                case CertificateVerificationStatus.CNNotMatch:
                    return "Server hostname does not match the certificate.";

                case CertificateVerificationStatus.UnknownError:
                    return string.Format("Error {0:x} encountered while validating server's certificate.", code);

                default:
                    return status.ToString();
            }
        }

        static void _client_CertificateRequired(object sender, ComponentPro.Security.CertificateRequiredEventArgs e)
        {
            // Load certificates from the local machine.
            X509Store my = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            my.Open(OpenFlags.ReadOnly);

            // Retrieve a list of available certificates.
            X509Certificate2Collection certs = my.Certificates;

            // If no certificate found, return.
            if (certs.Count == 0)
            {
                e.Certificates = null;
                return;
            }

            // Show all certificates.
            Console.WriteLine("Select certificate:");
            for (int i = 0; i <= certs.Count; i++)
            {
                if (i == 0)
                {
                    Console.WriteLine(string.Format("{0}. [Nothing, skip this step]", i));
                    continue;
                }

                Console.WriteLine(string.Format("{0}. {1}", i, certs[i - 1].SubjectName.Name));
            }

            // And ask user to choose an appropriate certificate.
            while (true)
            {
                Console.Write(string.Format("Select certificate [0 - {0}]: ", certs.Count));

                int certIndex;

                try
                {
                    certIndex = int.Parse(Console.ReadLine());
                }
                catch
                {
                    Console.WriteLine("ERROR: Wrong certificate index input!");
                    continue;
                }

                if (certIndex > 0 && certIndex <= certs.Count)
                {
                    e.Certificates = new X509Certificate2Collection(certs[certIndex]);
                    return;
                }

                if (certIndex == 0)
                    break;

                Console.WriteLine(string.Format("ERROR: You must enter number between 0 and {0}.", certs.Count));
            }
        }

        static void _client_CertificateReceived(object sender, ComponentPro.Security.CertificateReceivedEventArgs e)
        {
            X509Certificate2 cert = e.ServerCertificates[0];

            CertificateVerificationStatus status = e.Status;

            CertificateVerificationStatus[] values = (CertificateVerificationStatus[])Enum.GetValues(typeof(CertificateVerificationStatus));

            StringBuilder sbIssues = new StringBuilder();
            for (int i = 0; i < values.Length; i++)
            {
                // Matches the validation status?
                if ((status & values[i]) == 0)
                    continue;

                // The issue is processed.
                status ^= values[i];

                sbIssues.AppendFormat("{0}\r\n", GetCertProblem(values[i], e.ErrorCode));
            }

            Console.WriteLine("Issue: " + sbIssues.ToString());

            Console.ForegroundColor = ConsoleUtil.TextColorInfo;
            Console.WriteLine("Subject: " + cert.SubjectName.Name);
            Console.WriteLine("Issuer: " + cert.IssuerName.Name);
            Console.WriteLine("Effective Date: " + cert.NotBefore);
            Console.WriteLine("Expiry Date: " + cert.NotAfter);
            Console.ResetColor();
            Console.Write("Do you want to accept this certificate (Add to trusted list, Yes, No) [a,y,n]?");

            string response = Console.ReadLine().Trim().ToLower();

            // Add certiticate of the issuer CA to the trusted list.
            if (response == "a")
            {
                e.AddToTrustedRoot = true;
            }
            else if (response == "y")
            {
                e.Accept = true;
            }
        }

        [STAThread]
        private static void Main(string[] args)
        {
            // Print out copyright.
            Console.ForegroundColor = ConsoleUtil.TextColorInfo;
            Console.WriteLine("UltimateMail Console Client Demo");
            Console.WriteLine("UltimateMail is available at http://www.componentpro.com");
            Console.WriteLine();
            Console.ResetColor();

            Pop3ConsoleClient client = new Pop3ConsoleClient();

            // If the program started with parameters.
            if (args.Length > 0)
            {
                try
                {
                    // Open a new connection and authenticate with the specified parameters.
                    if (client.OpenConnection(args))
                        client.AuthenticateUser(null);
                }
                catch (Exception e)
                {
                    ConsoleUtil.WriteError(e.Message);
                }
            }

            // Go to the main loop.
            client.MainLoop();
        }

        /// <summary>
        /// Processes the parameter list and return a list of options and params. An option is recognized when it has '-' at the beginning.
        /// </summary>
        /// <param name="parameters">The list of parameters.</param>
        /// <param name="min">The minimum number of items in the input param list (for validation).</param>
        /// <param name="max">The maximum number of items in the input param list (for validation)</param>
        /// <param name="minopts">The minimum number of options (for validation)</param>
        /// <param name="maxopts">The maximum number of options (for validation)</param>
        /// <param name="outOptions">The output option list.</param>
        /// <param name="minparams">The minimum number of parameters (for validation)</param>
        /// <param name="maxparams">The maximum number of parameters (for validation)</param>
        /// <param name="outParams">The output parameter list.</param>
        /// <returns>true if the given input param list is valid; otherwise is false.</returns>
        static bool GetOptionsAndParams(string[] parameters, int min, int max, int minopts, int maxopts, out string[] outOptions, int minparams, int maxparams, out string[] outParams)
        {
            // If the input parameter is not specified.
            if (parameters == null)
            {
                outOptions = null;
                outParams = null;
                return min == 0;
            }

            if (parameters.Length < min || parameters.Length > max)
            {
                outOptions = null;
                outParams = null;
                return false;
            }

            List<string> opts = new List<string>();
            List<string> prs = new List<string>();

            foreach (string s in parameters)
            {
                if (s[0] == '-')
                    opts.Add(s);
                else
                {
                    prs.Add(s);
                }
            }

            bool correct = opts.Count >= minopts && opts.Count <= maxopts && 
                           prs.Count >= minparams && prs.Count <= maxparams;

            if (correct)
            {
                outOptions = opts.Count == 0 ? null : opts.ToArray();
                outParams = prs.Count == 0 ? null : prs.ToArray();
            }
            else
            {
                outOptions = null;
                outParams = null;
            }

            return correct;
        }

        /// <summary>
        /// Reads password line.
        /// </summary>
        /// <returns>The read password.</returns>
        public static string ReadLine()
        {
            StringBuilder line = new StringBuilder();
            bool complete = false;

            while (!complete)
            {
                ConsoleKeyInfo key = Console.ReadKey(true);
                switch (key.Key)
                {
                    case ConsoleKey.Enter:
                        complete = true;
                        break;

                    case ConsoleKey.Backspace:
                        if (line.Length > 0)
                        {
                            line = line.Remove(line.Length - 1, 1);
                            Console.Write(key.KeyChar);
                            Console.Write(" ");
                            Console.Write(key.KeyChar);
                        }
                        break;

                    default:
                        if ((key.KeyChar >= 0x20) || (key.Key == ConsoleKey.Tab))
                        {
                            line = line.Append(key.KeyChar);
                            Console.Write("*");
                        }
                        break;
                }
            }

            Console.WriteLine();
            return line.ToString();
        }

        /// <summary>
        /// Handles the client's CommandResponse event.
        /// </summary>
        /// <param name="sender">The client object.</param>
        /// <param name="e">The event arguments.</param>
        public void CommandResponse(object sender, CommandResponseEventArgs e)
        {
            if (e.Command != null)
                ConsoleUtil.WriteLine(ConsoleUtil.TextColorCommandResponse, e.Command);
            else
                ConsoleUtil.WriteLine(ConsoleUtil.TextColorCommandResponse, e.Response);
        }

        /// <summary>
        /// Handles the client's Progress event.
        /// </summary>
        /// <param name="sender">The client object.</param>
        /// <param name="e">The event arguments.</param>
        public void Progress(object sender, Pop3ProgressEventArgs e)
        {
            if (e.State == MailClientTransferState.None)
                return;

            if ((DateTime.Now - _operationTime).TotalSeconds <= 1) return;

            ConsoleUtil.WriteInfo(e.BytesTransferred + " bytes.");
            _operationTime = DateTime.Now;
        }

#if LOGTLSSSL
        public static void TlsSslLogEventReceived(object sender, TlsSslLogEventArgs args)
        {
            Console.ForegroundColor = ConsoleUtil.TextColorSecure;

            switch (args.Group)
            {
                case TlsSslLogEventGroup.Alert:
                    Console.Write("alert: ");
                    break;
                case TlsSslLogEventGroup.Info:
                    Console.Write("info: ");
                    break;
                case TlsSslLogEventGroup.StateChange:
                    Console.Write("state: ");
                    break;
            }

            Console.Write(args.Type);

            switch (args.Source)
            {
                case TlsSslLogEventSource.Sent:
                    Console.Write(" sent.");
                    break;
                case TlsSslLogEventSource.Received:
                    Console.Write(" received.");
                    break;
            }

            Console.WriteLine();

            if (args.Type != TlsSslLogEventType.Secured)
            {
                goto Finish;
            }

            Pop3 client = (Pop3) sender;
            Console.WriteLine("Connection was secured using Cipher Protocol {0}.", client.SecureSocket.Cipher.Protocol);
            Console.WriteLine("Connection is using cipher {0}.", client.SecureSocket.Cipher);
        Finish:
            Console.ResetColor();
        }
#endif

        /// <summary>
        /// Shows all supported commands.
        /// </summary>
        private static void ShowHelp()
        {
            Console.ForegroundColor = ConsoleUtil.TextColorInfo;
            Console.WriteLine("!           get           head");
            Console.WriteLine("?           delete        undelete");
            Console.WriteLine("connect     list          quit");
            Console.WriteLine("user        close         ");
            
            Console.ResetColor();
        }

        /// <summary>
        /// Closes the connection.
        /// </summary>
        private void CloseConnection()
        {
            if (_client.State == MailClientState.Disconnected)
            {
                ConsoleUtil.WriteError("Not connected.");
                return;
            }

            ConsoleUtil.WriteInfo("Disconnecting...");
            _client.Disconnect();
        }

        /// <summary>
        /// Opens a new connection.
        /// </summary>
        /// <param name="parameters">The input parameters.</param>
        /// <returns>true if successful; otherwise is false.</returns>
        private bool OpenConnection(string[] parameters)
        {
            try
            {
                string host;

                if (_client.State != MailClientState.Disconnected)
                {
                    ConsoleUtil.WriteError("Already connected. Disconnect first.");
                    return false;
                }

                if (parameters == null || parameters.Length == 0)
                {
                    ConsoleUtil.WriteInfo("Usage: hostname [port] [security]\r\n   port       The server port (eg. 143)\r\n   security   This can be explicit(ex) or implicit(im).");
                    while (true)
                    {
                        Console.Write("Connect to: ");
                        host = Console.ReadLine();
                        if (host.Trim().Length == 0)
                            ConsoleUtil.WriteError("Host name cannot be empty");
                        else
                        {
                            break;
                        }
                    }

                    parameters = ConsoleUtil.ParseParams(host);
                    if (parameters.Length == 0)
                    {
                        ConsoleUtil.WriteError("Host cannot be empty");
                        return false;
                    }
                }

                if (parameters.Length > 3)
                {
                    ConsoleUtil.WriteInfo("Usage: hostname [port] [security]\r\n   port    The server port (eg. 143)\r\n   security   This can be explicit(ex) or implicit(im).");
                    return false;
                }

                host = parameters[0];
                int port = 0;
                SecurityMode securityMode = SecurityMode.None;
                switch (parameters.Length)
                {
                    case 1: // only server name is specified.
                        port = 21;
                        break;
                    case 2: // server name and port are specified
                        try
                        {
                            port = int.Parse(parameters[1]);
                        }
                        catch
                        {
                            port = -1;
                        }
                        if (port <= 0 || port > 65535)
                        {
                            ConsoleUtil.WriteError("Invalid port number.");
                            return false;
                        }
                        break;
                    case 3: // server name, port, and security mode are specified.
                        try
                        {
                            port = int.Parse(parameters[1]);
                        }
                        catch
                        {
                            port = -1;
                        }
                        if (port <= 0 || port > 65535)
                        {
                            ConsoleUtil.WriteError("Invalid port number.");
                            return false;
                        }

                        if (string.Compare(parameters[2], "implicit", true) == 0 || string.Compare(parameters[2], "im") == 0)
                        {
                            securityMode = SecurityMode.Implicit;
                        }
                        else if (string.Compare(parameters[2], "explicit", true) == 0 || string.Compare(parameters[2], "ex") == 0)
                        {
                            securityMode = SecurityMode.Explicit;
                        }
                        break;
                }

                _client.Connect(host, port, securityMode);
                return true;
            }
            catch (Exception e)
            {
                ConsoleUtil.WriteError(e.Message);
                return false;
            }
        }

        /// <summary>
        /// Authenticates user. If the specified authentication parameter is not specified, it will prompt user for username and password.
        /// </summary>
        /// <param name="parameters">The input parameters.</param>
        private void AuthenticateUser(string[] parameters)
        {
            if (_client.State == MailClientState.Disconnected)
            {
                ConsoleUtil.WriteError("Not connected.");
                return;
            }

            string user = null;

            if (parameters == null || parameters.Length == 0)
            {
                Console.Write("User: ");
                user = Console.ReadLine();
            }
            else if (parameters.Length != 1)
            {
                ConsoleUtil.WriteInfo("Usage: auth [username]");
            }
            else
            {
                user = parameters[0];
            }

            Console.Write("Password: ");
            string pass = ReadLine();

            // Login to the server.
            _client.Authenticate(user, pass);
        }

        /// <summary>
        /// Closes the connection, if connected, and exit the program.
        /// </summary>
        private void Quit()
        {
            if (_client.State != MailClientState.Disconnected)
                CloseConnection();

            ConsoleUtil.WriteInfo("Thanks for using UltimateMail .NET");
        }

        #region Messages

        /// <summary>
        /// Converts a string to a number. It returns 0 if the specified string is not valid.
        /// </summary>
        /// <param name="id">The id number.</param>
        /// <returns>The converted integer.</returns>
        static int GetId(string id)
        {
            try
            {
                return int.Parse(id);
            }
            catch
            {
                return 0;
            }
        }

        /// <summary>
        /// Shows headers of one message.
        /// </summary>
        /// <param name="messageId">Message ID.</param>
        /// <param name="parameters">The parsed parameter list.</param>
        private void ShowHeaders(string[] parameters, string messageId)
        {
            // check session state
            if (_client.State == MailClientState.Disconnected)
            {
                ConsoleUtil.WriteError("Not connected.");
                return;
            }

            // Show help if message id is empty
            if (string.IsNullOrEmpty(messageId))
            {
                ConsoleUtil.WriteInfo("Usage: head id");
                return;
            }

            // Try to parse message id
            int id = GetId(messageId);

            // is id valid?
            if (id <= 0)
            {
                Console.WriteLine("Invalid message ID.");
                return;
            }

            // Download message headers.
            Pop3Message messageInfo = _client.DownloadPop3Message(id, Pop3EnvelopeParts.FullHeaders);

            // Show message headers.
            for (int i = 0; i < messageInfo.Headers.Count; i++)
                Console.WriteLine(messageInfo.Headers[i]);
        }

        /// <summary>
        /// Download the specified message.
        /// </summary>
        /// <param name="parameters">The parsed parameter list.</param>
        /// <param name="rawParams">The raw parameters.</param>
        private void DownloadMessage(string[] parameters, string rawParams)
        {
            if (_client.State == MailClientState.Disconnected)
            {
                ConsoleUtil.WriteError("Not connected.");
                return;
            }

            string[] prs;
            string[] opts;
            // Validate and process parameters.
            if (!GetOptionsAndParams(parameters, 1, 2, 0, 0, out opts, 1, 2, out prs))
            {
                ConsoleUtil.WriteInfo("Usage: get id [destination path]\r\n   id                 The message id to search for.\r\n   destination path   The path of the local file. This cannot be a directory.");
                return;
            }

            // Try to parse the message id.
            int id = GetId(prs[0]);

            // Is id valid?
            if (id <= 0)
            {
                Console.WriteLine("Invalid message ID.");
                return;
            }

            if (prs.Length == 1)
            {
                // display message to console
                _client.DownloadMessage(id, Console.OpenStandardOutput());
            }
            else
            {
                // download message and save it to file
                _client.DownloadMessage(id, prs[1]);
            }
        }

        /// <summary>
        /// Shows message list.
        /// </summary>
        private void List()
        {
            if (_client.State == MailClientState.Disconnected)
            {
                ConsoleUtil.WriteError("Not connected.");
                return;
            }

            // Get message list.
            Pop3MailboxStat info = _client.GetMailboxStat();

            ConsoleUtil.WriteInfo(
                string.Format("Getting {0} message(s) ({1})...", info.MessageCount, FormatSize(info.Size)));

            Pop3MessageCollection messageList = _client.ListMessages(Pop3EnvelopeParts.MessageInboxIndex | Pop3EnvelopeParts.Size | Pop3EnvelopeParts.UniqueId);

            if (messageList.Count == 0)
            {
                Console.WriteLine("No messages found.");
                return;
            }

            // Show header.
            Console.WriteLine("+---------------------------------------------------------------------+");
            Console.WriteLine("| #    | Unique ID                                        |    Length |");
            Console.WriteLine("+---------------------------------------------------------------------+");

            // Show messages.
            foreach (Pop3Message message in messageList)
            {
                Console.WriteLine(
                    "| {0} | {1} |  {2} |",
                    message.MessageInboxIndex.ToString().PadLeft(4),
                    message.UniqueId.PadLeft(48),
                    FormatSize(message.Size).PadLeft(8)
                );

            }

            Console.WriteLine("+---------------------------------------------------------------------+");
        }

        /// <summary>
        /// Deletes message.
        /// </summary>
        /// <param name="parameters">The parsed parameter list.</param>
        /// <param name="messageId">The message id.</param>
        private void Delete(string[] parameters, string messageId)
        {
            if (_client.State == MailClientState.Disconnected)
            {
                ConsoleUtil.WriteError("Not connected.");
                return;
            }

            if (string.IsNullOrEmpty(messageId))
            {
                ConsoleUtil.WriteInfo("Usage: delete id");
                return;
            }

            int id = GetId(messageId);

            if (id <= 0)
            {
                Console.WriteLine("Invalid message ID.");
                return;
            }

            // Mark message as deleted
            _client.Delete(id);
        }

        /// <summary>
        /// Undeletes deleted messages.
        /// </summary>
        private void Undelete()
        {
            if (_client.State == MailClientState.Disconnected)
            {
                ConsoleUtil.WriteError("Not connected.");
                return;
            }

            _client.Undelete();
        }

        #endregion

        /// <summary>
        /// Processes user input commands.
        /// </summary>
        public void MainLoop()
        {
            while (true)
            {
                Console.Write("Pop3> ");
                string command = Console.ReadLine().Trim();
                string[] arr = ConsoleUtil.ParseParams(command);

                string cmd = arr[0];
                if (cmd.Length == 0)
                    continue;

                string[] parameters = new string[arr.Length - 1];
                for (int i = 0; i < parameters.Length; i++)
                {
                    parameters[i] = arr[i + 1];
                }

                int n = command.IndexOfAny(new char[] { '\t', ' ' });
                string rawParams = n != -1 ? command.Substring(n + 1).Trim(new char[] { '\t', ' ', '"' }) : string.Empty;

                try
                {
                    switch (cmd)
                    {
                        case "bye":
                            goto case "quit";
                        case "!":
                            goto case "quit";
                        case "exit":
                            goto case "quit";
                        case "quit":
                            Quit();
                            return;
                        case "disconnect":
                            goto case "close";
                        case "close":
                            CloseConnection();
                            break;
                        case "connect":
                            goto case "open";
                        case "open":
                            if (!OpenConnection(parameters)) break;
                            parameters = null;
                            goto case "user";
                        case "user":
                            AuthenticateUser(parameters);
                            break;
                        case "?":
                            goto case "help";
                        case "help":
                            ShowHelp();
                            break;

                        case "head":
                            ShowHeaders(parameters, rawParams);
                            break;

                        case "list":
                            List();
                            break;

                        case "get":
                            DownloadMessage(parameters, rawParams);
                            break;

                        case "del":
                        case "delete":
                            Delete(parameters, rawParams);
                            break;

                        case "undel":
                        case "undelete":
                            Undelete();
                            break;

                        default:
                            ConsoleUtil.WriteError(string.Format("'{0}' is not recognized as a supported command.", cmd));
                            break;
                    }
                }
                catch (Exception exc)
                {
                    Pop3Exception e = exc as Pop3Exception;
                    ConsoleUtil.WriteError(exc.Message);
                    if (e != null && e.Status != MailClientExceptionStatus.ProtocolError && e.Status != MailClientExceptionStatus.ConnectionClosed && e.Message.IndexOf("not supported") == 0)
                    {
                        _client.Disconnect();
                    }
                }
            }
        }

        /// <summary>
        /// Returns a formatted file size in bytes, kbytes, or mbytes.
        /// </summary>
        /// <param name="size">The input file size.</param>
        /// <returns>The formatted file size.</returns>
        public static string FormatSize(long size)
        {
            if (size < 1024)
                return size + " B";
            if (size < 1024*1024)
                return string.Format("{0:#.#} KB", size/1024.0f);
            return size < 1024*1024*1024 ? string.Format("{0:#.#} MB", size/1024.0f/1024.0f) : size.ToString();
        }
    }
}