Using the email library Mel with CL
Tuesday, March 30th, 2010The mel library, if people aren’t familiar with it, is an extremely nice library for parsing various email sources. I started using this as a bit of a project to pull in emails to my central search store. I was extremely surprised how well done this library was – although I admit the documentation is a bit rough around the edges.
So first, if you want to install this you can either install via asdf, likely clbuild, or just grab the darcs repository. Personally I had problems with the asdf install so I went with the stable darcs repo instead. For my install I’ve done the following:
cd /usr/lib/sbcl/site
darcs clone http://common-lisp.net/project/mel-base/darcs/mel-base/
cd ../site-systems
ln -s /usr/lib/sbcl/site/mel-base/mel-base.asd .
The one problem I’ve found is that there is little documentation out there, but we can create some to give a starting point at the very least.
cd /usr/lib/sbcl/site/mel-base/docs/manual
texi2pdf mel.texinfo
You’ll get a mel.pdf if you have texinfo installed.
I will concentrate a bit on using Maildir, since that’s kinda what I was starting with. You connect to a Maildir directory by the following:
(defvar inbox (make-maildir-folder “/home/dthole/Maildir/”))
Form there, you can run quite a few things. The most notable ones around are the functions:
messages
map-messages
messages itself, as the docs describe it, will return all the messages. This is fairly helpful, but the real power comes from map-messages. With map-messages, we can pass a function to evaluate from each message that’s processed. Some sample code of what I’ve done to determine specific email from/to is the following:
(defun mapMessageTest ()
"From the map-messages, we're given an object, and we can call methods on that."
(let ((num-ui-froms 0)
(num-to-greg 0))
(map-messages #'(lambda (x)
(if (scan "foo@bar.com" (address-spec (from x)))
(incf num-ui-froms))
(if (and (to x) (scan "bar@foo.com" (address-spec
(if (listp (to x))
(car (to x))
(to x)))))
(incf num-to-greg)))
*inbox*)
(format t "Num from foo@bar.com: ~a~%" num-ui-froms)
(format t "Num to bar@foo.com: ~a~%" num-to-greg)))
So what I’m doing here is passing an anonymous function that does two if conditions. x in this case is an message CLOS-type object from mel. We can call (from), (to), (message-string) and so on. Many of these aren’t documented, but the code is there. Anyways, the one curious part people may notice is the issue related to the (address-spec (if ..)) section. The to address can contains a list of elements, if you have more than one email, since you can have multiple tos in an email. I check to say if it’s a list, then get the first element else just return the element. All this is doing is creating two counters and outputting the result at the end.
Another function I mentioned just breifly a bit ago is (message-string). There are a lot of message-* functions out there, this one returns the text of the email.
One big reason why I enjoy this library is the caching it does. I found from my testing that going through 14k emails in my MailDir folder took about 10 seconds or so. Subsequent calls to (mapMessageTest) was MUCH quicker, taking less than a second. There’s some interesting caching the library implemented that’s really helpful while in the REPL. I haven’t looked at the code yet for this, but I’m excited to do it.
The reason I’m finding to use this library is to do some recognition on the emails and copy them to another area on disk. This discussion on documentation and search will be in a later blog article.





