Using Mutt with Exchange

2020-03-10

Update: Although everything in this post is (to the best of my knowledge) correct, a more comprehensive setup guide for mutt and exchange can be found here: https://jonathanh.co.uk/blog/mutt-setup.html.

Mutt is an extremely powerful, vim like email client. If your company uses Exchange for email and calenders, you may have a hard time making them work together. As a Linux user, I can’t (and don’t want) to install outlook and I don’t want to have to open a web browser, every time I want to check an email. My mission was to find a way to manage my work emails using Mutt.

For the purpose of this blog, I assume that you cannot connect to the Exchange server directly with IMAP or SMTP. If you are unsure, check at yourdomain.com/owa/#path=/options/popandimap. If you can use IMAP natively, use it.

DavMail

The solution to my dilemma was Davmail. This works as a middleman (person) between the Exchange server and any standards compliant email client, in my case Mutt. Davmail will translate IMAP and SMTP requests to the Exchange equivalents, as well as LDAP for contacts. The result: you can use whichever email client you like to manage your cooperate emails.

Configuring DavMail

The setup help for DavMail is good, so I am not going to try to reinvent the wheel. I suggest you read the documentation.

If you find it easier to configure the server using a GUI, you can. Run davmail and fill in the boxes. Nothing groundbreaking here.

If you would like to run Davmail without a GUI in the future, simply change the davmail.server property from false to true in the configuration file (by default this is in your home directory).

sed -i '/davmail\.server=/s/false/true/' .davmail.properties

Now, you can start the server with

nohup davmail davmail.properties &

Configuring Mutt

You can now connect to the locally running Davmail server as though it was an IMAP / SMTP server. Unless you have configured it differently, you will need to connect using port 1143 for IMAP and 1025 for SMTP.

Your credentials will be you Active Directory username and password and, with that, you should be able to send and receive emails using the exchange server.

set folder      = "imap://127.0.0.1:1143/"
set imap_user   = "domain/user.name"
set imap_pass   = "password"
set spoolfile   = +INBOX
mailboxes       = +INBOX

set realname = 'Your Name'
set from = 'your-email@company.com
set use_from = yes

set smtp_url="smtp://$imap_user:$imap_pass@127.0.0.1:1025"

set ssl_force_tls = no
set ssl_starttls = no

Note here I have disabled tls authentication. This is for the communication between the mail client and the local server, so the information never leaves the computer.

Also, you might prefer not to put your password in a configuration file in clear text. You can run shell commands to populate variables so, in my case, I use the command line password manager Pass.

This allows me to change the imap_pass line to

set imap_pass = `pass show domainCreds | head -n 1`

Adding contacts

One potential solution is to use this perl script. After setting Mutt’s query_command, it will query the LDAP server to search for contacts when typing in the recipient fields.

It works but, in my opinion, has 2 shortcomings.

Firstly, it relies on an internet connection, meaning it is not possible to compose an email offline to send it when you get online. Admittedly, it is rare that I will be doing much with emails offline, but I like to have the option.

Secondly, and more importantly for me, the lookup is slow, particularly if you have to connect through a slow VPN.

Local contacts

Storing contacts locally solves solves both problems, with the cost of using up disk space.

I created a contacts folder and ran the following in bash.

for i in {a..z}; do
    ldapsearch -H 'ldap://localhost:1389/'  -D 'DOMAIN\USERNAME' -w 'PASSWORD' -b ou=people  "mail=$i*" > "$i.people"
done

Change the credentials as needed and you will end up with a file for each letter of the alphabet. I did this because the Exchange server in question wouldn’t return more than 100 results at a time. Making a request for each letter meant I didn’t have to work out how to deal with paging with Davmail.

To use this in Mutt, I created a script called lookup that takes all the contacts and puts them in the form email<tab>Name<tab>title.

    #!/usr/bin/env bash

cat $HOME/Contacts/*.people | awk -v RS="\n\n" -v ORS="\n" '{gsub("\n","§",$0); print $0}' | while read line; do
    echo "$line" | tr '§' '\n' | egrep '^(cn|mail|title):' | cut -d':' -f2- | tr '\n' '\t'
done

Then in my Mutt configuration, I have the line:

set query_command = "/home/jonathan/Contacts/lookup | grep '%s'"

This means I can start typing a name or email address, press ctrl+t, and you can select a contact.

Contact lookup in VIM

If I am writing an email to more than one person, I find it much easier to add the recipients in vim using FZF. To make this happen, I pair the fzf.vim plugin with the following snippet in my vimrc file:

    function! s:make_email_list(lines)
    let l:emails = []
    "return type( a:lines )
    for email in a:lines
        let l:address = substitute(split( email, "\t" )[0], " ","","" )
        let l:name = split( email, "\t" )[1]
        let l:emails = add( l:emails, l:name . " <" . l:address . ">" )
    endfor
    return join(l:emails, ', ')
endfunction

inoremap <expr> <c-c> fzf#vim#complete({
            \ 'source': '$HOME/Contacts/lookup',
            \ 'reducer': function('<sid>make_email_list'),
            \ 'options': '--multi',
            \ 'down': '30%' })

This uses the same lookup script as before, but this time it is passed to FZF. I can fuzzy search for contacts by pressing ctrl+c.

This is only useful if you have edit_headers set in your .muttrc file which allows you to set and modify the email headers in your editor.