30th October 2018
I recently wanted to write a compact Lisp program to encode strings into Base64 encoding , for use with a set of example Wi-Fi programs for my uLisp interpreter running on an ESP32 microcontroller . Here it is, in case it's useful.
First the function char64 generates the Base64 character set. Then the routine base64 takes a string and a stream, and outputs the Base64-encoded version of the string to the stream:
(defun char64 (n) (char "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" (logand n #x3F)))
(defun base64 (str stm) (let* ((l (length str)) (max (ceiling l 3))) (setq str (concatenate 'string str (string #\null) (string #\null))) (dotimes (i max) (let* ((w (* i 3)) (a (char-code (char str w))) (b (char-code (char str (1+ w)))) (c (char-code (char str (+ 2 w))))) (princ (char64 (ash a -2)) stm) (princ (char64 (logior (ash a 4) (ash b -4))) stm) (princ (if (>= (1+ w) l) "=" (char64 (logior (ash b 2) (ash c -6)))) stm) (princ (if (>= (+ 2 w) l) "=" (char64 c)) stm))) (princ #\return stm) (princ #\newline stm)))
It's based on the fact that every three ASCII characters are encoded as four Base 64 characters, using "=" characters to pad strings with a length that's not a multiple of three. For example:
> (base64 "Mind your own business" nil) TWluZCB5b3VyIG93biBidXNpbmVzcw== #\Newline
It terminates the stream with return, newline, as required by the mail protocol.
Sending an email
Here's the function send which sends an email from the ESP32 :
(defun send (message) (with-client (s "mail.mysite.com" 25) (println "EHLO mysite.com" s) (println "AUTH LOGIN" s) (base64 "david" s) (base64 "secret" s) (println "MAIL FROM:email@example.com" s) (println "RCPT TO:firstname.lastname@example.org" s) (println "DATA" s) (println message s) (println "." s) (println "QUIT" s)))
The username and password are provided on the two lines following the AUTH LOGIN command, encoded using Base64 encoding.
For example, you could run this by evaluating:
(send "Hello from Lispology!")
There are other headers you can provide, such as a subject line, but this example shows the minimum. The body of the message follows the DATA command, and is terminated by a line containing just a full stop.
This example assumes that there are no errors, and ignores the responses from the server. A better implementation would read the server response and check the return code at the start of each line.
blog comments powered by Disqus