1235 lines
64 KiB
HTML
1235 lines
64 KiB
HTML
|
<!DOCTYPE html>
|
|||
|
<html lang='en'><head><meta charset='utf-8' /><meta name='pinterest' content='nopin' /><link href='../../../../static/css/style.css' rel='stylesheet' type='text/css' /><link href='../../../../static/css/print.css' rel='stylesheet' type='text/css' media='print' /><title>The Homely Mutt / Steve Losh</title></head><body><header><a id='logo' href='https://stevelosh.com/'>Steve Losh</a><nav><a href='../../../index.html'>Blog</a> - <a href='https://stevelosh.com/projects/'>Projects</a> - <a href='https://stevelosh.com/photography/'>Photography</a> - <a href='https://stevelosh.com/links/'>Links</a> - <a href='https://stevelosh.com/rss.xml'>Feed</a></nav></header><hr class='main-separator' /><main id='page-blog-entry'><article><h1><a href='index.html'>The Homely Mutt</a></h1><p class='date'>Posted on October 1st, 2012.</p><p>Now that <a href="http://sparrowmailapp.com/">Sparrow</a> is <a href="http://www.theverge.com/2012/7/20/3172222/google-buys-sparrow-mail">effectively dead</a> many of its users will
|
|||
|
be looking for a new email client. If you're not afraid of the terminal you may
|
|||
|
want to give <a href="http://www.mutt.org/">Mutt</a> a try.</p>
|
|||
|
|
|||
|
<p>Mutt certainly isn't the prettiest email client around, and its
|
|||
|
setup/configuration process is one of the ugliest out there. But once you get
|
|||
|
it set up it's got a lot of advantages over many other email clients.</p>
|
|||
|
|
|||
|
<p>In this post I'll show you how to set up Mutt on OS X like I do.</p>
|
|||
|
|
|||
|
<ol class="table-of-contents"><li><a href="index.html#s1-how-i-use-email">How I Use Email</a></li><li><a href="index.html#s2-other-guides-and-resources">Other Guides and Resources</a></li><li><a href="index.html#s3-overview">Overview</a></li><li><a href="index.html#s4-getting-email">Getting Email</a><ol><li><a href="index.html#s5-why-local-email">Why Local Email?</a></li><li><a href="index.html#s6-the-alternative">The Alternative</a></li><li><a href="index.html#s7-installing-offlineimap">Installing offlineimap</a></li><li><a href="index.html#s8-configuring-offlineimap">Configuring offlineimap</a></li><li><a href="index.html#s9-retrieving-passwords">Retrieving Passwords</a></li><li><a href="index.html#s10-running-offlineimap">Running offlineimap</a></li></ol></li><li><a href="index.html#s11-mutt">Mutt!</a><ol><li><a href="index.html#s12-installing">Installing</a></li><li><a href="index.html#s13-configuring">Configuring</a></li><li><a href="index.html#s14-running">Running</a></li></ol></li><li><a href="index.html#s15-reading-email">Reading Email</a><ol><li><a href="index.html#s16-the-index">The Index</a></li><li><a href="index.html#s17-the-pager">The Pager</a></li><li><a href="index.html#s18-attachments">Attachments</a></li><li><a href="index.html#s19-urls">URLs</a></li></ol></li><li><a href="index.html#s20-writing-email">Writing Email</a></li><li><a href="index.html#s21-sending-email">Sending Email</a></li><li><a href="index.html#s22-postponing-drafts">Postponing Drafts</a></li><li><a href="index.html#s23-contacts">Contacts</a><ol><li><a href="index.html#s24-autocompleting">Autocompleting</a></li><li><a href="index.html#s25-adding-contacts">Adding Contacts</a></li></ol></li><li><a href="index.html#s26-searching-email">Searching Email</a><ol><li><a href="index.html#s27-vanilla-searching">Vanilla Searching</a></li><li><a href="index.html#s28-vanilla-limiting">Vanilla Limiting</a></li><li><a href="index.html#s29-full-text-searching">Full-Text Searching</a></li></ol></li><li><a href="index.html#s30-conclusion">Conclusion</a></li></ol>
|
|||
|
|
|||
|
<h2 id="s1-how-i-use-email"><a href="index.html#s1-how-i-use-email">How I Use Email</a></h2>
|
|||
|
|
|||
|
<p>This setup is going to be specific to the way I work with email. Notably:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li>I have a Google Apps account that provides my steve@stevelosh.com email
|
|||
|
address.</li>
|
|||
|
<li>I have many other email addresses, but they all simply forward to my main one.</li>
|
|||
|
<li>All mail I send comes from steve@stevelosh.com.</li>
|
|||
|
<li>I store my contacts in the OS X address book.</li>
|
|||
|
<li>All email comes into my inbox (or to a folder for a specific mailing list).</li>
|
|||
|
<li>Once I'm done with an email, I remove it from my inbox and it lives in the
|
|||
|
"All Mail" archive. I don't sort email into folders after it arrives.</li>
|
|||
|
<li>I sometimes read email offline and mark it for deletion, then sync that
|
|||
|
deletion back to the server once I get online again.</li>
|
|||
|
<li>Sometimes I write email without an internet connection and send it once I get
|
|||
|
connected again.</li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<p>My email setup is tailored around those requirements, so that's what it does
|
|||
|
best. Mutt is very configurable though, so if you work differently you can
|
|||
|
probably bend it to make it do what you want.</p>
|
|||
|
|
|||
|
<p>In particular, extending this setup to work with multiple email accounts
|
|||
|
wouldn't be too much trouble. I used to work with two separate accounts until
|
|||
|
I said "screw it, I'll just use the one".</p>
|
|||
|
|
|||
|
<h2 id="s2-other-guides-and-resources"><a href="index.html#s2-other-guides-and-resources">Other Guides and Resources</a></h2>
|
|||
|
|
|||
|
<p>I've used a lot of other guides to figure out how to get this giant Rube
|
|||
|
Goldberg machine of an email client working. Here are a few of them:</p>
|
|||
|
|
|||
|
<ul>
|
|||
|
<li><a href="http://thomas.pelletier.im/2010/10/low-memory-mail-client/">http://thomas.pelletier.im/2010/10/low-memory-mail-client/</a></li>
|
|||
|
<li><a href="http://www.andrews-corner.org/mutt.html">http://www.andrews-corner.org/mutt.html</a></li>
|
|||
|
<li><a href="http://jstorimer.com/shells/2010/01/19/using-mutt-with-gmail-on-osx.html">http://jstorimer.com/shells/2010/01/19/using-mutt-with-gmail-on-osx.html</a></li>
|
|||
|
<li><a href="http://www.vijaykiran.com/2010/01/27/mutt-for-gmail-imap-on-mac-os-x/">http://www.vijaykiran.com/2010/01/27/mutt-for-gmail-imap-on-mac-os-x/</a></li>
|
|||
|
<li><a href="http://hynek.me/articles/my-mutt-gmail-setup/">http://hynek.me/articles/my-mutt-gmail-setup/</a></li>
|
|||
|
<li><a href="https://wiki.archlinux.org/index.php/Mutt">https://wiki.archlinux.org/index.php/Mutt</a></li>
|
|||
|
<li><a href="http://linsec.ca/Using_mutt_on_OS_X">http://linsec.ca/Using_mutt_on_OS_X</a></li>
|
|||
|
<li><a href="http://www.mutt.org/doc/manual/manual.html">http://www.mutt.org/doc/manual/manual.html</a></li>
|
|||
|
<li><a href="http://pbrisbin.com/posts/two_accounts_in_mutt">http://pbrisbin.com/posts/two_accounts_in_mutt</a></li>
|
|||
|
<li><a href="http://upsilon.cc/~zack/blog/posts/2011/01/how_to_use_Notmuch_with_Mutt/">http://upsilon.cc/~zack/blog/posts/2011/01/how_to_use_Notmuch_with_Mutt/</a></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<h2 id="s3-overview"><a href="index.html#s3-overview">Overview</a></h2>
|
|||
|
|
|||
|
<p>I'm going to give it to you straight: getting this whole contraption set up is
|
|||
|
going to take at least an hour from start to finish, not counting the time it'll
|
|||
|
take to download all of your email and install stuff. Set aside an evening if
|
|||
|
you're serious about this.</p>
|
|||
|
|
|||
|
<p>It's an investment, and you might not want to make it. If not, go use
|
|||
|
Thunderbird, er, Sparrow, er, I don't know, the Gmail web interface or
|
|||
|
something.</p>
|
|||
|
|
|||
|
<p>Mutt on its own doesn't do very much, so we're going to combine it with a few
|
|||
|
other things to get the job done. Here's a bird's eye view of what it'll look
|
|||
|
like when we're done:</p>
|
|||
|
|
|||
|
<p><img src="../../../../static/images/blog/2012/10/what-the-mutt.png" alt="Diagram"></p>
|
|||
|
|
|||
|
<p>If this diagram doesn't make you run screaming you might just be masochistic
|
|||
|
enough to make it through the initial setup of Mutt. If you do, you'll be
|
|||
|
rewarded with email bliss that won't go away when Google or Facebook decide to
|
|||
|
toss some money around.</p>
|
|||
|
|
|||
|
<h2 id="s4-getting-email"><a href="index.html#s4-getting-email">Getting Email</a></h2>
|
|||
|
|
|||
|
<p>First thing's first: we're going to pull down our email from Gmail to our local
|
|||
|
machine. All of it. It'll take a while the first time you sync, but has a few
|
|||
|
benefits.</p>
|
|||
|
|
|||
|
<h3 id="s5-why-local-email"><a href="index.html#s5-why-local-email">Why Local Email?</a></h3>
|
|||
|
|
|||
|
<p>Having a local copy of all of your email means you've always got access to it,
|
|||
|
no matter where you are. Looking for that one person's address they emailed you
|
|||
|
six years ago when you're trying to find their house and you don't have an
|
|||
|
internet connection? No problem, it's on your hard drive.</p>
|
|||
|
|
|||
|
<p>This also acts as a backup in case Google ever decides to kill your Gmail
|
|||
|
account. It'll be stored in a common format that a lot of programs can read, so
|
|||
|
you've got a safety net. And the email is stored as normal files, so if you use
|
|||
|
something like Time Machine or <a href="http://www.backblaze.com/partner/af3574">Backblaze</a> that's yet another free backup.</p>
|
|||
|
|
|||
|
<p>In this setup <strong>all of your email is stored as plain text</strong>. If you want it
|
|||
|
encrypted just use OS X's full-disk encryption and you're set.</p>
|
|||
|
|
|||
|
<p>I use <a href="http://offlineimap.org/">offlineimap</a> to pull email down from Gmail and get it on my hard drive.
|
|||
|
Offlineimap will also sync any changes you make to this local copy back up to
|
|||
|
Gmail.</p>
|
|||
|
|
|||
|
<h3 id="s6-the-alternative"><a href="index.html#s6-the-alternative">The Alternative</a></h3>
|
|||
|
|
|||
|
<p>You may not care as much about reading your email offline as I do. If you can
|
|||
|
tolerate always needing an internet connection to read your mail, you can skip
|
|||
|
this painful section and follow <a href="http://empt1e.blogspot.com/2009/10/using-mutt-with-gmail-imap-complete.html">this guide</a> instead.</p>
|
|||
|
|
|||
|
<p>You'll probably still find the other sections of this post interesting though.</p>
|
|||
|
|
|||
|
<h3 id="s7-installing-offlineimap"><a href="index.html#s7-installing-offlineimap">Installing offlineimap</a></h3>
|
|||
|
|
|||
|
<p>I've gone through a number of laptops in the past few years, and each time
|
|||
|
I spend a painful half hour or so screwing around with the latest version of
|
|||
|
offlineimap's backwards-incompatible changes.</p>
|
|||
|
|
|||
|
<p>If you're determined to run the latest version of offlineimap, you can install
|
|||
|
it with pip or something. If you just want to download your fucking email and
|
|||
|
get on with your life, you can follow the instructions I've laid out for you
|
|||
|
here:</p>
|
|||
|
|
|||
|
<pre><code>git clone git://github.com/spaetz/offlineimap.git
|
|||
|
cd offlineimap
|
|||
|
git checkout 679c491c56c981961e18aa43b31955900491d7a3
|
|||
|
python setup.py install</code></pre>
|
|||
|
|
|||
|
<p>That's the version I'm using. It works. You can use a newer one if you want,
|
|||
|
but expect to spend some time figuring out how to fix the configuration in this
|
|||
|
post to work with whatever breaking changes have been made since then. The last
|
|||
|
time I tried this I got to rewrite all my nametrans stuff. That was fun.</p>
|
|||
|
|
|||
|
<h3 id="s8-configuring-offlineimap"><a href="index.html#s8-configuring-offlineimap">Configuring offlineimap</a></h3>
|
|||
|
|
|||
|
<p>Once you've got offlineimap installed, you'll need to create
|
|||
|
a <code>~/.offlineimaprc</code> file. You can keep it in your dotfiles repo and symlink it
|
|||
|
into place if you want. Here's a sample to get you started:</p>
|
|||
|
|
|||
|
<pre><code>[general]
|
|||
|
ui = TTY.TTYUI
|
|||
|
accounts = SteveLosh
|
|||
|
pythonfile=~/.mutt/offlineimap.py
|
|||
|
fsync = False
|
|||
|
|
|||
|
[Account SteveLosh]
|
|||
|
localrepository = SteveLosh-Local
|
|||
|
remoterepository = SteveLosh-Remote
|
|||
|
status_backend = sqlite
|
|||
|
postsynchook = notmuch new
|
|||
|
|
|||
|
[Repository SteveLosh-Local]
|
|||
|
type = Maildir
|
|||
|
localfolders = ~/.mail/steve-stevelosh.com
|
|||
|
nametrans = lambda folder: {'drafts': '[Gmail]/Drafts',
|
|||
|
'sent': '[Gmail]/Sent Mail',
|
|||
|
'flagged': '[Gmail]/Starred',
|
|||
|
'trash': '[Gmail]/Trash',
|
|||
|
'archive': '[Gmail]/All Mail',
|
|||
|
}.get(folder, folder)
|
|||
|
|
|||
|
[Repository SteveLosh-Remote]
|
|||
|
maxconnections = 1
|
|||
|
type = Gmail
|
|||
|
remoteuser = steve@stevelosh.com
|
|||
|
remotepasseval = get_keychain_pass(account="steve@stevelosh.com", server="imap.gmail.com")
|
|||
|
realdelete = no
|
|||
|
nametrans = lambda folder: {'[Gmail]/Drafts': 'drafts',
|
|||
|
'[Gmail]/Sent Mail': 'sent',
|
|||
|
'[Gmail]/Starred': 'flagged',
|
|||
|
'[Gmail]/Trash': 'trash',
|
|||
|
'[Gmail]/All Mail': 'archive',
|
|||
|
}.get(folder, folder)
|
|||
|
folderfilter = lambda folder: folder not in ['[Gmail]/Trash',
|
|||
|
'Nagios',
|
|||
|
'Django',
|
|||
|
'Flask',
|
|||
|
'[Gmail]/Important',
|
|||
|
'[Gmail]/Spam',
|
|||
|
]</code></pre>
|
|||
|
|
|||
|
<p>It's kind of long, so let's go through it line by line and see what's going on.</p>
|
|||
|
|
|||
|
<pre><code>[general]
|
|||
|
ui = TTY.TTYUI
|
|||
|
accounts = SteveLosh
|
|||
|
pythonfile=~/.mutt/offlineimap.py
|
|||
|
fsync = False</code></pre>
|
|||
|
|
|||
|
<p>First we tell offlineimap to use the <code>TTY.TTYUI</code> ui. Yes, this program that
|
|||
|
syncs your email has multiple user interfaces. I guess if you can't decide what
|
|||
|
color the bikeshed should be you can just build a whole bunch of bikesheds
|
|||
|
instead.</p>
|
|||
|
|
|||
|
<p>Then we specify the accounts. There's only one because as I said before:
|
|||
|
I only use a single email account that all my addresses forward to. If you
|
|||
|
wanted to have many, you'd change this line.</p>
|
|||
|
|
|||
|
<p>The <code>pythonfile</code> is just a file that offlineimap will parse (as Python) before
|
|||
|
loading the rest of the config, so you can define custom helper functions more
|
|||
|
easily. We'll see more of this later.</p>
|
|||
|
|
|||
|
<p>We're also telling offlineimap that it doesn't need to fsync after every single
|
|||
|
operation. This will speed things up, and since it's just a local copy it's
|
|||
|
typically not a big deal if we lose an email here and there from a crash (it'll
|
|||
|
just be synced the next time anyway).</p>
|
|||
|
|
|||
|
<pre><code>[Account SteveLosh]
|
|||
|
localrepository = SteveLosh-Local
|
|||
|
remoterepository = SteveLosh-Remote
|
|||
|
status_backend = sqlite</code></pre>
|
|||
|
|
|||
|
<p>This next section hooks up a few things. First, it tells offlineimap which
|
|||
|
local and remote repositories to use for the account. Manual configuration
|
|||
|
instead of sane defaults is a recurring theme we'll see throughout this process.</p>
|
|||
|
|
|||
|
<p>Hey, I titled the entry "The <em>Homely</em> Mutt" for a reason.</p>
|
|||
|
|
|||
|
<p>We're also going to use a SQLite-based cache for this account. If you don't
|
|||
|
already have SQLite you'll want to get it with <code>brew install sqlite</code>.</p>
|
|||
|
|
|||
|
<pre><code>[Repository SteveLosh-Local]
|
|||
|
type = Maildir
|
|||
|
localfolders = ~/.mail/steve-stevelosh.com
|
|||
|
nametrans = lambda folder: {'drafts': '[Gmail]/Drafts',
|
|||
|
'sent': '[Gmail]/Sent Mail',
|
|||
|
'flagged': '[Gmail]/Starred',
|
|||
|
'trash': '[Gmail]/Trash',
|
|||
|
'archive': '[Gmail]/All Mail',
|
|||
|
}.get(folder, folder)</code></pre>
|
|||
|
|
|||
|
<p>Now we're getting to the meat of the configuration. This "local repository" is
|
|||
|
going to be the mail as it sits on our hard drive. We're going to use the
|
|||
|
<a href="https://en.wikipedia.org/wiki/Maildir">Maildir format</a> because it plays nicely with Mutt (and tons of other
|
|||
|
stuff).</p>
|
|||
|
|
|||
|
<p>Then we specify the path where we're going to keep the mail. This is going to
|
|||
|
take a lot of space if you've got a lot of mail. Attachments are downloaded
|
|||
|
too. When I said you're getting an offline copy of all your email I meant <em>all</em>
|
|||
|
of it.</p>
|
|||
|
|
|||
|
<p>I think offlineimap needs the <code>~/.mail</code> directory created for it. It's been
|
|||
|
a while since I did this, so I might be wrong, but if it complains about not
|
|||
|
being able to access the mail folders just go ahead and <code>mkdir ~/.mail</code>.</p>
|
|||
|
|
|||
|
<p>Next we have the craziest part of the configuration: name translation.</p>
|
|||
|
|
|||
|
<p>Here's the issue: offlineimap needs to know how to translate the names of
|
|||
|
folders on the IMAP server to folder names on your hard drive.</p>
|
|||
|
|
|||
|
<p>Also, Gmail doesn't actually use <em>folders</em> but its own concept called "labels".
|
|||
|
But since the IMAP protocol doesn't know about labels, it fakes them by making
|
|||
|
them appear to be folders.</p>
|
|||
|
|
|||
|
<p>User-created labels in Gmail (like "Mercurial" or "Clients") will appear as
|
|||
|
folders with those names through IMAP.</p>
|
|||
|
|
|||
|
<p>Built-in, special Gmail folders have names that start with <code>[Gmail]/</code>. We need
|
|||
|
to turn those into something sane for our hard drive, so that's what this
|
|||
|
nametrans setting is for. It's a Python function that takes the remote folder
|
|||
|
name and returns the name that should be used on your local hard drive.</p>
|
|||
|
|
|||
|
<p>Yes, you read that right. This is Python code embedded in the right hand side
|
|||
|
of an INI file's setting assignment. I am not fucking with you, this is
|
|||
|
seriously how you do it. Go ahead and crack open that beer now.</p>
|
|||
|
|
|||
|
<p>So the "Sent Mail" folder in your Gmail account will be synced to
|
|||
|
<code>~/.mail/steve-stevelosh.com/sent</code>. Cool.</p>
|
|||
|
|
|||
|
<p>(No, I don't know what would happen if you created a label called <code>[Gmail]/All
|
|||
|
Mail</code> in Gmail. If you try please let me know, but I take no responsibility if
|
|||
|
it ends with all your email being deleted.)</p>
|
|||
|
|
|||
|
<pre><code>[Repository SteveLosh-Remote]
|
|||
|
maxconnections = 1
|
|||
|
type = Gmail
|
|||
|
remoteuser = steve@stevelosh.com
|
|||
|
remotepasseval = get_keychain_pass(account="steve@stevelosh.com", server="imap.gmail.com")
|
|||
|
realdelete = no
|
|||
|
nametrans = lambda folder: {'[Gmail]/Drafts': 'drafts',
|
|||
|
'[Gmail]/Sent Mail': 'sent',
|
|||
|
'[Gmail]/Starred': 'flagged',
|
|||
|
'[Gmail]/Trash': 'trash',
|
|||
|
'[Gmail]/All Mail': 'archive',
|
|||
|
}.get(folder, folder)
|
|||
|
folderfilter = lambda folder: folder not in ['[Gmail]/Trash',
|
|||
|
'Nagios',
|
|||
|
'Django',
|
|||
|
'Flask',
|
|||
|
'[Gmail]/Important',
|
|||
|
'[Gmail]/Spam',
|
|||
|
]</code></pre>
|
|||
|
|
|||
|
<p>Finally, the home stretch. The last section described the folder on our local
|
|||
|
hard drive, and this one describes our Gmail account.</p>
|
|||
|
|
|||
|
<p>First, we tell offlineimap to only ever use a single connection at a time. You
|
|||
|
can try increasing this number for better performance, but in my experience
|
|||
|
Google is not afraid to enforce its rate limits and would cut me off fairly
|
|||
|
often when I tried that. Just leave it at one if you want to be safe.</p>
|
|||
|
|
|||
|
<p>Next is the type. Luckily offlineimap provides a <code>Gmail</code> type that handles
|
|||
|
a lot of the craziness that is Gmail's IMAP setup. Nice.</p>
|
|||
|
|
|||
|
<p>Then we have the username. Nothing special here, except that if you have
|
|||
|
a non-apps account (i.e.: an actual vanilla Gmail account) you may or may not
|
|||
|
need to include the <code>@gmail.com</code> in the username. I don't know. If one doesn't
|
|||
|
work, just try the other.</p>
|
|||
|
|
|||
|
<p>Next we have <code>remotepasseval</code>. This is a bit of Python code (drink!) that
|
|||
|
should return the password for the account.</p>
|
|||
|
|
|||
|
<p>What is this <code>get_keychain_pass</code> function? Well, remember when we saw the
|
|||
|
<code>pythonfile</code> setting back in the general section? It's a function defined in
|
|||
|
there. I'll talk about that in the next section, for now just accept that it
|
|||
|
works.</p>
|
|||
|
|
|||
|
<p>Next we set <code>realdelete</code> to no. If this is set to yes, then deleting an email
|
|||
|
in your inbox would actually delete it entirely. When you set it to no, then
|
|||
|
deleting an email from your inbox (or any label's folder) will leave it in
|
|||
|
Gmail's All Mail.</p>
|
|||
|
|
|||
|
<p>If you want to really delete an email, you'll need to delete it from All Mail
|
|||
|
(which is named archive on our local filesystem, remember?). I feel like this
|
|||
|
is a good compromise. I rarely care about actually deleting mail, given that
|
|||
|
I have many unused gigabytes available on Gmail.</p>
|
|||
|
|
|||
|
<p>Next we have another nametrans setting. This is a Python function (drink!) just
|
|||
|
like the one for the local repository, except it goes in the other direction.
|
|||
|
It takes the name of a local folder and returns the name of the folder on the
|
|||
|
IMAP server. Knowing this, it should be easy to understand this setting.</p>
|
|||
|
|
|||
|
<p>Finally, we have <code>folderfilter</code>. This is a Python function (drink!) that takes
|
|||
|
a <strong>remote</strong> folder name and returns <code>True</code> if that folder should be synced, or
|
|||
|
<code>False</code> if it should <strong>not</strong> be synced. I've chosen to skip syncing my Spam and
|
|||
|
Trash folders, as well as a few mailing list labels I don't check all that
|
|||
|
often. Customize this to your own taste.</p>
|
|||
|
|
|||
|
<h3 id="s9-retrieving-passwords"><a href="index.html#s9-retrieving-passwords">Retrieving Passwords</a></h3>
|
|||
|
|
|||
|
<p>We're almost ready, but there's one more thing we need to do, and that's
|
|||
|
implement a secure way for offlineimap to get access to our Gmail password.</p>
|
|||
|
|
|||
|
<p>If you don't care too much about security, you <em>can</em> configure offlineimap with
|
|||
|
a plaintext password right in the config file. But don't do that. It'll only
|
|||
|
take a minute to do this securely.</p>
|
|||
|
|
|||
|
<p>First, you need to add your Gmail password into your OS X keychain. Open the
|
|||
|
Keychain Access app and press the <code>+</code> button:</p>
|
|||
|
|
|||
|
<p><img src="../../../../static/images/blog/2012/10/keychain-1.png" alt="Keychain 1"></p>
|
|||
|
|
|||
|
<p>Then fill out the form. The "Keychain Item Name" should be
|
|||
|
<code>http://imap.gmail.com</code>. The "Account Name" should be your email address. The
|
|||
|
password should be your password:</p>
|
|||
|
|
|||
|
<p><img src="../../../../static/images/blog/2012/10/keychain-2.png" alt="Keychain 2"></p>
|
|||
|
|
|||
|
<p>Press "Add". Now repeat the process for the SMTP server. The "Keychain Item
|
|||
|
Name" should be <code>smtp://smtp.gmail.com</code>. The "Account Name" should be your
|
|||
|
email address. The password should be your password:</p>
|
|||
|
|
|||
|
<p><img src="../../../../static/images/blog/2012/10/keychain-3.png" alt="Keychain 3"></p>
|
|||
|
|
|||
|
<p>Now we need to create the <code>offlineimap.py</code> file we pointed offlineimap to
|
|||
|
earlier. It needs to contain the <code>get_keychain_pass</code> function, which takes an
|
|||
|
<code>account</code> and <code>server</code> and return the password. Here's the file I'm using:</p>
|
|||
|
|
|||
|
<pre><code><span class="code"><span class="comment">#!/usr/bin/python
|
|||
|
</span><span class="symbol">import</span> re, subprocess
|
|||
|
<span class="special">def</span><span class="keyword"> get_keychain_pass</span><span class="paren1">(<span class="code">account=None, server=None</span>)</span>:
|
|||
|
params = <span class="paren1">{<span class="code">
|
|||
|
<span class="string">'security'</span>: <span class="string">'/usr/bin/security'</span>,
|
|||
|
<span class="string">'command'</span>: <span class="string">'find-internet-password'</span>,
|
|||
|
<span class="string">'account'</span>: account,
|
|||
|
<span class="string">'server'</span>: server,
|
|||
|
<span class="string">'keychain'</span>: <span class="string">'/Users/sjl/Library/Keychains/login.keychain'</span>,
|
|||
|
</span>}</span>
|
|||
|
command = <span class="string">"sudo -u sjl %(security)s -v %(command)s -g -a %(account)s -s %(server)s %(keychain)s"</span> % params
|
|||
|
output = subprocess.check_output<span class="paren1">(<span class="code">command, shell=True, stderr=subprocess.STDOUT</span>)</span>
|
|||
|
outtext = <span class="paren1">[<span class="code">l <span class="symbol">for</span> l <span class="symbol">in</span> output.splitlines<span class="paren2">(<span class="code"></span>)</span>
|
|||
|
<span class="symbol">if</span> l.startswith<span class="paren2">(<span class="code"><span class="string">'password: '</span></span>)</span></span>]</span><span class="paren1">[<span class="code">0</span>]</span>
|
|||
|
|
|||
|
<span class="symbol">return</span> re.match<span class="paren1">(<span class="code">r<span class="string">'password: "(.*)"'</span>, outtext</span>)</span>.group<span class="paren1">(<span class="code">1</span>)</span></span></code></pre>
|
|||
|
|
|||
|
<p>In a nutshell, it uses <code>/usr/bin/security</code> to retrieve the password. Read
|
|||
|
through the code if you're curious.</p>
|
|||
|
|
|||
|
<p>This is not <em>completely</em> secure, but it's better than having your password in
|
|||
|
a plaintext file in your home directory.</p>
|
|||
|
|
|||
|
<p>Whew! Time to actually run this thing and pull down our email!</p>
|
|||
|
|
|||
|
<h3 id="s10-running-offlineimap"><a href="index.html#s10-running-offlineimap">Running offlineimap</a></h3>
|
|||
|
|
|||
|
<p>Assuming everything is in place, open a terminal and run offlineimap:</p>
|
|||
|
|
|||
|
<pre><code>offlineimap
|
|||
|
</code></pre>
|
|||
|
|
|||
|
<p>Go read a book, because this is going to pull down all the email (with
|
|||
|
attachments) in any folders you didn't exclude in the config file.</p>
|
|||
|
|
|||
|
<p><strong>If there's an error, stop and figure out what went wrong</strong>. Remember,
|
|||
|
offlineimap is a <em>two-way</em> sync, so there's always the possibility it'll eat
|
|||
|
your email if you seriously mess something up! I wish it had a
|
|||
|
<code>--dont-touch-remote</code> option you could use as a safety net for the original
|
|||
|
sync, but it doesn't, so be careful!</p>
|
|||
|
|
|||
|
<p>In the future you can use <code>offlineimap -q</code> to run it in "quick mode". It'll
|
|||
|
perform fewer checks but will generally be much faster.</p>
|
|||
|
|
|||
|
<p>If you want to set up offlineimap to run every 5 minutes or so, you can use
|
|||
|
<code>launchd</code>. <code>cron</code> does not work for some reason. I'm not entirely sure why.</p>
|
|||
|
|
|||
|
<p>Personally I actually <em>like</em> having to press a key to fetch new mail. It's less
|
|||
|
of a distraction than having new mail rolling in all the time. I can get new
|
|||
|
email when I'm ready to actually look at it, rather than having it nagging me
|
|||
|
all the time.</p>
|
|||
|
|
|||
|
<p>The great part about offlineimap is that once you've got it configured and it's
|
|||
|
successfully run once, it's pretty much rock solid from then on. You can run it
|
|||
|
often, <code>Ctrl-c</code> it, put the laptop to sleep in the middle of a run, or <code>kill -9</code>
|
|||
|
it, and it still won't lose emails. On the next sync it'll fix anything that's
|
|||
|
missing.</p>
|
|||
|
|
|||
|
<h2 id="s11-mutt"><a href="index.html#s11-mutt">Mutt!</a></h2>
|
|||
|
|
|||
|
<p>Now that you've got your email on your computer, it's finally time to start
|
|||
|
using Mutt itself!</p>
|
|||
|
|
|||
|
<h3 id="s12-installing"><a href="index.html#s12-installing">Installing</a></h3>
|
|||
|
|
|||
|
<p>Mutt can be installed in a bunch of different ways, but the easiest is through
|
|||
|
Homebrew:</p>
|
|||
|
|
|||
|
<pre><code>brew install mutt --sidebar-patch</code></pre>
|
|||
|
|
|||
|
<p>The sidebar patch is a third-party patch that adds a sidebar to Mutt. I don't
|
|||
|
know why it's not in core Mutt because it's insanely useful. Oh well, at least
|
|||
|
Homebrew makes it simple to get.</p>
|
|||
|
|
|||
|
<p>That's pretty much it for installation, but don't get too relaxed because you're
|
|||
|
far from done.</p>
|
|||
|
|
|||
|
<h3 id="s13-configuring"><a href="index.html#s13-configuring">Configuring</a></h3>
|
|||
|
|
|||
|
<p>Mutt is <em>very</em> configurable. This is great once you've become a power user and
|
|||
|
want to mold it to your will, but terrible when you're just getting started.</p>
|
|||
|
|
|||
|
<p>Mutt settings are kept in a <code>~/.muttrc</code> file. If this file doesn't exist Mutt
|
|||
|
will look for <code>~/.mutt/muttrc</code> (note the lack of a dot in the filename), so you
|
|||
|
can put it there if you prefer.</p>
|
|||
|
|
|||
|
<p>Let's start by creating a basic <code>~/.muttrc</code> piece by piece (a lot of this was
|
|||
|
taken from <a href="http://pbrisbin.com/posts/two_accounts_in_mutt">this article</a>).</p>
|
|||
|
|
|||
|
<p>Once you've got a bit of Mutt under your belt you'll want to read <a href="http://www.mutt.org/doc/manual/manual-6.html">the
|
|||
|
documentation</a> for these settings, but for now just use them to keep
|
|||
|
things sane.</p>
|
|||
|
|
|||
|
<pre><code># Paths ----------------------------------------------
|
|||
|
set folder = ~/.mail # mailbox location
|
|||
|
set alias_file = ~/.mutt/alias # where to store aliases
|
|||
|
set header_cache = ~/.mutt/cache/headers # where to store headers
|
|||
|
set message_cachedir = ~/.mutt/cache/bodies # where to store bodies
|
|||
|
set certificate_file = ~/.mutt/certificates # where to store certs
|
|||
|
set mailcap_path = ~/.mutt/mailcap # entries for filetypes
|
|||
|
set tmpdir = ~/.mutt/temp # where to keep temp files
|
|||
|
set signature = ~/.mutt/sig # my signature file</code></pre>
|
|||
|
|
|||
|
<p>Here we tell Mutt where to find the various folders it needs.</p>
|
|||
|
|
|||
|
<pre><code># Basic Options --------------------------------------
|
|||
|
set wait_key = no # shut up, mutt
|
|||
|
set mbox_type = Maildir # mailbox type
|
|||
|
set timeout = 3 # idle time before scanning
|
|||
|
set mail_check = 0 # minimum time between scans
|
|||
|
unset move # gmail does that
|
|||
|
set delete # don't ask, just do
|
|||
|
unset confirmappend # don't ask, just do!
|
|||
|
set quit # don't ask, just do!!
|
|||
|
unset mark_old # read/new is good enough for me
|
|||
|
set beep_new # bell on new mails
|
|||
|
set pipe_decode # strip headers and eval mimes when piping
|
|||
|
set thorough_search # strip headers and eval mimes before searching</code></pre>
|
|||
|
|
|||
|
<p>These are some basic options to make Mutt behave a bit more sanely.</p>
|
|||
|
|
|||
|
<pre><code># Sidebar Patch --------------------------------------
|
|||
|
set sidebar_delim = ' │'
|
|||
|
set sidebar_visible = yes
|
|||
|
set sidebar_width = 24
|
|||
|
color sidebar_new color221 color233</code></pre>
|
|||
|
|
|||
|
<p>These options are specific to the sidebar patch.</p>
|
|||
|
|
|||
|
<pre><code># Status Bar -----------------------------------------
|
|||
|
set status_chars = " *%A"
|
|||
|
set status_format = "───[ Folder: %f ]───[%r%m messages%?n? (%n new)?%?d? (%d to delete)?%?t? (%t tagged)? ]───%>─%?p?( %p postponed )?───"</code></pre>
|
|||
|
|
|||
|
<p>This gives us a pretty status bar with the information we care about (and none
|
|||
|
of the stuff we don't).</p>
|
|||
|
|
|||
|
<pre><code># Header Options -------------------------------------
|
|||
|
ignore * # ignore all headers
|
|||
|
unignore from: to: cc: date: subject: # show only these
|
|||
|
unhdr_order * # some distros order things by default
|
|||
|
hdr_order from: to: cc: date: subject: # and in this order</code></pre>
|
|||
|
|
|||
|
<p>These options hide some of the extra email headers we don't care about when
|
|||
|
viewing and composing email.</p>
|
|||
|
|
|||
|
<p>Now it's time to fill on our account details:</p>
|
|||
|
|
|||
|
<pre><code># Account Settings -----------------------------------
|
|||
|
|
|||
|
# Default inbox.
|
|||
|
set spoolfile = "+steve-stevelosh.com/INBOX"
|
|||
|
|
|||
|
# Alternate email addresses.
|
|||
|
alternates sjl@pculture.org still\.?life@gmail.com steve@ladyluckblues.com steve@pculture.org
|
|||
|
|
|||
|
# Mailboxes to show in the sidebar.
|
|||
|
mailboxes +steve-stevelosh.com/INBOX \
|
|||
|
+steve-stevelosh.com/vim \
|
|||
|
+steve-stevelosh.com/clojure \
|
|||
|
+steve-stevelosh.com/python \
|
|||
|
+steve-stevelosh.com/mercurial \
|
|||
|
+steve-stevelosh.com/archive \
|
|||
|
+steve-stevelosh.com/sent \
|
|||
|
+steve-stevelosh.com/drafts \
|
|||
|
|
|||
|
# Other special folders.
|
|||
|
set mbox = "+steve-stevelosh.com/archive"
|
|||
|
set postponed = "+steve-stevelosh.com/drafts"</code></pre>
|
|||
|
|
|||
|
<p>Most of those should be self-explanatory. Fill in the appropriate values for
|
|||
|
your mail folder(s).</p>
|
|||
|
|
|||
|
<p>We'll add more as we go through the next few sections, but that's enough to get
|
|||
|
us started.</p>
|
|||
|
|
|||
|
<h3 id="s14-running"><a href="index.html#s14-running">Running</a></h3>
|
|||
|
|
|||
|
<p>Now that you've got Mutt configured you can run it:</p>
|
|||
|
|
|||
|
<pre><code>mutt</code></pre>
|
|||
|
|
|||
|
<p>I like to always be in my <code>~/Desktop</code> folder when in Mutt, so that when I save
|
|||
|
emails or attachments they go there by default. I have a little shell function
|
|||
|
set up that <code>cd</code>s there for me before running Mutt:</p>
|
|||
|
|
|||
|
<pre><code>alias mutt 'cd ~/Desktop && mutt'</code></pre>
|
|||
|
|
|||
|
<p>If you run the <a href="http://ridiculousfish.com/shell/">new fish shell</a>, this is going to cause problems later
|
|||
|
(long story, but it's related to the <code>read</code> builtin). Do yourself a favor and
|
|||
|
head those confusing issues off at the pass with a fish function:</p>
|
|||
|
|
|||
|
<pre><code>function mutt
|
|||
|
bash --login -c 'cd ~/Desktop; /usr/local/bin/mutt' $argv;
|
|||
|
end</code></pre>
|
|||
|
|
|||
|
<p>Remember that if you use another shell like this you'll want to set up any
|
|||
|
aliases and your <code>PATH</code> for that shell properly (probably identically to your
|
|||
|
main shell).</p>
|
|||
|
|
|||
|
<h2 id="s15-reading-email"><a href="index.html#s15-reading-email">Reading Email</a></h2>
|
|||
|
|
|||
|
<p>Once you start Mutt you should be looking at a list of the email in your inbox.
|
|||
|
If so: congratulations! If not: stop and figure out what went wrong.</p>
|
|||
|
|
|||
|
<h3 id="s16-the-index"><a href="index.html#s16-the-index">The Index</a></h3>
|
|||
|
|
|||
|
<p>When viewing a folder, Mutt presents you with a list of your email. This view
|
|||
|
is called the "index":</p>
|
|||
|
|
|||
|
<p><img src="../../../../static/images/blog/2012/10/mutt-index.png" alt="Mutt's Index"></p>
|
|||
|
|
|||
|
<p>This entry isn't meant be a guide to setting up Mutt on OS X. For a full guide
|
|||
|
on how to <em>use</em> Mutt, you can Google around for some tutorials, or just learn as
|
|||
|
you go with <code>?</code>. The <code>?</code> key will show you a list of all the keys you can use
|
|||
|
wherever you currently are, and what they do.</p>
|
|||
|
|
|||
|
<p>Let's add a few lines to our <code>~/.muttrc</code> to make the index view behave a bit
|
|||
|
more nicely:</p>
|
|||
|
|
|||
|
<pre><code># Index View Options ---------------------------------
|
|||
|
set date_format = "%m/%d"
|
|||
|
set index_format = "[%Z] %D %-20.20F %s"
|
|||
|
set sort = threads # like gmail
|
|||
|
set sort_aux = reverse-last-date-received # like gmail
|
|||
|
set uncollapse_jump # don't collapse on an unread message
|
|||
|
set sort_re # thread based on regex
|
|||
|
set reply_regexp = "^(([Rr][Ee]?(\[[0-9]+\])?: *)?(\[[^]]+\] *)?)*"</code></pre>
|
|||
|
|
|||
|
<p>I won't go into what those do here. You can read the documentation if you're
|
|||
|
curious.</p>
|
|||
|
|
|||
|
<p>Quit and rerun Mutt to see your changes. Mutt is a very lightweight program so
|
|||
|
this should be fast.</p>
|
|||
|
|
|||
|
<p>Let's also add a few key bindings in the index to make it easier to use:</p>
|
|||
|
|
|||
|
<pre><code># Index Key Bindings ---------------------------------
|
|||
|
bind index gg first-entry
|
|||
|
bind index G last-entry
|
|||
|
|
|||
|
bind index R group-reply
|
|||
|
bind index <tab> sync-mailbox
|
|||
|
bind index <space> collapse-thread
|
|||
|
|
|||
|
# Ctrl-R to mark all as read
|
|||
|
macro index \Cr "T~U<enter><tag-prefix><clear-flag>N<untag-pattern>.<enter>" "mark all messages as read"
|
|||
|
|
|||
|
# Sync email
|
|||
|
macro index O "<shell-escape>offlineimap<enter>" "run offlineimap to sync all mail"
|
|||
|
macro index o "<shell-escape>offlineimap -qf INBOX<enter>" "run offlineimap to sync inbox"
|
|||
|
|
|||
|
# Saner copy/move dialogs
|
|||
|
macro index C "<copy-message>?<toggle-mailboxes>" "copy a message to a mailbox"
|
|||
|
macro index M "<save-message>?<toggle-mailboxes>" "move a message to a mailbox"</code></pre>
|
|||
|
|
|||
|
<p>Remember to quit and rerun Mutt for them to take effect.</p>
|
|||
|
|
|||
|
<p>We're going to use <code>j</code> and <code>k</code> to move around, so we may as well support Vim
|
|||
|
keys like <code>gg</code> and <code>G</code> too. We'll use <code>R</code> for reply all, since that comes in
|
|||
|
handy fairly often. <code>Ctrl-R</code> will mark all messages in the current folder as
|
|||
|
read.</p>
|
|||
|
|
|||
|
<p>Don't worry if you don't understand how all these bindings and macros work right
|
|||
|
now. You can read the documentation later.</p>
|
|||
|
|
|||
|
<p>The <code>tab</code> key is going to "commit" changes we've made in Mutt (like deleting an
|
|||
|
email) to our local Maildir folder. Once those changes are in the Maildir
|
|||
|
folder offlineimap will sync them to the server the next time it runs. This is
|
|||
|
nice because it lets us recover if we accidentally do something stupid like
|
|||
|
deleting the wrong email.</p>
|
|||
|
|
|||
|
<p><strong>Note:</strong> Mutt will also sync changes for a folder when you switch to
|
|||
|
a different folder, and when you quit Mutt, so be aware of those.</p>
|
|||
|
|
|||
|
<p>The <code>space</code> key will toggle collapsing of threads, which can be convenient when
|
|||
|
viewing mailing lists (or any conversations with many messages).</p>
|
|||
|
|
|||
|
<p>The <code>o</code> and <code>O</code> keys will run offlineimap to sync mail. Like I said before,
|
|||
|
I prefer having to press a button to grab mail instead of it constantly grabbing
|
|||
|
and nagging me. <code>o</code> will sync only the inbox (fast), and <code>O</code> will sync
|
|||
|
everything (much slower).</p>
|
|||
|
|
|||
|
<p>Finally we rebind <code>C</code> and <code>M</code> to perform the same operations they usually do,
|
|||
|
but in a more user-friendly manner.</p>
|
|||
|
|
|||
|
<p>While we're at it, let's add a way to navigate around the sidebar so we can
|
|||
|
switch folders:</p>
|
|||
|
|
|||
|
<pre><code># Sidebar Navigation ---------------------------------
|
|||
|
bind index,pager <down> sidebar-next
|
|||
|
bind index,pager <up> sidebar-prev
|
|||
|
bind index,pager <right> sidebar-open</code></pre>
|
|||
|
|
|||
|
<p>We're binding the <code>up</code> and <code>down</code> arrow keys to switch between folders, and
|
|||
|
<code>right</code> to "enter" a folder. Give it a try.</p>
|
|||
|
|
|||
|
<p>We don't need the arrows because we can navigate with <code>j</code> and <code>k</code>, but if you
|
|||
|
prefer to rebind them to something else feel free.</p>
|
|||
|
|
|||
|
<p>Practice moving between folders and around in the list, then we'll move on to
|
|||
|
actually reading emails.</p>
|
|||
|
|
|||
|
<h3 id="s17-the-pager"><a href="index.html#s17-the-pager">The Pager</a></h3>
|
|||
|
|
|||
|
<p>Press <code>return</code> in the index to open the selected email. This view is called the
|
|||
|
pager:</p>
|
|||
|
|
|||
|
<p><img src="../../../../static/images/blog/2012/10/mutt-pager.png" alt="Mutt's Pager"></p>
|
|||
|
|
|||
|
<p>Like before, let's add a few settings:</p>
|
|||
|
|
|||
|
<pre><code># Pager View Options ---------------------------------
|
|||
|
set pager_index_lines = 10 # number of index lines to show
|
|||
|
set pager_context = 3 # number of context lines to show
|
|||
|
set pager_stop # don't go to next message automatically
|
|||
|
set menu_scroll # scroll in menus
|
|||
|
set tilde # show tildes like in vim
|
|||
|
unset markers # no ugly plus signs
|
|||
|
|
|||
|
set quote_regexp = "^( {0,4}[>|:#%]| {0,4}[a-z0-9]+[>|]+)+"
|
|||
|
alternative_order text/plain text/enriched text/html</code></pre>
|
|||
|
|
|||
|
<p>This is a good, sane starting point. And now for a few extra key bindings:</p>
|
|||
|
|
|||
|
<pre><code># Pager Key Bindings ---------------------------------
|
|||
|
bind pager k previous-line
|
|||
|
bind pager j next-line
|
|||
|
bind pager gg top
|
|||
|
bind pager G bottom
|
|||
|
|
|||
|
bind pager R group-reply
|
|||
|
|
|||
|
# View attachments properly.
|
|||
|
bind attach <return> view-mailcap</code></pre>
|
|||
|
|
|||
|
<p>The first few make scrolling behave like it does in the index. We're also going
|
|||
|
to use the same key for reply all here. Consistency will make it easier to get
|
|||
|
Mutt into your fingers.</p>
|
|||
|
|
|||
|
<p>Don't worry about the last one — that's to make sure Mutt treats attachments
|
|||
|
properly.</p>
|
|||
|
|
|||
|
<p>Go ahead and try reading some emails. Remember that <code>?</code> will always give you
|
|||
|
a list of keys and their functions.</p>
|
|||
|
|
|||
|
<h3 id="s18-attachments"><a href="index.html#s18-attachments">Attachments</a></h3>
|
|||
|
|
|||
|
<p>Now that we're all set for reading plain text email, it's time to deal with
|
|||
|
attachments.</p>
|
|||
|
|
|||
|
<p>When you're in the pager view reading an email with attachments, you can press
|
|||
|
<code>v</code> to view a list of them:</p>
|
|||
|
|
|||
|
<p><img src="../../../../static/images/blog/2012/10/mutt-attachments.png" alt="Attachment List"></p>
|
|||
|
|
|||
|
<p>Scroll through the list with <code>j</code> and <code>k</code> and press <code>return</code> to view one. But
|
|||
|
first we need to tell Mutt how to view things that aren't text!</p>
|
|||
|
|
|||
|
<p>For that we need to create a <code>~/.mutt/mailcap</code> file. Here's a sample to get you
|
|||
|
started:</p>
|
|||
|
|
|||
|
<pre><code># MS Word documents
|
|||
|
application/msword; ~/.mutt/view_attachment.sh %s "-" '/Applications/TextEdit.app'
|
|||
|
|
|||
|
# Images
|
|||
|
image/jpg; ~/.mutt/view_attachment.sh %s jpg
|
|||
|
image/jpeg; ~/.mutt/view_attachment.sh %s jpg
|
|||
|
image/pjpeg; ~/.mutt/view_attachment.sh %s jpg
|
|||
|
image/png; ~/.mutt/view_attachment.sh %s png
|
|||
|
image/gif; ~/.mutt/view_attachment.sh %s gif
|
|||
|
|
|||
|
# PDFs
|
|||
|
application/pdf; ~/.mutt/view_attachment.sh %s pdf
|
|||
|
|
|||
|
# HTML
|
|||
|
text/html; ~/.mutt/view_attachment.sh %s html
|
|||
|
|
|||
|
# Unidentified files
|
|||
|
application/octet-stream; ~/.mutt/view_attachment.sh %s "-"</code></pre>
|
|||
|
|
|||
|
<p>The <code>view_attachment.sh</code> script is from <a href="http://linsec.ca/Using_mutt_on_OS_X#mailcap">here</a>. Here's a link to
|
|||
|
<a href="https://bitbucket.org/sjl/dotfiles/src/tip/mutt/view_attachment.sh">my copy</a> in case that site ever goes down. Grab the script,
|
|||
|
chmod it to executable, and stick it in <code>~/.mutt</code>.</p>
|
|||
|
|
|||
|
<p>You can poke around and figure out how it works, or you can just not worry about
|
|||
|
it and get on with life. I recommend the latter (at least for now).</p>
|
|||
|
|
|||
|
<p>Now you can press <code>return</code> to open an attachment in the proper program.</p>
|
|||
|
|
|||
|
<h3 id="s19-urls"><a href="index.html#s19-urls">URLs</a></h3>
|
|||
|
|
|||
|
<p>One thing you'll probably want to do while reading email is open links. Many
|
|||
|
terminal programs like iTerm2 let you command-click on a link to open it, but
|
|||
|
this is Mutt! We shouldn't have to use the mouse!</p>
|
|||
|
|
|||
|
<p>We're going to use a small helper program called urlview to make it easy to open
|
|||
|
URLs in email. First, install it with <code>brew install urlview</code>. Then make
|
|||
|
a <code>~/.urlview</code> file with the following contents:</p>
|
|||
|
|
|||
|
<pre><code>COMMAND open %s</code></pre>
|
|||
|
|
|||
|
<p>This tells urlview what command to use to open a URL. We're just going to use
|
|||
|
the OS X <code>open</code> command to do the right thing.</p>
|
|||
|
|
|||
|
<p>Next, add the following line to your <code>~/.muttrc</code>:</p>
|
|||
|
|
|||
|
<pre><code>macro pager \Cu "|urlview<enter>" "call urlview to open links"</code></pre>
|
|||
|
|
|||
|
<p>Now when you're reading an email with links in it you can press <code>Ctrl-u</code> to open
|
|||
|
urlview. You'll see a screen like this:</p>
|
|||
|
|
|||
|
<p><img src="../../../../static/images/blog/2012/10/mutt-urls.png" alt="urlview screen"></p>
|
|||
|
|
|||
|
<p>Navigate with <code>j</code>, <code>k</code>, <code>gg</code>, <code>G</code>, or <code>/</code> and press <code>return</code> when the desired
|
|||
|
link is selected. That link will be filled in at the bottom of the screen in
|
|||
|
case you want to edit it, and you can press <code>return</code> one more time to actually
|
|||
|
open it in your default browser.</p>
|
|||
|
|
|||
|
<p>That about wraps it up for reading email. Now it's time to write some!</p>
|
|||
|
|
|||
|
<h2 id="s20-writing-email"><a href="index.html#s20-writing-email">Writing Email</a></h2>
|
|||
|
|
|||
|
<p>Writing email is one of the best parts of Mutt. First let's add a few settings
|
|||
|
to get things nice and sane:</p>
|
|||
|
|
|||
|
<pre><code># Compose View Options -------------------------------
|
|||
|
set realname = "Steve Losh" # who am i?
|
|||
|
set envelope_from # which from?
|
|||
|
set sig_dashes # dashes before sig
|
|||
|
set edit_headers # show headers when composing
|
|||
|
set fast_reply # skip to compose when replying
|
|||
|
set askcc # ask for CC:
|
|||
|
set fcc_attach # save attachments with the body
|
|||
|
unset mime_forward # forward attachments as part of body
|
|||
|
set forward_format = "Fwd: %s" # format of subject when forwarding
|
|||
|
set forward_decode # decode when forwarding
|
|||
|
set attribution = "On %d, %n wrote:" # format of quoting header
|
|||
|
set reply_to # reply to Reply to: field
|
|||
|
set reverse_name # reply as whomever it was to
|
|||
|
set include # include message in replies
|
|||
|
set forward_quote # include message in forwards</code></pre>
|
|||
|
|
|||
|
<p>You can reply to an email with <code>r</code> in the index or pager, or start a fresh one
|
|||
|
with <code>m</code>.</p>
|
|||
|
|
|||
|
<p>There's actually not a lot to say about writing mail, because Mutt itself
|
|||
|
doesn't handle it! Mutt passes control off to the text editor of your choice.
|
|||
|
Just specify your editor in your <code>~/.muttrc</code>:</p>
|
|||
|
|
|||
|
<pre><code>set editor = "vim" # Use terminal Vim to compose email.
|
|||
|
set editor = "mvim -f" # Use MacVim to compose email.
|
|||
|
set editor = "subl -w" # Use Sublime Text 2 to compose email.</code></pre>
|
|||
|
|
|||
|
<p>Any command that takes a filename and doesn't return until you're done can be
|
|||
|
used here.</p>
|
|||
|
|
|||
|
<p>This is fantastic because it means you can use an editor you're already
|
|||
|
comfortable and fast in to write email instead of learning yet another set of
|
|||
|
shortcuts.</p>
|
|||
|
|
|||
|
<p>Once you save the email in your editor and close it, Mutt will present you with
|
|||
|
a menu that looks like this:</p>
|
|||
|
|
|||
|
<p><img src="../../../../static/images/blog/2012/10/mutt-send-1.png" alt="Sending Screen"></p>
|
|||
|
|
|||
|
<p>You can press <code>e</code> to go back and edit the mail, <code>a</code> to add attachments, and so
|
|||
|
on (the options are listed at the top of the screen).</p>
|
|||
|
|
|||
|
<p>Before we can continue we need to tell Mutt how to send email. Press <code>q</code> to
|
|||
|
discard the email for now.</p>
|
|||
|
|
|||
|
<h2 id="s21-sending-email"><a href="index.html#s21-sending-email">Sending Email</a></h2>
|
|||
|
|
|||
|
<p>Mutt does have (some) built-in SMTP support, but we're going to use a separate
|
|||
|
program to do our sending for a few reasons.</p>
|
|||
|
|
|||
|
<p>First, Mutt's SMTP support was considered "experimental" the last time
|
|||
|
I checked. Sending email is kind of important, so we'll stick with something
|
|||
|
tried and true.</p>
|
|||
|
|
|||
|
<p>Second, we want a method that won't require our password in a plaintext config
|
|||
|
file.</p>
|
|||
|
|
|||
|
<p>Go ahead and install the <code>msmtp</code> program through Homebrew with <code>brew install
|
|||
|
msmtp</code>.</p>
|
|||
|
|
|||
|
<p>Next we're going to need to create a <code>~/.msmtprc</code> file with the following
|
|||
|
contents:</p>
|
|||
|
|
|||
|
<pre><code>account stevelosh
|
|||
|
host smtp.gmail.com
|
|||
|
port 587
|
|||
|
protocol smtp
|
|||
|
auth on
|
|||
|
from steve@stevelosh.com
|
|||
|
user steve@stevelosh.com
|
|||
|
tls on
|
|||
|
tls_trust_file ~/.mutt/Equifax_Secure_CA.cert
|
|||
|
|
|||
|
account default : stevelosh</code></pre>
|
|||
|
|
|||
|
<p><code>msmtp</code> will look in your keychain for your SMTP password, which we added
|
|||
|
earlier. No plaintext passwords!</p>
|
|||
|
|
|||
|
<p>The other "interesting" bit here is the <code>tls_trust_file</code>. We're going to be
|
|||
|
connecting to Gmail's SMTP server over SSL, and <code>msmtp</code> needs to know if it can
|
|||
|
trust the certificate that the server on the other end is sending back.</p>
|
|||
|
|
|||
|
<p>Copy the following and paste it into the path <code>tls_trust_file</code> is set to:</p>
|
|||
|
|
|||
|
<pre><code>-----BEGIN CERTIFICATE-----
|
|||
|
MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJVUzEQMA4GA1UE
|
|||
|
ChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
|
|||
|
MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoT
|
|||
|
B0VxdWlmYXgxLTArBgNVBAsTJEVxdWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCB
|
|||
|
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPR
|
|||
|
fM6fBeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+AcJkVV5MW
|
|||
|
8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kCAwEAAaOCAQkwggEFMHAG
|
|||
|
A1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UE
|
|||
|
CxMkRXF1aWZheCBTZWN1cmUgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoG
|
|||
|
A1UdEAQTMBGBDzIwMTgwODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvS
|
|||
|
spXXR9gjIBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQFMAMB
|
|||
|
Af8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUAA4GBAFjOKer89961
|
|||
|
zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y7qj/WsjTVbJmcVfewCHrPSqnI0kB
|
|||
|
BIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee95
|
|||
|
70+sB3c4
|
|||
|
-----END CERTIFICATE-----</code></pre>
|
|||
|
|
|||
|
<p>If you're paranoid and don't trust that I'm giving you the right cert (or that
|
|||
|
someone has hacked my site and changed it), you can generate it yourself. I'll
|
|||
|
leave that to you — if you care enough about it you'll figure it out.</p>
|
|||
|
|
|||
|
<p>Now we need to tell Mutt to use msmtp. Add the following to your <code>~/.muttrc</code>
|
|||
|
file:</p>
|
|||
|
|
|||
|
<pre><code>set from = "steve@stevelosh.com"
|
|||
|
set sendmail = "/usr/local/bin/msmtp -a stevelosh"
|
|||
|
set sendmail_wait = 0
|
|||
|
unset record</code></pre>
|
|||
|
|
|||
|
<p>The <code>-a stevelosh</code> will need to change to whatever you named your account in the
|
|||
|
msmtp config.</p>
|
|||
|
|
|||
|
<p>The <code>unset record</code> line tells Mutt to not append a copy of every email you send
|
|||
|
to a file on your hard drive. Gmail will save the emails you send in the sent
|
|||
|
folder, so you'll get the the next time you sync with offlineimap anyway.</p>
|
|||
|
|
|||
|
<p>The <code>sendmail_wait</code> line tells Mutt to wait for the msmtp program to finish
|
|||
|
sending the mail before returning control, instead of running it in the
|
|||
|
background. This makes it obvious if there's a problem sending a message, which
|
|||
|
I prefer to silent, backgrounded failures.</p>
|
|||
|
|
|||
|
<p>Now you can send email! Awesome!</p>
|
|||
|
|
|||
|
<p>Once you've composed a test email and saved it you'll be presented with a screen
|
|||
|
like this (which we saw in the previous section):</p>
|
|||
|
|
|||
|
<p><img src="../../../../static/images/blog/2012/10/mutt-send-1.png" alt="Sending Screen"></p>
|
|||
|
|
|||
|
<p>The keys you need are listed along the top. Pressing <code>y</code> now will invoke msmtp
|
|||
|
and send your email!</p>
|
|||
|
|
|||
|
<p>You'll see "Sending message..." at the bottom of the screen while msmtp is
|
|||
|
working. If there's a problem, Mutt will tell you the error. Figure it out
|
|||
|
before moving on.</p>
|
|||
|
|
|||
|
<h2 id="s22-postponing-drafts"><a href="index.html#s22-postponing-drafts">Postponing Drafts</a></h2>
|
|||
|
|
|||
|
<p>Sometimes I like to read and respond to email without an internet connection,
|
|||
|
then actually send the replies when I get back to civilization.</p>
|
|||
|
|
|||
|
<p>Since all of my email is stored locally, reading it offline is trivial.</p>
|
|||
|
|
|||
|
<p>To reply or compose offline, I use Mutt's "postpone" feature. First I've added
|
|||
|
the following line to my <code>~/.muttrc</code>:</p>
|
|||
|
|
|||
|
<pre><code>bind compose p postpone-message</code></pre>
|
|||
|
|
|||
|
<p>Now when I'm at the sending screen instead of pressing <code>y</code> to send I can press
|
|||
|
<code>p</code> to postpone the message. This places the message in the drafts folder.</p>
|
|||
|
|
|||
|
<p>The drafts folder in Mutt is just a normal folder of emails like any other.
|
|||
|
When you sync with offlineimap your postponed email will get pushed up to Gmail
|
|||
|
as a draft. You can edit it in the Gmail web interface if you like, and those
|
|||
|
edits will sync back down too.</p>
|
|||
|
|
|||
|
<p>If you want to edit your drafts (postponed messages) locally, you need to
|
|||
|
"recall" them. I have the following line in my <code>~/.muttrc</code> to set the key to
|
|||
|
the same one as I used to postpone it in the first place:</p>
|
|||
|
|
|||
|
<pre><code>bind index p recall-message</code></pre>
|
|||
|
|
|||
|
<p>You can also use <code>m</code> to start writing a new email, and mutt will prompt you if
|
|||
|
there are existing postponed messages.</p>
|
|||
|
|
|||
|
<p>Once you hit <code>p</code> (or <code>m</code> and then select "yes") Mutt will show you a list of the
|
|||
|
postponed messages. Select one and press <code>return</code> to start editing it again.
|
|||
|
If there's only one it will skip this step and simply open it for you
|
|||
|
immediately.</p>
|
|||
|
|
|||
|
<p>Once you're done editing you can either postpone it again or send it as usual.</p>
|
|||
|
|
|||
|
<p>Unfortunately you have to go through the "recall → edit → send" process each
|
|||
|
time. As far as I know there's no way to simply run down a list of postponed
|
|||
|
emails sending each one with a single keystroke. But that's not <em>too</em> bad, and
|
|||
|
it's great to be able to work offline like this.</p>
|
|||
|
|
|||
|
<p>Once you send the postponed email (through Mutt or the Gmail web interface) it
|
|||
|
disappears from the drafts folder and the postponed list as you would expect.</p>
|
|||
|
|
|||
|
<h2 id="s23-contacts"><a href="index.html#s23-contacts">Contacts</a></h2>
|
|||
|
|
|||
|
<p>Next we'll want to get Mutt to autocomplete our contacts from the OS X address
|
|||
|
book. I like using the OS X address book because it automatically syncs between
|
|||
|
my laptops and phone, so I only need to maintain one address list.</p>
|
|||
|
|
|||
|
<h3 id="s24-autocompleting"><a href="index.html#s24-autocompleting">Autocompleting</a></h3>
|
|||
|
|
|||
|
<p>Unfortunately I've got some bad news for you: you're going to need to install
|
|||
|
XCode.</p>
|
|||
|
|
|||
|
<p>No, not the command-line developer tools. The full XCode. I'm sorry, but trust
|
|||
|
me when I say it's going to save you a lot of pain, so just grumble to yourself
|
|||
|
a bit and do it.</p>
|
|||
|
|
|||
|
<p>Okay, now that you've got XCode you can install the <code>contacts</code> program through
|
|||
|
Homebrew with <code>brew install contacts</code>.</p>
|
|||
|
|
|||
|
<p><code>contacts</code> is a command-line program that you can use to query your address
|
|||
|
book. To tell Mutt how to use it add the following lines to your
|
|||
|
<code>~/.mutt/muttrc</code>:</p>
|
|||
|
|
|||
|
<pre><code>set query_command = "contacts -Sf '%eTOKEN%n' '%s' | sed -e 's/TOKEN/\t/g'"
|
|||
|
bind editor <Tab> complete-query
|
|||
|
bind editor ^T complete</code></pre>
|
|||
|
|
|||
|
<p>Now when you're filling out an email address field you can type a few characters
|
|||
|
and hit Tab to get a screen like this:</p>
|
|||
|
|
|||
|
<p><img src="../../../../static/images/blog/2012/10/mutt-contacts-1.png" alt="Contacts"></p>
|
|||
|
|
|||
|
<p>You can use <code>j</code> and <code>k</code> to select an item, press return to complete it. Press
|
|||
|
<code>q</code> if you've changed your mind and want to cancel the completion. Look at the
|
|||
|
top of the screen for more handy little keys you can use here.</p>
|
|||
|
|
|||
|
<p>If there's only one item in the list Mutt won't bother showing you this screen
|
|||
|
and will just complete it right away.</p>
|
|||
|
|
|||
|
<p>This completion searches more than just the email address. It'll also search
|
|||
|
the names and possibly other fields from the address book entries as well.</p>
|
|||
|
|
|||
|
<h3 id="s25-adding-contacts"><a href="index.html#s25-adding-contacts">Adding Contacts</a></h3>
|
|||
|
|
|||
|
<p>What about adding contacts to your address book? Any contacts you add on your
|
|||
|
phone will automatically be synced, but what if you're reading your mail in Mutt
|
|||
|
and just want to add the sender as a contact without leaving your command line?</p>
|
|||
|
|
|||
|
<p>For this I use a little script that <a href="http://simx.me/">Simone Manganelli</a> wrote for me
|
|||
|
called <code>addcontact</code>. You can <a href="https://bitbucket.org/sjl/dotfiles/src/tip/bin/addcontact">get it here</a> and stick it in your
|
|||
|
<code>$PATH</code> somewhere. It's just a command-line utility that you can use like this:</p>
|
|||
|
|
|||
|
<pre><code>$ addcontact Steve Losh steve@stevelosh.com
|
|||
|
$ addcontact "Steve Losh" steve@stevelosh.com
|
|||
|
$ addcontact Steve Losh work steve@pculture.org</code></pre>
|
|||
|
|
|||
|
<p>As you can see, it's pretty flexible.</p>
|
|||
|
|
|||
|
<p><strong>Note:</strong> This utility always adds a new contact record, so if you add someone
|
|||
|
that's already in there you're going to get a duplicate entry. If that happens
|
|||
|
you can search for the entries in Contacts.app, select the duplicates, and use
|
|||
|
"Card → Merge Selected Cards" to combine them.</p>
|
|||
|
|
|||
|
<p>Okay, so we can now add contacts from the command line. You could set up a Mutt
|
|||
|
macro to automatically add senders without too much trouble, but I don't do
|
|||
|
that. If I want to add a contact I just hit <code>!</code> and type out the shell command.
|
|||
|
It's not that much work, and sometimes the name in the "From:" field is in
|
|||
|
a weird format like "Last, First Middle" instead of "First Last", so this gives
|
|||
|
me a chance to clean it up before I add it.</p>
|
|||
|
|
|||
|
<h2 id="s26-searching-email"><a href="index.html#s26-searching-email">Searching Email</a></h2>
|
|||
|
|
|||
|
<p>If you have more than a screen full of email in any given folder, you're going
|
|||
|
to want a way to search through it. Mutt's built-in searching is a good start,
|
|||
|
but I also use another program to get a bit more power.</p>
|
|||
|
|
|||
|
<h3 id="s27-vanilla-searching"><a href="index.html#s27-vanilla-searching">Vanilla Searching</a></h3>
|
|||
|
|
|||
|
<p>There are two main ways to search your email in Mutt: plain searching and
|
|||
|
"limiting".</p>
|
|||
|
|
|||
|
<p>Plain searching is done with the <code>/</code> key. It works similarly to Vim or less:
|
|||
|
you press <code>/</code>, type your query, and press enter to perform the search. You can
|
|||
|
use <code>n</code> to move to the next match, and the search will loop around to the top if
|
|||
|
it hits the bottom.</p>
|
|||
|
|
|||
|
<p>I have the following lines in my <code>~/.muttrc</code> to bind <code>N</code> to go to the <em>previous</em>
|
|||
|
match:</p>
|
|||
|
|
|||
|
<pre><code>bind index N search-opposite
|
|||
|
bind pager N search-opposite</code></pre>
|
|||
|
|
|||
|
<p>Normally the <code>N</code> key marks a message as unread (or "new"). I personally never
|
|||
|
want to do that. Unread mail should be "mail that has not been read". If you
|
|||
|
use that feature you'll want to rebind it to something else.</p>
|
|||
|
|
|||
|
<p>Two things about queries:</p>
|
|||
|
|
|||
|
<ol>
|
|||
|
<li>They are regular expressions (actually it's more powerful than than, see <a href="http://www.mutt.org/doc/manual/manual-4.html#ss4.2">the
|
|||
|
documentation</a> for more information).</li>
|
|||
|
<li>They only search the To and Subject fields (<em>not</em> the message bodies!).</li>
|
|||
|
</ol>
|
|||
|
|
|||
|
<p>I generally use this kind of searching when I see the email I want to open on
|
|||
|
the screen but don't feel like pressing <code>j</code> or <code>k</code> forty times to move through
|
|||
|
the list. When I'm actually trying to find a message I can't already see on the
|
|||
|
screen I use limiting.</p>
|
|||
|
|
|||
|
<h3 id="s28-vanilla-limiting"><a href="index.html#s28-vanilla-limiting">Vanilla Limiting</a></h3>
|
|||
|
|
|||
|
<p>Limiting is the other way Mutt provides for searching mail. It's done with the
|
|||
|
<code>l</code> key by default.</p>
|
|||
|
|
|||
|
<p>Like <code>/</code>, <code>l</code> will ask you for a pattern. But instead of simply moving you to
|
|||
|
the next message that matches the pattern, Mutt will <em>hide</em> messages that don't
|
|||
|
match.</p>
|
|||
|
|
|||
|
<p>This lets you see all the ones that match in a single list. This list works
|
|||
|
just like a normal list. You can search it with <code>/</code>, tag things, and so on as
|
|||
|
you normally would. It's really quite nice once you get used to it.</p>
|
|||
|
|
|||
|
<p>To remove the limiting once you're done, you can limit to the special value
|
|||
|
<code>all</code>. I've added a line to my <code>~/.muttrc</code> so I can do that with a single key:</p>
|
|||
|
|
|||
|
<pre><code>macro index a "<limit>all\n" "show all messages (undo limit)"</code></pre>
|
|||
|
|
|||
|
<p><strong>Note:</strong> This shadows the <code>create-alias</code> function which I never use.</p>
|
|||
|
|
|||
|
<p>Limit queries work exactly like search queries, so you can do powerful stuff
|
|||
|
like <code>~f arthur ~C honza ~s api</code> ("limit to messages from 'arthur', to or cced
|
|||
|
to 'honza', containing 'api' in the subject").</p>
|
|||
|
|
|||
|
<h3 id="s29-full-text-searching"><a href="index.html#s29-full-text-searching">Full-Text Searching</a></h3>
|
|||
|
|
|||
|
<p>By now you're probably wondering how to search the full text of messages. There
|
|||
|
are two ways: one simple and slow, the other complicated and fast.</p>
|
|||
|
|
|||
|
<p>First is the simple, slow way. You can use <code>~B</code> in your searches and limits to
|
|||
|
search inside the entire message. If your folder only has a hundred messages
|
|||
|
this works great. But once you have a few more (my archive has about 30,000 at
|
|||
|
the moment, and I prune it fairly often) it quickly becomes too slow to be
|
|||
|
usable.</p>
|
|||
|
|
|||
|
<p>I use a program called <a href="http://notmuchmail.org/">notmuch</a> to index and search my email. It's blazingly
|
|||
|
fast and works pretty well.</p>
|
|||
|
|
|||
|
<p>First, install it with <code>brew install notmuch</code>. Now you need
|
|||
|
a <code>~/.notmuch-config</code> file. Run <code>notmuch setup</code> to generate one. It's pretty
|
|||
|
straightforward. When it asks you for the path to your archive, that's the path
|
|||
|
to the folder containing all your individual IMAP folders
|
|||
|
(<code>~/.mail/steve-stevelosh.com</code> in my setup).</p>
|
|||
|
|
|||
|
<p>Now run <code>notmuch new</code> to perform the initial index. It might take a while if
|
|||
|
you have a lot of folders. Subsequent indexes will be much faster since they'll
|
|||
|
only reindex whatever changed since the last time.</p>
|
|||
|
|
|||
|
<p>You'll want to reindex when your email changes, so add a hook in your
|
|||
|
<code>~/.offlineimaprc</code> to run <code>notmuch new</code> whenever offlineimap syncs your email:</p>
|
|||
|
|
|||
|
<pre><code>[Account SteveLosh]
|
|||
|
localrepository = SteveLosh-Local
|
|||
|
remoterepository = SteveLosh-Remote
|
|||
|
status_backend = sqlite
|
|||
|
postsynchook = notmuch new</code></pre>
|
|||
|
|
|||
|
<p>Now you can use <code>notmuch search foo</code> to search your mail for "foo" from the
|
|||
|
command line. Try a couple of queries and make sure it works.</p>
|
|||
|
|
|||
|
<p>Now we need to hook notmuch into Mutt. There are a number of different ways to
|
|||
|
do this, all of them hacky and ugly. I'll describe how I do it.</p>
|
|||
|
|
|||
|
<p>A quick overview of how this is going to work:</p>
|
|||
|
|
|||
|
<ol>
|
|||
|
<li>You'll press a key in Mutt to activate searching.</li>
|
|||
|
<li>You'll type your query and press return.</li>
|
|||
|
<li>Your mail will be searched with <code>notmuch</code>.</li>
|
|||
|
<li>Τhe resulting messages will be symlinked into a temporary maildir folder.</li>
|
|||
|
<li>That temporary folder will be opened in Mutt.</li>
|
|||
|
</ol>
|
|||
|
|
|||
|
<p>First get <a href="https://github.com/honza/mutt-notmuch-py">mutt-notmuch-py</a>. The original <code>mutt-notmuch</code> is a Perl script
|
|||
|
with many external requirements that are a pain to install, and it doesn't work
|
|||
|
on OS X. <code>mutt-notmuch-py</code> is a Python script with zero external requirements.</p>
|
|||
|
|
|||
|
<p><code>mutt-notmuch-py</code> is going to handle steps 2 through 4 in the list. Get the
|
|||
|
script into your <code>$PATH</code> somehow and then run it:</p>
|
|||
|
|
|||
|
<pre><code>$ mutt-notmuch-py -G ~/.mail/temporary/search
|
|||
|
Query: foo
|
|||
|
|
|||
|
$ ls ~/.mail/temporary/search
|
|||
|
cur new</code></pre>
|
|||
|
|
|||
|
<p>The <code>-G</code> tells it to not perform certain Gmail-specific stuff that we don't
|
|||
|
need. The path is where it will create the temporary maildir folder with the
|
|||
|
results. Each time you run it this folder will be wiped clean before the new
|
|||
|
results are linked into it.</p>
|
|||
|
|
|||
|
<p>Now we need to handle the first and last points in the list. I have the
|
|||
|
following mapping in my <code>~/.muttrc</code>:</p>
|
|||
|
|
|||
|
<pre><code>macro index S "<enter-command>unset wait_key<enter><shell-escape>mutt-notmuch-py ~/.mail/temporary/search<enter><change-folder-readonly>+temporary/search<enter>" "search mail (using notmuch)"</code></pre>
|
|||
|
|
|||
|
<p>That's a lot to take in, so let's see how it works piece by piece:</p>
|
|||
|
|
|||
|
<pre><code>macro index S</code></pre>
|
|||
|
|
|||
|
<p>We're going to use the <code>S</code> key to perform a full search of all of our mail.</p>
|
|||
|
|
|||
|
<pre><code><enter-command>unset wait_key<enter></code></pre>
|
|||
|
|
|||
|
<p>Unset the <code>wait_key</code> Mutt option to prevent Mutt from asking us to press a key
|
|||
|
after the search has finished.</p>
|
|||
|
|
|||
|
<pre><code><shell-escape>mutt-notmuch-py -G ~/.mail/temporary/search<enter></code></pre>
|
|||
|
|
|||
|
<p>Run <code>mutt-notmuch-py</code>. Control will pass over to that script and it will ask
|
|||
|
you for your query, run the search, set up the results folder, and then hand
|
|||
|
control back to Mutt.</p>
|
|||
|
|
|||
|
<pre><code><change-folder-readonly>+temporary/search<enter></code></pre>
|
|||
|
|
|||
|
<p>Now we change over to the temporary folder in Mutt, and we're now looking at
|
|||
|
a list of search results! Since this is a real Maildir folder like any other
|
|||
|
one we can use the full range of tools to interact with it (searching, limiting,
|
|||
|
etc).</p>
|
|||
|
|
|||
|
<p>Finally, let's get this search results folder in our sidebar so we can see where
|
|||
|
we are visually at all times:</p>
|
|||
|
|
|||
|
<pre><code>mailboxes +steve-stevelosh.com/INBOX \
|
|||
|
+steve-stevelosh.com/vim \
|
|||
|
+steve-stevelosh.com/clojure \
|
|||
|
...
|
|||
|
+temporary/search \</code></pre>
|
|||
|
|
|||
|
<p>Now the search results folder can be navigated like any other one. That's it
|
|||
|
for email searching! Now you should have a setup that you can use in real life
|
|||
|
to manage your email.</p>
|
|||
|
|
|||
|
<h2 id="s30-conclusion"><a href="index.html#s30-conclusion">Conclusion</a></h2>
|
|||
|
|
|||
|
<p>Mutt is definitely a beast. It's old, crufty, and ugly, but if you spend the
|
|||
|
time to set it up and learn to use it you'll be rewarded with a fast, powerful,
|
|||
|
customizable environment for working with your email.</p>
|
|||
|
|
|||
|
<p>A wonderful trend these days is that more and more sites are including the
|
|||
|
ability to respond to comments and such by simply replying to their notification
|
|||
|
emails. This means that often you can reply to Facebook emails, comment on
|
|||
|
GitHub pull requests, and respond to Bitbucket issues all without leaving the
|
|||
|
comfort of your finely-tuned email client.</p>
|
|||
|
|
|||
|
<p>Mutt's not for everyone, but if you deal with a lot of email and have some time
|
|||
|
to spend you should give it a try. You just might learn to love the old dog.</p>
|
|||
|
</article></main><hr class='main-separator' /><footer><nav><a href='https://github.com/sjl/'>GitHub</a> ・ <a href='https://twitter.com/stevelosh/'>Twitter</a> ・ <a href='https://instagram.com/thirtytwobirds/'>Instagram</a> ・ <a href='https://hg.stevelosh.com/.plan/'>.plan</a></nav></footer></body></html>
|