ftpcontrolsocket.cpp

来自「一个支持FTP,SFTP的客户端程序」· C++ 代码 · 共 2,319 行 · 第 1/5 页

CPP
2,319
字号
// FileZilla - a Windows ftp client

// 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.

// CFtpControlSocket.cpp: Implementierungsdatei
//

#include "stdafx.h"
#include "FtpControlSocket.h"
#include "mainthread.h"
#include "transfersocket.h"
#include "fileexistsdlg.h"
#include "pathfunctions.h"
#include "asyncproxysocketlayer.h"
#include "AsyncSslSocketLayer.h"
#include "AsyncGssSocketLayer.h"
#include "filezillaapi.h"
#include "misc/utf8.h"

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

class CFtpControlSocket::CFileTransferData : public CFtpControlSocket::t_operation::COpData
{
public:
	CFileTransferData()
	{
		pDirectoryListing=0;
		nGotTransferEndReply=0;
		nWaitNextOpState=0;
		nMKDOpState=-1;
		pFileTime=0;
		pFileSize=0;
		bUseAbsolutePaths = FALSE;
		bTriedPortPasvOnce = FALSE;
		askOnResumeFail = false;
	};
	~CFileTransferData()
	{
		if (pDirectoryListing)
			delete pDirectoryListing;
		pDirectoryListing=0;
		delete pFileSize;
		delete pFileTime;
	};
	CString rawpwd;
	t_transferfile transferfile;
	t_transferdata transferdata;
	CString host;
	int port;
	BOOL bPasv;
	int nGotTransferEndReply;
	t_directory *pDirectoryListing;
	int nWaitNextOpState;
	CServerPath MKDCurrent;
	std::list<CString> MKDSegments;
	int nMKDOpState;
	CTime ListStartTime;
	CTime *pFileTime; //Used when downloading and OPTION_PRESERVEDOWNLOADFILETIME is set or when LIST fails
	_int64 *pFileSize; //Used when LIST failes
	BOOL bUseAbsolutePaths;
	BOOL bTriedPortPasvOnce;
	int newZlibLevel;
	bool askOnResumeFail;
};

class CFtpControlSocket::CListData:public CFtpControlSocket::t_operation::COpData
{
public:
	CListData()
	{
		pDirectoryListing = 0;
		bTriedPortPasvOnce = FALSE;
		lastCmdSentCDUP = false;
	}
	virtual ~CListData()
	{
		if (pDirectoryListing)
			delete pDirectoryListing;
	}
	CString rawpwd;
	CServerPath path;
	CString subdir;
	int nListMode;
	BOOL bPasv;
	CString host;
	UINT port;
	int nFinish;
	t_directory *pDirectoryListing;
	CTime ListStartTime;
	BOOL bTriedPortPasvOnce;
	int newZlibLevel;
	bool lastCmdSentCDUP;
};

class CFtpControlSocket::CMakeDirData : public CFtpControlSocket::t_operation::COpData
{
public:
	CMakeDirData() {}
	virtual ~CMakeDirData() {}
	CServerPath path;
	CServerPath Current;
	std::list<CString> Segments;
};

#define MKD_INIT -1
#define MKD_FINDPARENT 0
#define MKD_MAKESUBDIRS 1
#define MKD_CHANGETOSUBDIR 2

/////////////////////////////////////////////////////////////////////////////
// CFtpControlSocket

CFtpControlSocket::CFtpControlSocket(CMainThread *pMainThread) : CControlSocket(pMainThread)
{
	ASSERT(pMainThread);
	m_Operation.nOpMode=0;
	m_Operation.nOpState=-1;
	m_Operation.pData=0;
	m_pTransferSocket=0;
	m_pDataFile=0;
	m_pDirectoryListing=0;
	m_pOwner=pMainThread;
	srand( (unsigned)time( NULL ) );
	m_bKeepAliveActive=FALSE;
	m_bCheckForTimeout=TRUE;
	m_bDidRejectCertificate = FALSE;

	m_useZlib = false;
	m_zlibSupported = false;
	m_zlibLevel = 8;

	m_bUTF8 = false;
	m_hasClntCmd = false;

	m_awaitsReply = false;
	m_skipReply = false;

	m_sendBuffer = 0;
	m_sendBufferLen = 0;

	m_bProtP = false;

	m_mayBeMvsFilesystem = false;
	m_mayBeBS2000Filesystem = false;
}

CFtpControlSocket::~CFtpControlSocket()
{
	LogMessage(__FILE__, __LINE__, this,FZ_LOG_DEBUG, _T("~CFtpControlSocket()"));
	DoClose();
	if (m_pTransferSocket)
	{
		m_pTransferSocket->Close();
		delete m_pTransferSocket;
		m_pTransferSocket=0;
	}
	if (m_pDataFile)
	{
		delete m_pDataFile;
		m_pDataFile=0;
	}
}

/////////////////////////////////////////////////////////////////////////////
// Member-Funktion CFtpControlSocket
#define CONNECT_INIT -1
#define CONNECT_GSS_INIT -2
#define CONNECT_GSS_AUTHDONE -3
#define CONNECT_GSS_CWD -4
#define CONNECT_GSS_FAILED -5
#define CONNECT_GSS_NEEDPASS -6
#define CONNECT_GSS_NEEDUSER -7
#define CONNECT_SSL_INIT -8
#define CONNECT_SSL_NEGOTIATE -9
#define CONNECT_SSL_WAITDONE -10
#define CONNECT_SSL_PBSZ -11
#define CONNECT_SSL_PROT -12
#define CONNECT_FEAT -13
#define CONNECT_SYST -14
#define CONNECT_OPTSUTF8 -15
#define CONNECT_CLNT -16

void CFtpControlSocket::Connect(t_server &server)
{
	USES_CONVERSION;

	// Some sanity checks
	if (m_pOwner->IsConnected())
	{
		ShowStatus(_T("Internal error: Connect called while still connected"), 1);
		if (!m_Operation.nOpMode)
			m_Operation.nOpMode = CSMODE_CONNECT;
		DoClose(FZ_REPLY_CRITICALERROR);
		return;
	}
	if (m_Operation.nOpMode)
	{
		ShowStatus(_T("Internal error: m_Operation.nOpMode not zero in Connect"), 1);
		m_Operation.nOpMode = CSMODE_CONNECT;
		DoClose(FZ_REPLY_CRITICALERROR);
		return;
	}

	m_Operation.nOpMode = CSMODE_CONNECT;

	if (m_pSslLayer)
	{
		ShowStatus(_T("Internal error: m_pSslLayer not zero in Connect"), 1);
		DoClose(FZ_REPLY_CRITICALERROR);
		return;
	}
	if (m_pProxyLayer)
	{
		ShowStatus(_T("Internal error: m_pProxyLayer not zero in Connect"), 1);
		DoClose(FZ_REPLY_CRITICALERROR);
		return;
	}

	if (server.nServerType & FZ_SERVERTYPE_LAYER_SSL_IMPLICIT ||
		server.nServerType & FZ_SERVERTYPE_LAYER_SSL_EXPLICIT ||
		server.nServerType & FZ_SERVERTYPE_LAYER_TLS_EXPLICIT)
	{
		m_pSslLayer = new CAsyncSslSocketLayer;
		AddLayer(m_pSslLayer);
		TCHAR buffer[1000];
		GetModuleFileName(NULL, buffer, 1000);
		CString filename = buffer;
		int pos = filename.ReverseFind('\\');
		if (pos != -1)
		{
			filename = filename.Left(pos + 1);
			filename += _T("cacert.pem");
			m_pSslLayer->SetCertStorage(filename);
		}
		else
			filename = "cacert.pem";
	}

	if (!server.fwbypass)
	{
		int nProxyType = COptions::GetOptionVal(OPTION_PROXYTYPE);
		if (nProxyType != PROXYTYPE_NOPROXY)
		{
			m_pProxyLayer = new CAsyncProxySocketLayer;
			if (nProxyType == PROXYTYPE_SOCKS4)
				m_pProxyLayer->SetProxy(PROXYTYPE_SOCKS4, T2CA(COptions::GetOption(OPTION_PROXYHOST)), COptions::GetOptionVal(OPTION_PROXYPORT));
			else if (nProxyType==PROXYTYPE_SOCKS4A)
				m_pProxyLayer->SetProxy(PROXYTYPE_SOCKS4A, T2CA(COptions::GetOption(OPTION_PROXYHOST)),COptions::GetOptionVal(OPTION_PROXYPORT));
			else if (nProxyType==PROXYTYPE_SOCKS5)
				if (COptions::GetOptionVal(OPTION_PROXYUSELOGON))
					m_pProxyLayer->SetProxy(PROXYTYPE_SOCKS5, T2CA(COptions::GetOption(OPTION_PROXYHOST)),
											COptions::GetOptionVal(OPTION_PROXYPORT),
											T2CA(COptions::GetOption(OPTION_PROXYUSER)),
											T2CA(CCrypt::decrypt(COptions::GetOption(OPTION_PROXYPASS))));
				else
					m_pProxyLayer->SetProxy(PROXYTYPE_SOCKS5, T2CA(COptions::GetOption(OPTION_PROXYHOST)),
											COptions::GetOptionVal(OPTION_PROXYPORT));
			else if (nProxyType==PROXYTYPE_HTTP11)
				if (COptions::GetOptionVal(OPTION_PROXYUSELOGON))
					m_pProxyLayer->SetProxy(PROXYTYPE_HTTP11, T2CA(COptions::GetOption(OPTION_PROXYHOST)),
											COptions::GetOptionVal(OPTION_PROXYPORT),
											T2CA(COptions::GetOption(OPTION_PROXYUSER)),
											T2CA(CCrypt::decrypt(COptions::GetOption(OPTION_PROXYPASS))));
				else
					m_pProxyLayer->SetProxy(PROXYTYPE_HTTP11, T2CA(COptions::GetOption(OPTION_PROXYHOST)) ,COptions::GetOptionVal(OPTION_PROXYPORT));
			else
				ASSERT(FALSE);
			AddLayer(m_pProxyLayer);
		}
	}

	BOOL bUseGSS = FALSE;
	if (COptions::GetOptionVal(OPTION_USEGSS) && !m_pSslLayer)
	{
		CString GssServers=COptions::GetOption(OPTION_GSSSERVERS);
		LPCSTR lpszAscii=T2CA(server.host);
		hostent *fullname=gethostbyname(lpszAscii);
		CString host;
		if (fullname)
			host=fullname->h_name;
		else
			host=server.host;
		host.MakeLower();
		int i;
		while ((i=GssServers.Find(_T(";")))!=-1)
		{
			if (("."+GssServers.Left(i))==host.Right(GssServers.Left(i).GetLength()+1) || GssServers.Left(i)==host)
			{
				bUseGSS=TRUE;
				break;
			}
			GssServers=GssServers.Mid(i+1);
		}
	}
	if (bUseGSS)
	{
		m_pGssLayer = new CAsyncGssSocketLayer;
		AddLayer(m_pGssLayer);
		if (!m_pGssLayer->InitGSS())
		{
			ShowStatus(_T("Unable to initialize GSS api"), 1);
			DoClose(FZ_REPLY_CRITICALERROR);
			return;
		}
	}
	m_Operation.nOpState = m_pGssLayer?CONNECT_GSS_INIT:CONNECT_INIT;
	m_Operation.nOpMode = CSMODE_CONNECT;
	
	if (!Create())
	{
		DoClose();
		return;
	}
	AsyncSelect();

	if (server.nServerType&FZ_SERVERTYPE_LAYER_SSL_IMPLICIT)
	{
		if (!m_pSslLayer)
		{
			ShowStatus(_T("Internal error: m_pSslLayer not initialized"), 1);
			DoClose(FZ_REPLY_CRITICALERROR);
			return;
		}
		int res = m_pSslLayer->InitSSLConnection(true);
		if (res == SSL_FAILURE_LOADDLLS)
			ShowStatus(IDS_ERRORMSG_CANTLOADSSLDLLS, 1);
		else if (res == SSL_FAILURE_INITSSL)
			ShowStatus(IDS_ERRORMSG_CANTINITSSL, 1);
		if (!res)
			PostMessage(m_pOwner->m_hOwnerWnd, m_pOwner->m_nReplyMessageID, FZ_MSG_MAKEMSG(FZ_MSG_SECURESERVER, 1), 0);
		else
		{
			DoClose();
			return;
		}
	}

	int logontype = COptions::GetOptionVal(OPTION_LOGONTYPE);
	int port;
	CString buf,temp;
	if (server.fwbypass)
		logontype=0;

	// are we connecting directly to the host (logon type 0) or via a firewall? (logon type>0)
	CString fwhost;
	int fwport;
	if(!logontype)
	{
		temp=server.host;
		port=server.port;
	}
	else
	{
		fwhost=COptions::GetOption(OPTION_FWHOST);
		fwport=COptions::GetOptionVal(OPTION_FWPORT);
		temp=fwhost;
		port=fwport;
		if(fwport!=21)
			fwhost.Format( _T("%s:%d"), fwhost, fwport); // add port to fwhost (only if port is not 21)
	}
	m_CurrentServer = server;

	CString hostname = server.host;
	if(server.port!=21)
		hostname.Format( _T("%s:%d"), hostname, server.port); // add port to hostname (only if port is not 21)
	CString str;
	str.Format(IDS_STATUSMSG_CONNECTING, hostname);
	ShowStatus(str, 0);

	if ((server.nServerType & FZ_SERVERTYPE_LAYERMASK) & (FZ_SERVERTYPE_LAYER_SSL_EXPLICIT | FZ_SERVERTYPE_LAYER_TLS_EXPLICIT))
		m_Operation.nOpState = CONNECT_SSL_INIT;

	if (!CControlSocket::Connect(temp, port))
	{
		if (WSAGetLastError() != WSAEWOULDBLOCK)
		{
			DoClose();
			return;
		}
	}
	m_ServerName = logontype?fwhost:hostname;
	m_LastRecvTime = m_LastSendTime = CTime::GetCurrentTime();
}

void CFtpControlSocket::LogOnToServer(BOOL bSkipReply /*=FALSE*/)
{
	int logontype = m_pGssLayer ? 0 : COptions::GetOptionVal(OPTION_LOGONTYPE);
	const int LO = -2, ER = -1;
	CString buf, temp;
	const int NUMLOGIN = 9; // currently supports 9 different login sequences
	int logonseq[NUMLOGIN][20] = {
		// this array stores all of the logon sequences for the various firewalls
		// in blocks of 3 nums. 1st num is command to send, 2nd num is next point in logon sequence array
		// if 200 series response is rec'd from server as the result of the command, 3rd num is next
		// point in logon sequence if 300 series rec'd
		{0,LO,3, 1,LO,6,  12,LO,ER}, // no firewall
		{3,6,3,  4,6,ER, 5,9,9, 0,LO,12, 1,LO,15, 12,LO,ER}, // SITE hostname
		{3,6,3,  4,6,ER, 6,LO,9, 1,LO,12, 12,LO,ER}, // USER after logon
		{7,3,3,  0,LO,6, 1,LO,9, 12,LO,ER}, //proxy OPEN
		{3,6,3,  4,6,ER, 0,LO,9, 1,LO,12, 12,LO,ER}, // Transparent
		{6,LO,3, 1,LO,6, 12,LO,ER}, // USER remoteID@remotehost
		{8,6,3,  4,6,ER, 0,LO,9, 1,LO,12, 12,LO,ER}, //USER fireID@remotehost
		{9,ER,3, 1,LO,6, 2,LO,ER}, //USER remoteID@remotehost fireID
		{10,LO,3,11,LO,6,2,LO,ER} // USER remoteID@fireID@remotehost
	};
	if (m_CurrentServer.fwbypass)
		logontype = 0;

	if (m_Operation.nOpState == CONNECT_SSL_INIT)
	{
		if (m_CurrentServer.nServerType & FZ_SERVERTYPE_LAYER_SSL_EXPLICIT)
		{
			if (!Send("AUTH SSL"))
				return;
		}
		else
		{
			if (!Send("AUTH TLS"))
				return;
		}
		m_Operation.nOpState = CONNECT_SSL_NEGOTIATE;
		return;
	}
	else if (m_Operation.nOpState == CONNECT_SSL_NEGOTIATE)
	{
		int res = GetReplyCode();
		if (res!=2 && res!=3)
		{
			DoClose();
			return;
		}
		else
		{
			if (!m_pSslLayer)
			{
				ShowStatus(_T("Internal error: m_pSslLayer not initialized"), 1);
				DoClose(FZ_REPLY_CRITICALERROR);
				return;
			}
			int res = m_pSslLayer->InitSSLConnection(true);
			if (res == SSL_FAILURE_LOADDLLS)
				ShowStatus(IDS_ERRORMSG_CANTLOADSSLDLLS, 1);
			else if (res == SSL_FAILURE_INITSSL)
				ShowStatus(IDS_ERRORMSG_CANTINITSSL, 1);
			if (res)
			{

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?