DEV Community

vindarel
vindarel

Posted on

1

FTP and SFTP clients for Common Lisp

You thought all companies would provide well-documented web APIs today? Well, some use a blank .docx and (S)FTP.

For Common Lisp, cl-ftp is one of those libraries that look unmaintained and undocumented, but it works very well, and it is short enough to quickly grab how to use it. It's quite well thought-out, even. It is a pure Lisp library, no system dependencies required.

For example you can do this to send a file:

(ftp:with-ftp-connection (conn :hostname hostname
                                   :username (find-ftp-username)
                                   :password password
                                   :passive-ftp-p t)
      (ftp:store-file conn local-filename filename))
Enter fullscreen mode Exit fullscreen mode

You'll have to look at this for more commands: https://github.com/pinterface/cl-ftp/blob/master/simple-client.lisp

we have ftp-shell, ftp-put, ftp-list, ftp-get, ftp-pwd, and etc for ls, help, cd, dir.

For SFTP, we must find another means. I didn't find a pure CL library, and it is annoyingly not straightforward to run a SFTP command with a password on the command line. Some solutions exist and I chose lftp. It is included in Debian.

We can run:

lftp sftp://user:password@host  -e "put local-file.name; bye"
Enter fullscreen mode Exit fullscreen mode

or better, with the password in an environment variable:

export LFTP_PASSWORD="just_an_example"
lftp --env-password sftp://user@host  -e "put local-file.name; bye"
Enter fullscreen mode Exit fullscreen mode

In my scripts I need to handle a connection profile, and even several ones for development, so I ended up with code that I replicate from project to project, hence a new utility:

https://github.com/vindarel/lftp-wrapper

Now do:

CL-USER> (use-package :lftp-wrapper)  ;; optional, or:
CL-USER> (uiop:add-package-local-nickname :lftp :lftp-wrapper)

CL-USER> (defvar profile (make-profile-from-plist (uiop:read-file-form "CREDS.lisp-expr"))
#<PROFILE protocol: "sftp", login: "user", port: 10022, server: "prod.com", password? T>

CL-USER> (defvar command (put :cd "I/" :local-filename "data.csv"))
#<PUT cd: "I/", filename: "data.csv" {1007153883}>

CL-USER> (run profile command)

success!
Enter fullscreen mode Exit fullscreen mode

We created a profile from credentials on file and from environment variables, we created a command object, and we executed the command for the profile.

We could wrap many more features from lftp. But I follow needs-driven development. So far, it works and it sends my files.

Do your career a big favor. Join DEV. (The website you're on right now)

It takes one minute, it's free, and is worth it for your career.

Get started

Community matters

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

👋 Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay