3714 lines
93 KiB
C++
3714 lines
93 KiB
C++
|
// 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);
|
||
|
}
|
||
|
}
|