/// <summary>
        /// Control thread
        /// TODO: separate the functionalities into functions to reduce maintenance cost
        /// </summary>
        private void WorkerThread()
        {
            FtpCommand command;
            FtpState   state = FtpState.WaitUser;

            string tempName = null;         // store old name for "rename from" command
            string response;
            int    timeout = 0;

            try
            {
                while (m_IsAlive)
                {
                    if (m_IsDisposed)
                    {
                        break;
                    }
                    else if (!m_Welcomed)
                    {
                        response = "220 MyFTP " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString() +
                                   " Server [" + m_HostIp.ToString() + "] \r\n";
                        m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                        m_Welcomed = true;
                    }
                    else if (m_CommandSocket == null)
                    {
                        Logging.Print("Session socket has been closed.");
                        break;
                    }
                    else if (m_CommandSocket.Poll(1000, SelectMode.SelectRead))
                    {
                        if (m_CommandSocket.Available == 0)
                        {
                            Logging.Print("REMOTE DISCONNECT " + m_CommandSocket.RemoteEndPoint.ToString());
                            m_IsAlive = false;
                            break;
                        }
                        // read until find the end of a line
                        command = FtpCommandCreator.Create(ReadCommand());

                        if (command == null)
                        {
                            break;
                        }
                        else
                        {
                            switch (command.Type)
                            {
                            case FtpCommandType.User:
                                response = "331 " + command.Content + " login ok \r\n";
                                m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                state    = FtpState.WaitPwd;
                                tempName = command.Content;
                                break;

                            case FtpCommandType.Pass:
                                if (state != FtpState.WaitPwd)
                                {
                                    response = "332 Need Account for Login.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else
                                {
                                    UserAuthenticatorArgs args = new UserAuthenticatorArgs(tempName, command.Content);
                                    FtpListener.RaiseEvent(this, args);
                                    if (args.Result == UserAuthenticationResult.Approved)
                                    {
                                        User.UserName = tempName;
                                        User.PassWord = command.Content;
                                        response      = "230 access granted for " + User.UserName + ", restrictions apply. \r\n";
                                        m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                        state = FtpState.WaitCommand;
                                        break;
                                    }
                                    else
                                    {
                                        response = "530 Login incorrect. \r\n";
                                        m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                        state = FtpState.WaitCommand;
                                        break;
                                    }
                                }

                            case FtpCommandType.Cwd:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitUser)
                                {
                                    response = "332 Need Account for Login.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    if (command.Content == null)
                                    {
                                        response = m_BadSequence;
                                        m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                        break;
                                    }
                                    else
                                    {
                                        FtpListenerRequest request = new FtpListenerRequest(WebRequestMethodsEx.Ftp.ChangeDirectory, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this);
                                        FtpListenerContext context = new FtpListenerContext(this, request);
                                        m_SendContext.AddContext(context);
                                        break;
                                    }
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.Quit:
                                m_IsAlive = false;
                                CloseDataChannel();
                                response = "221 Goodbye.\r\n";
                                m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                m_CommandSocket.Close();
                                m_CommandSocket = null;
                                break;

                            case FtpCommandType.Pasv:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    CloseDataChannel();
                                    m_DataThread = new Thread(PassiveThread);
                                    m_DataThread.Start();
                                    m_DataReadyEvent.WaitOne();                    // wait until port has been successfully assigned

                                    byte[] addrs = m_HostIp.GetAddressBytes();

                                    int upper = m_HostPort / 256;
                                    int lower = m_HostPort % 256;
                                    if (addrs.Length == 4)     //IPv4
                                    {
                                        response = "227 Entering Passive Mode (";
                                        foreach (int i in addrs)
                                        {
                                            response += i.ToString() + ",";
                                        }
                                        response += upper.ToString() + "," + lower.ToString() + ")\r\n";
                                    }
                                    else     // currently do not support IPv6
                                    {
                                        throw new NotImplementedException("currently does not support IPv6");
                                    }
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.Type:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    response = "200 Command OK.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.List:
                                if (!m_DataModeON)
                                {
                                    response = "425 Use PORT or PASV first.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    response = "150 Opening UTF8 mode data connection for *** \r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.ListDirectoryDetails, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this);
                                    FtpListenerContext context = new FtpListenerContext(this, request);
                                    m_SendContext.AddContext(context);
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.NList:
                                if (!m_DataModeON)
                                {
                                    response = "425 Use PORT or PASV first.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    response = "150 Opening UTF8 mode data connection for *** \r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.ListDirectory, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this);
                                    FtpListenerContext context = new FtpListenerContext(this, request);
                                    m_SendContext.AddContext(context);
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.Port:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand && command.Content != null)
                                {
                                    CloseDataChannel();
                                    string[] epad = command.Content.Split(new char[] { ',' });
                                    if (epad.Length != 6)
                                    {
                                        response = "500 Invalid PORT command.\r\n";
                                        m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    }
                                    else
                                    {
                                        try
                                        {
                                            m_ClientIp = IPAddress.Parse(epad[0] + "." + epad[1] + "." + epad[2] + "." + epad[3]);

                                            m_ClientPort = Int32.Parse(epad[4]) * 256 + Int32.Parse(epad[5]);
                                            if (m_ClientPort <= 0 || m_ClientPort > 65535)
                                            {
                                                response = "500 Invalid PORT command.\r\n";
                                                m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                            }
                                            else
                                            {
                                                m_ActThreadW = true;
                                                m_DataThread = new Thread(ActiveThread);
                                                m_DataThread.Start();

                                                response = "200 PORT command successful.\r\n";
                                                m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                            }
                                        }
                                        catch (Exception)
                                        {
                                            response = "500 Invalid PORT command.\r\n";
                                            m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                        }
                                    }
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.Sys:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    response = "215 System Type: EMIC\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.Feature:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    response = "211-Features:\r\n SIZE\r\n211 End\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.Pwd:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    response = "257 \"" + m_CurrentDirectory.GetNetPath() + "\" is the current directory. \r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.Retr:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    if (!m_DataModeON)
                                    {
                                        response = "425 Use PORT or PASV first.\r\n";
                                        m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                        break;
                                    }
                                    response = "150 Opening BINARY mode data connection for " + command.Content + ". \r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.DownloadFile, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this);
                                    FtpListenerContext context = new FtpListenerContext(this, request);
                                    m_SendContext.AddContext(context);
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.Opts:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    if (command.Content == "utf8")
                                    {
                                        response = "213 Always in utf8 mode \r\n";
                                    }
                                    else
                                    {
                                        response = "550 Requested action not taken. Mode not support. \r\n";
                                    }
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.Size:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.GetFileSize, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this);
                                    FtpListenerContext context = new FtpListenerContext(this, request);
                                    m_SendContext.AddContext(context);
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.Store:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    if (!m_DataModeON)
                                    {
                                        response = "425 Use PORT or PASV first.\r\n";
                                        m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                        break;
                                    }
                                    response = "150 Opening BINARY mode data connection for " + command.Content + ". \r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.UploadFile, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this);
                                    FtpListenerContext context = new FtpListenerContext(this, request);
                                    m_SendContext.AddContext(context);
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.Noop:
                                response = "200 NOOP command successful. \r\n";
                                m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                break;

                            case FtpCommandType.Delete:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.DeleteFile, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this);
                                    FtpListenerContext context = new FtpListenerContext(this, request);
                                    m_SendContext.AddContext(context);
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.MkDir:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.MakeDirectory, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this);
                                    FtpListenerContext context = new FtpListenerContext(this, request);
                                    m_SendContext.AddContext(context);
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.Rmd:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    FtpListenerRequest request = new FtpListenerRequest(WebRequestMethods.Ftp.RemoveDirectory, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this);
                                    FtpListenerContext context = new FtpListenerContext(this, request);
                                    m_SendContext.AddContext(context);
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.Rnfr:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    FtpListenerRequest request = new FtpListenerRequest(WebRequestMethodsEx.Ftp.RenameFrom, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this);
                                    FtpListenerContext context = new FtpListenerContext(this, request);
                                    m_SendContext.AddContext(context);
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            case FtpCommandType.Rnto:
                                if (state == FtpState.WaitPwd)
                                {
                                    response = "331 Need Password.\r\n";
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }
                                else if (state == FtpState.WaitCommand)
                                {
                                    FtpListenerRequest request = new FtpListenerRequest(WebRequestMethodsEx.Ftp.RenameTo, m_CurrentDirectory.Combine(command.Content).GetNetPath(), this);
                                    FtpListenerContext context = new FtpListenerContext(this, request);
                                    m_SendContext.AddContext(context);
                                    break;
                                }
                                else
                                {
                                    response = m_BadSequence;
                                    m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                    break;
                                }

                            default:
                                response = "502 Command not implemented. \r\n";
                                if (state == FtpState.WaitPwd)
                                {
                                    state = FtpState.WaitUser;
                                }
                                m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                                break;
                            }
                        }
                        // reset time out
                        timeout = 0;
                    }
                    else if (m_CommandSocket.Poll(1000, SelectMode.SelectError))
                    {
                        Logging.Print("Disconnected unproperly.");
                        break;
                    }
                    else
                    {
                        if (!m_Welcomed)
                        {
                            response = "220 MyFTP " + typeof(FtpListenerSession).Assembly.GetName().Version.ToString() +
                                       " Server [" + m_HostIp.ToString() + "] \r\n";
                            m_CommandSocket.Send(Encoding.UTF8.GetBytes(response));
                            m_Welcomed = true;
                        }
                        else
                        {
                            if (SessionTimeOut != -1)
                            {
                                if (timeout > SessionTimeOut)
                                {
                                    m_IsAlive = false;
                                    Logging.Print("Connection time out.");
                                    break;
                                }
                                timeout += 50;
                            }
                            Thread.Sleep(50);
                        }
                    }
                }
            }
            catch (SocketException se)
            {
                Logging.Print("Control Socket Exception : " + se.ErrorCode.ToString());
            }
            catch (IOException ioe)
            {
                Logging.Print("IOException: " + ioe.Message);
            }
            finally
            {
                m_IsAlive = false;
                if (m_CommandSocket != null)
                {
                    m_CommandSocket.Close();
                    m_CommandSocket = null;
                }
                if (m_ListenSocket != null)
                {
                    m_ListenSocket.Close();
                    m_CommandSocket = null;
                }
                Logging.Print("Client Disconnected.");
            }
        }