Sending email in common-lisp

Summary:

This page covers how to send an email from your common-lisp coding using the cl-smtp library using googles gmail servers.

Introduction

This post covers a working example on how to send email from the common-lisp (cl) language using googles servers. The provided example has been tested as an individual, and regularly used from the 'google workspace' account.

Requirements:

An Application password.

Google detailed getting one here. One fact that was missed is that you now need 2FA configured for the account or you can not make app passwords.

The code:

Listed below is the common-lisp code that is required to send email. The "password" is stored in the environment variable GMAIL_KEY, as a single string. Google often displays on the 'app password' page as 4 sets of 4 characters, aka xxxx xxxx xxxx xxxx , however experience shows that there was no space required.

(ql:quickload "cl-smtp")
(ql:quickload "cl-mime")

(defun email-report (&key to cc subject html)
       "Generic send SMTP mail with some TEXT to RECIEPIENTS"
       (let ((login "youraccount@gmail.com")
             (passwd (uiop:getenv "GMAIL_KEY")))

         (cl-smtp:with-smtp-mail (out "smtp.gmail.com" to cc
                                      :ssl :tls
                                      :authentication `(,login ,passwd))

           (format out "To: ~A~%" to )
           (format out "Subject: ~A~%" subject)

           (cl-mime:print-mime out (make-instance 'cl-mime:mime
                                                  :type "text" :subtype "html"
                                                  :encoding :base64
                                                  :content html) t t))))

(devar html-mail-content "<html><h1> THis is html, but not really. </h1></html>")

(email-report :to "someone@example.com"
              :cc *cc-list*
              :subject email-subject
              :html html-content)

Ideas for improvement.

1. Secret Storage mechanism.

The application password needs to be stored somewhere, my advice is not to have it stored in plaintext.

If your application is hosted on a containerized platform, you can pass it as environment variables, or some of them have secrets file.

2. Preventing secret leaks.

After the secret is loaded into memory, destroy the file or environment setting so it is harder for attackers to abuse the information.

(setf (uiop:getenv "SOME_ENVIRONMENT_VARIABLE") "PASSWORD-REMOVED-FOR-SECURITY-REASONS")

When it is loaded, use Secret Values which will prevent passwords appearing in backtraces or logs.

3. Preventing accidental/intention spamming.

Your application may accidentally send out hundreds of mail if a function is run repeatedly accidentally. It is important that the program keep an accurate, reliable mechanism to know when a limits are abused.

Conclusion

Resources: