Jump to content

C++ FTP class for Windows


Info
 Share

Recommended Posts

#ifndef FTP_H
#define FTP_H

#include <string>
#include <vector>
#include <wininet.h> //you will also need to link to wininet.lib

enum FTP_SIZE_TYPE { FTP_SIZE_BYTE, FTP_SIZE_KB, FTP_SIZE_MB, FTP_SIZE_GB };

class ftp
{
public:
    ftp(const std::string& agentName="ftp");
    ~ftp();

    bool Connect(const std::string& host, const std::string& username="", const std::string& password="", INTERNET_PORT port=INTERNET_DEFAULT_FTP_PORT, bool passive=false);
    bool Disconnect();

    bool GetConnectionState() const;
    bool List(std::vector<WIN32_FIND_DATA>& vec, const std::string& rootDir="") const;
    bool DownloadFile(const std::string& remoteFile, const std::string& localFile, bool overwrite = false) const;
    bool UploadFile(const std::string& localFile, const std::string& remoteFile) const;
    DWORD GetFileSize(const std::string& remoteFile, FTP_SIZE_TYPE type=FTP_SIZE_BYTE) const;
    bool CreateDirectory(const std::string& remoteDir);
    bool DeleteFile(const std::string& remoteFile);
    bool DeleteDirectory(const std::string& remoteDir);
    bool RenameFile(const std::string& remoteFile, const std::string& newFile); //both parameters must containt full path

private:
    HINTERNET m_hInternet;
    HINTERNET m_hConnection;
    bool m_bConnected;

    ftp(const ftp& other); // copy ctor
    ftp& operator=(const ftp& other); // asignment operator
};

ftp::ftp(const std::string& agentName/*="ftp"*/) : m_bConnected(false)
{
    m_hInternet = InternetOpen(agentName.c_str() , INTERNET_OPEN_TYPE_DIRECT, 0, 0, 0);
}

ftp::~ftp()
{
    ftp::Disconnect();
    InternetCloseHandle(m_hConnection);
}

bool ftp::Connect(const std::string& host, const std::string& username/*=""*/, const std::string& password/*=""*/, INTERNET_PORT port/*=INTERNET_DEFAULT_FTP_PORT*/, bool passive/*=false*/)
{
    DWORD flag;
    if(passive)
        flag = INTERNET_FLAG_PASSIVE;
    else
        flag = 0;

    m_hConnection = InternetConnect(m_hInternet, host.c_str(), port, username.c_str(), password.c_str(), INTERNET_SERVICE_FTP, flag, 0);

    m_bConnected = (bool)m_hConnection;
    return m_bConnected;
}

bool ftp::Disconnect()
{
    BOOL bClose = InternetCloseHandle(m_hConnection);
    m_bConnected = (bool)!bClose;

    return bClose;
}

bool ftp::GetConnectionState() const
{
    return m_bConnected;
}

bool ftp::List(std::vector<WIN32_FIND_DATA>& vec, const std::string& rootDir/*=""*/) const
{
    if(!m_bConnected)
        return false;

    HINTERNET hFind;
    WIN32_FIND_DATA fileInfo;

    hFind = FtpFindFirstFile(m_hConnection, rootDir.c_str(), &fileInfo, 0, 0);
    if(hFind == NULL)
        return false;
    vec.push_back(fileInfo);

    while(InternetFindNextFile(hFind, &fileInfo) == TRUE)
    {
        vec.push_back(fileInfo);
    }

    InternetCloseHandle(hFind);

    return true;
}

bool ftp::DownloadFile(const std::string& remoteFile, const std::string& localFile, bool overwrite/*=false*/) const
{
    return (bool)FtpGetFile(m_hConnection, remoteFile.c_str(), localFile.c_str(), (BOOL)overwrite, FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_UNKNOWN, 0);
}

bool ftp::UploadFile(const std::string& localFile, const std::string& remoteFile) const
{
    return (bool)FtpPutFile(m_hConnection, localFile.c_str(), remoteFile.c_str(), FTP_TRANSFER_TYPE_UNKNOWN, 0);
}

DWORD ftp::GetFileSize(const std::string& remoteFile, FTP_SIZE_TYPE type/*=FTP_SIZE_BYTE*/) const
{
    if(!m_bConnected)
        return 0;

    HINTERNET hFile;
    DWORD size;

    hFile = FtpOpenFile(m_hConnection, remoteFile.c_str(), GENERIC_READ, FTP_TRANSFER_TYPE_UNKNOWN, 0);
    size = FtpGetFileSize(hFile, 0);

    InternetCloseHandle(hFile);

    switch(type)
    {
        case FTP_SIZE_KB:
            return size / 1024;

        case FTP_SIZE_MB:
            return size / (1024*1024);

        case FTP_SIZE_GB:
            return size / (1024*1024*1024);

        default:
            return size;
    }
}

bool ftp::CreateDirectory(const std::string& remoteDir)
{
    return (bool)FtpCreateDirectory(m_hConnection, remoteDir.c_str());
}

bool ftp::DeleteFile(const std::string& remoteFile)
{
    return (bool)FtpDeleteFile(m_hConnection, remoteFile.c_str());
}

bool ftp::DeleteDirectory(const std::string& remoteDir)
{
    return (bool)FtpRemoveDirectory(m_hConnection, remoteDir.c_str());
}

bool ftp::RenameFile(const std::string& remoteFile, const std::string& newFile)
{
    return (bool)FtpRenameFile(m_hConnection, remoteFile.c_str(), newFile.c_str());
}

#endif // FTP_H

The only thing I'm missing is creating a callback function inside the class that will help me update m_bConnected if anything happens to the connection with the server.

Any suggestions?

Link to comment
Share on other sites

I'm sure Valik wasn't keen on this either:

m_bConnected = (bool)m_hConnection

Didn't see it. Now that I do, yeah, that's just stupid. Then again all the casts are stupid so par for the course I suppose. Makes me glad we don't have people like that working on AutoIt (anymore).
Link to comment
Share on other sites

Could you explain why is that so stupid? Will it be better with static_cast?

Also, according to www.codeguru.com/forum/showthread.php?t=312456 (sorry, linking doesn't seem to work), all the C++ type casting operators are for classes, pointers to classes and references to classes.

So which way will be better for converting BOOL to bool?

Edited by Info
Link to comment
Share on other sites

Could you explain why is that so stupid? Will it be better with static_cast?

Also, according to www.codeguru.com/forum/showthread.php?t=312456 (sorry, linking doesn't seem to work), all the C++ type casting operators are for classes, pointers to classes and references to classes.

I suggest you read that page closer.

So which way will be better for converting BOOL to bool?

All of them are better than what you are doing. There are at least 3 ways to write those expressions (2 of which don't involve casts at all). If you can't think of at least one way then you need to stop what you are doing and go back to reading.
Link to comment
Share on other sites

  • Administrators

I used to use C casts all the time until Valik started being the cast police. We had a pretty hard time converting from x86 to x64 in the AutoIt source code and one of the reasons was casting was hiding some pretty horrific assumptions. There was also no way "search" for C casts as they are just () rather than a keyword, so we couldn't even search the problem areas of code. They also provide a bit of sanity checking where static_cast won't let you do really stupid things where it really doesn't make sense whereas the C style cast doesn't give a **** and will cast anything to anything regardless of if it will break everything.

For this

m_bConnected = (bool)m_hConnection

Traditionally I would have actually done it longhand (I prefer readability over cleverness myself, and clever versions would only likely produce the same assembly as the readable version anyhow)

if (m_hConnection)
  m_bConnected = true;
else
  m_bConnected = false;

But Valik got me into these types of statements and I can read them without panicing these days:

m_bConnected = m_hConnection ? true : false;

I think this would also work, but would be my worst nightmare to read:

m_bConnected = !!m_hConnection

Link to comment
Share on other sites

I don't get it. Doesn't this

if (m_hConnection)
  m_bConnected = true;
else
  m_bConnected = false;

and this

m_bConnected = m_hConnection ? true : false;

also force a BOOL to bool conversion? And what's so bad about C-style casting?

Link to comment
Share on other sites

I used to use C casts all the time until Valik started being the cast police

So fast forward to this morning when I see the COM bugfix commit in my email and there are C-style casts in it.

Traditionally I would have actually done it longhand (I prefer readability over cleverness myself, and clever versions would only likely produce the same assembly as the readable version anyhow)

if (m_hConnection)
  m_bConnected = true;
else
  m_bConnected = false;

But Valik got me into these types of statements and I can read them without panicing these days:

m_bConnected = m_hConnection ? true : false;

And by "got me into" he means "Valik wrote a shitload of them in AutoIt and I had to cope".

I think this would also work, but would be my worst nightmare to read:

m_bConnected = !!m_hConnection

Agreed. That just obfuscates what's going on. That being said I've seen that code in the wild.

I don't get it. Doesn't this

if (m_hConnection)
  m_bConnected = true;
else
  m_bConnected = false;

and this

m_bConnected = m_hConnection ? true : false;

also force a BOOL to bool conversion?

It doesn't force anything, it evaluates an expression and explicitly sets the results.

And what's so bad about C-style casting?

How wasn't Jon clear? Because you have no clue what the fuck a C style cast is really doing. It might just simply be a (relatively) harmless static cast or it might be a far more dangerous and probably just plain wrong reinterpret cast. A lot of times it's a const cast and const should only be cast away after careful study to make sure it really is safe to pass const data to a non-const function.. Explicit C++ casting forces you to think about and understand the code and how it works. C casts let you be lazy and a bad programmer who doesn't understand parts of their code.
Link to comment
Share on other sites

  • Administrators

So fast forward to this morning when I see the COM bugfix commit in my email and there are C-style casts in it.

lol, that would have been trancexx and I must have missed it when I added it :huh2:

Edit: Actually I can't see any new ones (apart from a const_cast I'm not sure about) I think they are leftovers but look like new because the function changed so much.

Edited by Jon
Link to comment
Share on other sites

lol, that would have been trancexx and I must have missed it when I added it :huh2:

Edit: Actually I can't see any new ones (apart from a const_cast I'm not sure about) I think they are leftovers but look like new because the function changed so much.

Nah, I knew they weren't new. But they still weren't fixed.

Link to comment
Share on other sites

According to this page: http://www.codeguru.com/forum/showthread.php?t=332831, functions that return BOOL can sometimes return a -1 for error, so take that into account too. You can't just check for true or false.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...