ftphs-1.0.6: FTP Client and Server Library

Portabilitysystems with networking
Stabilityexperimental
MaintainerJohn Goerzen <jgoerzen@complete.org>

Network.FTP.Client

Contents

Description

This module provides a client-side interface to the File Transfer Protocol as defined by RFC959 and RFC1123.

Written by John Goerzen, jgoerzen@complete.org

Welcome to the FTP module for Haskell.

Here is a quick usage example to get you started. This is a log of a real session with ghci:

(This would be similar in a do block. You could also save it to a file and run that with Hugs.)

 Prelude> :l Network.FTP.Client
 ...

The above loads the module.

Next, we enable the debugging. This will turn on all the FTP sent and FTP received messages you'll see.

 Prelude Network.FTP.Client> enableFTPDebugging

Now, connect to the server on ftp.kernel.org.

 *Network.FTP.Client> h <- easyConnectFTP "ftp.kernel.org"
 FTP received: 220 Welcome to ftp.kernel.org.

And log in anonymously.

 *Network.FTP.Client> loginAnon h
 FTP sent: USER anonymous
 FTP received: 331 Please specify the password.
 FTP sent: PASS anonymous@
 ...
 FTP received: 230 Login successful.

Change the directory...

 Prelude Network.FTP.Client> cwd h "/pub/linux/kernel/Historic"
 FTP sent: CWD /pub/linux/kernel/Historic
 FTP received: 250 Directory successfully changed.

Let's look at the directory. nlst returns a list of strings, each string corresponding to a filename. Here, putStrLn . unlines will simply print them out, one per line.

 Prelude Network.FTP.Client> nlst h Nothing >>= putStrLn . unlines
 FTP sent: TYPE A
 FTP received: 200 Switching to ASCII mode.
 FTP sent: PASV
 FTP received: 227 Entering Passive Mode (204,152,189,116,130,143)
 FTP sent: NLST
 FTP received: 150 Here comes the directory listing.
 linux-0.01.tar.bz2
 linux-0.01.tar.bz2.sign
 linux-0.01.tar.gz
 linux-0.01.tar.gz.sign
 linux-0.01.tar.sign
 old-versions
 v0.99
 FTP received: 226 Directory send OK.

Let's try downloading something and print it to the screen. Again, we use putStrLn. We use fst here because getbinary returns a tuple consisting of a string representing the data and a FTPResult code.

 Prelude Network.FTP.Client> getbinary h "linux-0.01.tar.gz.sign" >>= putStrLn . fst
 FTP sent: TYPE I
 FTP received: 200 Switching to Binary mode.
 FTP sent: PASV
 FTP received: 227 Entering Passive Mode (204,152,189,116,121,121)
 FTP sent: RETR linux-0.01.tar.gz.sign
 FTP received: 150 Opening BINARY mode data connection for linux-0.01.tar.gz.sign (248 bytes).
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.0.0 (GNU/Linux)
 Comment: See http://www.kernel.org/signature.html for info
 
 iD8DBQA54rf0yGugalF9Dw4RAqelAJ9lafFni4f/QyJ2IqDXzW2nz/ZIogCfRPtg
 uYpWffOhkyByfhUt8Lcelec=
 =KnLA
 -----END PGP SIGNATURE-----
 FTP received: 226 File send OK.

Here's an example showing you what the result code looks like.

 Prelude Network.FTP.Client> getbinary h "linux-0.01.tar.gz.sign" >>= print . snd
 ...
 (226,["File send OK."])

The first component of the FTPResult object is the numeric status code from the server. The second component is a list of message lines from the server.

Now, let's get a more detailed directory listing:

 Prelude Network.FTP.Client> dir h Nothing >>= putStrLn . unlines
 ...
 -r--r--r--    1 536      536         63362 Oct 30  1993 linux-0.01.tar.bz2
 -r--r--r--    1 536      536           248 Oct 30  1993 linux-0.01.tar.bz2.sign
 -r--r--r--    1 536      536         73091 Oct 30  1993 linux-0.01.tar.gz
 -r--r--r--    1 536      536           248 Oct 30  1993 linux-0.01.tar.gz.sign
 -r--r--r--    1 536      536           248 Oct 30  1993 linux-0.01.tar.sign
 drwxrwsr-x    5 536      536          4096 Mar 20  2003 old-versions
 drwxrwsr-x    2 536      536          4096 Mar 20  2003 v0.99
 FTP received: 226 Directory send OK.

And finally, log out:

 Prelude Network.FTP.Client> quit h
 FTP sent: QUIT
 FTP received: 221 Goodbye.

Here is one big important caution:

/You MUST consume all data from commands that return file data before you issue any other FTP commands./

That's due to the lazy nature of Haskell. This means that, for instance, you can't just iterate over the items nlst returns, trying to getbinary each one of them -- the system is still transferring nlst data while you are trying that, and confusion will ensue. Either open two FTP connections or make sure you consume the nlst data first.

Here is a partial list of commands effected: nlst, dir, getbinary, getlines, downloadbinary.

The Data.List.Utils.seqList function could be quite helpful here. For instance:

 x <- nlst h Nothing
 map (\fn -> ...download files from FTP... ) (seqList x)

If you omit the call to Data.List.Utils.seqList, commands to download files will be issued before the entire directory listing is read. FTP cannot handle this.

The corrolary is:

/Actions that yield lazy data for data uploading must not issue FTP commands themselves./

This will be fairly rare. Just be aware of this.

This module logs messages under Network.FTP.Client for outgoing traffic and Network.FTP.Client.Parser for incoming traffic, all with the System.Log.DEBUG priority, so by default, no log messages are seen. The enableFTPDebugging function will adjust the priorities of these two handlers so debug messages are seen. Only control channel conversations are logged. Data channel conversations are never logged.

All exceptions raised by this module have a string beginning with "FTP: ". Most errors will be IO userErrors. In a few extremely rare cases, errors may be raised by the Prelude error function, though these will also have a string beginning with "FTP: ". Exceptions raised by the underlying networking code will be passed on to you unmodified.

Useful standards:

Synopsis

Establishing/Removing connections

easyConnectFTP :: HostName -> IO FTPConnectionSource

Connect to the remote FTP server and read but discard the welcome. Assumes default FTP port, 21, on remote.

connectFTP :: HostName -> PortNumber -> IO (FTPConnection, FTPResult)Source

Connect to remote FTP server and read the welcome.

loginAnon :: FTPConnection -> IO FTPResultSource

Log in anonymously.

loginSource

Arguments

:: FTPConnection

Connection

-> String

Username

-> Maybe String

Password

-> Maybe String

Account (rarely used)

-> IO FTPResult 

Log in to an FTP account.

quit :: FTPConnection -> IO FTPResultSource

Log off the server and quit.

Configuration

setPassive :: FTPConnection -> Bool -> FTPConnectionSource

Sets whether passive mode is used (returns new connection object reflecting this)

enableFTPDebugging :: IO ()Source

Enable logging of FTP messages through System.Log.Logger. This sets the log levels of Network.FTP.Client.Parser and Network.FTP.Client to DEBUG. By default, this means that full protocol dumps will be sent to stderr.

The effect is global and persists until changed.

Directory listing

nlstSource

Arguments

:: FTPConnection 
-> Maybe String

The directory to list. If Nothing, list the current directory.

-> IO [String] 

Retrieves a list of files in the given directory.

FIXME: should this take a list of dirs?

dirSource

Arguments

:: FTPConnection 
-> Maybe String

The directory to list. If Nothing, list the current directory.

-> IO [String] 

Retrieve the system-specific long form of a directory list.

FIXME: should this take a list of dirs?

File downloads

getlines :: FTPConnection -> String -> IO ([String], FTPResult)Source

Retrieves the specified file in text mode.

getbinary :: FTPConnection -> String -> IO (String, FTPResult)Source

Retrieves the specified file in binary mode.

downloadbinary :: FTPConnection -> String -> IO FTPResultSource

Downloads a file from remote and saves to disk in binary mode. Note: filename is used for both local and remote.

File uploads

putlines :: FTPConnection -> String -> [String] -> IO FTPResultSource

Puts data in the specified file in text mode. The first string is the filename.

putbinary :: FTPConnection -> String -> String -> IO FTPResultSource

Puts data in the specified file in binary. The first string is the filename.

uploadbinary :: FTPConnection -> String -> IO FTPResultSource

Uploads a file from disk in binary mode. Note: filename is used for both local and remote.

File manipulation

renameSource

Arguments

:: FTPConnection 
-> String

Old name

-> String

New name

-> IO FTPResult 

Rename or move a file.

delete :: FTPConnection -> String -> IO FTPResultSource

Delete (unlink) a file.

size :: (Num a, Read a) => FTPConnection -> String -> IO aSource

Get the size of a file.

This command is non-standard and may possibly fail.

Directory manipulation

cwd :: FTPConnection -> String -> IO FTPResultSource

Change the working directory.

mkdir :: FTPConnection -> String -> IO (Maybe String, FTPResult)Source

Make new directory. Returns the absolute name of the new directory if possible.

rmdir :: FTPConnection -> String -> IO FTPResultSource

Remove a directory.

pwd :: FTPConnection -> IO (Maybe String, FTPResult)Source

Print the current working directory. The first component of the result is the parsed directory name, if the servers response was parsable.

Low-level advanced commands

transfercmd :: FTPConnection -> String -> IO HandleSource

Returns the socket part from calling ntransfercmd.

ntransfercmd :: FTPConnection -> String -> IO (Handle, Maybe Integer)Source

Establishes a connection to the remote.

FIXME: need support for rest

retrlines :: FTPConnection -> String -> IO ([String], FTPResult)Source

Retrieves lines of data from the remote. The string gives the command to issue.

storlines :: FTPConnection -> String -> [String] -> IO FTPResultSource

Stores the lines of data to the remote. The string gives the commands to issue.