Programmatically uploading a file to an FTP site


Today's Little Program uploads a file to an FTP site in binary mode with the assistance of the Wininet library. This program has sat in my bag of tools for years.

#define STRICT
#define UNICODE
#include <windows.h>
#include <wininet.h>
#include <shellapi.h>

int __cdecl wmain(int argc, PWSTR argv[])
{
 if (argc == 6) {
  HINTERNET hintRoot = InternetOpen(TEXT("ftpput/1.0"),
            INTERNET_OPEN_TYPE_DIRECT,
            NULL, NULL, 0);
  if (hintRoot) {
   HINTERNET hintFtp = InternetConnect(hintRoot,
            argv[1],
            INTERNET_DEFAULT_FTP_PORT,
            argv[2],
            argv[3],
            INTERNET_SERVICE_FTP,
            INTERNET_FLAG_PASSIVE,
            NULL);
   if (hintFtp) {
    FtpPutFile(hintFtp, argv[4], argv[5],
         FTP_TRANSFER_TYPE_BINARY,
         NULL);

    InternetCloseHandle(hintFtp);
   }

   InternetCloseHandle(hintRoot);
  }
 }

 return 0;
}

The program accepts five command line arguments:

  1. site (no "ftp://" in front)
  2. userid
  3. password
  4. path for the file to upload
  5. location to place the uploaded file

For example, I might say ftpput ftp.contoso.com admin seinfeld newversion.zip subdir/newversion.zip

Comments (24)
  1. ajb says:

    Something isn't working.  I tried to run your program, but it told me that 'seinfeld' is not the correct password.  Can you help me figure out the problem?

  2. Anon says:

    Can't tell if Poe's Law…

    [Hint: In order to get the "invalid password" error, you first must have successfully connected to the site. -Raymond]
  3. Mike Caron says:

    @ajb The password is actually '<a href="…">seinfeld</a>'

  4. Rich S. says:

    @ajb – Raymond of course has obfuscated the password. I believe the actual Secret Code for seinfeld is BOSCO.

  5. Rahul says:

    Wait Wait , when can we access to the entire bag of tools you speak of :-)

  6. Joshua says:

    @Rahul: A truly wise man does not publish all his tools; how otherwise to appear to be a miracle worker.

  7. Ken in NH says:

    @Rahul

    Their all on the referenced FTP site if you can figure out how to get past @ajb's password issue.

  8. David Crowell says:

    I bought <u>Essential WinInet</u> years ago.  I'd forgotten about it until now.

  9. Maurits says:

    Um… you do know that http://ftp.exe accepts a script argument, right?

  10. Mordachai says:

    Obligatory "but http://FTP.EXE sucks" – it cannot do passive mode.  Makes it useless behind many firewalls.

    WinSCP working great. :)

  11. Joshua says:

    @Steve Wolf: If they fix IPv6 we'll soon stop caring about passive.

  12. Karellen says:

    @ajb, Mike, Rich: I think it's actually hunter2

  13. Antonio &#39;Grijan&#39; says:

    @Karellen: Nice reference to bash.org :-) .

  14. Joker_vD says:

    @Joshua: And by "they" you mean?.. I am actually interested in who's in charge of making people to shift to IPv6 — because it still isn't happening yet. Tons of new code is being written with explicit use of sockaddr_in.

  15. 12BitSlab says:

    We use IPV6 quite a bit at work.  I am guessing that 80% or better of the traffic on the inside of our network is IPV6.  There are some good products out there that ease the trasition a lot.  I won't mention names since this is not the right forum to promote products.

  16. Yuri Khan says:

    Alternatives for those without access to Raymond’s bag of tools:

    * Generate a script and feed it to http://ftp.exe.

    * Have in your own toolbag another program that can programmatically upload to ftp. My personal go-to tool for such tasks is lftp.

  17. Cheong says:

    I believe everyone that has experience on network related programming would have their implementation of FTP service/client, using libraries or not, sooner or later.

    Even when .NET has FTP client support natively, I still found a lot of time wasting time on my own rewritten client doing non-trival tasks.

  18. Cesar says:

    Ugh, wininet.

    Isn't wininet the library which had a "Unicode" function that always returned an error, while the corresponding "ANSI" function worked as expected? (A quick web search told me that the function in question was "InternetReadFileExW").

    I believe the current recommendation was to use winhttp instead, even though it was documented as a "server" variant.

  19. Joker_vD says:

    @cheong00: I personally use curl. For something open-source, it's surprisingly easy to build yourself on an actual OS — you can even do without Cygwin!

  20. Marc K says:

    I've never been comfortable with passing a password over a command line.  If you needed to upload a large file, the password would be sitting accessible by a tool like Process Explorer for the entire time.  We actually log all command lines on some of our systems for analysis purposes.  Just because someone is an admin of those systems doesn't mean he is an admin of everything.

  21. Anon says:

    @Marc K

    If they're an admin on the machine, you've already lost. They could log keystrokes, and the password is sitting in memory regardless (How do you think the characters were transmitted?)

  22. ErikF says:

    If you're batching a command and need to specify a password, it needs to go somewhere! That's the most common reason why I've seen that particular feature used, anyways (and the file permissions on the batch file are set so that most people only have execute permissions, not read.)

  23. GovindParmar says:

    I like the way programs like RunAs are designed; it's intentional that the password is not an argument, that way people won't write batch files that have passwords them.

  24. Joker_vD says:

    @GovindParmar: Still, people will go ridiculous lengths to break security, so they write

    rem thankfuly, it doesn't work…

    echo.password1|runas /user:admin launch_missiles.bat 1 2 3

    Of course, runas is smart enough to disregard stdin and read straight from the CONIN$. That's why people write helper tools like sshpass that use WriteConsoleInput to bypass *that* additional security:

    rem …but this one actually works

    sshpass -p password1 "%comspec%" /K runas /user:admin launch_missiles.bat 1 2 3

    I have also heard of drivers that auto-click UAC confirmation dialogs and/or type in admin credentials for non-privilleged users automatically, though I haven't actually seen them.

Comments are closed.