/* The original version of this program can be found at http://damb.dk */
#include <iostream>
#include <windows.h>
#include <wininet.h>
#include <string>
#include <sstream>
#include <fstream>
// --- This section could be put into some header file.
// A handy helper function
template <typename T>
std::string ToString(T aValue)
{
std::stringstream ss;
ss << aValue;
return ss.str();
}
// Any error is reported by throw'ing a FtpExceptionClass exception
class FtpExceptionClass
{
public:
FtpExceptionClass(const char *aExc);
const std::string &What() const { return Message; }
private:
std::string Message;
};
class FtpInternetConnectionClass;
// This class is a file object, it's created with OpenFile in the connection
class FtpFileClass
{
public:
~FtpFileClass();
void Write(const char *aStr);
bool Read(std::string &aStr); // Read a string until '\n'
private:
FtpFileClass(); // Not to be implemented
friend class FtpConnectionClass;
// Only FtpConnectionClass can create me
FtpFileClass(FtpConnectionClass *aConnection, const char *aFileName, DWORD aAccess, DWORD aFlags);
HINTERNET HFile;
FtpConnectionClass *Connection;
};
// A directory listing class, created by calling CreateDirList in the connection
class FtpDirListClass
{
public:
~FtpDirListClass();
std::string GetFileName() { return FindData.cFileName; }
bool Next() { return InternetFindNextFile(Handle, &FindData); }
private:
friend class FtpConnectionClass;
FtpDirListClass(); // Not to be implemented
// Only FtpConnectionClass can create me
FtpDirListClass(FtpConnectionClass *aConnection, const char *aPath);
HINTERNET Handle;
WIN32_FIND_DATA FindData;
FtpConnectionClass *Connection;
};
// The Connection create one to have a ftp connection.
class FtpConnectionClass
{
public:
FtpConnectionClass() : HConnection(0), Busy(false)
{}
// Call Connect to open a connection.
bool Connect(const char *aServerName, const char *aUserName = 0, const char *aPassWord = 0);
enum FileAccessType
{
FileAccessRead = 1,
FileAccessWrite = 2
};
enum FileModeType
{
FileModeBinary = 4,
FileModeAcsii = 8
};
// Note: Only one operation can be pending at a time, due to limitations in the ftp protocol,
// if you need more than one operation at a time, create another connection.
// Call OpenFile to open a file
FtpFileClass *OpenFile(const char *aFileName, FileAccessType aFileAccess, FileModeType aFileMode);
// Call CreateDirList to be able to get a dir list.
FtpDirListClass *CreateDirList(const char *aFile);
// Get the current directory on the ftp server
std::string GetCurrentDirectory();
// Change dir on the ftp server
void SetCurrentDirectory(const char *aDir);
// Get a file from the ftp server
void GetFile(const char *aFileName);
// Transfer a file to the ftp server
void PutFile(const char *aFileName);
private:
HINTERNET GetHandle() { return HConnection; }
friend class FtpFileClass;
friend class FtpDirListClass;
void Done() { Busy = false; }
HINTERNET HConnection;
bool Busy;
static HINTERNET HInternet;
};
// --- A test application
int main()
{
try
{
FtpConnectionClass FtpConnection; // The Connection
FtpConnection.Connect("ftp.microsoft.com", 0, 0); // And connect
// List all files on the server
FtpDirListClass *DirList = FtpConnection.CreateDirList("*");
do
{
std::cout << DirList->GetFileName() << std::endl;
}
while(DirList->Next());
delete DirList;
// Create a file to write
FtpFileClass *File = FtpConnection.OpenFile("abc.txt", FtpConnectionClass::FileAccessWrite, FtpConnectionClass::FileModeBinary);
// Write a string to it
File->Write("Hello Worldrn");
// Close the file again
delete File;
// Now create a file to read
File = FtpConnection.OpenFile("abc.txt", FtpConnectionClass::FileAccessRead, FtpConnectionClass::FileModeBinary);
std::string S;
File->Read(S);
std::cout << "Read: " << S << std::endl;
delete File;
// Get the file we just wrote
FtpConnection.GetFile("abc.txt");
// Create a local file
std::ofstream of("abc2.txt");
of << "Some text" << std::endl;
of.close();
// And upload it to the ftp server
FtpConnection.PutFile("abc2.txt");
// Get and set directory
std::cout << "Current Dir: " << FtpConnection.GetCurrentDirectory() << std::endl;
FtpConnection.SetCurrentDirectory("new");
std::cout << "Current Dir: " << FtpConnection.GetCurrentDirectory() << std::endl;
}
catch (FtpExceptionClass &FtpException)
{ // In case something went wrong
std::cerr << FtpException.What() << std::endl;
}
}
// --- The rest is implementation of the ftp class'es, should be put into some .cpp file
HINTERNET FtpConnectionClass::HInternet;
FtpFileClass::FtpFileClass(FtpConnectionClass *aConnection, const char *aFileName, DWORD aAccess, DWORD aFlags)
{
Connection = aConnection;
HFile = FtpOpenFile(Connection->GetHandle(), aFileName, aAccess, aFlags, 0);
if(!HFile)
{
throw FtpExceptionClass("Failed to open file");
}
}
FtpFileClass::~FtpFileClass()
{
InternetCloseHandle(HFile);
Connection->Done();
}
FtpDirListClass::~FtpDirListClass()
{
InternetCloseHandle(Handle);
Connection->Done();
}
FtpDirListClass::FtpDirListClass(FtpConnectionClass *aConnection, const char *aPath)
{
Connection = aConnection;
Handle = FtpFindFirstFile(Connection->GetHandle(), aPath, &FindData, INTERNET_FLAG_RELOAD, 0);
if(!Handle)
{
throw FtpExceptionClass("Failed to start dir list");
}
}
void FtpFileClass::Write(const char *aStr)
{
DWORD Dummy;
if(!InternetWriteFile(HFile, aStr, strlen(aStr), &Dummy))
{
throw FtpExceptionClass("Failed to write");
}
}
bool FtpFileClass::Read(std::string &aStr)
{
aStr.clear();
char Data[2];
Data[1] = 0;
DWORD Dummy;
while(InternetReadFile(HFile, Data, 1, &Dummy) && Dummy)
{
aStr += Data;
if(Data[0] == '\n')
return true;
}
return false;
}
FtpExceptionClass::FtpExceptionClass(const char *aExc)
{
char *MsgBuf = 0;
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
0,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(char *)&MsgBuf, 0, 0);
Message = "Ftp exception: ";
Message += aExc;
Message += "n";
if(MsgBuf)
{
Message += MsgBuf;
LocalFree(MsgBuf);
}
else
{
Message += "Unknown Error: ";
Message += ToString(GetLastError());
}
}
bool FtpConnectionClass::Connect(const char *aServerName, const char *aUserName, const char *aPassWord)
{
if(!HInternet)
{
HInternet = InternetOpen("MyApp", INTERNET_OPEN_TYPE_PROXY, 0, 0, 0);
if(!HInternet)
{
throw(FtpExceptionClass("Unable to open internet"));
}
}
if(HConnection)
{
InternetCloseHandle(HConnection);
}
HConnection = InternetConnect(HInternet, aServerName, INTERNET_DEFAULT_FTP_PORT, aUserName, aPassWord, INTERNET_SERVICE_FTP, 0, 1);
if(!HConnection)
{
throw FtpExceptionClass("Failed to open Connection");
}
return true;
}
FtpFileClass *FtpConnectionClass:: OpenFile(const char *aFileName, FileAccessType aFileAccess, FileModeType aFileMode)
{
if(!HConnection)
{
throw FtpExceptionClass("Connection not opened");
}
if(Busy)
{
throw FtpExceptionClass("Only one action at a time");
}
FtpFileClass *FtpFile = new FtpFileClass(this,
aFileName,
aFileAccess == FileAccessRead ? GENERIC_READ : GENERIC_WRITE,
aFileMode == FileModeBinary ? FTP_TRANSFER_TYPE_BINARY : FTP_TRANSFER_TYPE_ASCII);
Busy = true;
return FtpFile;
}
FtpDirListClass *FtpConnectionClass::CreateDirList(const char *aFile)
{
if(!HConnection)
{
throw FtpExceptionClass("Connection not opened");
}
if(Busy)
{
throw FtpExceptionClass("Only one action at a time");
}
FtpDirListClass *FtpDirList = new FtpDirListClass(this, aFile);
Busy = true;
return FtpDirList;
}
std::string FtpConnectionClass::GetCurrentDirectory()
{
std::string Path;
Path.resize(MAX_PATH + 1);
DWORD Size = MAX_PATH;
if(!FtpGetCurrentDirectory(HConnection, (char *)Path.c_str(), &Size))
{
throw FtpExceptionClass("Failed to get current dir");
}
return Path;
}
void FtpConnectionClass::SetCurrentDirectory(const char *aDir)
{
if(!FtpSetCurrentDirectory(HConnection, aDir))
{
throw FtpExceptionClass("Failed to set current dir");
}
}
void FtpConnectionClass::GetFile(const char *aFileName)
{
if(!FtpGetFile(HConnection, aFileName, aFileName, false, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_BINARY, 0))
{
throw FtpExceptionClass("Failed to get file");
}
}
void FtpConnectionClass::PutFile(const char *aFileName)
{
if(!FtpPutFile(HConnection, aFileName, aFileName, FTP_TRANSFER_TYPE_BINARY, 0))
{
throw FtpExceptionClass("Failed to put file");
}
}
// --- EOF