////////////////////////////////////////////////////////////////////////////////
//
// 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 <limits>
#include <algorithm>
#include "FTPListParse.h"
#include "FTPFileState.h"
#undef max
#ifdef __AFX_H__ // MFC only
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
#endif
using namespace nsHelper;
namespace nsFTP
{
class CMakeString
{
public:
CMakeString& operator<<(DWORD dwNum)
{
DWORD dwTemp = dwNum;
int iCnt=1; // name lookup of 'iCnt' changed for new ISO 'for' scoping
for( ; (dwTemp/=10) != 0; iCnt++ )
;
m_str.resize(m_str.size() + iCnt);
tsprintf(&(*m_str.begin()), _T("%s%u"), m_str.c_str(), dwNum);
return *this;
}
CMakeString& operator<<(const tstring& strAdd)
{
m_str += strAdd;
return *this;
}
operator tstring() const { return m_str; }
private:
tstring m_str;
};
tstring& ReplaceStr(tstring& strTarget, const tstring& strToReplace, const tstring& strReplacement)
{
size_t pos = strTarget.find(strToReplace);
while( pos != tstring::npos )
{
strTarget.replace(pos, strToReplace.length(), strReplacement);
pos = strTarget.find(strToReplace, pos+1);
}
return strTarget;
}
class CFile : public ITransferNotification
{
FILE* m_pFile;
public:
enum T_enOrigin { orBegin=SEEK_SET, orEnd=SEEK_END, orCurrent=SEEK_CUR };
CFile() : m_pFile(NULL) {}
~CFile()
{
Close();
}
bool Open(const tstring& strFileName, const tstring& strMode)
{
m_pFile = fopen(CCnv::ConvertToString(strFileName).c_str(),
CCnv::ConvertToString(strMode).c_str());
return m_pFile!=NULL;
}
bool Close()
{
FILE* pFile = m_pFile;
m_pFile = NULL;
return pFile && fclose(pFile)==0;
}
bool Seek(long lOffset, T_enOrigin enOrigin)
{
return m_pFile && fseek(m_pFile, lOffset, enOrigin)==0;
}
long Tell()
{
if( !m_pFile )
return -1L;
return ftell(m_pFile);
}
size_t Write(const void* pBuffer, size_t itemSize, size_t itemCount)
{
if( !m_pFile )
return 0;
return fwrite(pBuffer, itemSize, itemCount, m_pFile);
}
size_t Read(void* pBuffer, size_t itemSize, size_t itemCount)
{
if( !m_pFile )
return 0;
return fread(pBuffer, itemSize, itemCount, m_pFile);
}
virtual void OnBytesReceived(const TByteVector& vBuffer, long lReceivedBytes)
{
Write(&(*vBuffer.begin()), sizeof(TByteVector::value_type), lReceivedBytes);
}
virtual void OnPreBytesSend(TByteVector& vBuffer, size_t& bytesToSend)
{
bytesToSend = Read(&(*vBuffer.begin()), sizeof(TByteVector::value_type), vBuffer.size());
}
};
class COutputStream : public ITransferNotification
{
const tstring mc_strEolCharacterSequence;
tstring m_vBuffer;
tstring::iterator m_itCurrentPos;
public:
COutputStream(const tstring& strEolCharacterSequence) :
mc_strEolCharacterSequence(strEolCharacterSequence),
m_itCurrentPos(m_vBuffer.end()) {}
void SetStartPosition()
{
m_itCurrentPos = m_vBuffer.begin();
}
bool GetNextLine(tstring& strLine)// const
{
tstring::iterator it = std::search(m_itCurrentPos, m_vBuffer.end(), mc_strEolCharacterSequence.begin(), mc_strEolCharacterSequence.end());
if( it == m_vBuffer.end() )
return false;
strLine.assign(m_itCurrentPos, it);
m_itCurrentPos = it + mc_strEolCharacterSequence.size();
return true;
}
virtual void OnBytesReceived(const TByteVector& vBuffer, long lReceivedBytes)
{
std::copy(vBuffer.begin(), vBuffer.begin()+lReceivedBytes, std::back_inserter(m_vBuffer));
}
};
};
using namespace nsFTP;
//////////////////////////////////////////////////////////////////////
// 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
/// da
- 1
- 2
前往页