!C99Shell v. 1.0 pre-release build #13!

Software: Apache. PHP/5.5.15 

uname -a: Windows NT SVR-DMZ 6.1 build 7600 (Windows Server 2008 R2 Enterprise Edition) i586 

SYSTEM 

Safe-mode: OFF (not secure)

C:\dmz\FileZillaFTP\source\   drwxrwxrwx
Free 4.13 GB of 39.52 GB (10.45%)
Detected drives: [ a ] [ c ] [ d ] [ e ] [ f ]
Home    Back    Forward    UPDIR    Refresh    Search    Buffer    Encoder    Tools    Proc.    FTP brute    Sec.    SQL    PHP-code    Update    Feedback    Self remove    Logout    


Viewing file:     ControlSocket.cpp (96.33 KB)      -rw-rw-rw-
Select action/file-type:
(+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
// FileZilla Server - a Windows ftp server

// Copyright (C) 2002-2004 - Tim Kosse <tim.kosse@gmx.de>

// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

// ControlSocket.cpp: Implementierungsdatei
//

#include "stdafx.h"
#include "ControlSocket.h"
#include "transfersocket.h"
#include "ServerThread.h"
#include "Options.h"
#include "Permissions.h"
#include "AsyncGssSocketLayer.h"
#include "AsyncSslSocketLayer.h"
#include <math.h>
#include "iputils.h"
#include "autobanmanager.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CControlSocket

std::map<CStdString, int> CControlSocket::m_UserCount;
CCriticalSectionWrapper CControlSocket::m_Sync;

CControlSocket::CControlSocket(CServerThread *pOwner)
    : m_hash_algorithm(CHashThread::SHA1)
{
    m_status.loggedon = FALSE;
    m_status.hammerValue = 0;
    m_transferstatus.socket = NULL;
    m_transferstatus.ip = _T("");
    m_transferstatus.port = -1;
    m_transferstatus.pasv = -1;
    m_transferstatus.rest = 0;
    m_transferstatus.type = -1;
    m_transferstatus.family = AF_UNSPEC;
    m_bWaitGoOffline = FALSE;
    GetSystemTime(&m_LastTransferTime);
    GetSystemTime(&m_LastCmdTime);
    GetSystemTime(&m_LoginTime);
    m_bQuitCommand = FALSE;

    ASSERT(pOwner);
    m_pOwner = pOwner;

    m_nTelnetSkip = 0;
    m_nRecvBufferPos = 0;

    m_pSendBuffer = NULL;
    m_nSendBufferLen = 0;

    m_pGssLayer = NULL;
    m_pSslLayer = NULL;

    for (int i = 0; i < 2; i++)
    {
        m_SlQuotas[i].bContinue = false;
        m_SlQuotas[i].nBytesAllowedToTransfer = -1;
        m_SlQuotas[i].nTransferred = 0;
        m_SlQuotas[i].bBypassed = true;
    }

    m_transferMode = mode_stream;

    m_zlibLevel = 8;

    m_antiHammeringWaitTime = 0;
    m_bProtP = false;

    m_useUTF8 = true;

    for (int i = 0; i < 3; i++)
        m_facts[i] = true;
    m_facts[fact_perm] = false;

    m_shutdown = false;

    m_hash_id = 0;
}

CControlSocket::~CControlSocket()
{
    if (m_status.loggedon)
    {
        DecUserCount(m_status.user);
        m_pOwner->DecIpCount(m_status.ip);
        m_status.loggedon = FALSE;
    }
    t_connop *op = new t_connop;
    op->data = 0;
    op->op = USERCONTROL_CONNOP_REMOVE;
    op->userid = m_userid;
    m_pOwner->SendNotification(FSM_CONNECTIONDATA, (LPARAM)op);
    if (m_transferstatus.socket)
        delete m_transferstatus.socket;
    m_transferstatus.socket=0;

    delete [] m_pSendBuffer;
    m_nSendBufferLen = 0;

    RemoveAllLayers();
    delete m_pGssLayer;
    delete m_pSslLayer;
}

/////////////////////////////////////////////////////////////////////////////
// Member-Funktion CControlSocket

#define BUFFERSIZE 500
void CControlSocket::OnReceive(int nErrorCode)
{
    if (m_antiHammeringWaitTime)
    {
        if (nErrorCode)
        {
            //Control connection has been closed
            Close();
            SendStatus(_T("disconnected."), 0);
            m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);
        }
        return;
    }

    int len = BUFFERSIZE;
    int nLimit = GetSpeedLimit(upload);
    if (!nLimit)
    {
        ParseCommand();
        return;
    }
    if (len > nLimit && nLimit != -1)
        len = nLimit;

    unsigned char *buffer = new unsigned char[BUFFERSIZE];
    int numread = Receive(buffer, len);
    if (numread != SOCKET_ERROR && numread)
    {
        if (nLimit != -1)
            m_SlQuotas[upload].nTransferred += numread;

        m_pOwner->IncRecvCount(numread);
        //Parse all received bytes
        for (int i = 0; i<numread; i++)
        {
            if (!m_nRecvBufferPos)
            {
                //Remove telnet characters
                if (m_nTelnetSkip)
                {
                    if (buffer[i] < 240)
                        m_nTelnetSkip = 0;
                    else
                        continue;
                }
                else if (buffer[i] == 255)
                {
                    m_nTelnetSkip = 1;
                    continue;
                }
            }

            //Check for line endings
            if ((buffer[i] == '\r')||(buffer[i] == 0)||(buffer[i] == '\n'))
            {
                //If input buffer is not empty...
                if (m_nRecvBufferPos)
                {
                    m_RecvBuffer[m_nRecvBufferPos] = 0;
                    m_RecvLineBuffer.push_back(m_RecvBuffer);
                    m_nRecvBufferPos = 0;

                    //Signal that there is a new command waiting to be processed.
                    GetSystemTime(&m_LastCmdTime);
                }
            }
            else
                //The command may only be 2000 chars long. This ensures that a malicious user can't
                //send extremely large commands to fill the memory of the server
                if (m_nRecvBufferPos < 2000)
                    m_RecvBuffer[m_nRecvBufferPos++] = buffer[i];
        }
    }
    else
    {
        if (!numread || GetLastError() != WSAEWOULDBLOCK)
        {
            //Control connection has been closed
            Close();
            SendStatus(_T("disconnected."), 0);
            m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);

            delete [] buffer;
            return;
        }
    }

    ParseCommand();

    delete [] buffer;
}

BOOL CControlSocket::GetCommand(CStdString &command, CStdString &args)
{
    //Get first command from input buffer
    CStdStringA str;
    if (m_RecvLineBuffer.empty())
        return FALSE;
    str = m_RecvLineBuffer.front();
    m_RecvLineBuffer.pop_front();

    //Output command in status window
    CStdString str2;
    if (m_useUTF8)
    {
#ifdef _UNICODE
    str2 = ConvFromNetwork(str);
#else
    str2 = ConvToLocal(ConvFromNetwork(str));
#endif
    }
    else
    {
#ifdef _UNICODE
    str2 = ConvFromLocal(str);
#else
    str2 = str;
#endif
    }
    
    //Hide passwords if the server admin wants to.
    if (!str2.Left(5).CompareNoCase(_T("PASS ")))
    {    if (m_pOwner->m_pOptions->GetOptionVal(OPTION_LOGSHOWPASS))
            SendStatus(str2, 2);
        else
        {
            CStdString msg = str2;
            for (int i = 5; i < msg.GetLength(); i++)
                msg[i] = '*';
            SendStatus(msg, 2);
        }
    }
    else
        SendStatus(str2, 2);

    //Split command and arguments
    int pos = str2.Find(_T(" "));
    if (pos != -1)
    {
        command = str2.Left(pos);
        if (pos == str2.GetLength() - 1)
            args = _T("");
        else
        {
            args = str2.Mid(pos + 1);
            if (args == _T(""))
            {
                Send(_T("501 Syntax error, failed to decode string"));
                return FALSE;
            }
        }
    }
    else
    {
        args = _T("");
        command = str2;
    }
    if (command == _T(""))
        return FALSE;
    command.MakeUpper();
    return TRUE;
}

void CControlSocket::SendStatus(LPCTSTR status, int type)
{
    t_statusmsg *msg = new t_statusmsg;
    _tcscpy(msg->ip, m_RemoteIP);
    GetLocalTime(&msg->time);
    if (!m_status.loggedon)
    {
        msg->user = new TCHAR[16];
        _tcscpy(msg->user, _T("(not logged in)"));
    }
    else
    {
        msg->user = new TCHAR[_tcslen(m_status.user) + 1];
        _tcscpy(msg->user, m_status.user);
    }
    msg->userid = m_userid;
    msg->type = type;
    msg->status = new TCHAR[_tcslen(status) + 1];
    _tcscpy(msg->status, status);
    m_pOwner->SendNotification(FSM_STATUSMESSAGE, (LPARAM)msg);
}

BOOL CControlSocket::Send(LPCTSTR str, bool sendStatus /*=true*/)
{
    if (sendStatus)
        SendStatus(str, 3);

    char* buffer;
    int len;
    if (m_useUTF8)
    {
        char* utf8 = ConvToNetwork(str);
        if (!utf8)
        {
            Close();
            SendStatus(_T("Failed to convert reply to UTF-8"), 1);
            m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);

            return false;
        }

        buffer = new char[strlen(utf8) + 3];
        strcpy(buffer, utf8);
        strcat(buffer, "\r\n");
        len = strlen(buffer);
        delete [] utf8;
    }
    else
    {
        CStdStringA local = ConvToLocal(str);
        if (local == "")
        {
            Close();
            SendStatus(_T("Failed to convert reply to local charset"), 1);
            m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);

            return false;
        }

        buffer = new char[strlen(local) + 3];
        strcpy(buffer, local);
        strcat(buffer, "\r\n");
        len = strlen(buffer);
    }


    //Add line to back of send buffer if it's not empty
    if (m_pSendBuffer)
    {
        char *tmp = m_pSendBuffer;
        m_pSendBuffer = new char[m_nSendBufferLen + len];
        memcpy(m_pSendBuffer, tmp, m_nSendBufferLen);
        memcpy(m_pSendBuffer+m_nSendBufferLen, buffer, len);
        delete [] tmp;
        m_nSendBufferLen += len;
        delete [] buffer;
        return TRUE;
    }

    int nLimit = GetSpeedLimit(download);
    if (!nLimit)
    {
        if (!m_pSendBuffer)
        {
            m_pSendBuffer = buffer;
            m_nSendBufferLen = len;
        }
        else
        {
            char *tmp = m_pSendBuffer;
            m_pSendBuffer = new char[m_nSendBufferLen + len];
            memcpy(m_pSendBuffer, tmp, m_nSendBufferLen);
            memcpy(m_pSendBuffer+m_nSendBufferLen, buffer, len);
            delete [] tmp;
            m_nSendBufferLen += len;
            delete [] buffer;
        }
        return TRUE;
    }
    int numsend = nLimit;
    if (numsend == -1 || len < numsend)
        numsend = len;

    int res = CAsyncSocketEx::Send(buffer, numsend);
    if (res==SOCKET_ERROR && GetLastError() == WSAEWOULDBLOCK)
    {
        res = 0;
    }
    else if (!res || res==SOCKET_ERROR)
    {
        delete [] buffer;
        Close();
        SendStatus(_T("could not send reply, disconnected."), 0);
        m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);
        return FALSE;
    }

    if (nLimit != -1)
        m_SlQuotas[download].nTransferred += res;

    if (res != len)
    {
        if (!m_pSendBuffer)
        {
            m_pSendBuffer = new char[len-res];
            memcpy(m_pSendBuffer, buffer+res, len-res);
            m_nSendBufferLen = len-res;
        }
        else
        {
            char *tmp = m_pSendBuffer;
            m_pSendBuffer = new char[m_nSendBufferLen + len - res];
            memcpy(m_pSendBuffer, tmp, m_nSendBufferLen);
            memcpy(m_pSendBuffer+m_nSendBufferLen, buffer+res, len-res);
            delete [] tmp;
            m_nSendBufferLen += len-res;
        }
        TriggerEvent(FD_WRITE);
    }
    delete [] buffer;

    m_pOwner->IncSendCount(res);
    return TRUE;
}

void CControlSocket::OnClose(int nErrorCode)
{
    Close();
    SendStatus(_T("disconnected."), 0);
    m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);
    CAsyncSocketEx::OnClose(nErrorCode);
}

#define COMMAND_USER    0
#define COMMAND_PASS    1
#define COMMAND_QUIT    2
#define COMMAND_CWD        3
#define COMMAND_PWD        4
#define COMMAND_PORT    5
#define COMMAND_PASV    6
#define COMMAND_TYPE    7
#define COMMAND_LIST    8
#define COMMAND_REST    9
#define COMMAND_CDUP    10
#define COMMAND_RETR    11
#define COMMAND_STOR    12
#define COMMAND_SIZE    13
#define COMMAND_DELE    14
#define COMMAND_RMD        15
#define COMMAND_MKD        16
#define COMMAND_RNFR    17
#define COMMAND_RNTO    18
#define COMMAND_ABOR    19
#define COMMAND_SYST    20
#define COMMAND_NOOP    21
#define COMMAND_APPE    22
#define COMMAND_NLST    23
#define COMMAND_MDTM    24
#define COMMAND_XPWD    25
#define COMMAND_XCUP    26
#define COMMAND_XMKD    27
#define COMMAND_XRMD    28
#define COMMAND_NOP        29
#define COMMAND_EPSV    30
#define COMMAND_EPRT    31
#define COMMAND_AUTH    32
#define COMMAND_ADAT    33
#define COMMAND_PBSZ    34
#define COMMAND_PROT    35
#define COMMAND_FEAT    36
#define COMMAND_MODE    37
#define COMMAND_OPTS    38
#define COMMAND_HELP    39
#define COMMAND_ALLO    40
#define COMMAND_MLST    41
#define COMMAND_MLSD    42
#define COMMAND_SITE    43
#define COMMAND_PASVSMC    44 // some bugged SMC routers convert incoming PASV into P@SW
#define COMMAND_STRU    45
#define COMMAND_CLNT    46
#define COMMAND_MFMT    47
#define COMMAND_HASH    48

typedef struct
{
    int nID;
    TCHAR command[5];
    BOOL bHasargs;
    BOOL bValidBeforeLogon;
} t_command;

static const t_command commands[]={    COMMAND_USER, _T("USER"), TRUE,     TRUE,
                                    COMMAND_PASS, _T("PASS"), FALSE, TRUE,
                                    COMMAND_QUIT, _T("QUIT"), FALSE, TRUE,
                                    COMMAND_CWD,  _T("CWD"),  FALSE, FALSE,
                                    COMMAND_PWD,  _T("PWD"),  FALSE, FALSE,
                                    COMMAND_PORT, _T("PORT"), TRUE,  FALSE,
                                    COMMAND_PASV, _T("PASV"), FALSE, FALSE,
                                    COMMAND_TYPE, _T("TYPE"), TRUE,  FALSE,
                                    COMMAND_LIST, _T("LIST"), FALSE, FALSE,
                                    COMMAND_REST, _T("REST"), TRUE,  FALSE,
                                    COMMAND_CDUP, _T("CDUP"), FALSE, FALSE,
                                    COMMAND_RETR, _T("RETR"), TRUE,  FALSE,
                                    COMMAND_STOR, _T("STOR"), TRUE,  FALSE,
                                    COMMAND_SIZE, _T("SIZE"), TRUE,  FALSE,
                                    COMMAND_DELE, _T("DELE"), TRUE,  FALSE,
                                    COMMAND_RMD,  _T("RMD"),  TRUE,  FALSE,
                                    COMMAND_MKD,  _T("MKD"),  TRUE,  FALSE,
                                    COMMAND_RNFR, _T("RNFR"), TRUE,  FALSE,
                                    COMMAND_RNTO, _T("RNTO"), TRUE,  FALSE,
                                    COMMAND_ABOR, _T("ABOR"), FALSE, FALSE,
                                    COMMAND_SYST, _T("SYST"), FALSE, TRUE,
                                    COMMAND_NOOP, _T("NOOP"), FALSE, FALSE,
                                    COMMAND_APPE, _T("APPE"), TRUE,  FALSE,
                                    COMMAND_NLST, _T("NLST"), FALSE, FALSE,
                                    COMMAND_MDTM, _T("MDTM"), TRUE,  FALSE,
                                    COMMAND_XPWD, _T("XPWD"), FALSE, FALSE,
                                    COMMAND_XCUP, _T("XCUP"), FALSE, FALSE,
                                    COMMAND_XMKD, _T("XMKD"), TRUE,  FALSE,
                                    COMMAND_XRMD, _T("XRMD"), TRUE,  FALSE,
                                    COMMAND_NOP,  _T("NOP"),  FALSE, FALSE,
                                    COMMAND_EPSV, _T("EPSV"), FALSE, FALSE,
                                    COMMAND_EPRT, _T("EPRT"), TRUE,  FALSE,
                                    COMMAND_AUTH, _T("AUTH"), TRUE,  TRUE,
                                    COMMAND_ADAT, _T("ADAT"), TRUE,  TRUE,
                                    COMMAND_PBSZ, _T("PBSZ"), TRUE,  TRUE,
                                    COMMAND_PROT, _T("PROT"), TRUE,  TRUE,
                                    COMMAND_FEAT, _T("FEAT"), FALSE, TRUE,
                                    COMMAND_MODE, _T("MODE"), TRUE,  FALSE,
                                    COMMAND_OPTS, _T("OPTS"), TRUE,  FALSE,
                                    COMMAND_HELP, _T("HELP"), FALSE, TRUE,
                                    COMMAND_ALLO, _T("ALLO"), FALSE, FALSE,
                                    COMMAND_MLST, _T("MLST"), FALSE, FALSE,
                                    COMMAND_MLSD, _T("MLSD"), FALSE, FALSE,
                                    COMMAND_SITE, _T("SITE"), TRUE,  TRUE,
                                    COMMAND_PASVSMC, _T("P@SW"), FALSE, FALSE,
                                    COMMAND_STRU, _T("STRU"), TRUE, FALSE,
                                    COMMAND_CLNT, _T("CLNT"), TRUE, TRUE,
                                    COMMAND_MFMT, _T("MFMT"), TRUE, FALSE,
                                    COMMAND_HASH, _T("HASH"), TRUE, FALSE
                        };

void CControlSocket::ParseCommand()
{
    if (m_antiHammeringWaitTime)
        return;

    //Get command
    CStdString command;
    CStdString args;
    if (!GetCommand(command, args))
        return;

    //Check if command is valid
    int nCommandID = -1;
    for (int i = 0; i < (sizeof(commands)/sizeof(t_command)); i++)
    {
        if (command == commands[i].command)
        {
            //Does the command needs an argument?
            if (commands[i].bHasargs && (args == _T("")))
            {
                Send(_T("501 Syntax error"));
                if (!m_RecvLineBuffer.empty())
                    m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_COMMAND, m_userid);
                return;
            }
            //Can it be issued before logon?
            else if (!m_status.loggedon && !commands[i].bValidBeforeLogon)
            {
                Send(_T("530 Please log in with USER and PASS first."));
                if (!m_RecvLineBuffer.empty())
                    m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_COMMAND, m_userid);
                return;
            }
            nCommandID = commands[i].nID;
            break;
        }
    }
    //Command not recognized
    if (nCommandID == -1)
    {
        Send(_T("500 Syntax error, command unrecognized."));
        if (!m_RecvLineBuffer.empty())
            m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_COMMAND, m_userid);
        return;
    }

    //Now process the commands
    switch (nCommandID)
    {
    case COMMAND_USER:
        {
            AntiHammerIncrease();

            if (m_status.loggedon)
            {
                GetSystemTime(&m_LoginTime);
                DecUserCount(m_status.user);
                m_pOwner->DecIpCount(m_status.ip);
                t_connop *op = new t_connop;
                op->op = USERCONTROL_CONNOP_CHANGEUSER;
                t_connectiondata_changeuser *conndata = new t_connectiondata_changeuser;
                op->data = conndata;
                op->userid = m_userid;
                m_pOwner->SendNotification(FSM_CONNECTIONDATA, (LPARAM)op);

                m_status.loggedon = FALSE;
                m_CurrentServerDir = _T("");
            }
            if (m_pOwner->m_pOptions->GetOptionVal(OPTION_ENABLESSL) && m_pOwner->m_pOptions->GetOptionVal(OPTION_ALLOWEXPLICITSSL) &&
                m_pOwner->m_pOptions->GetOptionVal(OPTION_SSLFORCEEXPLICIT) && !m_pSslLayer)
            {
                Send(_T("530 Have to use explicit SSL/TLS before logging on."));
                break;
            }
            RenName = _T("");
            args.MakeLower();
            m_status.user = args;
            if (m_pGssLayer && m_pGssLayer->AuthSuccessful())
            {
                char sendme[4096];

#ifdef _UNICODE
                int res = m_pGssLayer->ProcessCommand("USER", ConvToLocal(args), sendme);
#else
                int res = m_pGssLayer->ProcessCommand("USER", args, sendme);
#endif
                if (res != -1)
                {
                    if (DoUserLogin(0, true))
                    {
#ifdef _UNICODE
                        Send(ConvFromLocal(sendme));
#else
                        Send(sendme);
#endif
                    }
                    else
                        m_status.user = _T("");
                }
                break;
            }
            if (!m_pSslLayer)
            {
                CUser user;
                if (m_pOwner->m_pPermissions->CheckUserLogin(m_status.user, _T(""), user, true) && user.ForceSsl())
                {
                    m_status.user = _T("");
                    Send(_T("530 SSL required"));
                    break;
                }
            }
            Send(_T("331 Password required for ") + args);
        }
        break;
    case COMMAND_PASS:
        AntiHammerIncrease();

        if (m_status.loggedon)
            Send(_T("503 Bad sequence of commands."));
        else if (m_pGssLayer && m_pGssLayer->AuthSuccessful())
        {
            char sendme[4096];
#ifdef _UNICODE
            int res = m_pGssLayer->ProcessCommand("PASS", ConvToLocal(m_status.user), ConvToLocal(args), sendme);
#else
            int res = m_pGssLayer->ProcessCommand("PASS", m_status.user, args, sendme);
#endif

            if (res != -1)
            {
                if (DoUserLogin(_T(""), true))
#ifdef _UNICODE
                    Send(ConvFromLocal(sendme));
#else
                    Send(sendme);
#endif
            }
        }
        else if (DoUserLogin(args))
            Send(_T("230 Logged on"));
        break;
    case COMMAND_QUIT:
        m_bQuitCommand = TRUE;
        if (!m_transferstatus.socket || !m_transferstatus.socket->InitCalled())
        {
            Send(_T("221 Goodbye"));
            if (m_pSslLayer)
            {
                if (ShutDown() || WSAGetLastError() != WSAEWOULDBLOCK)
                    ForceClose(5);
            }
            else if (CanQuit())
                ForceClose(5);
        }
        break;
    case COMMAND_CWD:
        {
            //Unquote args
            if (!UnquoteArgs(args))
            {
                Send(_T("501 Syntax error"));
                break;
            }

            if (args == _T(""))
            {
                CStdString str;
                str.Format(_T("250 Broken client detected, missing argument to CWD. \"%s\" is current directory."), m_CurrentServerDir);
                Send(str);
                break;
            }

            int res = m_pOwner->m_pPermissions->ChangeCurrentDir(m_status.user, m_CurrentServerDir, args);
            if (!res)
            {
                CStdString str;
                str.Format(_T("250 CWD successful. \"%s\" is current directory."), m_CurrentServerDir);
                Send(str);
            }
            else if (res & PERMISSION_DENIED)
            {
                CStdString str;
                str.Format(_T("550 CWD failed. \"%s\": Permission denied."), args);
                Send(str);
            }
            else if (res & PERMISSION_INVALIDNAME)
            {
                CStdString str;
                str.Format(_T("550 CWD failed. \"%s\": Filename invalid."), args);
                Send(str);
            }
            else if (res)
            {
                CStdString str;
                str.Format(_T("550 CWD failed. \"%s\": directory not found."), args);
                Send(str);
            }
        }
        break;
    case COMMAND_PWD:
    case COMMAND_XPWD:
        {
            CStdString str;
            str.Format(_T("257 \"%s\" is current directory."), m_CurrentServerDir);
            Send(str);
        }
        break;
    case COMMAND_PORT:
        {
            if (GetFamily() != AF_INET)
            {
                Send(_T("500 You are connected using IPv6. PORT is only for IPv4. You have to use the EPRT command instead."));
                break;
            }

            if (m_transferstatus.socket)
            {
                SendTransferinfoNotification();
                delete m_transferstatus.socket;
                m_transferstatus.socket = 0;
            }
            int count = 0;
            int pos = 0;
            //Convert commas to dots
            args.Replace(_T(","), _T("."));
            while (1)
            {
                pos = args.Find(_T("."), pos);
                if (pos != -1)
                    count++;
                else
                    break;
                pos++;
            }
            if (count != 5)
            {
                Send(_T("501 Syntax error"));
                m_transferstatus.pasv = -1;
                break;
            }
            CStdString ip;
            int port = 0;
            int i = args.ReverseFind('.');
            port = _ttoi(args.Right(args.GetLength() - (i + 1))); //get ls byte of server socket
            args = args.Left(i);
            i = args.ReverseFind('.');
            port += 256 * _ttoi(args.Right(args.GetLength() - (i + 1))); // add ms byte to server socket
            ip = args.Left(i);

#ifdef _UNICODE
            int res = inet_addr(ConvToLocal(ip));
#else
            int res = inet_addr(ip);
#endif
            if (res == INADDR_NONE)
            {
                // Fix: Convert IP in PORT command to int and back to string (strips
                // leading zeros) because some FTP clients prepend zeros to it.
                // inet_addr() thinks this is an octal number and will return INADDR_NONE
                // if 8 or 9 are encountered.
                CStdString decIP;
                ip += _T(".");
                int pos = ip.Find('.');
                while (pos != -1)
                {
                    CStdString tmp;
                    tmp.Format(_T("%d."), _ttoi(ip.Left(pos)));
                    decIP += tmp;

                    ip = ip.Mid(pos + 1);
                    pos = ip.Find('.');
                }

                ip = decIP.Left(decIP.GetLength() - 1);
#ifdef _UNICODE
                res = inet_addr(ConvToLocal(ip));
#else
                res = inet_addr(ip);
#endif
            }

            if (res == INADDR_NONE || port < 1 || port > 65535)
             {
                Send(_T("501 Syntax error"));
                m_transferstatus.pasv = -1;
                break;
            }

            if (m_pOwner->m_pOptions->GetOptionVal(OPTION_ACTIVE_IGNORELOCAL))
            {
                CStdString peerAddr;
                UINT peerPort = 0;
                if (GetPeerName(peerAddr, peerPort))
                {
                    if (!IsRoutableAddress(ip) && IsRoutableAddress(peerAddr))
                        ip = peerAddr;
                }
            }

            m_transferstatus.ip = ip;
            m_transferstatus.port = port;
            m_transferstatus.pasv = 0;
            m_transferstatus.family = AF_INET;
            Send(_T("200 Port command successful"));
            break;
        }
    case COMMAND_PASV:
    case COMMAND_PASVSMC:
        {
            if (GetFamily() != AF_INET)
            {
                Send(_T("500 You are connected using IPv6. PASV is only for IPv4. You have to use the EPSV command instead."));
                break;
            }

            if (m_transferstatus.socket)
            {
                SendTransferinfoNotification();
                delete m_transferstatus.socket;
            }
            m_transferstatus.family = AF_INET;
            m_transferstatus.socket = new CTransferSocket(this);

            unsigned int retries = 3;
            unsigned int port = 0;

            CStdString pasvIP = GetPassiveIP();
            if (pasvIP == _T(""))
            {
                delete m_transferstatus.socket;
                m_transferstatus.socket = NULL;
                Send(_T("421 Can't create socket"));
                m_transferstatus.pasv = -1;
                break;
            }

            while (retries > 0)
            {
                if (m_pOwner->m_pOptions->GetOptionVal(OPTION_USECUSTOMPASVPORT))
                {
                    static UINT customPort = 0;
                    unsigned int minPort = (unsigned int)m_pOwner->m_pOptions->GetOptionVal(OPTION_CUSTOMPASVMINPORT);
                    unsigned int maxPort = (unsigned int)m_pOwner->m_pOptions->GetOptionVal(OPTION_CUSTOMPASVMAXPORT);
                    if (minPort > maxPort) {
                        unsigned int temp = minPort;
                        minPort = maxPort;
                        maxPort = temp;
                    }
                    if (customPort < minPort || customPort > maxPort) {
                        customPort = minPort;
                    }
                    port = customPort;

                    ++customPort;
                } else {
                    port = 0;
                }
                if (m_transferstatus.socket->Create(port, SOCK_STREAM, FD_ACCEPT))
                    break;
                --retries;
            }
            if (retries <= 0) {
                delete m_transferstatus.socket;
                m_transferstatus.socket = NULL;
                Send(_T("421 Can't create socket"));
                m_transferstatus.pasv = -1;
                break;
            }

            if (m_pGssLayer && m_pGssLayer->AuthSuccessful())
                m_transferstatus.socket->UseGSS(m_pGssLayer);

            if (m_pSslLayer && m_bProtP)
                m_transferstatus.socket->UseSSL(m_pSslLayer->GetContext());

            if (!m_transferstatus.socket->Listen())
            {
                delete m_transferstatus.socket;
                m_transferstatus.socket = NULL;
                Send(_T("421 Can't create socket"));
                m_transferstatus.pasv = -1;
                break;
            }

            //Now retrieve the port
            CStdString dummy;
            if (m_transferstatus.socket->GetSockName(dummy, port))
            {
                //Reformat the ip
                pasvIP.Replace(_T("."), _T(","));
                //Put the answer together
                CStdString str;
                if (nCommandID == COMMAND_PASVSMC)
                    str.Format(_T("227 Warning: Router with P@SW bug detected. Entering Passive Mode (%s,%d,%d)"), pasvIP, port / 256, port % 256);
                else
                    str.Format(_T("227 Entering Passive Mode (%s,%d,%d)"), pasvIP, port / 256, port % 256);
                Send(str);
                m_transferstatus.pasv = 1;
            }
            else
            {
                delete m_transferstatus.socket;
                m_transferstatus.socket = NULL;
                Send(_T("421 Can't create socket"));
                m_transferstatus.pasv = -1;
                break;
            }
            
            break;
        }
    case COMMAND_TYPE:
        {
            args.MakeUpper();
            if (args[0] != 'I' && args[0] != 'A' && args != _T("L 8"))
            {
                Send(_T("501 Unsupported type. Supported types are I, A and L 8"));
                break;
            }
            m_transferstatus.type = (args[0] == 'A') ? 1 : 0;
            CStdString str;
            str.Format(_T("200 Type set to %s"), args);
            Send(str);
        }
        break;
    case COMMAND_LIST:
        if (m_transferstatus.pasv == -1)
        {
            Send(_T("503 Bad sequence of commands."));
            break;
        }
        if (!m_transferstatus.pasv && (m_transferstatus.ip == _T("") || m_transferstatus.port == -1))
        {
            Send(_T("503 Bad sequence of commands."));
            break;
        }
        if (m_pSslLayer && m_pOwner->m_pOptions->GetOptionVal(OPTION_FORCEPROTP) && !m_bProtP)
        {
            Send(_T("521 PROT P required"));
            break;
        }
        else
        {
            //Check args, currently only supported argument is the directory which will be listed.
            CStdString dirToList;
            args.TrimLeft(_T(" "));
            args.TrimRight(_T(" "));
            if (args != _T(""))
            {
                BOOL bBreak = FALSE;
                while (args[0] == '-') //No parameters supported
                {
                    if (args.GetLength() < 2)
                    { //Dash without param
                        Send(_T("501 Syntax error"));
                        bBreak = TRUE;
                        break;
                    }

                    int pos = args.Find(' ');
                    CStdString params;
                    if (pos != -1)
                    {
                        params = args.Left(1);
                        params = params.Left(pos - 1);
                        args = args.Mid(pos + 1);
                        args.TrimLeft(_T(" "));
                    }
                    else
                        args = _T("");
                    while (params != _T(""))
                    {
                        //Some parameters are not support
                        if (params[0] == 'R')
                        {
                            Send(_T("504 Command not implemented for that parameter"));
                            bBreak = TRUE;
                            break;
                        }
                        //Ignore other parameters
                        params = params.Mid(1);
                    }

                    if (args == _T(""))
                        break;
                }
                if (bBreak)
                    break;
                if (args != _T(""))
                {
                    //Unquote args
                    if (!UnquoteArgs(args))
                    {
                        Send(_T("501 Syntax error"));
                        break;
                    }

                    dirToList = args;
                }
            }

            t_dirlisting *pResult;
            CStdString physicalDir, logicalDir;
            int error = m_pOwner->m_pPermissions->GetDirectoryListing(m_status.user, m_CurrentServerDir, dirToList, pResult, physicalDir, logicalDir, CPermissions::AddLongListingEntry, m_useUTF8);
            if (error & PERMISSION_DENIED)
            {
                Send(_T("550 Permission denied."));
                ResetTransferstatus();
                break;
            }
            else if (error & PERMISSION_INVALIDNAME)
            {
                Send(_T("550 Filename invalid."));
                ResetTransferstatus();
                break;
            }
            else if (error)
            {
                Send(_T("550 Directory not found."));
                ResetTransferstatus();
                break;
            }

            if (!m_transferstatus.pasv)
            {
                if (m_transferstatus.socket)
                {
                    SendTransferinfoNotification();
                    delete m_transferstatus.socket;
                }
                CTransferSocket *transfersocket = new CTransferSocket(this);
                m_transferstatus.socket = transfersocket;
                transfersocket->Init(pResult, TRANSFERMODE_LIST);
                if (m_transferMode == mode_zlib)
                {
                    if (!transfersocket->InitZLib(m_zlibLevel))
                    {
                        Send(_T("550 could not initialize zlib, please use MODE S instead"));
                        ResetTransferstatus();
                        break;
                    }
                }

                if (!CreateTransferSocket(transfersocket))
                    break;

                SendTransferinfoNotification(TRANSFERMODE_LIST, ConvToLocal(physicalDir), ConvToLocal(logicalDir));
                Send(_T("150 Opening data channel for directory list."));
            }
            else
            {
                if (!m_transferstatus.socket)
                {
                    CPermissions::DestroyDirlisting(pResult);
                    Send(_T("503 Bad sequence of commands."));
                    break;
                }
                m_transferstatus.socket->Init(pResult, TRANSFERMODE_LIST);
                if (m_transferMode == mode_zlib)
                {
                    if (!m_transferstatus.socket->InitZLib(m_zlibLevel))
                    {
                        Send(_T("550 could not initialize zlib, please use MODE S instead"));
                        ResetTransferstatus();
                        break;
                    }
                }

                SendTransferinfoNotification(TRANSFERMODE_LIST, ConvToLocal(physicalDir), ConvToLocal(logicalDir));
                m_transferstatus.socket->PasvTransfer();
            }
        }
        break;
    case COMMAND_REST:
        {
            BOOL error = FALSE;
            for (int i = 0; i < args.GetLength(); i++)
                if (args[i] < '0' || args[i] > '9')
                {
                    error = TRUE;
                    break;
                }
            if (error)
            {
                Send(_T("501 Bad parameter. Numeric value required"));
                break;
            }
            m_transferstatus.rest = _ttoi64(args);
            CStdString str;
            str.Format(_T("350 Rest supported. Restarting at %I64d"), m_transferstatus.rest);
            Send(str);
        }
        break;
    case COMMAND_CDUP:
    case COMMAND_XCUP:
        {
            CStdString dir = _T("..");
            int res = m_pOwner->m_pPermissions->ChangeCurrentDir(m_status.user, m_CurrentServerDir, dir);
            if (!res)
            {
                CStdString str;
                str.Format(_T("200 CDUP successful. \"%s\" is current directory."), m_CurrentServerDir);
                Send(str);
            }
            else if (res & PERMISSION_DENIED)
                Send(_T("550 CDUP failed, permission denied."));
            else if (res & PERMISSION_INVALIDNAME)
                Send(_T("550 CDUP failed, filename invalid."));
            else if (res)
                Send(_T("550 CDUP failed, directory not found."));
        }
        break;
    case COMMAND_RETR:
        {
            if (m_transferstatus.pasv == -1)
            {
                Send(_T("503 Bad sequence of commands."));
                break;
            }
            if (!m_transferstatus.pasv && (m_transferstatus.ip == _T("") || m_transferstatus.port == -1))
            {
                Send(_T("503 Bad sequence of commands."));
                break;
            }
            if (m_pSslLayer && m_pOwner->m_pOptions->GetOptionVal(OPTION_FORCEPROTP) && !m_bProtP)
            {
                Send(_T("521 PROT P required"));
                break;
            }
            //Much more checks

            //Unquote args
            if (!UnquoteArgs(args))
            {
                Send( _T("501 Syntax error") );
                break;
            }


            CStdString physicalFile, logicalFile;
            int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_READ, physicalFile, logicalFile);
            if (error & PERMISSION_DENIED)
            {
                Send(_T("550 Permission denied"));
                ResetTransferstatus();
            }
            else if (error & PERMISSION_INVALIDNAME)
            {
                Send(_T("550 Filename invalid."));
                ResetTransferstatus();
            }
            else if (error)
            {
                Send(_T("550 File not found"));
                ResetTransferstatus();
            }
            else
            {
                if (!m_transferstatus.pasv)
                {
                    if (m_transferstatus.socket)
                    {
                        SendTransferinfoNotification();
                        delete m_transferstatus.socket;
                    }
                    CTransferSocket *transfersocket = new CTransferSocket(this);
                    m_transferstatus.socket = transfersocket;
                    transfersocket->Init(physicalFile, TRANSFERMODE_SEND, m_transferstatus.rest);
                    if (m_transferMode == mode_zlib)
                    {
                        if (!transfersocket->InitZLib(m_zlibLevel))
                        {
                            Send(_T("550 could not initialize zlib, please use MODE S instead"));
                            ResetTransferstatus();
                            break;
                        }
                    }

                    if (!CreateTransferSocket(transfersocket))
                        break;

                    __int64 totalSize;
                    if (!GetLength64(physicalFile, totalSize))
                        totalSize = -1;
                    SendTransferinfoNotification(TRANSFERMODE_SEND, physicalFile, logicalFile, m_transferstatus.rest, totalSize);

                    Send(_T("150 Opening data channel for file transfer."));
                }
                else
                {
                    if (!m_transferstatus.socket)
                    {
                        Send(_T("503 Bad sequence of commands."));
                        break;
                    }

                    m_transferstatus.socket->Init(physicalFile, TRANSFERMODE_SEND, m_transferstatus.rest);
                    if (m_transferMode == mode_zlib)
                    {
                        if (!m_transferstatus.socket->InitZLib(m_zlibLevel))
                        {
                            Send(_T("550 could not initialize zlib, please use MODE S instead"));
                            ResetTransferstatus();
                            break;
                        }
                    }

                    __int64 totalSize;
                    if (!GetLength64(physicalFile, totalSize))
                        totalSize = -1;
                    SendTransferinfoNotification(TRANSFERMODE_SEND, physicalFile, logicalFile, m_transferstatus.rest, totalSize);

                    m_transferstatus.socket->PasvTransfer();
                }
                GetSystemTime(&m_LastTransferTime);
            }
            break;
        }
    case COMMAND_STOR:
        {
            if (m_transferstatus.pasv == -1)
            {
                Send(_T("503 Bad sequence of commands."));
                break;
            }
            if(!m_transferstatus.pasv && (m_transferstatus.ip == _T("") || m_transferstatus.port == -1))
            {
                Send(_T("503 Bad sequence of commands."));
                break;
            }
            if (m_pSslLayer && m_pOwner->m_pOptions->GetOptionVal(OPTION_FORCEPROTP) && !m_bProtP)
            {
                Send(_T("521 PROT P required"));
                break;
            }
            //Much more checks

            //Unquote args
            if (!UnquoteArgs(args))
            {
                Send( _T("501 Syntax error") );
                break;
            }

            CStdString physicalFile, logicalFile;
            int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, m_transferstatus.rest ? FOP_APPEND : FOP_WRITE, physicalFile, logicalFile);
            if (error & PERMISSION_DENIED)
            {
                Send(_T("550 Permission denied"));
                ResetTransferstatus();
            }
            else if (error & PERMISSION_INVALIDNAME)
            {
                Send(_T("550 Filename invalid."));
                ResetTransferstatus();
            }
            else if (error)
            {
                Send(_T("550 Filename invalid"));
                ResetTransferstatus();
            }
            else
            {
                if (!m_transferstatus.pasv)
                {
                    CTransferSocket *transfersocket = new CTransferSocket(this);
                    transfersocket->Init(physicalFile, TRANSFERMODE_RECEIVE, m_transferstatus.rest);
                    m_transferstatus.socket = transfersocket;
                    if (m_transferMode == mode_zlib)
                    {
                        if (!transfersocket->InitZLib(m_zlibLevel))
                        {
                            Send(_T("550 could not initialize zlib, please use MODE S instead"));
                            ResetTransferstatus();
                            break;
                        }
                    }

                    if (!CreateTransferSocket(transfersocket))
                        break;

                    SendTransferinfoNotification(TRANSFERMODE_RECEIVE, physicalFile, logicalFile, m_transferstatus.rest);
                    Send(_T("150 Opening data channel for file transfer."));
                }
                else
                {
                    if (!m_transferstatus.socket)
                    {
                        Send(_T("503 Bad sequence of commands."));
                        break;
                    }

                    m_transferstatus.socket->Init(physicalFile, TRANSFERMODE_RECEIVE, m_transferstatus.rest);
                    if (m_transferMode == mode_zlib)
                    {
                        if (!m_transferstatus.socket->InitZLib(m_zlibLevel))
                        {
                            Send(_T("550 could not initialize zlib, please use MODE S instead"));
                            ResetTransferstatus();
                            break;
                        }
                    }
                    SendTransferinfoNotification(TRANSFERMODE_RECEIVE, physicalFile, logicalFile, m_transferstatus.rest);
                    m_transferstatus.socket->PasvTransfer();
                }
                GetSystemTime(&m_LastTransferTime);
            }
        }
        break;
    case COMMAND_SIZE:
        {
            //Unquote args
            if (!UnquoteArgs(args))
            {
                Send( _T("501 Syntax error") );
                break;
            }

            CStdString physicalFile, logicalFile;
            int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_LIST, physicalFile, logicalFile);
            if (error & PERMISSION_DENIED)
                Send(_T("550 Permission denied"));
            else if (error & PERMISSION_INVALIDNAME)
                Send(_T("550 Filename invalid."));
            else if (error)
                Send(_T("550 File not found"));
            else
            {
                CStdString str;
                _int64 length;
                if (GetLength64(physicalFile, length))
                    str.Format(_T("213 %I64d"), length);
                else
                    str = _T("550 File not found");
                Send(str);
            }
        }
        break;
    case COMMAND_DELE:
        {
            //Unquote args
            if (!UnquoteArgs(args))
            {
                Send(_T("501 Syntax error"));
                break;
            }

            CStdString physicalFile, logicalFile;
            int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_DELETE, physicalFile, logicalFile);
            if (error & PERMISSION_DENIED)
                Send(_T("550 Permission denied"));
            else if (error & PERMISSION_INVALIDNAME)
                Send(_T("550 Filename invalid."));
            else if (error)
                Send(_T("550 File not found"));
            else
            {
                bool success = DeleteFile(physicalFile) == TRUE;
                if (!success && GetLastError() == ERROR_ACCESS_DENIED)
                {
                    DWORD attr = GetFileAttributes(physicalFile);
                    if (attr != INVALID_FILE_ATTRIBUTES && attr & FILE_ATTRIBUTE_READONLY)
                    {
                        attr &= ~FILE_ATTRIBUTE_READONLY;
                        SetFileAttributes(physicalFile, attr);

                        success = DeleteFile(physicalFile) == TRUE;
                    }
                }
                if (!success)
                    Send(_T("500 Failed to delete the file."));
                else
                    Send(_T("250 File deleted successfully"));
            }
        }
        break;
    case COMMAND_RMD:
    case COMMAND_XRMD:
        {
            //Unquote args
            if (!UnquoteArgs(args))
            {
                Send( _T("501 Syntax error") );
                break;
            }

            CStdString physicalFile, logicalFile;
            int error = m_pOwner->m_pPermissions->CheckDirectoryPermissions(m_status.user, args, m_CurrentServerDir, DOP_DELETE, physicalFile, logicalFile);
            if (error & PERMISSION_DENIED)
                Send(_T("550 Permission denied"));
            else if (error & PERMISSION_INVALIDNAME)
                Send(_T("550 Filename invalid."));
            else if (error)
                Send(_T("550 Directory not found"));
            else
            {
                if (!RemoveDirectory(physicalFile))
                {
                    if (GetLastError() == ERROR_DIR_NOT_EMPTY)
                        Send(_T("550 Directory not empty."));
                    else
                        Send(_T("450 Internal error deleting the directory."));
                }
                else
                    Send(_T("250 Directory deleted successfully"));
            }
        }
        break;
    case COMMAND_MKD:
    case COMMAND_XMKD:
        {
            //Unquote args
            if (!UnquoteArgs(args))
            {
                Send( _T("501 Syntax error") );
                break;
            }

            CStdString physicalFile, logicalFile;
            int error = m_pOwner->m_pPermissions->CheckDirectoryPermissions(m_status.user, args, m_CurrentServerDir, DOP_CREATE, physicalFile, logicalFile);
            if (error & PERMISSION_DENIED)
                Send(_T("550 Can't create directory. Permission denied"));
            else if (error & PERMISSION_INVALIDNAME)
                Send(_T("550 Filename invalid."));
            else if (error & PERMISSION_DOESALREADYEXIST && (error & PERMISSION_FILENOTDIR)!=PERMISSION_FILENOTDIR)
                Send(_T("550 Directory already exists"));
            else if (error & PERMISSION_FILENOTDIR)
                Send(_T("550 File with same name already exists"));
            else if (error)
                Send(_T("550 Directoryname not valid"));
            else
            {
                CStdString str;
                BOOL res = FALSE;
                BOOL bReplySent = FALSE;
                physicalFile += _T("\\");
                while (physicalFile != _T(""))
                {
                    CStdString piece = physicalFile.Left(physicalFile.Find('\\')+1);
                    if (piece.Right(2) == _T(".\\"))
                    {
                        Send(_T("550 Directoryname not valid"));
                        bReplySent = TRUE;
                        break;
                    }
                    str += piece;
                    physicalFile = physicalFile.Mid(physicalFile.Find('\\') + 1);
                    res = CreateDirectory(str, 0);
                }
                if (!bReplySent)
                    if (!res)//CreateDirectory(result+"\\",0))
                    {
                        int error = GetLastError();
                        if (error == ERROR_ALREADY_EXISTS)
                            Send(_T("550 Directory already exists"));
                        else
                            Send(_T("450 Internal error creating the directory."));
                    }
                    else
                        Send(_T("257 \"") + logicalFile + _T("\" created successfully"));
            }
        }
        break;
    case COMMAND_RNFR:
        {
            //Unquote args
            if (!UnquoteArgs(args))
            {
                Send( _T("501 Syntax error") );
                break;
            }

            RenName = _T("");

            CStdString physicalFile, logicalFile;
            int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_DELETE, physicalFile, logicalFile);
            if (!error)
            {
                RenName = physicalFile;
                bRenFile = TRUE;
                Send(_T("350 File exists, ready for destination name."));
                break;
            }
            else if (error & PERMISSION_DENIED)
                Send(_T("550 Permission denied"));
            else if (error & PERMISSION_INVALIDNAME)
                Send(_T("550 Filename invalid."));
            else
            {
                int error2 = m_pOwner->m_pPermissions->CheckDirectoryPermissions(m_status.user, args, m_CurrentServerDir, DOP_DELETE, physicalFile, logicalFile);
                if (!error2)
                {
                    RenName = physicalFile;
                    bRenFile = FALSE;
                    Send(_T("350 Directory exists, ready for destination name."));
                }
                else if (error2 & PERMISSION_DENIED)
                    Send(_T("550 Permission denied"));
                else if (error2 & PERMISSION_INVALIDNAME)
                    Send(_T("550 Filename invalid."));
                else
                    Send(_T("550 file/directory not found"));
                break;
            }
        }
        break;
    case COMMAND_RNTO:
        {
            if (RenName == _T(""))
            {
                Send(_T("503 Bad sequence of commands!"));
                break;
            }

            //Unquote args
            if (!UnquoteArgs(args))
            {
                Send( _T("501 Syntax error") );
                break;
            }

            if (bRenFile)
            {
                CStdString physicalFile, logicalFile;
                int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_CREATENEW, physicalFile, logicalFile);
                if (error)
                    RenName = _T("");
                if (error & PERMISSION_DENIED)
                    Send(_T("550 Permission denied"));
                else if (error & PERMISSION_INVALIDNAME)
                    Send(_T("553 Filename invalid."));
                else if (error & PERMISSION_DOESALREADYEXIST && (error & PERMISSION_DIRNOTFILE)!=PERMISSION_DIRNOTFILE)
                    Send(_T("553 file exists"));
                else if (error)
                    Send(_T("553 Filename invalid"));
                else
                {
                    if (!MoveFile(RenName, physicalFile))
                        Send(_T("450 Internal error renaming the file"));
                    else
                        Send(_T("250 file renamed successfully"));
                }
            }
            else
            {
                CStdString physicalFile, logicalFile;
                int error = m_pOwner->m_pPermissions->CheckDirectoryPermissions(m_status.user, args, m_CurrentServerDir, DOP_CREATE, physicalFile, logicalFile);
                if (error)
                    RenName = _T("");
                if (error & PERMISSION_DENIED)
                    Send(_T("550 Permission denied"));
                else if (error & PERMISSION_INVALIDNAME)
                    Send(_T("550 Filename invalid."));
                else if (error & PERMISSION_DOESALREADYEXIST && (error & PERMISSION_FILENOTDIR)!=PERMISSION_FILENOTDIR)
                    Send(_T("550 file exists"));
                else if (error)
                    Send(_T("550 Filename invalid"));
                else
                {
                    if (!MoveFile(RenName, physicalFile))
                        Send(_T("450 Internal error renaming the file"));
                    else
                        Send(_T("250 file renamed successfully"));
                }
            }
        }
        break;
    case COMMAND_ABOR:
        {
            if (m_transferstatus.socket)
            {
                if (m_transferstatus.socket->Started())
                    Send(_T("426 Connection closed; transfer aborted."));
            }
            Send(_T("226 ABOR command successful"));
            ResetTransferstatus();
        break;
        }
    case COMMAND_SYST:
        Send(_T("215 UNIX emulated by FileZilla"));
        break;
    case COMMAND_NOOP:
    case COMMAND_NOP:
        Send(_T("200 OK"));
        break;
    case COMMAND_APPE:
        {
            if (m_transferstatus.pasv == -1)
            {
                Send(_T("503 Bad sequence of commands."));
                break;
            }
            if (!m_transferstatus.pasv && (m_transferstatus.ip == _T("") || m_transferstatus.port == -1))
            {
                Send(_T("503 Bad sequence of commands."));
                break;
            }
            if (m_pSslLayer && m_pOwner->m_pOptions->GetOptionVal(OPTION_FORCEPROTP) && !m_bProtP)
            {
                Send(_T("521 PROT P required"));
                break;
            }
            //Much more checks

            //Unquote args
            if (!UnquoteArgs(args))
            {
                Send( _T("501 Syntax error") );
                break;
            }

            CStdString physicalFile, logicalFile;
            int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_APPEND, physicalFile, logicalFile);
            if (error & PERMISSION_DENIED)
            {
                Send(_T("550 Permission denied"));
                ResetTransferstatus();
            }
            else if (error & PERMISSION_INVALIDNAME)
            {
                Send(_T("550 Filename invalid."));
                ResetTransferstatus();
            }
            else if (error)
            {
                Send(_T("550 Filename invalid"));
                ResetTransferstatus();
            }
            else
            {
                _int64 size = 0;
                if (!GetLength64(physicalFile, size))
                    size = 0;

                m_transferstatus.rest = size;

                if (!m_transferstatus.pasv)
                {
                    CTransferSocket *transfersocket = new CTransferSocket(this);
                    transfersocket->Init(physicalFile, TRANSFERMODE_RECEIVE, m_transferstatus.rest);
                    m_transferstatus.socket = transfersocket;
                    if (m_transferMode == mode_zlib)
                    {
                        if (!transfersocket->InitZLib(m_zlibLevel))
                        {
                            Send(_T("550 could not initialize zlib, please use MODE S instead"));
                            ResetTransferstatus();
                            break;
                        }
                    }

                    if (!CreateTransferSocket(transfersocket))
                        break;

                    SendTransferinfoNotification(TRANSFERMODE_RECEIVE, physicalFile, logicalFile, m_transferstatus.rest);

                    CStdString str;
                    str.Format(_T("150 Opening data channel for file transfer, restarting at offset %I64d"), size);
                    Send(str);
                }
                else
                {
                    if (!m_transferstatus.socket)
                    {
                        Send(_T("503 Bad sequence of commands."));
                        break;
                    }
                    m_transferstatus.socket->Init(physicalFile, TRANSFERMODE_RECEIVE, m_transferstatus.rest);
                    if (m_transferMode == mode_zlib)
                    {
                        if (!m_transferstatus.socket->InitZLib(m_zlibLevel))
                        {
                            Send(_T("550 could not initialize zlib, please use MODE S instead"));
                            ResetTransferstatus();
                            break;
                        }
                    }
                    SendTransferinfoNotification(TRANSFERMODE_RECEIVE, physicalFile, logicalFile, m_transferstatus.rest);
                    m_transferstatus.socket->PasvTransfer();
                }
                GetSystemTime(&m_LastTransferTime);
            }
        }
        break;
    case COMMAND_NLST:
        if (m_transferstatus.pasv == -1)
        {
            Send(_T("503 Bad sequence of commands."));
            break;
        }
        if (!m_transferstatus.pasv && (m_transferstatus.ip == _T("") || m_transferstatus.port == -1))
        {
            Send(_T("503 Bad sequence of commands."));
            break;
        }
        if (m_pSslLayer && m_pOwner->m_pOptions->GetOptionVal(OPTION_FORCEPROTP) && !m_bProtP)
        {
            Send(_T("521 PROT P required"));
            break;
        }
        //Much more checks
        else
        {
            //Check args, currently only supported argument is the directory which will be listed.
            if (args != _T(""))
            {
                BOOL bBreak = FALSE;
                while (args[0] == '-') //No parameters supported
                {
                    if (args.GetLength() < 2)
                    { //Dash without param
                        Send(_T("501 Syntax error"));
                        bBreak = TRUE;
                        break;
                    }

                    int pos = args.Find(' ');
                    CStdString params;
                    if (pos != -1)
                    {
                        params = args.Left(1);
                        params = params.Left(pos-1);
                        args = args.Mid(pos+1);
                        args.TrimLeft(_T(" "));
                    }
                    else
                        args = _T("");
                    while (params != _T(""))
                    {
                        //Some parameters are not support
                        if (params[0] == 'R')
                        {
                            Send(_T("504 Command not implemented for that parameter"));
                            bBreak = TRUE;
                            break;
                        }
                        //Ignore other parameters
                        params = params.Mid(1);
                    }

                    if (args == _T(""))
                        break;
                }
                if (bBreak)
                    break;
                if (args != _T(""))
                {
                    //Unquote args
                    if (!UnquoteArgs(args))
                    {
                        Send( _T("501 Syntax error") );
                        break;
                    }
                }
            }

            t_dirlisting *pResult;
            CStdString physicalDir, logicalDir;
            int error = m_pOwner->m_pPermissions->GetDirectoryListing(m_status.user, m_CurrentServerDir, args, pResult, physicalDir, logicalDir, CPermissions::AddShortListingEntry, m_useUTF8);
            if (error & PERMISSION_DENIED)
            {
                Send(_T("550 Permission denied"));
                ResetTransferstatus();
            }
            else if (error & PERMISSION_INVALIDNAME)
            {
                Send(_T("550 Filename invalid."));
                ResetTransferstatus();
            }
            else if (error)
            {
                Send(_T("550 Directory not found"));
                ResetTransferstatus();
            }
            else
            {
                if (!m_transferstatus.pasv)
                {
                    CTransferSocket *transfersocket = new CTransferSocket(this);
                    m_transferstatus.socket = transfersocket;
                    transfersocket->Init(pResult, TRANSFERMODE_NLST);
                    if (m_transferMode == mode_zlib)
                    {
                        if (!transfersocket->InitZLib(m_zlibLevel))
                        {
                            Send(_T("550 could not initialize zlib, please use MODE S instead"));
                            ResetTransferstatus();
                            break;
                        }
                    }

                    if (!CreateTransferSocket(transfersocket))
                        break;

                    SendTransferinfoNotification(TRANSFERMODE_LIST, physicalDir, logicalDir); // Use TRANSFERMODE_LIST instead of TRANSFERMODE_NLST.
                    Send(_T("150 Opening data channel for directory list."));
                }
                else
                {
                    if (!m_transferstatus.socket)
                    {
                        CPermissions::DestroyDirlisting(pResult);
                        Send(_T("503 Bad sequence of commands."));
                        break;
                    }
                    m_transferstatus.socket->Init(pResult, TRANSFERMODE_NLST );
                    if (m_transferMode == mode_zlib)
                    {
                        if (!m_transferstatus.socket->InitZLib(m_zlibLevel))
                        {
                            Send(_T("550 could not initialize zlib, please use MODE S instead"));
                            ResetTransferstatus();
                            break;
                        }
                    }
                    SendTransferinfoNotification(TRANSFERMODE_LIST, physicalDir, logicalDir); // Use TRANSFERMODE_LIST instead of TRANSFERMODE_NLST.
                    m_transferstatus.socket->PasvTransfer();
                }
            }
        }
        break;
    case COMMAND_MDTM:
        {
            //Unquote args
            if (!UnquoteArgs(args))
            {
                Send( _T("501 Syntax error") );
                break;
            }

            CStdString physicalFile, logicalFile;
            int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_LIST, physicalFile, logicalFile);
            if (error & PERMISSION_DENIED)
                Send(_T("550 Permission denied"));
            else if (error & PERMISSION_INVALIDNAME)
                Send(_T("550 Filename invalid."));
            else if (error & 2)
                Send(_T("550 File not found"));
            else
            {
                CFileStatus64 status;
                GetStatus64(physicalFile, status);
                CStdString str;
                SYSTEMTIME time;
                FileTimeToSystemTime(&status.m_mtime, &time);
                str.Format(_T("213 %04d%02d%02d%02d%02d%02d"),
                            time.wYear,
                            time.wMonth,
                            time.wDay,
                            time.wHour,
                            time.wMinute,
                            time.wSecond);
                Send(str);
            }
        }
        break;
    case COMMAND_EPSV:
        {
            if (m_transferstatus.socket)
            {
                SendTransferinfoNotification();
                delete m_transferstatus.socket;
            }
            m_transferstatus.family = (GetFamily() == AF_INET) ? 1 : 2;
            m_transferstatus.socket = new CTransferSocket(this);

            unsigned int port = 0;
            unsigned int retries = 3;
            while (retries > 0) {
                if (m_pOwner->m_pOptions->GetOptionVal(OPTION_USECUSTOMPASVPORT)) {
                    static UINT customPort = 0;
                    unsigned int minPort = (unsigned int)m_pOwner->m_pOptions->GetOptionVal(OPTION_CUSTOMPASVMINPORT);
                    unsigned int maxPort = (unsigned int)m_pOwner->m_pOptions->GetOptionVal(OPTION_CUSTOMPASVMAXPORT);
                    if (minPort > maxPort) {
                        unsigned int temp = minPort;
                        minPort = maxPort;
                        maxPort = temp;
                    }
                    if (customPort < minPort || customPort > maxPort) {
                        customPort = minPort;
                    }
                    port = customPort;
                    ++customPort;
                }
                if (m_transferstatus.socket->Create(port, SOCK_STREAM, FD_ACCEPT, 0, GetFamily()))
                {
                    break;
                }
                --retries;
            }
            if (retries <= 0) {
                delete m_transferstatus.socket;
                m_transferstatus.socket=0;
                Send(_T("421 Can't create socket"));
                break;
            }

            if (m_pGssLayer && m_pGssLayer->AuthSuccessful())
                m_transferstatus.socket->UseGSS(m_pGssLayer);

            if (m_pSslLayer && m_bProtP)
                m_transferstatus.socket->UseSSL(m_pSslLayer->GetContext());

            if (!m_transferstatus.socket->Listen())
            {
                delete m_transferstatus.socket;
                m_transferstatus.socket=0;
                Send(_T("421 Can't create socket"));
                m_transferstatus.pasv = -1;
                break;
            }

            //Now retrieve the port
            CStdString dummy;
            if (m_transferstatus.socket->GetSockName(dummy, port))
            {
                //Put the answer together
                CStdString str;
                str.Format(_T("229 Entering Extended Passive Mode (|||%d|)"), port);
                Send(str);
                m_transferstatus.pasv=1;
            }
            else
            {
                delete m_transferstatus.socket;
                m_transferstatus.socket=0;
                Send(_T("421 Can't create socket"));
                m_transferstatus.pasv = -1;
            }
            break;
        }
    case COMMAND_EPRT:
        {
            if (m_transferstatus.socket)
            {
                SendTransferinfoNotification();
                delete m_transferstatus.socket;
                m_transferstatus.socket=0;
            }

            if (args[0] != '|')
            {
                Send(_T("501 Syntax error"));
                m_transferstatus.pasv = -1;
                break;
            }
            args = args.Mid(1);

            int pos = args.Find('|');
            if (pos < 1 || (pos>=(args.GetLength()-1)))
            {
                Send(_T("501 Syntax error"));
                m_transferstatus.pasv = -1;
                break;
            }
            int protocol = _ttoi(args.Left(pos));

            bool ipv6Allowed = m_pOwner->m_pOptions->GetOptionVal(OPTION_DISABLE_IPV6) == 0;
            if (protocol != 1 && (protocol != 2 || !ipv6Allowed))
            {
                if (ipv6Allowed)
                    Send(_T("522 Extended Port Failure - unknown network protocol. Supported protocols: (1,2)"));
                else
                    Send(_T("522 Extended Port Failure - unknown network protocol. Supported protocols: (1)"));
                m_transferstatus.pasv = -1;
                break;
            }
            args = args.Mid(pos + 1);

            pos = args.Find('|');
            if (pos < 1 || (pos>=(args.GetLength()-1)))
            {
                Send(_T("501 Syntax error"));
                m_transferstatus.pasv = -1;
                break;
            }
            CStdString ip = args.Left(pos);
            if (protocol == 1)
            {
#ifdef _UNICODE
                if (inet_addr(ConvToLocal(ip)) == INADDR_NONE)
#else
                if (inet_addr(ip) == INADDR_NONE)
#endif
                {
                    Send(_T("501 Syntax error, not a valid IPv4 address"));
                    m_transferstatus.pasv = -1;
                    break;
                }
            }
            else
            {
                ip = GetIPV6LongForm(ip);
                if (ip.IsEmpty())
                {
                    Send(_T("501 Syntax error, not a valid IPv6 address"));
                    m_transferstatus.pasv = -1;
                    break;
                }
            }
            args = args.Mid(pos + 1);

            pos = args.Find('|');
            if (pos < 1)
            {
                Send(_T("501 Syntax error"));
                m_transferstatus.pasv = -1;
                break;
            }
            int port = _ttoi(args.Left(pos));
            if (port < 1 || port > 65535)
            {
                Send(_T("501 Syntax error"));
                m_transferstatus.pasv = -1;
                break;
            }

            m_transferstatus.port = port;
            m_transferstatus.ip = ip;
            m_transferstatus.family = (protocol == 1) ? AF_INET : AF_INET6;

            m_transferstatus.pasv = 0;
            Send(_T("200 Port command successful"));
            break;
        }
    case COMMAND_AUTH:
        {
            if (m_nRecvBufferPos || m_RecvLineBuffer.size() ) {
                Send(_T("503 Bad sequence of commands. Received additional data after the AUTH command before this reply could be sent."));
                ForceClose(-1);
                break;
            }

            if (m_pGssLayer)
            {
                Send(_T("534 Authentication type already set to GSSAPI"));
                break;
            }
            else if (m_pSslLayer)
            {
                Send(_T("534 Authentication type already set to SSL"));
                break;
            }
            args.MakeUpper();

            if (args == _T("GSSAPI"))
            {
                if (!m_pOwner->m_pOptions->GetOptionVal(OPTION_USEGSS))
                {
                    Send(_T("502 GSSAPI authentication not implemented"));
                    break;
                }

                m_pGssLayer = new CAsyncGssSocketLayer;
                BOOL res = AddLayer(m_pGssLayer);
                if (res)
                {
                    res = m_pGssLayer->InitGSS(FALSE, (BOOL)m_pOwner->m_pOptions->GetOptionVal(OPTION_GSSPROMPTPASSWORD));
                    if (!res)
                        SendStatus(_T("Unable to init GSS"), 1);
                }
                if (!res)
                {
                    RemoveAllLayers();
                    delete m_pGssLayer;
                    m_pGssLayer = NULL;
                    Send(_T("431 Could not initialize GSSAPI libraries"));
                    break;
                }

                Send(_T("334 Using authentication type GSSAPI; ADAT must follow"));
            }
            else if (args == _T("SSL") || args == _T("TLS"))
            {
                if (m_pSslLayer)
                {
                    Send(_T("503 Already using SSL/TLS"));
                    break;
                }

                if (!m_pOwner->m_pOptions->GetOptionVal(OPTION_ENABLESSL) || !m_pOwner->m_pOptions->GetOptionVal(OPTION_ALLOWEXPLICITSSL))
                {
                    Send(_T("502 SSL/TLS authentication not allowed"));
                    break;
                }

                m_pSslLayer = new CAsyncSslSocketLayer;
                BOOL res = AddLayer(m_pSslLayer);

                if (res)
                {
                    CString error;
#ifdef _UNICODE
                    int res = m_pSslLayer->SetCertKeyFile(ConvToLocal(m_pOwner->m_pOptions->GetOption(OPTION_SSLCERTFILE)), ConvToLocal(m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYFILE)), ConvToLocal(m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYPASS)), &error);
#else
                    int res = m_pSslLayer->SetCertKeyFile(m_pOwner->m_pOptions->GetOption(OPTION_SSLCERTFILE), m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYFILE), m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYPASS), &error);
#endif
                    if (res == SSL_FAILURE_LOADDLLS)
                        SendStatus(_T("Failed to load SSL libraries"), 1);
                    else if (res == SSL_FAILURE_INITSSL)
                        SendStatus(_T("Failed to initialize SSL libraries"), 1);
                    else if (res == SSL_FAILURE_VERIFYCERT)
                    {
                        if (error != _T(""))
                            SendStatus(error, 1);
                        else
                            SendStatus(_T("Failed to set certificate and private key"), 1);
                    }
                    if (res)
                    {
                        RemoveAllLayers();
                        delete m_pSslLayer;
                        m_pSslLayer = NULL;
                        Send(_T("431 Could not initialize SSL connection"));
                        break;
                    }
                }

                if (res)
                {
                    int code = m_pSslLayer->InitSSLConnection(false);
                    if (code == SSL_FAILURE_LOADDLLS)
                        SendStatus(_T("Failed to load SSL libraries"), 1);
                    else if (code == SSL_FAILURE_INITSSL)
                        SendStatus(_T("Failed to initialize SSL library"), 1);

                    res = (code == 0);
                }

                if (res)
                {
                    if (args == _T("SSL"))
                    {
                        SendStatus(_T("234 Using authentication type SSL"), 3);
                        static const char* reply = "234 Using authentication type SSL\r\n";
                        const int len = strlen(reply);
                        res = (m_pSslLayer->SendRaw(reply, len) == len);
                    }
                    else // TLS
                    {
                        SendStatus(_T("234 Using authentication type TLS"), 3);
                        static const char* reply = "234 Using authentication type TLS\r\n";
                        const int len = strlen(reply);
                        res = (m_pSslLayer->SendRaw(reply, len) == len);
                    }
                }

                if (!res)
                {
                    RemoveAllLayers();
                    delete m_pSslLayer;
                    m_pSslLayer = NULL;
                    Send(_T("431 Could not initialize SSL connection"));
                    break;
                }
            }
            else
            {
                Send(_T("504 Auth type not supported"));
                break;
            }

            break;
        }
    case COMMAND_ADAT:
        if (m_pGssLayer)
        {
            char sendme[4096];
#ifdef _UNICODE
            m_pGssLayer->ProcessCommand("ADAT", ConvToLocal(args), sendme);
            Send(ConvFromLocal(sendme));
#else
            m_pGssLayer->ProcessCommand("ADAT", args, sendme);
            Send(sendme);
#endif
            
        }
        else
            Send(_T("502 Command not implemented for this authentication type"));
        break;
    case COMMAND_PBSZ:
        if (m_pGssLayer)
        {
            char sendme[4096];
#ifdef _UNICODE
            m_pGssLayer->ProcessCommand("PBSZ", ConvToLocal(args), sendme);
            Send(ConvFromLocal(sendme));
#else
            m_pGssLayer->ProcessCommand("PBSZ", args, sendme);
            Send(sendme);
#endif
        }
        else if (m_pSslLayer)
            Send(_T("200 PBSZ=0"));
        else
            Send(_T("502 Command not implemented for this authentication type"));
        break;
    case COMMAND_PROT:
        if (m_pGssLayer)
        {
            char sendme[4096];

#ifdef _UNICODE
            m_pGssLayer->ProcessCommand("PROT", ConvToLocal(args), sendme);
            Send(ConvFromLocal(sendme));
#else
            m_pGssLayer->ProcessCommand("PROT", args, sendme);
            Send(sendme);
#endif
        }
        else if (m_pSslLayer)
        {
            args.MakeUpper();
            if (args == _T("C"))
            {
                if (m_pOwner->m_pOptions->GetOptionVal(OPTION_FORCEPROTP))
                    Send(_T("534 This server requires an encrypted data connection with PROT P"));
                else
                {
                    if (m_transferstatus.socket)
                        m_transferstatus.socket->UseSSL(0);

                    Send(_T("200 Protection level set to C"));
                    m_bProtP = false;
                }
            }
            else if (args == _T("P"))
            {
                if (m_transferstatus.socket)
                    m_transferstatus.socket->UseSSL(m_pSslLayer->GetContext());

                Send(_T("200 Protection level set to P"));
                m_bProtP = true;
            }
            else if (args == _T("S") || args == _T("E"))
                Send(_T("504 Protection level ") + args + _T(" not supported"));
            else
                Send(_T("504 Protection level ") + args + _T(" not recognized"));
        }
        else
            Send(_T("502 Command not implemented for this authentication type"));
        break;
    case COMMAND_FEAT:
        {
        if (!Send(_T("211-Features:")))
            break;
        if (!Send(_T(" MDTM")))
            break;
        if (!Send(_T(" REST STREAM")))
            break;
        if (!Send(_T(" SIZE")))
            break;
        if (m_pOwner->m_pOptions->GetOptionVal(OPTION_MODEZ_USE))
        {
            if (!Send(_T(" MODE Z")))
                break;
        }
        if (!Send(_T(" MLST type*;size*;modify*;")))
            break;
        if (!Send(_T(" MLSD")))
            break;
        if (m_pOwner->m_pOptions->GetOptionVal(OPTION_ENABLESSL) && (m_pOwner->m_pOptions->GetOptionVal(OPTION_ALLOWEXPLICITSSL) || m_pSslLayer))
        {
            if (!Send(_T(" AUTH SSL")))
                break;
            if (!Send(_T(" AUTH TLS")))
                break;
            if (!Send(_T(" PROT")))
                break;
            if (!Send(_T(" PBSZ")))
                break;
        }
        if (!Send(_T(" UTF8")))
            break;
        if (!Send(_T(" CLNT")))
            break;
        if (!Send(_T(" MFMT")))
            break;
        if (m_pOwner->m_pOptions->GetOptionVal(OPTION_ENABLE_HASH))
        {
            CStdString hash = _T(" HASH ");
            hash += _T("SHA-1");
            if (m_hash_algorithm == CHashThread::SHA1)
                hash += _T("*");
            hash += _T(";SHA-512");
            if (m_hash_algorithm == CHashThread::SHA512)
                hash += _T("*");
            hash += _T(";MD5");
            if (m_hash_algorithm == CHashThread::MD5)
                hash += _T("*");
            if (!Send(hash))
                break;
        }
        if (!Send(_T("211 End")))
            break;
        break;
        }
    case COMMAND_MODE:
        if (args == _T("S") || args == _T("s"))
        {
            m_transferMode = mode_stream;
            Send(_T("200 MODE set to S."));
        }
        else if (args == _T("Z") || args == _T("z"))
        {
            if (m_pOwner->m_pOptions->GetOptionVal(OPTION_MODEZ_USE) || m_transferMode == mode_zlib)
            {
                if (m_transferMode == mode_zlib || CheckIpForZlib())
                {
                    m_transferMode = mode_zlib;
                    Send(_T("200 MODE set to Z."));
                }
                else
                    Send(_T("504 MODE Z not allowed from your IP"));
            }
            else
                Send(_T("504 MODE Z not enabled"));
        }
        else if (args == _T("C") || args == _T("c") || args == _T("B") || args == _T("b"))
            Send(_T("502 Unimplemented MODE type"));
        else
            Send(_T("504 Unknown MODE type"));
        break;
    case COMMAND_OPTS:
        args.MakeUpper();
        if (args.Left(13) == _T("MODE Z LEVEL "))
        {
            int level = _ttoi(args.Mid(13));
            if (m_zlibLevel == level || (level >= m_pOwner->m_pOptions->GetOptionVal(OPTION_MODEZ_LEVELMIN) && level <= m_pOwner->m_pOptions->GetOptionVal(OPTION_MODEZ_LEVELMAX)))
            {
                m_zlibLevel = level;
                CString str;
                str.Format(_T("200 MODE Z LEVEL set to %d"), level);
                Send(str);
            }
            else
                Send(_T("501 can't change MODE Z LEVEL do desired value"));
        }
        else if (args == _T("UTF8 ON"))
        {
            m_useUTF8 = true;
            Send(_T("200 UTF8 mode enabled"));
        }
        else if (args == _T("UTF8 OFF"))
        {
            m_useUTF8 = false;
            Send(_T("200 UTF8 mode disabled"));
        }
        else if (args.Left(4) == _T("MLST"))
            ParseMlstOpts(args.Mid(4));
        else if (args.Left(4) == _T("HASH"))
            ParseHashOpts(args.Mid(4));
        else
            Send(_T("501 Option not understood"));
        break;
    case COMMAND_HELP:
        if (args == _T(""))
        {
            Send(_T("214-The following commands are recognized:"));
            CString str;
            for (int i = 0; i < (sizeof(commands)/sizeof(t_command)); i++)
            {
                CString cmd = commands[i].command;
                while (cmd.GetLength() < 4)
                    cmd += _T(" ");
                str += _T("   ") + cmd;
                if (!((i + 1) % 8))
                {
                    Send(str);
                    str = _T("");
                }
            }
            if (str != _T(""))
                Send(str);
            Send(_T("214 Have a nice day."));
        }
        else
        {
            args.MakeUpper();

            int i;
            for (i = 0; i < (sizeof(commands)/sizeof(t_command)); i++)
            {
                if (args == commands[i].command)
                {
                    CStdString str;
                    str.Format(_T("214 Command %s is supported by FileZilla Server"), args);
                    Send(str);
                    break;
                }
            }
            if (i == (sizeof(commands)/sizeof(t_command)))
            {
                CStdString str;
                str.Format(_T("502 Command %s is not recognized or supported by FileZilla Server"), args);
                Send(str);
            }
        }
        break;
    case COMMAND_ALLO:
        Send(_T("202 No storage allocation neccessary."));
        break;
    case COMMAND_MLST:
        {
            CStdString fact;
            CStdString logicalName;
            int res = m_pOwner->m_pPermissions->GetFact(m_status.user, m_CurrentServerDir, args, fact, logicalName, m_facts);
            if (res & PERMISSION_DENIED)
            {
                Send(_T("550 Permission denied."));
                break;
            }
            else if (res & PERMISSION_INVALIDNAME)
            {
                Send(_T("550 Filename invalid."));
                break;
            }
            else if (res)
            {
                Send(_T("550 File or directory not found."));
                break;
            }
            CStdString str;
            str.Format(_T("250-Listing %s"), logicalName);
            if (!Send(str))
                break;
            fact = _T(" ") + fact;
            if (!Send(fact))
                break;

            Send(_T("250 End"));
        }
        break;
    case COMMAND_MLSD:
        if (m_transferstatus.pasv == -1)
        {
            Send(_T("503 Bad sequence of commands."));
            break;
        }
        if (!m_transferstatus.pasv && (m_transferstatus.ip == _T("") || m_transferstatus.port == -1))
            Send(_T("503 Bad sequence of commands."));
        //Much more checks
        else
        {
            if (m_pSslLayer && m_pOwner->m_pOptions->GetOptionVal(OPTION_FORCEPROTP) && !m_bProtP)
            {
                Send(_T("521 PROT P required"));
                break;
            }
            if (args != _T(""))
            {
                //Unquote args
                if (!UnquoteArgs(args))
                {
                    Send(_T("501 Syntax error"));
                    break;
                }
            }

            t_dirlisting *pResult;
            CStdString physicalDir, logicalDir;
            int error = m_pOwner->m_pPermissions->GetDirectoryListing(m_status.user, m_CurrentServerDir, args, pResult, physicalDir, logicalDir, CPermissions::AddFactsListingEntry, m_useUTF8, m_facts);
            if (error & PERMISSION_DENIED)
            {
                Send(_T("550 Permission denied"));
                ResetTransferstatus();
            }
            else if (error & PERMISSION_INVALIDNAME)
            {
                Send(_T("550 Filename invalid."));
                ResetTransferstatus();
            }
            else if (error)
            {
                Send(_T("550 Directory not found"));
                ResetTransferstatus();
            }
            else
            {
                if (!m_transferstatus.pasv)
                {
                    if (m_transferstatus.socket)
                    {
                        SendTransferinfoNotification();
                        delete m_transferstatus.socket;
                    }
                    CTransferSocket *transfersocket = new CTransferSocket(this);
                    m_transferstatus.socket = transfersocket;
                    transfersocket->Init(pResult, TRANSFERMODE_LIST);
                    if (m_transferMode == mode_zlib)
                    {
                        if (!transfersocket->InitZLib(m_zlibLevel))
                        {
                            Send(_T("550 could not initialize zlib, please use MODE S instead"));
                            ResetTransferstatus();
                            break;
                        }
                    }

                    if (!CreateTransferSocket(transfersocket))
                        break;

                    SendTransferinfoNotification(TRANSFERMODE_LIST, physicalDir, logicalDir);
                    Send(_T("150 Opening data channel for directory list."));
                }
                else
                {
                    if (!m_transferstatus.socket)
                    {
                        CPermissions::DestroyDirlisting(pResult);
                        Send(_T("503 Bad sequence of commands."));
                        break;
                    }
                    m_transferstatus.socket->Init(pResult, TRANSFERMODE_LIST );
                    if (m_transferMode == mode_zlib)
                    {
                        if (!m_transferstatus.socket->InitZLib(m_zlibLevel))
                        {
                            Send(_T("550 could not initialize zlib, please use MODE S instead"));
                            ResetTransferstatus();
                            break;
                        }
                    }

                    SendTransferinfoNotification(TRANSFERMODE_LIST, physicalDir, logicalDir);
                    m_transferstatus.socket->PasvTransfer();
                }
            }

        }
        break;
    case COMMAND_SITE:
        {
            CStdString cmd;

            args.MakeUpper();
            
            int pos = args.Find(' ');
            if (pos != -1)
            {
                cmd = args.Left(pos);
                args = args.Mid(pos + 1);
                args.TrimLeft(_T(" "));
            }
            else
            {
                cmd = args;
                args = _T("");
            }

            if (cmd == _T("NAMEFMT"))
            {
                if (args == _T("") || args == _T("1"))
                    Send(_T("200 Now using naming format \"1\""));
                else
                    Send(_T("504 Naming format not implemented"));
                break;
            }
            else
            {
                Send(_T("504 Command not implemented for that parameter"));
                break;
            }
            break;
        }
    case COMMAND_STRU:
        args.MakeUpper();
        if (args == _T("F"))
            Send(_T("200 Using file structure 'File'"));
        else
            Send(_T("504 Command not implemented for that parameter"));
        break;
    case COMMAND_CLNT:
        Send(_T("200 Don't care"));
        break;
    case COMMAND_MFMT:
        {
            int pos = args.find(' ');
            if (pos < 1)
            {
                Send(_T("501 Syntax error"));
                break;
            }

            CStdString timeval = args.Left(pos);
            args = args.Mid(pos + 1);

            if (timeval.GetLength() < 14)
            {
                Send( _T("501 Syntax error") );
                break;
            }

            bool numbersOnly = true;
            for (int i = 0; i < 14; i++)
            {
                if (timeval[i] < '0' || timeval[i] > '9')
                {
                    numbersOnly = false;
                    break;
                }
            }
            if (!numbersOnly)
            {
                Send( _T("501 Syntax error") );
                break;
            }

            int year = (timeval[0] - '0') * 1000 +
                    (timeval[1] - '0') * 100 +
                    (timeval[2] - '0') * 10 +
                    timeval[3] - '0';

            int month = (timeval[4] - '0') * 10 + timeval[5] - '0';
            int day = (timeval[6] - '0') * 10 + timeval[7] - '0';
            int hour = (timeval[8] - '0') * 10 + timeval[9] - '0';
            int minute = (timeval[10] - '0') * 10 + timeval[11] - '0';
            int second = (timeval[12] - '0') * 10 + timeval[13] - '0';

            if (year < 1000 || month < 1 || month > 12 || day < 1 || day > 31 || hour > 23 ||
                minute > 59 || second > 59)
            {
                Send( _T("501 Not a valid date") );
                break;
            }

            SYSTEMTIME st = {0};
            st.wYear = year;
            st.wMonth = month;
            st.wDay = day;
            st.wHour = hour;
            st.wMinute = minute;
            st.wSecond = second;

            FILETIME ft;
            if (!SystemTimeToFileTime(&st, &ft))
            {
                Send( _T("501 Not a valid date") );
                break;
            }

            //Unquote args
            if (!UnquoteArgs(args))
            {
                Send( _T("501 Syntax error") );
                break;
            }

            if (args == _T(""))
            {
                Send( _T("501 Syntax error") );
                break;
            }

            CStdString physicalFile, logicalFile;
            int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_LIST, physicalFile, logicalFile);
            if (error & PERMISSION_DENIED)
                Send(_T("550 Permission denied"));
            else if (error & PERMISSION_INVALIDNAME)
                Send(_T("550 Filename invalid."));
            else if (error & 2)
                Send(_T("550 File not found"));
            else
            {
                HANDLE hFile = CreateFile(physicalFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
                if (hFile == INVALID_HANDLE_VALUE)
                    Send(_T("550 Cannot access file"));
                else
                {
                    if (!SetFileTime(hFile, 0, 0, &ft))
                        Send(_T("550 Failed to set file modification time"));
                    else
                        Send(_T("213 modify=") + timeval.Left(14) + _T("; ") + logicalFile);

                    CloseHandle(hFile);
                }
            }
        }
        break;
    case COMMAND_HASH:
        {
            if (!m_pOwner->m_pOptions->GetOptionVal(OPTION_ENABLE_HASH))
            {
                Send(_T("500 Syntax error, command unrecognized."));
                break;
            }

            //Unquote args
            if (!UnquoteArgs(args))
            {
                Send( _T("501 Syntax error") );
                break;
            }

            CStdString physicalFile, logicalFile;
            int error = m_pOwner->m_pPermissions->CheckFilePermissions(m_status.user, args, m_CurrentServerDir, FOP_READ, physicalFile, logicalFile);
            if (error & PERMISSION_DENIED)
            {
                Send(_T("550 Permission denied"));
                ResetTransferstatus();
            }
            else if (error & PERMISSION_INVALIDNAME)
            {
                Send(_T("550 Filename invalid."));
                ResetTransferstatus();
            }
            else if (error)
            {
                Send(_T("550 File not found"));
                ResetTransferstatus();
            }
            else
            {
                int hash_res = m_pOwner->GetHashThread().Hash(physicalFile, m_hash_algorithm, m_hash_id, m_pOwner);
                if (hash_res == CHashThread::BUSY)
                    Send(_T("450 Another hash operation is already in progress."));
                else if (hash_res != CHashThread::PENDING)
                    Send(_T("550 Failed to hash file"));
            }
        }
        break;
    default:
        Send(_T("502 Command not implemented."));
    }

    if (!m_RecvLineBuffer.empty())
        m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_COMMAND, m_userid);

    return;
}

void CControlSocket::ProcessTransferMsg()
{
    if (!m_transferstatus.socket)
        return;
    int status = m_transferstatus.socket->GetStatus();

    GetSystemTime(&m_LastCmdTime);
    if (m_transferstatus.socket)
        if (m_transferstatus.socket->GetMode()==TRANSFERMODE_SEND || m_transferstatus.socket->GetMode()==TRANSFERMODE_RECEIVE)
            GetSystemTime(&m_LastTransferTime);

    if (status == 2 && m_transferstatus.pasv && m_transferstatus.usedResolvedIP)
        m_pOwner->ExternalIPFailed();
    
    int mode = m_transferstatus.socket->GetMode();
    _int64 zlibBytesIn = 0;
    _int64 zlibBytesOut = 0;
    if (m_transferMode == mode_zlib)
        m_transferstatus.socket->GetZlibStats(zlibBytesIn, zlibBytesOut);
    ResetTransferstatus();

    if (!status)
    {
        if ((mode == TRANSFERMODE_LIST || mode == TRANSFERMODE_NLST || mode == TRANSFERMODE_SEND) && zlibBytesIn && zlibBytesOut)
        {
            if (zlibBytesIn >= zlibBytesOut)
            {
                CString str;
                _int64 percent = 10000 - (zlibBytesOut * 10000 / zlibBytesIn);
                str.Format(_T("226 Transfer OK, compression saved %I64d of %I64d bytes (%I64d.%02I64d%%)"), zlibBytesIn - zlibBytesOut, zlibBytesIn, percent / 100, percent % 100);
                Send(str);
            }
            else
            {
                CString str;
                _int64 percent = (zlibBytesOut * 10000 / zlibBytesIn) - 10000;
                str.Format(_T("226 Transfer OK, unfortunately compression did increase the transfer size by %I64d bytes to %I64d bytes (%I64d.%02I64d%%)"), zlibBytesOut - zlibBytesIn, zlibBytesOut, percent / 100, percent % 100);
                Send(str);
            }
        }
        else if (mode == TRANSFERMODE_RECEIVE && zlibBytesIn && zlibBytesOut)
        {
            if (zlibBytesOut >= zlibBytesIn)
            {
                CString str;
                _int64 percent = 10000 - (zlibBytesIn * 10000 / zlibBytesOut);
                str.Format(_T("226 Transfer OK, compression saved %I64d of %I64d bytes (%I64d.%02I64d%%)"), zlibBytesOut - zlibBytesIn, zlibBytesOut, percent / 100, percent % 100);
                Send(str);
            }
            else
            {
                CString str;
                _int64 percent = (zlibBytesIn * 10000 / zlibBytesOut) - 10000;
                str.Format(_T("226 Transfer OK, unfortunately compression did increase the transfer size by %I64d bytes to %I64d bytes (%I64d.%02I64d%%)"), zlibBytesIn - zlibBytesOut, zlibBytesIn, percent / 100, percent % 100);
                Send(str);
            }
        }
        else
            Send(_T("226 Transfer OK"));
    }
    else if (status==1)
        Send(_T("426 Connection closed; transfer aborted."));
    else if (status==2)
        Send(_T("425 Can't open data connection."));
    else if (status==3)
        Send(_T("550 can't access file."));
    else if (status==4)
    {
        Send(_T("426 Connection timed out, aborting transfer"));
        ForceClose(1);
        return;
    }
    else if (status==5)
        Send(_T("425 Can't open data connection"));
    else if (status==6)
        Send(_T("450 zlib error"));
    if (status>=0 && m_bWaitGoOffline)
        ForceClose(0);
    else if (m_bQuitCommand)
    {
        Send(_T("221 Goodbye"));
        if (CanQuit())
            ForceClose(5);
    }
}

CTransferSocket* CControlSocket::GetTransferSocket()
{
    return m_transferstatus.socket;
}

void CControlSocket::ForceClose(int nReason)
{
    if (m_transferstatus.socket)
    {
        // Don't call SendTransferInfoNotification, since connection
        // does get removed real soon.
        m_transferstatus.socket->Close();
        delete m_transferstatus.socket;
        m_transferstatus.socket = 0;
    }
    if (m_shutdown && nReason == 1)
    {
        Close();
        m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);
        return;
    }

    if (!nReason)
        Send(_T("421 Server is going offline"));
    else if (nReason == 1)
        Send(_T("421 Connection timed out."));
    else if (nReason == 2)
        Send(_T("421 No-transfer-time exceeded. Closing control connection."));
    else if (nReason == 3)
        Send(_T("421 Login time exceeded. Closing control connection."));
    else if (nReason == 4)
        Send(_T("421 Kicked by Administrator"));
    else if (nReason == 5)
    {
        // 221 Goodbye
    }
    SendStatus(_T("disconnected."), 0);
    m_shutdown = true;
    int res = ShutDown();
    if (m_pSslLayer)
    {
        if (!res && GetLastError() == WSAEWOULDBLOCK)
            return;
    }
    Close();
    m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);
}

void CControlSocket::IncUserCount(const CStdString &user)
{
    int curcount=GetUserCount(user)+1;
    EnterCritSection(m_Sync);
    m_UserCount[user]=curcount;
    LeaveCritSection(m_Sync);
}

void CControlSocket::DecUserCount(const CStdString &user)
{
    int curcount=GetUserCount(user)-1;
    if (curcount<0)
        return;
    EnterCritSection(m_Sync);
    m_UserCount[user]=curcount;
    LeaveCritSection(m_Sync);
}

int CControlSocket::GetUserCount(const CStdString &user)
{
    EnterCritSection(m_Sync);
    int count=0;
    std::map<CStdString, int>::iterator iter = m_UserCount.find(user);
    if (iter!=m_UserCount.end())
        count = iter->second;
    LeaveCritSection(m_Sync);
    return count;
}

void CControlSocket::CheckForTimeout()
{
    if (m_antiHammeringWaitTime)
    {
        m_antiHammeringWaitTime -= 1000;
        if (m_antiHammeringWaitTime <= 0)
        {
            m_antiHammeringWaitTime = 0;
            TriggerEvent(FD_FORCEREAD);
        }
    }
    if (m_status.hammerValue > 0)
        m_status.hammerValue--;

    if (m_transferstatus.socket)
    {
        if (m_transferstatus.socket->CheckForTimeout())
            return;
    }
    _int64 timeout;
    if (m_shutdown)
        timeout = 3;
    else
        timeout = m_pOwner->m_pOptions->GetOptionVal(OPTION_TIMEOUT);
    if (!timeout)
        return;
    SYSTEMTIME sCurrentTime;
    GetSystemTime(&sCurrentTime);
    FILETIME fCurrentTime;
    SystemTimeToFileTime(&sCurrentTime, &fCurrentTime);
    FILETIME fLastTime;
    SystemTimeToFileTime(&m_LastCmdTime, &fLastTime);
    _int64 elapsed = ((_int64)(fCurrentTime.dwHighDateTime - fLastTime.dwHighDateTime) << 32) + fCurrentTime.dwLowDateTime - fLastTime.dwLowDateTime;
    if (elapsed > (timeout*10000000))
    {
        ForceClose(1);
        return;
    }
    if (m_status.loggedon)
    { //Transfer timeout
        _int64 nNoTransferTimeout=m_pOwner->m_pOptions->GetOptionVal(OPTION_NOTRANSFERTIMEOUT);
        if (!nNoTransferTimeout)
            return;
        SystemTimeToFileTime(&m_LastTransferTime, &fLastTime);
        elapsed = ((_int64)(fCurrentTime.dwHighDateTime - fLastTime.dwHighDateTime) << 32) + fCurrentTime.dwLowDateTime - fLastTime.dwLowDateTime;
        if (elapsed>(nNoTransferTimeout*10000000))
        {
            ForceClose(2);
            return;
        }
    }
    else
    { //Login timeout
        _int64 nLoginTimeout=m_pOwner->m_pOptions->GetOptionVal(OPTION_LOGINTIMEOUT);
        if (!nLoginTimeout)
            return;
        SystemTimeToFileTime(&m_LoginTime, &fLastTime);
        elapsed = ((_int64)(fCurrentTime.dwHighDateTime - fLastTime.dwHighDateTime) << 32) + fCurrentTime.dwLowDateTime - fLastTime.dwLowDateTime;
        if (elapsed>(nLoginTimeout*10000000))
        {
            ForceClose(3);
            return;
        }
    }
}

void CControlSocket::WaitGoOffline(bool wait /*=true*/)
{
    if (!wait)
    {
        m_bWaitGoOffline = FALSE;
        return;
    }

    if (m_transferstatus.socket)
    {
        if (!m_transferstatus.socket->Started())
            ForceClose(0);
        else
            m_bWaitGoOffline=TRUE;
    }
    else
        ForceClose(0);

}

void CControlSocket::ResetTransferstatus()
{
    if (m_transferstatus.socket)
    {
        SendTransferinfoNotification();
        delete m_transferstatus.socket;
    }
    m_transferstatus.socket = 0;
    m_transferstatus.ip = _T("");
    m_transferstatus.port = -1;
    m_transferstatus.pasv = -1;
    m_transferstatus.rest = 0;
}

BOOL CControlSocket::UnquoteArgs(CStdString &args)
{
    args.TrimLeft( _T(" ") );
    args.TrimRight( _T(" ") );
    int pos1 = args.Find('"');
    int pos2 = args.ReverseFind('"');
    if (pos1 == -1 && pos2 == -1)
        return TRUE;
    if (pos1 || pos2 != (args.GetLength()-1) || pos1 >= (pos2-1))
        return FALSE;
    args = args.Mid(1, args.GetLength() - 2);
    return TRUE;
}

void CControlSocket::OnSend(int nErrorCode)
{
    if (m_nSendBufferLen && m_pSendBuffer)
    {
        int nLimit = GetSpeedLimit(download);
        if (!nLimit)
            return;
        int numsend = nLimit;
        if (nLimit == -1 || nLimit > m_nSendBufferLen)
            numsend = m_nSendBufferLen;


        int numsent = CAsyncSocketEx::Send(m_pSendBuffer, numsend);

        if (numsent==SOCKET_ERROR && GetLastError() == WSAEWOULDBLOCK)
            return;
        if (!numsent || numsent == SOCKET_ERROR)
        {
            Close();
            SendStatus(_T("could not send reply, disconnected."), 0);
            m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);

            delete [] m_pSendBuffer;
            m_pSendBuffer = NULL;
            m_nSendBufferLen = 0;

            return;
        }

        if (nLimit != -1)
            m_SlQuotas[download].nTransferred += numsent;

        if (numsent == m_nSendBufferLen)
        {
            delete [] m_pSendBuffer;
            m_pSendBuffer = NULL;
            m_nSendBufferLen = 0;
        }
        else
        {
            char *tmp = m_pSendBuffer;
            m_pSendBuffer = new char[m_nSendBufferLen-numsent];
            memcpy(m_pSendBuffer, tmp+numsent, m_nSendBufferLen-numsent);
            delete [] tmp;
            m_nSendBufferLen -= numsent;
            TriggerEvent(FD_WRITE);
        }
    }
}

BOOL CControlSocket::DoUserLogin(LPCTSTR password, bool skipPass /*=false*/)
{
    CUser user;
    if (!m_pOwner->m_pPermissions->CheckUserLogin(m_status.user, password, user, skipPass))
    {
        AntiHammerIncrease(2);
        m_pOwner->AntiHammerIncrease(m_RemoteIP);

        if (m_pOwner->m_pAutoBanManager->RegisterAttempt(m_RemoteIP))
        {
            Send(_T("421 Temporarily banned for too many failed login attempts"));
            ForceClose(-1);
            return FALSE;
        }

        Send(_T("530 Login or password incorrect!"));
        return FALSE;
    }

    if (!user.IsEnabled())
    {
        Send(_T("530 Not logged in, user account has been disabled"));
        ForceClose(-1);
        return FALSE;
    }
    if (!user.BypassUserLimit())
    {
        int nMaxUsers = (int)m_pOwner->m_pOptions->GetOptionVal(OPTION_MAXUSERS);
        if (m_pOwner->GetGlobalNumConnections()>nMaxUsers&&nMaxUsers)
        {
            SendStatus(_T("Refusing connection. Reason: Max. connection count reached."), 1);
            Send(_T("421 Too many users are connected, please try again later."));
            ForceClose(-1);
            return FALSE;
        }
    }
    if (user.GetUserLimit() && GetUserCount(m_status.user)>=user.GetUserLimit())
    {
            CStdString str;
            str.Format(_T("Refusing connection. Reason: Max. connection count reached for the user \"%s\"."), m_status.user);
            SendStatus(str,1);
            Send(_T("421 Too many users logged in for this account. Try again later."));
            ForceClose(-1);
            return FALSE;
    }

    CStdString peerIP;
    UINT port = 0;

    BOOL bResult = GetPeerName(peerIP, port);
    if (bResult)
    {
        if (!user.AccessAllowed(peerIP))
        {
            Send(_T("521 This user is not allowed to connect from this IP"));
            ForceClose(-1);
            return FALSE;
        }
    }
    else
    {
        SendStatus(_T("Could not get peer name"), 1);
        Send(_T("421 Refusing connection. Could not get peer name."));
        ForceClose(-1);
        return FALSE;
    }

    int count = m_pOwner->GetIpCount(peerIP);
    if (user.GetIpLimit() && count >= user.GetIpLimit())
    {
        CStdString str;
        if (count==1)
            str.Format(_T("Refusing connection. Reason: No more connections allowed from this IP. (%s already connected once)"), peerIP.c_str());
        else
            str.Format(_T("Refusing connection. Reason: No more connections allowed from this IP. (%s already connected %d times)"), peerIP.c_str(), count);
        SendStatus(str, 1);
        Send(_T("421 Refusing connection. No more connections allowed from your IP."));
        ForceClose(-1);
        return FALSE;
    }

    m_CurrentServerDir = m_pOwner->m_pPermissions->GetHomeDir(m_status.user);
    if (m_CurrentServerDir == _T(""))
    {
        Send(_T("550 Could not get home dir!"));
        ForceClose(-1);
        return FALSE;
    }

    m_status.ip = peerIP;

    count = GetUserCount(user.user);
    if (user.GetUserLimit() && count >= user.GetUserLimit())
    {
        CStdString str;
        str.Format(_T("Refusing connection. Reason: Maximum connection count (%d) reached for this user"), user.GetUserLimit());
        SendStatus(str, 1);
        str.Format(_T("421 Refusing connection. Maximum connection count reached for the user '%s'"), user.user);
        Send(str);
        ForceClose(-1);
        return FALSE;
    }

    m_pOwner->IncIpCount(peerIP);
    IncUserCount(m_status.user);
    m_status.loggedon = TRUE;

    GetSystemTime(&m_LastTransferTime);

    m_pOwner->m_pPermissions->AutoCreateDirs(m_status.user);

    t_connectiondata_changeuser *conndata = new t_connectiondata_changeuser;
    t_connop *op = new t_connop;
    op->data = conndata;
    op->op = USERCONTROL_CONNOP_CHANGEUSER;
    op->userid = m_userid;
    conndata->user = m_status.user;

    m_pOwner->SendNotification(FSM_CONNECTIONDATA, (LPARAM)op);

    return TRUE;
}

void CControlSocket::Continue()
{
    if (m_SlQuotas[download].bContinue)
    {
        TriggerEvent(FD_WRITE);
        if (m_transferstatus.socket && m_transferstatus.socket->Started())
            m_transferstatus.socket->TriggerEvent(FD_WRITE);
        m_SlQuotas[download].bContinue = false;
    }

    if (m_SlQuotas[upload].bContinue)
    {
        TriggerEvent(FD_READ);
        if (m_transferstatus.socket && m_transferstatus.socket->Started())
            m_transferstatus.socket->TriggerEvent(FD_READ);
        m_SlQuotas[upload].bContinue = false;
    }
}

int CControlSocket::GetSpeedLimit(sltype mode)
{
    CUser user;
    int nLimit = -1;
    if (m_status.loggedon && m_pOwner->m_pPermissions->GetUser(m_status.user, user))
    {
        nLimit = user.GetCurrentSpeedLimit(mode);
    }
    if (nLimit > 0)
    {
        nLimit *= 100;
        if (m_SlQuotas[mode].nTransferred >= nLimit)
        {
            m_SlQuotas[mode].bContinue = TRUE;
            return 0;
        }
        else
            nLimit -= m_SlQuotas[mode].nTransferred;
    }
    else
        nLimit = -1;
    if (user.BypassServerSpeedLimit(mode))
        m_SlQuotas[mode].bBypassed = TRUE;
    else if (m_SlQuotas[mode].nBytesAllowedToTransfer != -1)
    {
        if (nLimit == -1 || nLimit > (m_SlQuotas[mode].nBytesAllowedToTransfer - m_SlQuotas[mode].nTransferred))
            nLimit = m_SlQuotas[mode].nBytesAllowedToTransfer - m_SlQuotas[mode].nTransferred;
    }

    if (!nLimit)
        m_SlQuotas[mode].bContinue = TRUE;

    return nLimit;
}

BOOL CControlSocket::CreateTransferSocket(CTransferSocket *pTransferSocket)
{
    /* Create socket
     * First try control connection port - 1, if that fails try
     * control connection port + 1. If that fails as well, let
     * the OS decide.
     */
    bool bFallback = false;
    BOOL bCreated = FALSE;

    // Fix: Formerly, the data connection would always be opened using the server's default (primary) IP.
    // This would cause Windows Firewall to freak out if control connection was opened on a secondary IP.
    // When using Active FTP behind Windows Firewall, no connection could be made. This fix ensures the data
    // socket is on the same IP as the control socket.
    CStdString controlIP;
    UINT controlPort = 0;
    BOOL bResult = this->GetSockName(controlIP, controlPort);

    if (bResult)
    {
        // Try create control conn. port - 1
        if (controlPort > 1)
            if (pTransferSocket->Create(controlPort - 1, SOCK_STREAM, FD_CONNECT, controlIP, m_transferstatus.family, true))
                bCreated = TRUE;
    }
    if (!bCreated)
    {
creation_fallback:
        bFallback = true;
        // Let the OS find a valid port
        if (!pTransferSocket->Create(0, SOCK_STREAM, FD_CONNECT, controlIP, m_transferstatus.family, true))
        {
            // Give up
            Send(_T("421 Can't create socket"));
            ResetTransferstatus();
            return FALSE;
        }
    }
    if (m_pGssLayer && m_pGssLayer->AuthSuccessful())
        m_transferstatus.socket->UseGSS(m_pGssLayer);

    if (pTransferSocket->Connect(m_transferstatus.ip, m_transferstatus.port) == 0)
    {
        if (!bFallback && GetLastError() == WSAEADDRINUSE)
        {
            pTransferSocket->Close();
            goto creation_fallback;
        }

        if (GetLastError() != WSAEWOULDBLOCK)
        {
            Send(_T("425 Can't open data connection"));
            ResetTransferstatus();
            return FALSE;
        }
    }

    if (m_pSslLayer && m_bProtP)
        pTransferSocket->UseSSL(m_pSslLayer->GetContext());

    return TRUE;
}

bool CControlSocket::CheckIpForZlib()
{
    CStdString peerIP;
    UINT port = 0;
    BOOL bResult = GetPeerName(peerIP, port);
    if (!bResult)
        return false;

    if (!m_pOwner->m_pOptions->GetOptionVal(OPTION_MODEZ_ALLOWLOCAL) && !IsRoutableAddress(peerIP))
        return false;

    CStdString ips = m_pOwner->m_pOptions->GetOption(OPTION_MODEZ_DISALLOWED_IPS);
    ips += " ";

    int pos = ips.Find(' ');
    while (pos != -1)
    {
        CStdString blockedIP = ips.Left(pos);
        ips = ips.Mid(pos + 1);
        pos = ips.Find(' ');

        if (MatchesFilter(blockedIP, peerIP))
            return false;
    }

    return true;
}

void CControlSocket::AntiHammerIncrease(int amount /*=1*/)
{
    if (m_status.hammerValue < 8000)
        m_status.hammerValue += amount * 200;

    if (m_status.hammerValue > 2000)
        m_antiHammeringWaitTime += 1000 * (int)pow(1.3, (m_status.hammerValue / 400) - 5);
}

void CControlSocket::SendTransferinfoNotification(const char transfermode, const CStdString& physicalFile, const CStdString& logicalFile, __int64 startOffset, __int64 totalSize)
{
    t_connop *op = new t_connop;
    op->op = USERCONTROL_CONNOP_TRANSFERINIT;
    op->userid = m_userid;

    t_connectiondata_transferinfo *conndata = new t_connectiondata_transferinfo;
    conndata->transferMode = transfermode;
    conndata->physicalFile = physicalFile;
    conndata->logicalFile = logicalFile;
    conndata->startOffset = startOffset;
    conndata->totalSize = totalSize;
    op->data = conndata;

    m_pOwner->SendNotification(FSM_CONNECTIONDATA, (LPARAM)op);
}

int CControlSocket::OnLayerCallback(std::list<t_callbackMsg>& callbacks)
{
    for (std::list<t_callbackMsg>::iterator iter = callbacks.begin(); iter != callbacks.end(); iter++)
    {
        if (m_pSslLayer && iter->pLayer == m_pSslLayer)
        {
            if (iter->nType == LAYERCALLBACK_LAYERSPECIFIC && iter->nParam1 == SSL_INFO && iter->nParam2 == SSL_INFO_ESTABLISHED)
                SendStatus(_T("SSL connection established"), 0);
            else if (iter->nType == LAYERCALLBACK_LAYERSPECIFIC && iter->nParam1 == SSL_INFO && iter->nParam2 == SSL_INFO_SHUTDOWNCOMPLETE)
            {
                if (m_shutdown)
                {
                    delete [] iter->str;
                    Close();
                    m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);
                    return 0;
                }
                if (!m_bQuitCommand)
                {
                    delete [] iter->str;
                    continue;
                }

                do
                {
                    delete [] iter->str;
                    iter++;
                } while (iter != callbacks.end());

                ForceClose(5);

                return 0;
            }
        }
        else if (iter->nType == LAYERCALLBACK_LAYERSPECIFIC && iter->nParam1 == SSL_VERBOSE_WARNING)
        {
            if (iter->str)
            {
                CStdString str = "SSL warning: ";
                str += iter->str;

                SendStatus(str, 1);
            }
        }
        delete [] iter->str;
    }
    return 0;//CAsyncSocketEx::OnLayerCallback(pLayer, nType, nParam1, nParam2);
}

bool CControlSocket::InitImplicitSsl()
{
    m_pSslLayer = new CAsyncSslSocketLayer;
    int res = AddLayer(m_pSslLayer) ? 1 : 0;
    if (!res)
    {
        delete m_pSslLayer;
        m_pSslLayer = 0;
        return false;
    }

    CString error;
#ifdef _UNICODE
    res = m_pSslLayer->SetCertKeyFile(ConvToLocal(m_pOwner->m_pOptions->GetOption(OPTION_SSLCERTFILE)), ConvToLocal(m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYFILE)), ConvToLocal(m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYPASS)), &error);
#else
    res = m_pSslLayer->SetCertKeyFile(m_pOwner->m_pOptions->GetOption(OPTION_SSLCERTFILE), m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYFILE), m_pOwner->m_pOptions->GetOption(OPTION_SSLKEYPASS), &error);
#endif
    if (res == SSL_FAILURE_LOADDLLS)
        SendStatus(_T("Failed to load SSL libraries"), 1);
    else if (res == SSL_FAILURE_INITSSL)
        SendStatus(_T("Failed to initialize SSL libraries"), 1);
    else if (res == SSL_FAILURE_VERIFYCERT)
    {
        if (error != _T(""))
            SendStatus(error, 1);
        else
            SendStatus(_T("Failed to set certificate and private key"), 1);
    }
    if (res)
    {
        RemoveAllLayers();
        delete m_pSslLayer;
        m_pSslLayer = NULL;
        Send(_T("431 Could not initialize SSL connection"), 1);
        return false;
    }

    int code = m_pSslLayer->InitSSLConnection(false);
    if (code == SSL_FAILURE_LOADDLLS)
        SendStatus(_T("Failed to load SSL libraries"), 1);
    else if (code == SSL_FAILURE_INITSSL)
        SendStatus(_T("Failed to initialize SSL library"), 1);

    if (!code)
        return true;

    RemoveAllLayers();
    delete m_pSslLayer;
    m_pSslLayer = NULL;
    Send(_T("431 Could not initialize SSL connection"));

    //Close socket
    Close();
    SendStatus(_T("disconnected."), 0);
    m_pOwner->PostThreadMessage(WM_FILEZILLA_THREADMSG, FTM_DELSOCKET, m_userid);

    return false;
}

bool CControlSocket::CanQuit()
{
    if (m_pSslLayer)
        return false;
    return true;
}

CStdString CControlSocket::GetPassiveIP()
{
    //Get the ip of the control socket
    CStdString localIP;
    UINT localPort;
    BOOL bValidSockAddr = GetSockName(localIP, localPort);

    //Get peer ip
    CStdString peerIP;
    UINT peerPort = 0;
    BOOL bResult = GetPeerName(peerIP, peerPort);
    if (bResult)
    {
        if (m_pOwner->m_pOptions->GetOptionVal(OPTION_NOEXTERNALIPONLOCAL) && !IsRoutableAddress(peerIP))
        {
            // Remote IP address from an unroutable subnet
            
            // Inside a NAT-in-NAT environment, two different unroutable address ranges are used.
            // If remote address comes from a different unroutable subnet, don't use local
            // address.
            // Note that in a NAT-in-NAT environment, the external IP address specified will either
            // be the worldwide one or the NAT one. Either external or single-NATed users won't be able
            // to use passive mode.
            m_transferstatus.usedResolvedIP = false;

            if (!bValidSockAddr)
                return _T("");
            return localIP;
        }
    }

    if (m_pOwner->m_pOptions->GetOptionVal(OPTION_CUSTOMPASVIPTYPE))
    {
        CStdString pasvIP = m_pOwner->GetExternalIP(localIP);
        if (pasvIP != _T("") && pasvIP != localIP)
        {
            m_transferstatus.usedResolvedIP = true;
            return pasvIP;
        }
    }

    m_transferstatus.usedResolvedIP = false;

    if (!bValidSockAddr)
        return _T("");

    return localIP;
}

void CControlSocket::ParseMlstOpts(CStdString args)
{
    if (args == _T(""))
    {
        for (int i = 0; i < 4; i++)
            m_facts[i] = false;
        Send(_T("200 MLST OPTS"));
        return;
    }
    if (args[0] != ' ')
    {
        Send(_T("501 Invalid MLST options"));
        return;
    }
    args = args.Mid(1);
    if (args.Find(' ') != -1)
    {
        Send(_T("501 Invalid MLST options"));
        return;
    }

    bool facts[4] = {0};
    while (args != _T(""))
    {
        int pos = args.Find(';');
        if (pos < 1)
        {
            Send(_T("501 Invalid MLST options"));
            return;
        }

        CStdString fact = args.Left(pos);
        args = args.Mid(pos + 1);

        if (fact == _T("TYPE"))
            facts[fact_type] = true;
        else if (fact == _T("SIZE"))
            facts[fact_size] = true;
        else if (fact == _T("MODIFY"))
            facts[fact_modify] = true;
        //else if (fact == _T("PERM"))
        //    facts[fact_perm] = true;
    }

    for (int i = 0; i < 4; i++)
        m_facts[i] = facts[i];

    CStdString factstr;
    if (facts[fact_type])
        factstr += _T("type;");
    if (facts[fact_size])
        factstr += _T("size;");
    if (facts[fact_modify])
        factstr += _T("modify;");
    if (facts[fact_perm])
        factstr += _T("perm;");
    
    CStdString result = _T("200 MLST OPTS");
    if (factstr != _T(""))
        result += _T(" ") + factstr;

    Send(result);
}

void CControlSocket::ParseHashOpts(CStdString args)
{
    if (args == _T(""))
    {
        switch (m_hash_algorithm)
        {
        case CHashThread::MD5:
            Send(_T("200 MD5"));
            break;
        case CHashThread::SHA512:
            Send(_T("200 SHA-512"));
            break;
        default:
            Send(_T("200 SHA-1"));
            break;
        }
        return;
    }
    if (args[0] != ' ')
    {
        Send(_T("501 Invalid HASH options"));
        return;
    }
    args = args.Mid(1);
    if (args.Find(' ') != -1)
    {
        Send(_T("501 Invalid HASH options"));
        return;
    }

    if (args == _T("SHA-1"))
    {
        m_hash_algorithm = CHashThread::SHA1;
        Send(_T("200 Hash algorithm set to SHA-1"));
    }
    else if (args == _T("SHA-512"))
    {
        m_hash_algorithm = CHashThread::SHA512;
        Send(_T("200 Hash algorithm set to SHA-512"));
    }
    else if (args == _T("MD5"))
    {
        m_hash_algorithm = CHashThread::MD5;
        Send(_T("200 Hash algorithm set to MD5"));
    }
    else
        Send(_T("501 Unknown algorithm"));
}

void CControlSocket::ProcessHashResult(int hash_id, int res, CHashThread::_algorithm alg, const CStdString& hash, const CStdString& file)
{
    if (hash_id != m_hash_id)
        return;
    
    m_hash_id = 0;

    if (res == CHashThread::BUSY)
        Send(_T("450 Another hash operation is already in progress."));
    else if (res == CHashThread::FAILURE_OPEN)
        Send(_T("550 Failed to open file"));
    else if (res == CHashThread::FAILURE_READ)
        Send(_T("550 Could not read from file"));
    else
    {
        CStdString algname;
        switch (alg)
        {
        case CHashThread::SHA1:
            algname = "SHA-1";
            break;
        case CHashThread::SHA512:
            algname = "SHA-512";
            break;
        case CHashThread::MD5:
            algname = "MD5";
            break;
        }
        Send(_T("213 ") + algname + _T(" ") + hash + _T(" ") + file);
    }
}

:: Command execute ::

Enter:
 
Select:
 

:: Search ::
  - regexp 

:: Upload ::
 
[ ok ]

:: Make Dir ::
 
[ ok ]
:: Make File ::
 
[ ok ]

:: Go Dir ::
 
:: Go File ::
 

--[ c99shell v. 1.0 pre-release build #13 powered by Captain Crunch Security Team | http://ccteam.ru | Generation time: 0.0312 ]--