Go to the tutorial main page.
The Allegro CL FTP Client Module is a collection of routines that implement the client side of FTP (File Transfer Protocol). Using it from a Common Lisp program you can implement useful utilities that transfer or maintain automatically-updated collections of files between computers. The documentation for the Allegro CL FTP Client Module is in doc/ftp.htm.
FTP is one of the oldest protocols used on the Internet; it is more than 30 years old. A FTP server responds to requests from a FTP client to transfer files from one computer to another. An FTP client on Computer A can even ask a FTP server on Computer B to transfer a file to the FTP server on Computer C, although usually transactions involve only the client and a single server. The client can also request information about files available on the remote server, such as the file length and file write date. FTP access is gated by normal user name and password access. Many public software-distribution sites allow user anonymous to retrieve files with any password whatever. There is a convention when connecting to such a site to use one's email address as the password so the user's identity will show up in the FTP logs, but this is neither required nor can it be enforced. Of course, few sites allow write access with anonymous identification.
Certain patches are necessary for these examples to work in both Allegro CL 6.2 and 7.0. Be sure that you have downloaded patches (with sys:update-allegro) before trying these examples.
Here is a simple example retrieving a known file from a FTP server. We retrieve the file ftp://ftp.franz.com/welcome.msg which contains some welcome text that a conventional interactive FTP client might print when entering this directory.
First we load the ftp module and (optionally) use the net.ftp.client package:
cl-user(2): (require :ftp) ; Fast loading from bundle code\ftp.fasl. ; Fast loading from bundle code\resource.fasl. ; Fast loading from bundle code\acldns.fasl. t cl-user(3): (use-package :net.ftp.client) t
Now we set the values of the variables *default-user* and *default-password* (we have package-qualified them in the example, but that is not necessary if you used the net.ftp.client package). Then we retrieve a file:
cl-user(4): (setq net.ftp.client:*default-user* "anonymous" net.ftp.client:*default-password* "[email protected]") "[email protected]" cl-user(5): (net.ftp.client:ftp-get "ftp.franz.com" "/welcome.msg" "./FranzWelcome.txt") t cl-user(6): (excl:file-contents "./FranzWelcome.txt") "Welcome to Franz Inc! "
Of course if you intended to use FTP interactively you could use whatever FTP client application is standard on your particular computer instead of typing at a Lisp listener. The real power of the FTP client module is revealed when it is used programatically, as in the next example.
In addition to transferring files from or to a remote server, FTP can query the remote server about the files in a remote directory. Suppose there is a FTP server somewhere that provides a set of downloadable files that are updated irregularly. You want to download new or revised files as they become available. To do that, you need to peruse the contents of the FTP server's directories and determine when a new file has been added or an existing file updated. The sys:update-allegro program does something very much like this, comparing files already downloaded with the contents of the patch directories on the Franz Inc. FTP server. sys:update-allegro has a lot of additional complexities, so we'll show here a simplified version as an example. Rather than downloading files we'll just show a list of files available in a particular directory sorted in time order, most recent first. This example could be extended into a synchronize function that actually brings a local directory tree up-to-date with a tree on a remote server, but any production version of such a function would require a lot of bulletproofing and consistency checking.
The ability to examine a remote server directory is provided by the map-over-ftp-directory function. It returns the file name, file length, and the file-write-date. We'll use the date to sort the files, but a synchronize function could use it and the file length to compare against previously-retrieved local copies.
cl-user(5): (defun sorted-ftp-directory (host path) (net.ftp.client:with-open-ftp-connection (ftp host) (let ((files nil)) (net.ftp.client:map-over-ftp-directory (lambda (file len date) (push (list file len date) files)) host path) (sort files #'> :key #'third)))) sorted-ftp-directory cl-user(6): (defun print-ftp-file-info (host path) (format t ";;; Contents of ~a ~a:~%" host path) (mapc (lambda (file) (format t "~25a ~8d " (enough-namestring (car file) path) (cadr file)) (locale-print-time (third file) :fmt "%a %d %b %y %T\\n")) (sorted-ftp-directory host path)) (values)) print-ftp-file-info cl-user(7): (print-ftp-file-info "ftp.franz.com" "/pub/patches/7.0/windows/code/") ;;; Contents of ftp.franz.com /pub/patches/7.0/windows/code/: DESCRIPTIONS 2166 Thu 11 Nov 04 09:25:26 ssl.001.gz 16393 Wed 10 Nov 04 13:14:20 ssl.001 71098 Wed 10 Nov 04 10:56:22 ftp.001.gz 14497 Thu 04 Nov 04 14:32:30 oracle.001.gz 44881 Thu 04 Nov 04 14:32:30 shell.001.gz 16551 Thu 04 Nov 04 14:32:30 shell.001 52369 Thu 04 Nov 04 14:18:00 oracle.001 291840 Thu 04 Nov 04 13:38:13 ftp.001 55370 Thu 04 Nov 04 13:30:59 cl-user(8):
Although not seen above, map-over-ftp-directory automatically recurses through subdirectories unless instructed with a :recurse nil keyword argument. It has many other useful options -- see the full documentation on the FTP client.
Here is a list of operators, variables, and classes defined in the FTP client module. The ones used in this tutorial are noted.
*default-password*
(used in the tutorial)
*default-user*
(used in the tutorial)
Go to the tutorial main page.