FTP

From Rosetta Code
Task
FTP
You are encouraged to solve this task according to the task description, using any language you may know.
Task

Connect to a server, change directory, list its contents and download a file as binary using the FTP protocol. Use passive mode if available.

BASIC

BaCon

Using libCURL.

OPTION PARSE FALSE

PRAGMA INCLUDE <curl/curl.h>
PRAGMA LDFLAGS -lcurl

DECLARE easyhandle TYPE CURL*

OPEN "data.txt" FOR WRITING AS download

easyhandle = curl_easy_init()
curl_easy_setopt(easyhandle, CURLOPT_URL, "ftp://localhost/pub/data.txt")
curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, download)
curl_easy_setopt(easyhandle, CURLOPT_USERPWD, "anonymous")
success = curl_easy_perform(easyhandle)
curl_easy_cleanup(easyhandle)

CLOSE FILE download

Full native implementation without dependency to external libraries.

FUNCTION interact$(command$, connection, use_pasv)

    LOCAL pasv$, data$, response$
    LOCAL port, passive

    IF use_pasv THEN
        SEND "PASV" & NL$ TO connection
        RECEIVE data$ FROM connection
        pasv$ = INBETWEEN$(data$, "(", ")")
        port = VAL(TOKEN$(pasv$, 5, ","))*256 + VAL(TOKEN$(pasv$, 6, ","))
        OPEN "localhost:" & STR$(port) FOR NETWORK AS passive
    ENDIF

    IF LEN(command$) THEN SEND command$ & NL$ TO connection

    WHILE WAIT(connection, 50)
        RECEIVE data$ FROM connection
        IF LEN(data$) = 0 THEN BREAK
        response$ = response$ & data$
    WEND

    IF use_pasv THEN
        WHILE WAIT(passive, 50)
            RECEIVE data$ FROM passive
            IF LEN(data$) = 0 THEN BREAK
            response$ = response$ & data$
        WEND
        CLOSE NETWORK passive
    ENDIF

    RETURN response$

ENDFUNC

OPEN "localhost:21" FOR NETWORK AS ftp

PRINT interact$("", ftp, 0)
PRINT interact$("USER anonymous", ftp, 0)
PRINT interact$("PASS ", ftp, 0)
PRINT interact$("CWD pub", ftp, 0)
PRINT interact$("LIST", ftp, 1)
PRINT interact$("TYPE I", ftp, 0)
PRINT interact$("RETR data.txt", ftp, 1)
PRINT interact$("QUIT", ftp, 0)

CLOSE NETWORK ftp


FreeBASIC

Original code programmed by PaulSquires [1]

Library: clsFTP
Works with: Windows

clsFTP.bas

''
''
''   FTP class (Public Domain code - enjoy). 
''   Windows only. Uses WinInet system functions.
''   Paul Squires of PlanetSquires Software (August 2015)
''
''   Compiler:  FreeBASIC 1.03 (32-bit) (64-bit) 
''

#Include Once "windows.bi"
#Include Once "\win\wininet.bi"

' //
' //
' //
Type clsFTP
    Private:
    m_hSession    As HINTERNET
    m_hConnection As HINTERNET
    m_LastError   As Integer 
    m_ServerPort  As Integer
    m_ServerName  As String
    m_UserName    As String
    m_Password    As String
    
    Public:                   
    Declare Constructor
    Declare Destructor
    Declare Property hSession() As HINTERNET
    Declare Property hSession(Byval nValue As HINTERNET) 
    Declare Property hConnection() As HINTERNET
    Declare Property hConnection(Byval nValue As HINTERNET)
    Declare Property LastError() As Integer
    Declare Property ServerPort(Byval nValue As Integer) 
    Declare Property ServerPort() As Integer
    Declare Property LastError(Byval nValue As Integer) 
    Declare Property ServerName() As String
    Declare Property ServerName(Byval sValue As String) 
    Declare Property UserName() As String
    Declare Property UserName(Byval sValue As String) 
    Declare Property Password() As String
    Declare Property Password(Byval sValue As String) 
    Declare Function Connect Overload() As WINBOOL
    Declare Function Connect Overload(Byval sServerName As String, _
    Byval sServerPort As Integer, _
    Byval sUserName As String, _
    Byval sPassword As String) As WINBOOL
    Declare Sub      Disconnect() 
    Declare Function SetCurrentFolder(Byval sFolderName As String) As WINBOOL
    Declare Function GetCurrentFolder() As String
    Declare Function RenameFile(Byval sOldFilename As String, _
    Byval sNewFilename As String) As WINBOOL
    Declare Function UploadFile(Byval sLocal As String, _
    Byval sRemote As String) As WINBOOL
    Declare Function DownloadFile(Byval sLocal As String, _
    Byval sRemote As String) As WINBOOL
    Declare Function KillFile(Byval sRemote As String) As WINBOOL
End Type


''
''  Initialize the class
''
Constructor clsFTP
    m_ServerPort = INTERNET_DEFAULT_FTP_PORT   ' port 21
End Constructor


''
''  Close any open connection and session
''
Destructor clsFTP
    this.Disconnect
End Destructor


''
''  hSession (Property)
'' 
Property clsFTP.hSession() As HINTERNET
    Property = this.m_hSession
End Property

Property clsFTp.hSession(Byval nValue As HINTERNET) 
    this.m_hSession = nValue
End Property


'' 
''  hConnection (Property)
'' 
Property clsFTP.hConnection() As HINTERNET
    Property = this.m_hConnection
End Property

Property clsFTp.hConnection(Byval nValue As HINTERNET)
    this.m_hConnection = nValue
End Property


'' 
''  LastError (Property)
'' 
Property clsFTP.LastError() As Integer
    Property = this.m_LastError
End Property

Property clsFTp.LastError(Byval nValue As Integer) 
    this.m_LastError = nValue
End Property


'' 
''  ServerPort (Property)
'' 
Property clsFTP.ServerPort() As Integer
    Property = this.m_ServerPort
End Property

Property clsFTp.ServerPort(Byval nValue As Integer) 
    this.m_ServerPort = nValue
End Property


''
''  ServerName (Property)
'' 
Property clsFTP.ServerName() As String
    Property = this.m_ServerName
End Property

Property clsFTp.ServerName(Byval sValue As String) 
    this.m_ServerName = sValue
End Property


''
''  UserName (Property)
'' 
Property clsFTP.UserName() As String
    Property = this.m_UserName
End Property

Property clsFTp.UserName(Byval sValue As String) 
this.m_UserName = sValue
End Property


''
''  Password (Property)
'' 
Property clsFTP.Password() As String
    Property = this.m_Password
End Property

Property clsFTp.Password(Byval sValue As String) 
    this.m_Password = sValue
End Property


''
''  Close current connection and end session
''
Sub clsFTP.Disconnect()
    InternetCloseHandle this.hConnection
    InternetCloseHandle this.hSession
    this.hConnection = 0: this.hSession = 0
End Sub


''
''  Connect to an ftp host (overload). Returns TRUE if successful.
''
Function clsFTP.Connect Overload() As WINBOOL
    this.hSession = InternetOpen("ftpClass", INTERNET_OPEN_TYPE_DIRECT, "", "", 0)
    If this.hSession = 0 Then
        this.LastError = GetLastError  
        Function = False: Exit Function
    End If
    
    this.hConnection = InternetConnect(_
    this.hSession, this.ServerName, this.ServerPort, _
    this.UserName, this.Password, _
    INTERNET_SERVICE_FTP, INTERNET_FLAG_PASSIVE, 0)
    
    If this.hConnection = 0 Then
        this.LastError = GetLastError
        InternetCloseHandle(this.hSession)
        Function = False: Exit Function
    End If
    
    Function = True
End Function


''
''  Connect to an ftp host (overload). Returns TRUE if successful.
''
Function clsFTP.Connect Overload (Byval sServerName As String, _
    Byval nServerPort As Integer, _
    Byval sUserName   As String, _
    Byval sPassword   As String) As WINBOOL
    
    this.ServerName = sServerName
    this.ServerPort = nServerPort
    this.UserName   = sUserName
    this.Password   = sPassword
    
    Function = this.Connect
End Function


''
''  Change to a folder on the server. Returns TRUE if successful.
''
Function clsFTP.SetCurrentFolder(Byval sFolderName As String) As WINBOOL
    Function = FtpSetCurrentDirectory(this.hConnection, sFolderName)
    this.LastError = GetLastError
End Function


''
''  Retrieves the name of current folder on the server. 
''
Function clsFTP.GetCurrentFolder() As String
    Dim zBuffer As ZString * MAX_PATH
    Dim nLength As Integer = MAX_PATH
    
    FtpGetCurrentDirectory(this.hConnection, zBuffer, Cast(LPDWORD, @nLength))
    this.LastError = GetLastError                              
    
    Function = zBuffer
End Function


''
''  Rename a file on the server. Returns TRUE if successful. 
''
Function clsFTP.RenameFile(Byval sOldFilename As String, Byval sNewFilename As String) As WINBOOL
    Function = FtpRenameFile(this.hConnection, sOldFilename, sNewFilename)
    this.LastError = GetLastError
End Function


''
''  Upload a file to the server. Returns TRUE if successful. 
''
Function clsFTP.UploadFile(Byval sLocal As String, Byval sRemote As String) As WINBOOL
    Function = FtpPutFile(this.hConnection, sLocal, sRemote, FTP_TRANSFER_TYPE_BINARY, 0) 
    this.LastError = GetLastError
End Function


''
''  Download a file from the server. Returns TRUE if successful. 
''
Function clsFTP.DownloadFile(Byval sLocal As String, Byval sRemote As String) As WINBOOL
    Function = FtpGetFile(this.hConnection, sRemote, sLocal, False, _
    FILE_ATTRIBUTE_NORMAL, FTP_TRANSFER_TYPE_BINARY Or INTERNET_FLAG_RELOAD, 0)
    this.LastError = GetLastError
End Function


''
''  Remove a file from the server. Returns TRUE if successful. 
''
Function clsFTP.KillFile(Byval sRemote As String) As WINBOOL
    Function = FtpDeleteFile(this.hConnection, sRemote) 
    this.LastError = GetLastError
End Function

simple_test.bas

#Include Once "windows.bi"
#Include Once "clsFTP.bas"

Dim ftp As clsFTP

If ftp.Connect("ftp.ed.ac.uk", 21, "anonymous", "aaa@gmail.com") = False Then
    Print "Error connecting to server. LastError = "; ftp.LastError
End If   

If ftp.SetCurrentFolder("pub/courses") = False Then
    Print "Error setting current folder. LastError = "; ftp.LastError
End If   

Print "Current folder = "; ftp.GetCurrentFolder()

If ftp.DownloadFile("make.notes.tar", "make.notes.tar") = False Then
    Print "Error downloading file. LastError = "; ftp.LastError
End If

ftp.Disconnect

Print "Done."

Sleep

FutureBasic

FB for Mac easily interfaces with the terminal command line. NOTE: The curl command line tool used in this example offers upload and sending capabilities. It supports FTP, FTPS, HTTP, HTTPS, SCP, SFTP, TFTP, TELNET, DICT, LDAP, LDAPS, FILE, POP3, IMAP, SMTP, RTMP and RTSP.

include "NSLog.incl"

#plist NSAppTransportSecurity @{NSAllowsArbitraryLoads:YES}

local fn RunTerminalCommand( cmd as CFStringRef ) as CFStringRef
  CFStringRef outputStr = NULL
  
  TaskRef task = fn TaskInit
  TaskSetExecutableURL( task, fn URLFileURLWithPath( @"/bin/zsh" ) )
  CFStringRef cmdStr = fn StringWithFormat( @"%@", cmd )
  CFArrayRef args = fn ArrayWithObjects( @"-c", cmdStr, NULL )
  TaskSetArguments( task, args )
  
  PipeRef p = fn PipeInit
  TaskSetStandardOutput( task, p )
  TaskSetStandardError( task, p )
  FileHandleRef fh = fn PipeFileHandleForReading( p )
  
  fn TaskLaunch( task, NULL )
  TaskWaitUntilExit( task )
  
  ErrorRef err
  CFDataRef dta = fn FileHandleReadDataToEndOfFile( fh, @err )
  if err then NSLog( @"%@", fn ErrorLocalizedDescription( err ) ) : exit fn
  outputStr = fn StringWithData( dta, NSUTF8StringEncoding )
end fn = outputStr

NSLog( @"%@", fn RunTerminalCommand( @"curl ftp://ftp.slackware.com/welcome.msg" ) )

HandleEvents
Output:
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100   754  100   754    0     0   1363      0 --:--:-- --:--:-- --:--:--  1361

---------------------------------------------------------------------------
                      R S Y N C . O S U O S L . O R G
                          Oregon State University
                              Open Source Lab

       Unauthorized use is prohibited - violators will be prosecuted
---------------------------------------------------------------------------

                For more information about the OSL visit:
                    http://osuosl.org/services/hosting

          This host is the home to the primary archives of several
           projects.  We would prefer that only primary/secondary
                    mirrors use this service.  Thanks!

---------------------------------------------------------------------------

Go

Using the FTP package from github.com/stacktic/ftp.

package main

import (
	"fmt"
	"io"
	"log"
	"os"

	"github.com/stacktic/ftp"
)

func main() {
	// Hard-coded demonstration values
	const (
		hostport = "localhost:21"
		username = "anonymous"
		password = "anonymous"
		dir      = "pub"
		file     = "somefile.bin"
	)

	conn, err := ftp.Connect(hostport)
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Quit()
	fmt.Println(conn)

	if err = conn.Login(username, password); err != nil {
		log.Fatal(err)
	}
	if err = conn.ChangeDir(dir); err != nil {
		log.Fatal(err)
	}
	fmt.Println(conn.CurrentDir())
	files, err := conn.List(".")
	if err != nil {
		log.Fatal(err)
	}
	for _, f := range files {
		fmt.Printf("%v %12d %v %v\n", f.Time, f.Size, f.Type, f.Name)
	}

	r, err := conn.Retr(file)
	if err != nil {
		log.Fatal(err)
	}
	defer r.Close()

	f, err := os.Create(file)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()

	n, err := io.Copy(f, r)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Wrote", n, "bytes to", file)
}

Batch File

This uses the native FTP.EXE in Windows. I am not sure, but I think FTP.EXE client does not support passive mode.

::Playing with FTP
::Batch File Implementation

@echo off

set site="ftp.hq.nasa.gov"
set user="anonymous"
set pass="ftptest@example.com"
set dir="pub/issoutreach/Living in Space Stories (MP3 Files)"
set download="Gravity in the Brain.mp3"

(
	echo.open %site%
	echo.user %user% %pass%
	echo.dir
	echo.!echo.
	echo.!echo.This is a just a text to seperate two directory listings. 
	echo.!echo.
	echo.cd %dir%
	echo.dir
	echo.binary
	echo.get %download%
	echo.disconnect
)|ftp -n
Output:
\Desktop>RCFTP
-rw-r--r--   1 ftpadmin ftp-adm      3997 May 26  1998 README
drwxrwx-wx   6 lgipson  armd       696320 Jan 23  2015 armd
drwxrwx-wx   2 chmgt    ftp-adm      4096 Aug 18 16:17 chmgt
-r-xr-xr-x   1 root     root        18120 Nov 28  2001 ftp-exec
drwxrws-wx   2 ftpadmin ftp-adm     57344 Aug 18 13:08 incoming
-rw-rw-r--   1 ftpadmin ftp-adm       133 Jan 29  1996 index.html
drwx------   2 root     root         4096 Apr 11  2003 lost+found
drwxr-sr-x   2 ftpadmin ftp-adm      4096 Apr 14  1998 office
drwxrwsr-x  17 ftpadmin ftp-adm      4096 Nov  4  2013 pub
-rw-r--r--   1 root     ftp-adm        26 Jan 27  2011 robots.txt

This is a just a text to seperate two directory listings.

-rw-rw-r--   1 109      space-station  2327118 May  9  2005 09sept_spacepropulsion.mp3
-rw-rw-r--   1 109      space-station  1260304 May  9  2005 Can People go to Mars.mp3
-rw-rw-r--   1 109      space-station  1350270 May  9  2005 Distill some water.mp3
-rw-rw-r--   1 109      space-station  1290888 May  9  2005 Good Vibrations.mp3
-rw-rw-r--   1 109      space-station  1431834 May  9  2005 Gravity Hurts_So good.mp3
-rw-rw-r--   1 109      space-station  1072644 May  9  2005 Gravity in the Brain.mp3
-rw-rw-r--   1 109      space-station  1230594 May  9  2005 Power to the ISS.mp3
-rw-rw-r--   1 109      space-station  1309062 May  9  2005 Space Bones.mp3
-rw-rw-r--   1 109      space-station  2292715 May  9  2005 Space Power.mp3
-rw-rw-r--   1 109      space-station   772075 May  9  2005 We have a solution.mp3
-rw-rw-r--   1 109      space-station  1134654 May  9  2005 When Space Makes you Dizzy.mp3

\Desktop>

C

Using ftplib

#include <ftplib.h>

int main(void)
{
    netbuf *nbuf;

    FtpInit();
    FtpConnect("kernel.org", &nbuf);
    FtpLogin("anonymous", "", nbuf);
    FtpOptions(FTPLIB_CONNMODE, FTPLIB_PASSIVE, nbuf);
    FtpChdir("pub/linux/kernel", nbuf);
    FtpDir((void*)0, ".", nbuf);
    FtpGet("ftp.README", "README", FTPLIB_ASCII, nbuf);
    FtpQuit(nbuf);

    return 0;
}

C++

Works with: c++ version 11

Using ftplib, libftp++

 /* client.cpp   
   
  libftp++ C++ classes for ftplib C ftp library
   
  compile: 
      clang++ -std=c++11 -o client client.cpp -lftp++ 
*/

#include <iostream>
#include <string>
#include <cstring>
#include <fstream>
#include <sys/stat.h>   // stat 
#include <ftplib.h>     // libftp 
#include <ftp++.hpp>    // libftp++ 


/* C++ classes:
   
   Connection        main ftp connection       (user class)

   ConnectionBase    base class for Connection (internal)
   
   DataConnection    connection type,          (internal)
                     read bytes from server,
                     write bytes to  server
		     
  ConnectionException  custom exception       (thrown by Connection)

 */


/** ftp::Connection class API 
    
data members:
    A Connection instance contains only a pointer as a data member
    
    {
      protected:
         netbuf * conn;   // pointer to ftplib netbuf struct
    }  

member functions:

 connect:

   Connection(const char * host);     // constructor

   void setConnectionMode(Mode mode); // passive or port

   void login(const char * user, const char * password); // Log in  

 info:

   const char * getLastResponse();    // last server response 

   std::string getSystemType();   // server type
    
   std::string getDirectory();  // remote pwd 
    
   void getList(const char * filename, const char * path);  // short dir list

   unsigned size(const char * path, TransferMode mode); 
    
 file transfer:
    
    void get(const char * local, const char * remote, TransferMode mode); 

    void put(const char * local, const char * remote, TransferMode mode);  

**/

// libc functions 
int stat(const char *pathname, struct stat *buf); // local file info
char *strerror(int errnum);  // explanation of error
char *basename(char *path);  // filename at end of path


// STL  classes and functions used
namespace stl
{
  using std::cout;           // stdout
  using std::cerr;           // stderr
  using std::string;         // string  
  using std::ifstream;       // files on disk
  using std::remove;         // delete file on disk
};

using namespace stl;


// connection modes
using Mode = ftp::Connection::Mode;
Mode PASV  = Mode::PASSIVE;
Mode PORT  = Mode::PORT;

//file transfer modes
using TransferMode  = ftp::Connection::TransferMode;
TransferMode BINARY = TransferMode::BINARY;
TransferMode TEXT   = TransferMode::TEXT;


/* ********************** */ 
// ftp session parameters
struct session
{
  const string server;  // server name
  const string port;    // usually 21
  const string user;    // username
  const string pass;    // password
  Mode  mode;           // PASV, PORT 
  TransferMode txmode;  // BINARY, TEXT
  string dir;           // current or default dir
};

/* ********************** */ 
// local helper functions 

ftp::Connection connect_ftp( const session& sess);
size_t get_ftp( ftp::Connection& conn, string const& path);
string readFile( const string& filename);
string login_ftp(ftp::Connection& conn, const session& sess);
string dir_listing( ftp::Connection& conn, const string& path);


/* ******************************** */ 
// Read a file into one long string

string readFile( const string& filename)
{
  struct stat stat_buf;  
  string contents;
  
  errno = 0;
  if (stat(filename.c_str() , &stat_buf) != -1) // file stats
    {  
      size_t len = stat_buf.st_size;            // size of file
  
      string bytes(len+1, '\0');                // string big enough
      
      ifstream ifs(filename); // open for input

      ifs.read(&bytes[0], len);  // read all bytes as chars into string

      if (! ifs.fail() ) contents.swap(bytes);  // swap into contents

      ifs.close();
   }
  else
    {
      cerr << "stat error: " << strerror(errno);
    }

  return contents;     
}

/* *************** */ 
// start a session

ftp::Connection connect_ftp( const session& sess)
  try
    {
      string constr = sess.server + ":" + sess.port;
      
      cerr << "connecting to " << constr << " ...\n";

      ftp::Connection conn{ constr.c_str() };
      
      cerr << "connected to " << constr << "\n";
      conn.setConnectionMode(sess.mode);

      return conn; 
   }
  catch (ftp::ConnectException e) 
    {
      cerr << "FTP error: could not connect to server" << "\n";
    }

/* ***** */  
// login

string login_ftp(ftp::Connection& conn, const session& sess)
{
  conn.login(sess.user.c_str() , sess.pass.c_str() );

  return conn.getLastResponse();
}

/* ***************************** */ 
// get remote directory listing

    //   ftplib writes to file for dir contents
    //   convert file contents to string

string dir_listing( ftp::Connection& conn, const string& path)
try
  {
      // file on disk to write to
      const char* dirdata = "/dev/shm/dirdata";
      
      conn.getList(dirdata, path.c_str() ); 
      // conn.getFullList(dirdata, path.c_str() ); 
      // conn.getFullList(NULL, path.c_str() ); // to stdout 
      
      string dir_string = readFile(dirdata);

      cerr << conn.getLastResponse() << "\n";
      
      errno = 0;
      if ( remove(dirdata) != 0 ) // delete file on disk
      	{
	  cerr << "error: " <<  strerror(errno) << "\n";
      	}
      
      return dir_string;
  }
 catch (...) {
    cerr << "error: getting dir contents: \n" 
	 << strerror(errno) << "\n";
  }

/* ************************* */ 
// retrieve file from server

size_t get_ftp( ftp::Connection& conn, const string& r_path)
{
  size_t received = 0;

  const char* path = r_path.c_str();

  unsigned remotefile_size = conn.size(path , BINARY);
 
  const char* localfile = basename(path);
  
  conn.get(localfile, path, BINARY);  // get file

  cerr << conn.getLastResponse() << "\n";

  // get local file size 
  struct stat stat_buf;

  errno = 0;
  if (stat(localfile, &stat_buf) != -1)
     received = stat_buf.st_size;   
  else
    cerr << strerror(errno);

  return received;
}

/* ******************** */ 
// default test session
const session sonic
{ 
    "mirrors.sonic.net", 
    "21" ,
    "anonymous", 
    "xxxx@nohost.org",
    PASV, 
    BINARY,
    "/pub/OpenBSD" 
    };

/* **** */
// main 

int main(int argc, char* argv[], char * env[] )
{
  const session remote = sonic;  // copy session info 

  try
    {
           
      // open an ftp connection
      ftp::Connection conn = connect_ftp(remote);

      // login with username and passwd
      cerr << login_ftp(conn, remote);

      // what type system
      cout << "System type: " << conn.getSystemType() << "\n";
      cerr << conn.getLastResponse() << "\n";

      // cd to default session dir
      conn.cd(remote.dir.c_str());  // change to dir on server 
      cerr << conn.getLastResponse() << "\n";

      // get current remote  directory
      string pwdstr = conn.getDirectory();
      cout << "PWD: " << pwdstr << "\n";
      cerr << conn.getLastResponse() << "\n";


      // get file listing
      string dirlist = dir_listing(conn, pwdstr.c_str() );
      cout << dirlist << "\n";
      
      string filename = "ftplist";       // small text file

      auto pos = dirlist.find(filename); // find filename in dir list

      auto notfound = string::npos;   

      if (pos != notfound)  // found filename
	{
	  // get file
	  size_t received = get_ftp(conn, filename.c_str() );

	  if (received == 0) 
	    cerr << "got 0 bytes\n";
	  else
	    cerr << "got " << filename  
		 << " ("   << received << " bytes)\n";
	}
      else
	{
	  cerr << "file " << filename 
	       << "not found on server. \n"; 
	}
      
    }
    catch (ftp::ConnectException e) 
      {
        cerr << "FTP error: could not connect to server" << "\n";
      }
    catch (ftp::Exception e) 
      {
        cerr << "FTP error: " << e << "\n";
      }
    catch (...) 
      {
        cerr << "error: " <<  strerror(errno) << "\n";
      }

  // logout, connection closes automatically when conn destructs

  return 0;
}
/* END */
Output:
connecting to mirrors.sonic.net:21 ...
connected to mirrors.sonic.net:21
230 Anonymous access granted, restrictions apply
System type: UNIX
215 UNIX Type: L8

250 CWD command successful

PWD: /openbsd
257 "/openbsd" is the current directory

226 Transfer complete

/openbsd/.
/openbsd/..
/openbsd/Changelogs
/openbsd/LibreSSL
/openbsd/OpenBGPD
/openbsd/OpenNTPD
/openbsd/OpenSSH
/openbsd/doc
/openbsd/patches
/openbsd/snapshots
/openbsd/songs
/openbsd/syspatch
/openbsd/tools
/openbsd/ftplist
/openbsd/timestamp
/openbsd/rpki-client
/openbsd/6.7
/openbsd/6.8

226 Transfer complete

got ftplist (4992 bytes)


Common Lisp

Using package cl-ftp.

(use-package :ftp)

(with-ftp-connection (conn :hostname "ftp.hq.nasa.gov" 
                           :passive-ftp-p t)
  (send-cwd-command conn "/pub/issoutreach/Living in Space Stories (MP3 Files)")
  (send-list-command conn t)
  (let ((filename "Gravity in the Brain.mp3"))
    (retrieve-file conn filename filename :type :binary)))
Output:
-rw-rw-r--   1 109      space-station  2327118 May  9  2005 09sept_spacepropulsion.mp3
-rw-rw-r--   1 109      space-station  1260304 May  9  2005 Can People go to Mars.mp3
-rw-rw-r--   1 109      space-station  1350270 May  9  2005 Distill some water.mp3
-rw-rw-r--   1 109      space-station  1290888 May  9  2005 Good Vibrations.mp3
-rw-rw-r--   1 109      space-station  1431834 May  9  2005 Gravity Hurts_So good.mp3
-rw-rw-r--   1 109      space-station  1072644 May  9  2005 Gravity in the Brain.mp3
-rw-rw-r--   1 109      space-station  1230594 May  9  2005 Power to the ISS.mp3
-rw-rw-r--   1 109      space-station  1309062 May  9  2005 Space Bones.mp3
-rw-rw-r--   1 109      space-station  2292715 May  9  2005 Space Power.mp3
-rw-rw-r--   1 109      space-station   772075 May  9  2005 We have a solution.mp3
-rw-rw-r--   1 109      space-station  1134654 May  9  2005 When Space Makes you Dizzy.mp3

Erlang

Erlang implementation using ftp module.

%%%-------------------------------------------------------------------
%%% To execute in shell, Run the following commands:-
%%% >c("ftp_example").
%%% >ftp_example:run().
%%%-------------------------------------------------------------------
-module(ftp_example).

-export([run/0]).

run() ->
    Host = "ftp.easynet.fr",
    Opts = [{mode, passive}],
    User = "anonymous",
    Password = "",

    Directory = "/debian/",
    File = "README.html",
    
    %%% Open connection with FTP Server
    io:format("Opening connection with host ~p ~n", [Host]),
    {ok, Pid} = ftp:open(Host, Opts),
    
    %%% Login as Anonymous user
    io:format("Logging in as user ~p ~n", [User]),
    ftp:user(Pid, User, Password),
            
    %%% Change Directory to "/debian/"
    io:format("Changing Directory to  ~p ~n", [Directory]),
    ftp:cd(Pid, Directory),
    
    %%% Listing contents of current Directory
    io:format("Contents of Current Directory ~n"),
    {ok, Listing} = ftp:ls(Pid),
    io:format("~p ~n", [Listing]),

    %%% Download file "README.html"
    io:format("Downloading File ~p to current directory ~n", [File]),
    ftp:recv(Pid, File),
    
    %%% Close connection
    io:format("Closing connection to FTP Server"),
    ftp:close(Pid).

Groovy

This is the code from Ran488 GitHub, modified to be executable. It relies on external Apache FTP client. Dependencies are automatically loaded with the @Grab annotation. let's say the code is saved in the file ftpTest.groovy:

   
@Grab(group='commons-net', module='commons-net', version='2.0')
import org.apache.commons.net.ftp.FTPClient

println("About to connect....");
new FTPClient().with {
    connect "ftp.easynet.fr"
    enterLocalPassiveMode()
    login "anonymous", "ftptest@example.com"
    changeWorkingDirectory "/debian/"
    def incomingFile = new File("README.html")
    incomingFile.withOutputStream { ostream -> retrieveFile "README.html", ostream }
    disconnect()
}
println("                      ...Done.");

By typing groovy ftpTest.groovy, you should see a README.html file on your directory.

Debian Archive

See http://www.debian.org/ for information about Debian GNU/Linux.

Current Releases

Four Debian releases are available on the main site:

Debian 6.0.10, or squeeze
Debian 6.0.10 was released Saturday, 19th July 2014. Please note that the 6.0 distribution is no longer receiving security support. If you are using the amd64 or i386 architecture and not able to upgrade to the current stable release, you may wish to investigate the "squeeze-lts" distribution. Installation and upgrading instructions, More information
....
...

Haskell

Example uses ftphs package:

module Main (main) where

import           Control.Exception (bracket)
import           Control.Monad (void)
import           Data.Foldable (for_)
import           Network.FTP.Client
                    ( cwd
                    , easyConnectFTP
                    , getbinary
                    , loginAnon
                    , nlst
                    , quit
                    , setPassive
                    )

main :: IO ()
main = bracket ((flip setPassive) True <$> easyConnectFTP "ftp.kernel.org") quit $ \h -> do
    -- Log in anonymously
    void $ loginAnon h

    -- Change directory
    void $ cwd h "/pub/linux/kernel/Historic"

    -- List current directory
    fileNames <- nlst h Nothing
    for_ fileNames $ \fileName ->
        putStrLn fileName

    -- Download in binary mode
    (fileData, _) <- getbinary h "linux-0.01.tar.gz.sign"
    print fileData

J

   require 'web/gethttp'
   gethttp 'ftp://anonymous:example@ftp.hq.nasa.gov/pub/issoutreach/Living%20in%20Space%20Stories%20(MP3%20Files)/'
-rw-rw-r--   1 109      space-station  2327118 May  9  2005 09sept_spacepropulsion.mp3
-rw-rw-r--   1 109      space-station  1260304 May  9  2005 Can People go to Mars.mp3
-rw-rw-r--   1 109      space-station  1350270 May  9  2005 Distill some water.mp3
-rw-rw-r--   1 109      space-station  1290888 May  9  2005 Good Vibrations.mp3
-rw-rw-r--   1 109      space-station  1431834 May  9  2005 Gravity Hurts_So good.mp3
-rw-rw-r--   1 109      space-station  1072644 May  9  2005 Gravity in the Brain.mp3
-rw-rw-r--   1 109      space-station  1230594 May  9  2005 Power to the ISS.mp3
-rw-rw-r--   1 109      space-station  1309062 May  9  2005 Space Bones.mp3
-rw-rw-r--   1 109      space-station  2292715 May  9  2005 Space Power.mp3
-rw-rw-r--   1 109      space-station   772075 May  9  2005 We have a solution.mp3
-rw-rw-r--   1 109      space-station  1134654 May  9  2005 When Space Makes you Dizzy.mp3
   #file=: gethttp rplc&(' ';'%20') 'ftp://anonymous:example@ftp.hq.nasa.gov/pub/issoutreach/Living in Space Stories (MP3 Files)/We have a solution.mp3'
772075

Java

requires apache.commons.net

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

public class FTPconn {

    public static void main(String[] args) throws IOException {
        String server = "ftp.hq.nasa.gov";
        int port = 21;
        String user = "anonymous";
        String pass = "ftptest@example.com";

        OutputStream output = null;

        FTPClient ftpClient = new FTPClient();
        try {
            ftpClient.connect(server, port);

            serverReply(ftpClient);

            int replyCode = ftpClient.getReplyCode();
            if (!FTPReply.isPositiveCompletion(replyCode)) {
                System.out.println("Failure. Server reply code: " + replyCode);
                return;
            }

            serverReply(ftpClient);

            if (!ftpClient.login(user, pass)) {
                System.out.println("Could not login to the server.");
                return;
            }

            String dir = "pub/issoutreach/Living in Space Stories (MP3 Files)/";
            if (!ftpClient.changeWorkingDirectory(dir)) {
                System.out.println("Change directory failed.");
                return;
            }

            ftpClient.enterLocalPassiveMode();

            for (FTPFile file : ftpClient.listFiles())
                System.out.println(file);

            String filename = "Can People go to Mars.mp3";
            output = new FileOutputStream(filename);

            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            if (!ftpClient.retrieveFile(filename, output)) {
                System.out.println("Retrieving file failed");
                return;
            }

            serverReply(ftpClient);

            ftpClient.logout();

        } finally {
            if (output != null)
                output.close();
        }
    }

    private static void serverReply(FTPClient ftpClient) {
        for (String reply : ftpClient.getReplyStrings()) {
            System.out.println(reply);
        }
    }
}

Output:

220-Warning: This system is owned and operated by the US Federal Government.
          Unauthorized access to this system is a violation of US Federal
          law and could lead to prosecution.
 
 This is NASA HQ ANONYMOUS FTP SERVER.
 
 Please read the README file located in the initial server root directory.
 
 IF you place files into the /incoming directory, it is IMPERATIVE that you
 notify ftp-admin@hq.nasa.gov that you have done so and of your intended
 disposition of those files.  Absent such notification, all files placed
 in /incoming that cannot be identified will be immediately deleted.
 
220 FTP Server Ready
220-Warning: This system is owned and operated by the US Federal Government.
          Unauthorized access to this system is a violation of US Federal
          law and could lead to prosecution.
 
 This is NASA HQ ANONYMOUS FTP SERVER.
 
 Please read the README file located in the initial server root directory.
 
 IF you place files into the /incoming directory, it is IMPERATIVE that you
 notify ftp-admin@hq.nasa.gov that you have done so and of your intended
 disposition of those files.  Absent such notification, all files placed
 in /incoming that cannot be identified will be immediately deleted.
 
220 FTP Server Ready
-rw-rw-r--   1 109      space-station  2327118 May  9  2005 09sept_spacepropulsion.mp3
-rw-rw-r--   1 109      space-station  1260304 May  9  2005 Can People go to Mars.mp3
-rw-rw-r--   1 109      space-station  1350270 May  9  2005 Distill some water.mp3
-rw-rw-r--   1 109      space-station  1290888 May  9  2005 Good Vibrations.mp3
-rw-rw-r--   1 109      space-station  1431834 May  9  2005 Gravity Hurts_So good.mp3
-rw-rw-r--   1 109      space-station  1072644 May  9  2005 Gravity in the Brain.mp3
-rw-rw-r--   1 109      space-station  1230594 May  9  2005 Power to the ISS.mp3
-rw-rw-r--   1 109      space-station  1309062 May  9  2005 Space Bones.mp3
-rw-rw-r--   1 109      space-station  2292715 May  9  2005 Space Power.mp3
-rw-rw-r--   1 109      space-station   772075 May  9  2005 We have a solution.mp3
-rw-rw-r--   1 109      space-station  1134654 May  9  2005 When Space Makes you Dizzy.mp3
226 Transfer complete

Julia

Works with: Julia version 0.6
using FTPClient

ftp = FTP(hostname = "ftp.ed.ac.uk", username = "anonymous")
cd(ftp, "pub/courses")
println(readdir(ftp))
bytes = read(download(ftp, "make.notes.tar"))

close(ftp)

Kotlin

Translation of: C
Library: ftplib
Works with: Ubuntu 14.04

Assuming that ftplib is already installed on your system in the default location(s), you first need to build ftplib.klib using the following .def file and the cinterop tool. As the NetBuf struct is not defined in ftplib.h (but is in ftplib.c) it has been included in the .def file so that the tool can deal with it (and its alias 'netbuf') properly from a Kotlin perspective.

headers = /usr/include/ftplib.h
linkerOpts.linux = -L/usr/lib -lftp

---

#include <sys/time.h>

struct NetBuf {
    char *cput,*cget;
    int handle;
    int cavail,cleft;
    char *buf;
    int dir;
    netbuf *ctrl;
    netbuf *data;    
    int cmode;
    struct timeval idletime;
    FtpCallback idlecb;
    void *idlearg;
    int xfered;
    int cbbytes;
    int xfered1;
    char response[256];
};

Next, you need to compile the following Kotlin program, linking against ftplib.klib.

// Kotlin Native v0.6

import kotlinx.cinterop.*
import ftplib.*

fun main(args: Array<String>) {
    val nbuf = nativeHeap.allocPointerTo<netbuf>()
    FtpInit()
    FtpConnect("ftp.easynet.fr", nbuf.ptr)
    val vnbuf = nbuf.value
    FtpLogin("anonymous", "ftptest@example.com", vnbuf)
    FtpOptions(FTPLIB_CONNMODE, FTPLIB_PASSIVE.toLong(), vnbuf)
    FtpChdir("/debian/", vnbuf)
    FtpDir(null, ".", vnbuf)
    FtpGet("ftp.README", "README.html", FTPLIB_ASCII.toByte(), vnbuf)
    FtpQuit(vnbuf)
    nativeHeap.free(nbuf)
}

Finally, the resulting .kexe file should be executed producing something similar to the following output:

drwxr-xr-x  23 1002     1002         4096 Dec  9 09:44 dists
drwxr-xr-x   4 1002     1002         4096 Mar  3 19:52 doc
-rw-r--r--   1 1002     1002       361654 Mar  3 20:49 extrafiles
drwxr-xr-x   3 1002     1002         4096 Mar  3 20:42 indices
-rw-r--r--   1 1002     1002     14948661 Mar  3 20:42 ls-lR.gz
drwxr-xr-x   5 1002     1002         4096 Dec 19  2000 pool
drwxr-xr-x   4 1002     1002         4096 Nov 17  2008 project
-rw-r--r--   1 1002     1002         1186 Dec  9 09:42 README
-rw-r--r--   1 1002     1002         1290 Jun 26  2010 README.CD-manufacture
-rw-r--r--   1 1002     1002         2903 Dec  9 09:42 README.html
-rw-r--r--   1 1002     1002          291 Mar  4  2017 README.mirrors.html
-rw-r--r--   1 1002     1002           86 Mar  4  2017 README.mirrors.txt
drwxr-xr-x   3 1002     1002         4096 Oct 10  2012 tools
drwxr-xr-x  23 1002     1002         4096 Jun 17  2017 zzz-dists

Lingo

Library: Curl Xtra
CURLOPT_URL = 10002
ch = xtra("Curl").new()
url = "ftp://domain.com"

-- change to remote dir "/foo/bar/"
put "/foo/bar/" after url

ch.setOption(CURLOPT_URL, url)
res = ch.exec(1)

-- print raw FTP listing as string
put res.readRawString(res.length)

-- download file "download.mp3" (passive mode is the internal default behavior)
filename = "download.mp3"
ch.setOption(CURLOPT_URL, url & filename)
ch.setDestinationFile(_movie.path & filename)
res = ch.exec()

LiveCode

libURLSetFTPMode "passive"  --default is passive anyway
put url "ftp://ftp.hq.nasa.gov/" into listing
repeat for each line ftpln in listing
    set itemdel to space
    if the first char of (the first item of ftpln) is "d" then
        -- is a directory
        put the last item of ftpln after dirlist
    else
        put the last item of ftpln after filelist
    end if
end repeat

put listing //(subset)
//  -rw-r--r--   1 ftpadmin ftp-adm      3997 May 26  1998 README
//  drwxrwsr-x  17 ftpadmin ftp-adm      4096 Sep 10 16:08 pub

put dirlist
//  armd 
//  chmgt
//  incoming
//  lost+found
//  office
//  pub

put filelist
//  README
//  ftp-exec
//  index.html
//  robots.txt

-- downloading a file (upload is same, but use put)
-- you don't have to cd manually
-- file up/down transfer is binary in livecode (always enforced by livecode)
put URL  "ftp://ftp.hq.nasa.gov/pub/robots.txt" into URL "file:myFile.txt"

You can execute any ftp command using the libURLftpCommand command
e.g. to know the working directory, issue "pwd", we could issue "list" for above too, 
but using an url with slash on the end with the ftp protocol causes a dir listing by default.
put libURLftpCommand("PWD",ftp.example.org)

Nim

Works with: Nim version 1.4
import asyncdispatch, asyncftpclient

const
  Host = "speedtest.tele2.net"
  Upload = "upload"
  File = "1KB.zip"

proc main {.async.} =

  # Create session and connect.
  let ftp = newAsyncFtpClient(Host, user = "anonymous", pass = "anything")
  await ftp.connect()
  echo "Connected."
  echo await ftp.send("PASV")   # Switch to passive mode.

  # Change directory and list its contents.
  await ftp.cd(Upload)
  echo "Changed to directory: ", Upload
  echo "Contents of directory: ", Upload
  for file in await ftp.listDirs():
    echo "  ", file

  # Download a file.
  await ftp.cd("/")
  echo "Returned to root directory."
  await ftp.retrFile(file = File, dest = File)
  echo "Downloaded file: ", File
  echo await ftp.send("QUIT")   # Disconnect.

waitFor main()
Output:
Connected.
227 Entering Passive Mode (90,130,70,73,94,108).
Changed to directory: upload
Contents of directory: upload
  1_2758858854070946631_17-9ULspeedtest.upt
  2MB.zip
  2_2758858854070946631_17-9ULspeedtest.upt
  3_2758858854070946631_17-9ULspeedtest.upt
  DCS-932LB1-WEBCAM-12021010316232001.jpg
  DCS-932LB1-WEBCAM-12021010316292601.jpg
  DCS-932LB1-WEBCAM-12021010316314601.jpg
  speedtest_uplink_1.1G.zip
  upload_file.txt
  
Returned to root directory.
Downloaded file: 1KB.zip
221 Goodbye.

Perl

use Net::FTP;

# set server and credentials
my $host     = 'speedtest.tele2.net';
my $user     = 'anonymous';
my $password = '';

# connect in passive mode
my $f = Net::FTP->new($host) or die "Can't open $host\n";
$f->login($user, $password)  or die "Can't login as $user\n";
$f->passive();

# change remote directory, list contents
$f->cwd('upload');
@files = $f->ls();
printf "Currently %d files in the 'upload' directory.\n", @files;

# download file in binary mode
$f->cwd('/');
$f->type('binary');
$local = $f->get('512KB.zip');
print "Your file was stored as $local in the current directory\n";
Output:
Currently 20 files in the 'upload' directory
Your file was stored as 512KB.zip in the current directory!

Phix

Library: Phix/libcurl
without js -- libcurl, allocate, file i/o
include libcurl.e
constant url = "ftp://speedtest.tele2.net/"
 
curl_global_init()
atom curl = curl_easy_init(),
     pErrorBuffer = allocate(CURL_ERROR_SIZE)
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, pErrorBuffer)
curl_easy_setopt(curl, CURLOPT_URL, url)
object res = curl_easy_perform_ex(curl)
if integer(res) then
    ?{res,peek_string(pErrorBuffer)}
else
    puts(1,res)
end if
 
string filename = "1KB.zip"
{} = delete_file(filename)
res = curl_easy_get_file(url&filename, "", filename) 
if res=CURLE_OK then
    printf(1,"successfully downloaded %s (size %s)\n",{filename,get_file_size(filename,true)})
else
    ?{"error",res}
end if
Output:
-rw-r--r--    1 0        0        1073741824000 Feb 19  2016 1000GB.zip
-rw-r--r--    1 0        0        107374182400 Feb 19  2016 100GB.zip
-rw-r--r--    1 0        0          102400 Feb 19  2016 100KB.zip
-rw-r--r--    1 0        0        104857600 Feb 19  2016 100MB.zip
-rw-r--r--    1 0        0        10737418240 Feb 19  2016 10GB.zip
-rw-r--r--    1 0        0        10485760 Feb 19  2016 10MB.zip
-rw-r--r--    1 0        0        1073741824 Feb 19  2016 1GB.zip
-rw-r--r--    1 0        0            1024 Feb 19  2016 1KB.zip
-rw-r--r--    1 0        0         1048576 Feb 19  2016 1MB.zip
-rw-r--r--    1 0        0        209715200 Feb 19  2016 200MB.zip
-rw-r--r--    1 0        0        20971520 Feb 19  2016 20MB.zip
-rw-r--r--    1 0        0         2097152 Feb 19  2016 2MB.zip
-rw-r--r--    1 0        0         3145728 Feb 19  2016 3MB.zip
-rw-r--r--    1 0        0        524288000 Feb 19  2016 500MB.zip
-rw-r--r--    1 0        0        52428800 Feb 19  2016 50MB.zip
-rw-r--r--    1 0        0          524288 Feb 19  2016 512KB.zip
-rw-r--r--    1 0        0         5242880 Feb 19  2016 5MB.zip
drwxr-xr-x    2 105      108        561152 Jul 18 13:11 upload
successfully downloaded 1KB.zip (size 1KB)

PHP

$server = "speedtest.tele2.net";
$user = "anonymous";
$pass = "ftptest@example.com";

$conn = ftp_connect($server);
if (!$conn) {
    die('unable to connect to: '. $server);
}
$login = ftp_login($conn, $user, $pass);
if (!$login) {
    echo 'unable to log in to '. $server. ' with user: '.$user.' and pass: '. $pass;
} else{
    echo 'connected successfully'.PHP_EOL;
    $directory = ftp_nlist($conn,'');
    print_r($directory);
}
if (ftp_get($conn, '1KB.zip', '1KB.zip', FTP_BINARY)) {
    echo "Successfully downloaded file".PHP_EOL;
} else {
    echo "failed to download file";
}
Output:
connected successfully
Array
(
    [0] => 1000GB.zip
    [1] => 100GB.zip
    [2] => 100KB.zip
    [3] => 100MB.zip
    [4] => 10GB.zip
    [5] => 10MB.zip
    [6] => 1GB.zip
    [7] => 1KB.zip
    [8] => 1MB.zip
    [9] => 200MB.zip
    [10] => 20MB.zip
    [11] => 2MB.zip
    [12] => 3MB.zip
    [13] => 500MB.zip
    [14] => 50MB.zip
    [15] => 512KB.zip
    [16] => 5MB.zip
    [17] => upload
)
Successfully downloaded file
Done.

PicoLisp

Passive is the default behavior of 'curl'

(in '(curl "-sl" "ftp://kernel.org/pub/site/")
   (while (line)
      (prinl @) ) )
(call "curl" "-s" "-o" "sha256sums.asc" "ftp://kernel.org/pub/site/sha256sums.asc")

Output:

README
sample_mirror_script.pl
sha256sums.asc

Python

Works with: Python version 2.7.10
from ftplib import FTP
ftp = FTP('kernel.org')
ftp.login()
ftp.cwd('/pub/linux/kernel')
ftp.set_pasv(True) # Default since Python 2.1
print ftp.retrlines('LIST')
print ftp.retrbinary('RETR README', open('README', 'wb').write)
ftp.quit()

Racket

Note: net/ftp in Racket uses passive mode exclusively.

#lang racket
(require net/ftp)
(let* ([server "kernel.org"]
       [remote-dir "/pub/linux/kernel/"]
       [conn (ftp-establish-connection
               server
               21
               "anonymous"
               "")])
  (ftp-cd conn remote-dir)
  (map
   (lambda (elem) (displayln (string-join elem "\t")))
   (ftp-directory-list conn "."))
  (ftp-download-file conn "." "README")
  (ftp-close-connection conn))

Raku

(formerly Perl 6)

Works with: rakudo version 2018.04
use Net::FTP;

my $host = 'speedtest.tele2.net';
my $user = 'anonymous';
my $password = '';

my $ftp = Net::FTP.new( host => $host, :passive );

$ftp.login( user => $user, pass => $password );

$ftp.cwd( 'upload' );

$ftp.cwd( '/' );

say $_<name> for $ftp.ls;

$ftp.get( '1KB.zip', :binary );
Output:
1000GB.zip
100GB.zip
100KB.zip
100MB.zip
10GB.zip
10MB.zip
1GB.zip
1KB.zip
1MB.zip
200MB.zip
20MB.zip
2MB.zip
3MB.zip
500MB.zip
50MB.zip
512KB.zip
5MB.zip
upload

REBOL

system/schemes/ftp/passive: on
print read ftp://kernel.org/pub/linux/kernel/
write/binary %README read/binary ftp://kernel.org/pub/linux/kernel/README

Ruby

require 'net/ftp'

Net::FTP.open('ftp.ed.ac.uk', "anonymous","aaa@gmail.com" ) do |ftp|
  ftp.passive = true  # default since Ruby 2.3
  ftp.chdir('pub/courses')
  puts ftp.list
  ftp.getbinaryfile("make.notes.tar")
end

The connection is closed automatically at the end of the block.

Rust

Using crate ftp version 3.0.1

use std::{error::Error, fs::File, io::copy};
use ftp::FtpStream;

fn main() -> Result<(), Box<dyn Error>> {
    let mut ftp = FtpStream::connect("ftp.easynet.fr:21")?;
    ftp.login("anonymous", "")?;
    ftp.cwd("debian")?;
    for file in ftp.list(None)? {
        println!("{}", file);
    }
    let mut stream = ftp.get("README")?;
    let mut file = File::create("README")?;
    copy(&mut stream, &mut file)?;
    Ok(())
}

Scala

Library: commons-net
import java.io.{File, FileOutputStream, InputStream}

import org.apache.commons.net.ftp.{FTPClient, FTPFile, FTPReply}

import scala.util.{Failure, Try}

object FTPconn extends App {
  val (server, pass) = ("ftp.ed.ac.uk", "-ftptest@example.com")
  val (dir, filename, ftpClient) = ("/pub/cartonet/", "readme.txt", new FTPClient())

  def canConnect(host: String): Boolean = {
    ftpClient.connect(host)
    val connectionWasEstablished = ftpClient.isConnected
    ftpClient.disconnect()
    connectionWasEstablished
  }

  def downloadFileStream(remote: String): InputStream = {
    val stream: InputStream = ftpClient.retrieveFileStream(remote)
    ftpClient.completePendingCommand()
    stream
  }

  def uploadFile(remote: String, input: InputStream): Boolean = ftpClient.storeFile(remote, input)

  if (Try {
    def cwd(path: String): Boolean = ftpClient.changeWorkingDirectory(path)

    def filesInCurrentDirectory: Seq[String] = listFiles().map(_.getName)

    def listFiles(): List[FTPFile] = ftpClient.listFiles.toList

    def downloadFile(remote: String): Boolean = {
      val os = new FileOutputStream(new File(remote))
      ftpClient.retrieveFile(remote, os)
    }

    def connectWithAuth(host: String,
                        password: String,
                        username: String = "anonymous",
                        port: Int = 21): Try[Boolean] = {
      def connect(): Try[Unit] = Try {
        try {
          ftpClient.connect(host, port)
        } catch {
          case ex: Throwable =>
            println(ex.getMessage)
            Failure
        }
        ftpClient.enterLocalPassiveMode()
        serverReply(ftpClient)

        val replyCode = ftpClient.getReplyCode
        if (!FTPReply.isPositiveCompletion(replyCode))
          println("Failure. Server reply code: " + replyCode)
      }

      for {
        connection <- connect()
        login <- Try {
          ftpClient.login(username, password)
        }
      } yield login
    }

    def serverReply(ftpClient: FTPClient): Unit =
      for (reply <- ftpClient.getReplyStrings) println(reply)

    connectWithAuth(server, pass)

    cwd(dir)
    listFiles().foreach(println)

    downloadFile(filename)
    serverReply(ftpClient)
    ftpClient.logout
  }.isFailure) println(s"Failure.")
}
Output:
See it in running in your browser by Scastie (JVM).

Seed7

The library ftp.s7i contains functions to open and handle an ftpFileSys.

$ include "seed7_05.s7i";
  include "ftp.s7i";

const proc: main is func
  local
    var ftpFileSys: ftp is fileSys.value;
    var string: line is "";
  begin
    ftp := openFtp("kernel.org");
    setActiveMode(ftp, FALSE);  # Passive is the default.
    chdir(ftp, "/pub/linux/kernel");
    for line range listDir(ftp, ".") do
      writeln(line);
    end for;
    setAsciiTransfer(ftp, FALSE);
    writeln(getFile(ftp, "README"));
    close(ftp);
  end func;

Sidef

Translation of: Ruby
require('Net::FTP');

var ftp = %s'Net::FTP'.new('ftp.ed.ac.uk', Passive => 1);
ftp.login('anonymous','aaa@gmail.com');
ftp.cwd('pub/courses');
[ftp.dir].each {|line| say line };
ftp.binary;   # set binary mode
ftp.get("make.notes.tar");
ftp.quit;

Tcl

Using package ftp

package require ftp

set conn [::ftp::Open kernel.org anonymous "" -mode passive]
::ftp::Cd $conn /pub/linux/kernel
foreach line [ftp::NList $conn] {
    puts $line
}
::ftp::Type $conn binary
::ftp::Get $conn README README

Using a virtual file system

An alternative approach that uses the package TclVFS to access ftp:// paths as a virtual file system.

package require vfs::urltype
vfs::urltype::Mount ftp

# Patch to enable FTP passive mode.
source vfsftpfix.tcl

set dir [pwd]
cd ftp://kernel.org/pub/linux/kernel
foreach line [glob -dir ftp://kernel.org/pub/linux/kernel *] {
    puts $line
}
file copy README [file join $dir README]

The file vfsftpfix.tcl with the passive mode patch (see http://wiki.tcl.tk/12837):

# Replace vfs::ftp::Mount to enable vfs::ftp to work in passive
# mode and make that the default.
package require vfs::ftp
proc vfs::ftp::Mount {dirurl local {mode passive}} {
    set dirurl [string trim $dirurl]
    ::vfs::log "ftp-vfs: attempt to mount $dirurl at $local"
    if {[string index $dirurl end] != "/"} {
        ::vfs::log "ftp-vfs: adding missing directory delimiter to mount point"
        append dirurl "/"
    }

    set urlRE {(?:ftp://)?(?:([^@:]*)(?::([^@]*))?@)?([^/:]+)(?::([0-9]*))?/(.*/)?$}
    if {![regexp $urlRE $dirurl - user pass host port path]} {
        return -code error "Sorry I didn't understand\
          the url address \"$dirurl\""
    }

    if {![string length $user]} {
        set user anonymous
    }

    if {![string length $port]} {
        set port 21
    }

    set fd [::ftp::Open $host $user $pass -port $port -output ::vfs::ftp::log -mode $mode]
    if {$fd == -1} {
        error "Mount failed"
    }

    if {$path != ""} {
        if {[catch {
            ::ftp::Cd $fd $path
        } err]} {
            ftp::Close $fd
            error "Opened ftp connection, but then received error: $err"
        }
    }

    if {![catch {vfs::filesystem info $dirurl}]} {
        # unmount old mount
        ::vfs::log "ftp-vfs: unmounted old mount point at $dirurl"
        vfs::unmount $dirurl
    }
    ::vfs::log "ftp $host, $path mounted at $fd"
    vfs::filesystem mount $local [list vfs::ftp::handler $fd $path]
    # Register command to unmount
    vfs::RegisterMount $local [list ::vfs::ftp::Unmount $fd]
    return $fd
}

UNIX Shell

Uses sftp which os available on all Linux distress by default. The commands are identical to ftp. This example uses the public free sftp server at test.rebex.net , the credentials are demo/password :

Aamrun $ sftp demo@test.rebex.net
Password: 
Connected to test.rebex.net.
sftp> ls
pub         readme.txt  
sftp> cd pub
sftp> ls
example  
sftp> ls example
example/KeyGenerator.png         example/KeyGeneratorSmall.png    example/ResumableTransfer.png    example/WinFormClient.png        example/WinFormClientSmall.png   example/imap-console-client.png  example/mail-editor.png          example/mail-send-winforms.png   
example/mime-explorer.png        example/pocketftp.png            example/pocketftpSmall.png       example/pop3-browser.png         example/pop3-console-client.png  example/readme.txt               example/winceclient.png          example/winceclientSmall.png     
sftp> cd example
sftp> get KeyGenerator.png
Fetching /pub/example/KeyGenerator.png to KeyGenerator.png
/pub/example/KeyGenerator.png                                                                                                                                                                                                               100%   36KB 146.4KB/s   00:00    
sftp> exit
Aamrun$

V (Vlang)

import net.ftp

fn main() {
	result := ftp_client_test() or {println('Error: something went wrong') exit(1)}
	println(result)
}


fn ftp_client_test() ?[]u8 {
	mut zftp := ftp.new()
	mut blob := []u8{}	
	defer {
		zftp.close() or {
			println('Error: failure to close ftp') 
			exit(10)
		}
	}
	connect_result := zftp.connect('ftp.redhat.com') or {
		println('Error: failed to connect') 
		exit(1)
	}
	login_result := zftp.login('ftp', 'ftp') or {
		println('Error: failed to login') 
		exit(2)
	}
	pwd := zftp.pwd() or {
		println('Error: failed to login') 
		exit(3)
	}
	if (connect_result == true) && (login_result == true) && (pwd.len > 0) {
		zftp.cd('/') or {
			println('Error: failed to get root directory') 
			exit(4)
		}
	}
	dir_list1 := zftp.dir() or {
		println('Error: failed to get directory listing') 
		exit(5)
	}
	if dir_list1.len > 0 {
		zftp.cd('/suse/linux/enterprise/11Server/en/SAT-TOOLS/SRPMS/') or {
			println('Error: failed to get directory listing') 
			exit(6)
		}
	}
	dir_list2 := zftp.dir() or {
		println('Error: failed to get directory listing') 
		exit(7)
	}
	if dir_list2.len > 0 {
		if dir_list2.contains('katello-host-tools-3.3.5-8.sles11_4sat.src.rpm')	== true {
			blob = zftp.get('katello-host-tools-3.3.5-8.sles11_4sat.src.rpm') or {
				println('Error: failed to get directory listing') 
				exit(8)
			}
		}
	}
	if blob.len <= 0 {
		println('Error: failed to get data') 
		exit(9)
	}
	return blob
}

Wren

Translation of: C
Library: ftplib

An embedded program so we can ask the C host to communicate with ftplib for us.

/* FTP.wren */

var FTPLIB_CONNMODE = 1
var FTPLIB_PASSIVE  = 1
var FTPLIB_ASCII    = 65 // 'A'

foreign class Ftp {
    foreign static init()

    construct connect(host) {}

    foreign login(user, pass)

    foreign options(opt, val)

    foreign chdir(path)

    foreign dir(outputFile, path)

    foreign get(output, path, mode)

    foreign quit()
}

Ftp.init()
var ftp = Ftp.connect("ftp.easynet.fr")
ftp.login("anonymous", "ftptest@example.com")
ftp.options(FTPLIB_CONNMODE, FTPLIB_PASSIVE)
ftp.chdir("/debian/")
ftp.dir("", ".")
ftp.get("ftp.README", "README", FTPLIB_ASCII)
ftp.quit()


We now embed this in the following C program, compile and run it.

#include <stdio.h>
#include <stdio_ext.h>
#include <stdlib.h>
#include <string.h>
#include <ftplib.h>
#include "wren.h"

/* C <=> Wren interface functions */

void C_init(WrenVM* vm) {
    FtpInit();
}

void C_ftpAllocate(WrenVM* vm) {
    netbuf *nbuf;
    const char *host = wrenGetSlotString(vm, 1);
    FtpConnect(host, &nbuf);
    netbuf** pnbuf = (netbuf**)wrenSetSlotNewForeign(vm, 0, 0, sizeof(netbuf*));
    *pnbuf = nbuf;
}

void C_login(WrenVM* vm) {
    netbuf* nbuf = *(netbuf**)wrenGetSlotForeign(vm, 0);
    const char *user = wrenGetSlotString(vm, 1);
    const char *pass = wrenGetSlotString(vm, 2);
    FtpLogin(user, pass, nbuf);
}

void C_options(WrenVM* vm) {
    netbuf* nbuf = *(netbuf**)wrenGetSlotForeign(vm, 0);
    int opt = (int)wrenGetSlotDouble(vm, 1);
    long val = (long)wrenGetSlotDouble(vm, 2);
    FtpOptions(opt, val, nbuf);
}

void C_chdir(WrenVM* vm) {
    netbuf* nbuf = *(netbuf**)wrenGetSlotForeign(vm, 0);
    const char *path = wrenGetSlotString(vm, 1);
    FtpChdir(path, nbuf);
}

void C_dir(WrenVM* vm) {
    netbuf* nbuf = *(netbuf**)wrenGetSlotForeign(vm, 0);
    const char *outputFile = wrenGetSlotString(vm, 1);
    if (strlen(outputFile) == 0) outputFile = NULL;
    const char *path = wrenGetSlotString(vm, 2);
    FtpDir(outputFile, path, nbuf);
}

void C_get(WrenVM* vm) {
    netbuf* nbuf = *(netbuf**)wrenGetSlotForeign(vm, 0);
    const char *output = wrenGetSlotString(vm, 1);
    if (strlen(output) == 0) output = NULL;
    const char *path = wrenGetSlotString(vm, 2);
    char mode = (char)wrenGetSlotDouble(vm, 3);
    FtpGet(output, path, mode, nbuf);
}

void C_quit(WrenVM* vm) {
    netbuf* nbuf = *(netbuf**)wrenGetSlotForeign(vm, 0);
    FtpQuit(nbuf);
}

WrenForeignClassMethods bindForeignClass(WrenVM* vm, const char* module, const char* className) {
    WrenForeignClassMethods methods;
    methods.finalize = NULL;
    if (strcmp(module, "main") == 0) {
        if (strcmp(className, "Ftp") == 0) {
            methods.allocate = C_ftpAllocate;
        }
    }
    return methods;
}

WrenForeignMethodFn bindForeignMethod(
    WrenVM* vm,
    const char* module,
    const char* className,
    bool isStatic,
    const char* signature) {
    if (strcmp(module, "main") == 0) {
        if (strcmp(className, "Ftp") == 0) {
            if ( isStatic && strcmp(signature, "init()") == 0)       return C_init;
            if (!isStatic && strcmp(signature, "login(_,_)") == 0)   return C_login;
            if (!isStatic && strcmp(signature, "options(_,_)") == 0) return C_options;
            if (!isStatic && strcmp(signature, "chdir(_)") == 0)     return C_chdir;
            if (!isStatic && strcmp(signature, "dir(_,_)") == 0)     return C_dir;
            if (!isStatic && strcmp(signature, "get(_,_,_)") == 0)   return C_get;
            if (!isStatic && strcmp(signature, "quit()") == 0)       return C_quit;
        }
    }
    return NULL;
}

static void writeFn(WrenVM* vm, const char* text) {
    printf("%s", text);
}

void errorFn(WrenVM* vm, WrenErrorType errorType, const char* module, const int line, const char* msg) {
    switch (errorType) {
        case WREN_ERROR_COMPILE:
            printf("[%s line %d] [Error] %s\n", module, line, msg);
            break;
        case WREN_ERROR_STACK_TRACE:
            printf("[%s line %d] in %s\n", module, line, msg);
            break;
        case WREN_ERROR_RUNTIME:
            printf("[Runtime Error] %s\n", msg);
            break;
    }
}

char *readFile(const char *fileName) {
    FILE *f = fopen(fileName, "r");
    fseek(f, 0, SEEK_END);
    long fsize = ftell(f);
    rewind(f);
    char *script = malloc(fsize + 1);
    fread(script, 1, fsize, f);
    fclose(f);
    script[fsize] = 0;
    return script;
}

int main(int argc, char **argv) {
    WrenConfiguration config;
    wrenInitConfiguration(&config);
    config.writeFn = &writeFn;
    config.errorFn = &errorFn;
    config.bindForeignClassFn = &bindForeignClass;
    config.bindForeignMethodFn = &bindForeignMethod;
    WrenVM* vm = wrenNewVM(&config);
    const char* module = "main";
    const char* fileName = "FTP.wren";
    char *script = readFile(fileName);
    WrenInterpretResult result = wrenInterpret(vm, module, script);
    switch (result) {
        case WREN_RESULT_COMPILE_ERROR:
            printf("Compile Error!\n");
            break;
        case WREN_RESULT_RUNTIME_ERROR:
            printf("Runtime Error!\n");
            break;
        case WREN_RESULT_SUCCESS:
            break;
    }
    wrenFreeVM(vm);
    free(script);
    return 0;
}
Output:

Sample output:

drwxr-xr-x  25 1002     1002         4096 Aug 15 06:55 dists
drwxr-xr-x   4 1002     1002         4096 Sep 27 07:52 doc
-rw-r--r--   1 1002     1002       254738 Sep 27 08:21 extrafiles
drwxr-xr-x   3 1002     1002         4096 Sep 27 08:15 indices
-rw-r--r--   1 1002     1002     14538903 Sep 27 08:15 ls-lR.gz
drwxr-xr-x   5 1002     1002         4096 Dec 19  2000 pool
drwxr-xr-x   4 1002     1002         4096 Nov 17  2008 project
-rw-r--r--   1 1002     1002         1320 Aug 14 09:28 README
-rw-r--r--   1 1002     1002         1290 Jun 26  2010 README.CD-manufacture
-rw-r--r--   1 1002     1002         3203 Aug 14 09:27 README.html
-rw-r--r--   1 1002     1002          291 Mar  4  2017 README.mirrors.html
-rw-r--r--   1 1002     1002           86 Mar  4  2017 README.mirrors.txt
drwxr-xr-x   3 1002     1002         4096 Oct 10  2012 tools
drwxr-xr-x  26 1002     1002         4096 Aug 14 13:55 zzz-dists

zkl

Using the cURL library, doing this from the REPL. Moving around in the tree isn't supported.

zkl: var cURL=Import("zklCurl")
zkl: var d=cURL().get("ftp.hq.nasa.gov/pub/issoutreach/Living in Space Stories (MP3 Files)/")
L(Data(2,567),1630,23) // downloaded listing, 1630 bytes of header, 23 bytes of trailer
zkl: d[0][1630,-23].text
-rw-rw-r--   1 109      space-station  2327118 May  9  2005 09sept_spacepropulsion.mp3
...
-rw-rw-r--   1 109      space-station  1134654 May  9  2005 When Space Makes you Dizzy.mp3

zkl: d=cURL().get("ftp.hq.nasa.gov/pub/issoutreach/Living in Space Stories (MP3 Files)/When Space Makes you Dizzy.mp3")
L(Data(1,136,358),1681,23)
zkl: File("foo.mp3","w").write(d[0][1681,-23])
1134654  // note that this matches size in listing

The resulting file foo.mp3 has a nice six minute description of what can happen when returning from space.