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.
BaCon
Using libCURL. <lang bacon>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</lang>
Full native implementation without dependency to external libraries. <lang bacon>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</lang>
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. <lang dos>::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</lang>
- 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 <lang c>
- 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;
} </lang>
C++
Using ftplib, libftp++ <lang cpp> /* 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 */ </lang>
- 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.
<lang lisp>(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)))
</lang>
- 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. <lang erlang> %%%------------------------------------------------------------------- %%% 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).
</lang>
Go
Using the FTP package from github.com/stacktic/ftp. <lang go>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) }</lang>
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: <lang 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."); </lang> 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:
<lang haskell>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</lang>
J
<lang 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</lang>
Java
requires apache.commons.net <lang java>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); } }
}</lang>
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
<lang julia>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)</lang>
Kotlin
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. <lang scala>// 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)
}</lang> 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
<lang lingo>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()</lang>
LiveCode
<lang 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)</lang>
Nim
<lang nim>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()</lang>
- 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
<lang 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";</lang>
- Output:
Currently 20 files in the 'upload' directory Your file was stored as 512KB.zip in the current directory!
Phix
<lang Phix>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</lang>
- 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
<lang 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";
}</lang>
- 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' <lang PicoLisp>(in '(curl "-sl" "ftp://kernel.org/pub/site/")
(while (line) (prinl @) ) )
(call "curl" "-s" "-o" "sha256sums.asc" "ftp://kernel.org/pub/site/sha256sums.asc")</lang> Output:
README sample_mirror_script.pl sha256sums.asc
Python
<lang Python> 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() </lang>
Racket
Note: net/ftp in Racket uses passive mode exclusively. <lang racket>
- 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))
</lang>
Raku
(formerly Perl 6)
<lang perl6>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 );</lang>
- 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
<lang 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 </lang>
Ruby
<lang 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</lang> The connection is closed automatically at the end of the block.
Rust
Using crate ftp
version 3.0.1
<lang Rust>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(())
}</lang>
Scala
<lang Scala>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.")
}</lang>
- Output:
See it in running in your browser by Scastie (JVM).
Seed7
The library ftp.s7i contains functions to open and handle an ftpFileSys. <lang seed7>$ 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;</lang>
Sidef
<lang 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;</lang>
Tcl
Using package ftp
<lang Tcl> 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
</lang>
Using a virtual file system
An alternative approach that uses the package TclVFS to access ftp:// paths as a virtual file system.
<lang tcl> 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] </lang>
The file vfsftpfix.tcl with the passive mode patch (see http://wiki.tcl.tk/12837): <lang tcl>
- 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
} </lang>
Wren
An embedded program so we can ask the C host to communicate with ftplib for us. <lang ecmascript>/* 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()</lang>
We now embed this in the following C program, compile and run it.
<lang c>#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;
}</lang>
- 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. <lang zkl>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</lang> The resulting file foo.mp3 has a nice six minute description of what can happen when returning from space.
- Programming Tasks
- Programming environment operations
- Networking and Web Interaction
- BaCon
- Batch File
- C
- C++
- Common Lisp
- Erlang
- Go
- Groovy
- Haskell
- J
- Java
- Julia
- Kotlin
- Ftplib
- Lingo
- Curl Xtra
- LiveCode
- Nim
- Perl
- Phix
- Phix/libcurl
- Php
- PicoLisp
- Python
- Racket
- Raku
- REBOL
- Ruby
- Rust
- Scala
- Commons-net
- Seed7
- Sidef
- Tcl
- Wren
- Zkl
- PARI/GP/Omit
- Commodore BASIC/Omit