Using Mutt with Exchange
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.