Introduction
CFTPClient
is a class to encapsulate the FTP protocol. I have tried to implement it as platform independent. For the purpose of communication, I have used the classes CBlockingSocket
, CSockAddr
, ... from David J. Kruglinski's "Inside Visual C++". These classes are only small wrappers for the sockets-API. Further, I have 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 different FTP LIST responses is taken from D. J. Bernstein's (parsing code). 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 and run smoothly.
The main features are:
- not based on MFC-sockets,
- not using other MFC-stuffs like
CString
(uses STL),
- supports Firewalls,
- supports resuming,
- supports file eXchange Protocol (FXP) - uses FTP to transfer data directly from one remote server to another (servers must support this feature),
- testet under Windows with Visual Studio 2008,
- testet under Linux (Suse 11.4) with Qt,
- smart pointer implementation can be easily replaced with boost::shared_ptr or std::shared_ptr by defining USE_BOOST_SMART_PTR or USE_STD_SMART_PTR,
- parser which parses the output of the LIST command can be replaced by implementing the interface "IFileListParser",
- can be easily extended.
The example shows how easy it is to use this class. With a few lines of code you can log the communication or visualize file transfers. Notice: The example is not a fully functional FTP-client-application. The example application is only for Windows platforms.
Background
The official specification of the File Transfer Protocol (FTP) is the RFC 959. Most of the documentation in my code are taken from this RFC.
Using the code
There are a lot of classes. But most of them are just simple "datatypes". The most important ones are the following:
CFTPClient
The heart of the application. It accepts a CLogonInfo
object. Handles the complete communication with the FTP-server like:
- get directory listing,
- download/upload files,
- delete directories/files,
- walk through directory-tree,
- passive mode,
- ...
CLogonInfo
A simple data structure for logon information, such as host, username, password, firewall, ...CFTPClient::IFileListParser
Interface for defining a parser class which can be set in the CFTPClient
class for parsing the output of the LIST command.
CFTPClient::ITransferNotification
Implementations of this interface can be used in the Download and Upload methods for controlling the byte streams which are be downloaded/uploaded. This can be used for example to download a file only into memory instead of a local file (see class COutputStream
).
CFTPClient::CNotification
The base class for notification mechanism. The class which derives from CFTPClient::CNotifaction
can be attached to the CFTPClient
class as an observer. The CFTPClient
object notifies all the attached observers about the various actions (see example application):
void TestFTP()
{
nsFTP::CFTPClient ftpClient;
nsFTP::CLogonInfo logonInfo(_T("localhost"), 21, _T("anonymous"),
_T("<a href="mailto:anonymous@user.com">anonymous@user.com"));
ftpClient.Login(logonInfo);
nsFTP::TFTPFileStatusShPtrVec list;
ftpClient.List(_T("/"), list);
for( nsFTP::TFTPFileStatusShPtrVec::iterator it=list.begin();
it!=list.end(); ++it )
TRACE(_T("\n%s"), (*it)->Name().c_str());
ftpClient.DownloadFile(_T("/pub/test.txt"), _T("c:\\temp\\test.txt"));
ftpClient.UploadFile(_T("c:\\temp\\test.txt"), _T("/upload/test.txt"));
ftpClient.Rename(_T("/upload/test.txt"), _T("/upload/NewName.txt"));
ftpClient.Delete(_T("/upload/NewName.txt"));
ftpClient.Logout();
}
void TestFXP()
{
nsFTP::CFTPClient ftpClientSource;
nsFTP::CLogonInfo logonInfoSource(_T("sourceftpserver"), 21, _T("anonymous"),
_T("<a href="mailto:anonymous@user.com">anonymous@user.com"));
nsFTP::CFTPClient ftpClientTarget;
nsFTP::CLogonInfo logonInfoTarget(_T("targetftpserver"), 21, _T("anonymous"),
_T("<a href="mailto:anonymous@user.com">anonymous@user.com"));
ftpClientSource.Login(logonInfoSource);
ftpClientTarget.Login(logonInfoTarget);
nsFTP::CFTPClient::TransferFile(ftpClientSource, _T("/file.txt"),
ftpClientTarget, _T("/newFile.txt"));
ftpClientTarget.Logout();
ftpClientSource.Logout();
}
void TestDownloadAsciiFileIntoTextBuffer()
{
nsFTP::CFTPClient ftpClientSource;
nsFTP::CLogonInfo logonInfoSource(_T("sourceftpserver"), 21, _T("anonymous"),
_T("<a href="mailto:anonymous@user.com">anonymous@user.com</a>"));
ftpClientSource.Login(logonInfoSource);
nsFTP::COutputStream outputStream(_T("\r\n"), _T("Example"));
ftpClientSource.DownloadFile(_T("/file.txt"), outputStream,
nsFTP::CRepresentation(nsFTP::CType::ASCII()));
tstring output = outputStream.GetBuffer();
ftpClientSource.Logout();
}
History
- 2004-10-25 - First public release.
- 2005-12-04 - Version 1.1
- Some interfaces changed (e.g.
CNotification
).
- Bug in
OpenPassiveDataConnection
removed: SendCommand
was called before data connection was established.
- Bugs in
GetSingleResponseLine
removed:
- Infinite loop if the 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 another 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 a specific scenario of a FTP communication).
- Replaced the magic numbers concerning the reply codes with a class.
- New example added. A console application created with Bloodshed Dev-C++. It is only a small application which should demonstrate the use of the classes in a non Microsoft environment.
- 2012-12-02 - Version 2.0
- fixed some bugs
- introduced more "data types" for more secure interfaces
- code modified so it also runs under Linux
- support for file eXchange Protocol (FXP)
What will be done next
- Example application with Linux GNU-C++.
- New features for FTP client class (for example: copy and delete recursively).
- Unit tests.