Extending mu4e to support many offlineimap accounts




As most of you will know by entering about any page in this website, I use Emacs regularly. In the last month I succeeded at setting up offlineimap + mu4e in my Debian laptop, so I no longer had to rely on getting home to use Thunderbird to read all my email in one place. And it had a bonus: mu4e is blazingly fast. Two keystrokes can get me to another account folder in a split second, and I keep all my shortcuts and text editing power of Emacs. Pressing M-q (fill-column) I can adjust my paragraphs width, and s (mu4e-headers-search) will filter all my emails by an expression immediately.

With this, the one and only reason why I still have Windows in my home computer is games. And even for that, Linux is incredibly improving.

But it was not that good. mu4e does not support many offlineimap accounts by default, so I had to google a snippet that allowed me to compose and answer emails from different accounts, providing account-dependent things like the signature, the source address, the source name, etc. I found it, and it worked greatly, but recently I detected some errors: after sending an email from an account if I tried to delete an email from another one it would be moved to the Trash folder of the former account. I had already had problems with sent emails not being moved to the correct Sent folder, and the Elisp function I had came up with to solve that was already to complicated to replicate without feeling terribly inefficient.

The solution itself is simple: given an email, find its corresponding Sent folder. But it gets complicated because every Mail Server implements their Folder hierarchy quite anarchically. For example, my Gmail Sent folder was /gmail/[Gmail].Sent, but my college mail Sent folder was /college/INBOX.Sent. My 1and1 mail account has a /web/Sent folder, which I think should be standard.

So I decided to abstract a little this function, and leave out the setup variables of mu4e, at least for the folders problem. I created a variable like this:

(defvar mu4e-account-folders
    ("gmail" :trash "/gmail/[Gmail].Trash"
             :sent "/gmail/[Gmail].Sent")
    ("college" :trash "/college/INBOX.Trash"
               :sent "/college/INBOX.Sent"))

;;  Also with the 1and1 account, and the :refile folder.

And decided that this setup should be enough for the different-accounts folder problem. mu4e setup variables mu4e-refile-folder, mu4e-trash-folder and mu4e-sent-folder can be either a string with the folder relative path or a function that receives a message as a parameter and returns that path. Out of the previously complicated mu4e-sent-folder function I had coded, I got the following functions:

(defun ryckes/mu4e-msg-account (msg)
    (let ((maildir (file-name-directory
        (mu4e-message-field msg :maildir))))
       (string-match "/\\(.*?\\)/" maildir)
       (match-string 1 maildir)))

(defun ryckes/mu4e-get-account-folder (account folderkey)
    (let ((account-folders (cdr (assoc account mu4e-account-folders))))
       (cadr (assoc folderkey account-folders))))

Now the remaining functions were easy:

(setq
    mu4e-refile-folder (lambda (msg)
                        (ryckes/mu4e-get-account-folder
                            (ryckes/mu4e-msg-account msg) :refile))
    mu4e-trash-folder (lambda (msg)
                        (ryckes/mu4e-get-account-folder
                            (ryckes/mu4e-msg-account msg) :trash))
    mu4e-sent-folder (lambda (msg)
                        (ryckes/mu4e-get-account-folder
                            (ryckes/mu4e-msg-account msg) :sent)))

I don't know if the way this function finds the strings in the mu4e-accounts-folder is the best one, I am just learning about assoc, cdr, car and keywords, and solving this problem has helped me a lot, and I plan to use the keyword-value list in other setup aspects of Emacs.