⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ftpclient.cpp

📁 自己改写的在WINCE上开发用的EVC++的FTP操作示例工程,希望能给相关人士提供帮助.
💻 CPP
📖 第 1 页 / 共 4 页
字号:
////////////////////////////////////////////////////////////////////////////////
//
// The official specification of the File Transfer Protocol (FTP) is the RFC 959.
// Most of the documentation are taken from this RFC.
// This is an implementation of an simple ftp client. I have tried to implement
// platform independent. For the communication i used the classes CBlockingSocket,
// CSockAddr, ... from David J. Kruglinski (Inside Visual C++). These classes are
// only small wrappers for the sockets-API.
// Further I used a smart pointer-implementation from Scott Meyers (Effective C++, 
// More Effective C++, Effective STL).
// The implementation of the logon-sequence (with firewall support) was published 
// in an article on Codeguru by Phil Anderson. 
// The code for the parsing of the different FTP LIST responses is taken from 
// D. J. Bernstein (http://cr.yp.to/ftpparse.html). I only wrapped the c-code in
// a class.
// I haven't tested the code on other platforms, but i think with little 
// modifications it would compile.
// 
// Copyright (c) 2004 Thomas Oswald
//
// Permission to copy, use, sell and distribute this software is granted
// provided this copyright notice appears in all copies.
// Permission to modify the code and to distribute modified code is granted
// provided this copyright notice appears in all copies, and a notice
// that the code was modified is included with the copyright notice.
//
// This software is provided "as is" without express or implied warranty,
// and with no claim as to its suitability for any purpose.
//
// History:
// v1.1 released 2005-12-04
//      - Bug in OpenPassiveDataConnection removed: SendCommand was called before data connection was established.
//      - Bugs in GetSingleResponseLine removed:
//         * Infinite loop if response line doesn't end with CRLF.
//         * Return value of std:string->find must be checked against npos.
//      - Now runs in unicode.
//      - Streams removed.
//      - Explicit detaching of observers are not necessary anymore.
//      - ExecuteDatachannelCommand now accepts an ITransferNotification object. 
//        Through this concept there is no need to write the received files to a file.
//        For example the bytes can be written only in memory or an other tcp stream.
//      - Added an interface for the blocking socket (IBlockingSocket).
//        Therefore it is possible to exchange the socket implementation, e.g. for 
//        writing unit tests (by simulating an specific scenario of an ftp communication).
//      - Replaced the magic numbers concerning the reply codes by a class.
// v1.0 released 2004-10-25
////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "FTPclient.h"
#include <tchar.h>
#include <limits>
#include <algorithm>

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

using namespace nsHelper;
using namespace nsSocket;

using namespace nsFTP;

long StringToLong(CStringA strIn)
{
	CHAR* pStopString = 0;
	return strtol(strIn.GetString(), &pStopString, 10);
}

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

/// constructor
/// @param[in] pSocket Instance of socket class which will be used for 
///                    communication with the ftp server.
///                    CFTPClient class takes ownership of this instance.
///                    It will be deleted on destruction of this object.
///                    If this pointer is NULL, the CBlockingSocket implementation
///                    will be used. 
///                    This gives the ability to set an other socket class.
///                    For example a socket class can be implemented which simulates
///                    a ftp server (for unit testing).
/// @param[in] uiTimeout Timeout used for socket operation.
/// @param[in] uiBufferSize Size of the buffer used for sending and receiving
///                         data via sockets. The size have an influence on 
///                         the performance. Through empiric test i come to the
///                         conclusion that 2048 is a good size.
/// @param[in] uiResponseWait Sleep time between receive calls to socket when getting 
///                           the response. Sometimes the socket hangs if no wait time
///                           is set. Normally not wait time is necessary.
CFTPClient::CFTPClient(IBlockingSocket* pSocket/*=NULL*/, unsigned int uiTimeout/*=10*/, 
					   unsigned int uiBufferSize/*=2048*/, unsigned int uiResponseWait/*=0*/) :
mc_uiTimeout(uiTimeout),
mc_uiResponseWait(uiResponseWait),
m_apSckControlConnection(pSocket)
{
	mc_strEolCharacterSequence = "\r\n";
	m_vBuffer.NewStack(uiBufferSize);
	m_fTransferInProgress = false;
	m_fAbortTransfer = false;
	m_fResumeIfPossible = true;
	m_pNotification = NULL;
	ASSERT( pSocket );
}

CFTPClient::~CFTPClient()
{
	if( IsTransferringData() )
		Abort();
	
	if( IsConnected() )
		Logout();
}

/// Enables or disables resuming for file transfer operations.
/// @param[in] fEnable True enables resuming, false disables it.
void CFTPClient::SetResumeMode(bool fEnable/*=true*/)
{
	m_fResumeIfPossible=fEnable;
}

/// Opens the control channel to the FTP server.
/// @param[in] strServerHost IP-address or name of the server
/// @param[in] iServerPort Port for channel. Usually this is port 21.
bool CFTPClient::OpenControlChannel(CStringA& strServerHost, USHORT ushServerPort/*=DEFAULT_FTP_PORT*/)
{
	CloseControlChannel();
	
	try
	{
		m_apSckControlConnection->Create(SOCK_STREAM);
		CSockAddr adr = m_apSckControlConnection->GetHostByName(strServerHost.GetString(), ushServerPort);
		m_apSckControlConnection->Connect(adr);
	}
	catch(CBlockingSocketException& blockingException)
	{
		ReportError(blockingException.GetErrorMessage(), __FILE__, __LINE__);
		m_apSckControlConnection->Cleanup();
		return false;
	}
	
	return true;
}

/// Returns the connection state of the client.
bool CFTPClient::IsConnected()
{
	return m_apSckControlConnection->operator SOCKET()!=0;
}

/// Returns true if a download/upload is running, otherwise false.
bool CFTPClient::IsTransferringData()
{
	return m_fTransferInProgress;
}

/// Closes the control channel to the FTP server.
void CFTPClient::CloseControlChannel() 
{
	try
	{
		m_apSckControlConnection->Close();
		CRepresentation *pRep = m_apCurrentRepresentation.release();
		delete pRep;
	}
	catch(CBlockingSocketException& blockingException)
	{
		blockingException.GetErrorMessage();
		m_apSckControlConnection->Cleanup();
	}
}

/// Analyse the repy code of a ftp-server-response.
/// @param[in] Reply Reply of a ftp server.
/// @retval FTP_OK    All runs perfect.
/// @retval FTP_ERROR Something went wrong. An other response was expected.
/// @retval NOT_OK    The command was not accepted.
int CFTPClient::SimpleErrorCheck(CReply& Reply)
{
	if( Reply.Code().IsNegativeReply() )
		return FTP_NOTOK;
	else if( Reply.Code().IsPositiveCompletionReply() )
		return FTP_OK;
	
	ASSERT( Reply.Code().IsPositiveReply() );
	
	return FTP_ERROR;
}

/// Logs on to an ftp-server.
/// @param[in] logonInfo Structure with logon information.
bool CFTPClient::Login(CLogonInfo& logonInfo)
{
	m_LastLogonInfo = logonInfo;
	
	enum {LO=-2,      ///< Logged On
		ER=-1,      ///< Error
		NUMLOGIN=9, ///< currently supports 9 different login sequences
	};
	
	int iLogonSeq[NUMLOGIN][18] = {
		// 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,   2,LO,ER                                  }, // no firewall
		{ 3, 6,3,    4, 6,ER,   5,ER, 9,   0,LO,12,   1,LO,15,   2,LO,ER }, // SITE hostname
		{ 3, 6,3,    4, 6,ER,   6,LO, 9,   1,LO,12,   2,LO,ER            }, // USER after logon
		{ 7, 3,3,    0,LO, 6,   1,LO, 9,   2,LO,ER                       }, // proxy OPEN
		{ 3, 6,3,    4, 6,ER,   0,LO, 9,   1,LO,12,   2,LO,ER            }, // Transparent
		{ 6,LO,3,    1,LO, 6,   2,LO,ER                                  }, // USER with no logon
		{ 8, 6,3,    4, 6,ER,   0,LO, 9,   1,LO,12,   2,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
	};
	
	// are we connecting directly to the host (logon type 0) or via a firewall? (logon type>0)
	CStringA   strTemp;
	USHORT    ushPort=0;
	
	if( logonInfo.FwType() == CFirewallType::None())
	{
		strTemp = logonInfo.Hostname();
		ushPort = logonInfo.Hostport();
	}
	else
	{
		strTemp = logonInfo.FwHost();
		ushPort = logonInfo.FwPort();
	}
	
	CStringA strHostnamePort(logonInfo.Hostname());
	if( logonInfo.Hostport()!=DEFAULT_FTP_PORT )
		strHostnamePort = (CMakeString() << logonInfo.Hostname() << ":" << logonInfo.Hostport()).GetString(); // add port to hostname (only if port is not 21)
	
	if( IsConnected() )
		Logout();
	
	if( !OpenControlChannel(strTemp, ushPort) )
		return false;
	
	// get initial connect msg off server
	CReply Reply;
	if( !GetResponse(Reply) || !Reply.Code().IsPositiveCompletionReply() )
		return false;
	
	int iLogonPoint=0;
	
	// go through appropriate logon procedure
#pragma warning(disable:4127)
	while( true )
#pragma warning(default:4127)
	{
		switch(iLogonSeq[logonInfo.FwType().AsEnum()][iLogonPoint])
		{
		case 0:
			strTemp = (CMakeString() << "USER " << logonInfo.Username()).GetString();
			break;
		case 1:
			strTemp= (CMakeString() << "PASS " << logonInfo.Password()).GetString();
			break;
		case 2:
			strTemp= (CMakeString() << "ACCT " << logonInfo.Account()).GetString();
			break;
		case 3:
			strTemp= (CMakeString() << "USER " << logonInfo.FwUsername()).GetString();
			break;
		case 4:
			strTemp= (CMakeString() << "PASS " << logonInfo.FwPassword()).GetString();
			break;
		case 5:
			strTemp= (CMakeString() << "SITE " << strHostnamePort).GetString();
			break;
		case 6:
			strTemp= (CMakeString() << "USER " << logonInfo.Username() << "@" << strHostnamePort).GetString();
			break;
		case 7:
			strTemp= (CMakeString() << "OPEN " << strHostnamePort).GetString();
			break;
		case 8:
			strTemp= (CMakeString() << "USER " << logonInfo.FwUsername() << "@" << strHostnamePort).GetString();
			break;
		case 9:
			strTemp= (CMakeString() << "USER " << logonInfo.Username() << "@" << strHostnamePort << " " << logonInfo.FwUsername()).GetString();
			break;
		case 10:
			strTemp= (CMakeString() << "USER " << logonInfo.Username() << "@" << logonInfo.FwUsername() << "@" << strHostnamePort).GetString();
			break;
		case 11:
			strTemp= (CMakeString() << "PASS " << logonInfo.Password() << "@" << logonInfo.FwPassword()).GetString();
			break;
		}
		
		// send command, get response
		CReply Reply;
		if( !SendCommand(strTemp, Reply) )
			return false;
		
		if( !Reply.Code().IsPositiveCompletionReply() && !Reply.Code().IsPositiveIntermediateReply() )
			return false;
		
		unsigned int uiFirstDigitOfReplyCode = StringToLong(Reply.Code().Value())/100;
		iLogonPoint=iLogonSeq[logonInfo.FwType().AsEnum()][iLogonPoint + uiFirstDigitOfReplyCode-1]; //get next command from array
		switch(iLogonPoint)
		{
		case ER: // ER means somewhat has gone wrong
			{
				ReportError("Logon failed.", __FILE__, __LINE__);
			}
			return false;
		case LO: // LO means we're fully logged on
			if( ChangeWorkingDirectory("/")!=FTP_OK )
				return false;
			return true;
		}
	}
	return false;
}

/// Rename a file on the ftp server.
/// @param[in] strOldName Name of the file to rename.
/// @param[in] strNewName The new name for the file.
/// @return see return values of CFTPClient::SimpleErrorCheck
int CFTPClient::Rename(CStringA& strOldName, CStringA& strNewName)
{
	CReply Reply;
	
	CStringA strCmd;
	strCmd = (CMakeString() << "RNFR " << strOldName).GetString();
	
	if( !SendCommand(strCmd, Reply) )
		return FTP_ERROR;
	
	if( Reply.Code().IsNegativeReply() )
		return FTP_NOTOK;
	else if( !Reply.Code().IsPositiveIntermediateReply() )
	{
		ASSERT( Reply.Code().IsPositiveCompletionReply() || Reply.Code().IsPositivePreliminaryReply() );
		return FTP_ERROR;
	}
	
	strCmd = (CMakeString() << "RNTO " << strNewName).GetString();
	if( !SendCommand(strCmd, Reply) )
		return FTP_ERROR;
	
	return SimpleErrorCheck(Reply);
}
/// Gets the directory listing of the ftp-server. Sends the LIST command to
/// the ftp-server.
/// @param[in] strPath Starting path for the list command.
/// @param[out] vstrFileList Returns a simple list of the files and folders of the specified directory.
/// @param[in] fPasv see documentation of CFTPClient::Passive
bool CFTPClient::List(CStringA strPath, TStringVector& vstrFileList, bool fPasv)
{
	COutputStream outputStream(mc_strEolCharacterSequence);
	if( !ExecuteDatachannelCommand(CDatachannelCmd::LIST(), strPath, CRepresentation(CType::ASCII()), fPasv, 0, &outputStream) )
		return false;
	
	vstrFileList.clear();
	CStringA strLine;
	outputStream.SetStartPosition();
	while( outputStream.GetNextLine(strLine) )
		vstrFileList.push_back(strPath + strLine);
	
	return true;
}

/// Gets the directory listing of the ftp-server. Sends the NLST command to
/// the ftp-server.
/// @param[in] strPath Starting path for the list command.
/// @param[out] vstrFileList Returns a simple list of the files and folders of the specified the directory.
/// @param[in] fPasv see documentation of CFTPClient::Passive
bool CFTPClient::NameList(CStringA strPath, TStringVector& vstrFileList, bool fPasv)
{
	COutputStream outputStream(mc_strEolCharacterSequence);
	if( !ExecuteDatachannelCommand(CDatachannelCmd::NLST(), strPath, CRepresentation(CType::ASCII()), fPasv, 0, &outputStream) )
		return false;
	
	vstrFileList.clear();
	CStringA strLine;
	outputStream.SetStartPosition();
	while( outputStream.GetNextLine(strLine) )
		vstrFileList.push_back(strPath + strLine);
	
	return true;
}

/// Gets the directory listing of the ftp-server. Sends the LIST command to
/// the ftp-server.
/// @param[in] strPath Starting path for the list command.
/// @param[out] vFileList Returns a detailed list of the files and folders of the specified directory.
///                       vFileList contains CFTPFileStatus-Objects. These Objects provide a lot of
///                       information about the file/folder.
/// @param[in] fPasv see documentation of CFTPClient::Passive
bool CFTPClient::List(CStringA strPath, TSpFTPFileStatusVector& vFileList, bool fPasv)
{
	COutputStream outputStream(mc_strEolCharacterSequence);
	if( !ExecuteDatachannelCommand(CDatachannelCmd::LIST(), strPath, CRepresentation(CType::ASCII()), fPasv, 0, &outputStream) )
		return false;
	
	vFileList.clear();
	CStringA strLine;
	CFTPListParse ftpListParser;
	outputStream.SetStartPosition();

⌨️ 快捷键说明

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