bd15116835e2

Add the Mutt entry.
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Sun, 30 Sep 2012 01:31:40 -0400
parents fe3eb7796d61
children 25c804434023
branches/tags (none)
files .ffignore content/blog/2012/10/the-homely-mutt.html media/images/blog/2012/07/keychain-1.png media/images/blog/2012/07/keychain-2.png media/images/blog/2012/07/keychain-3.png media/images/blog/2012/07/mutt-contacts-1.png media/images/blog/2012/07/mutt-quotes-1.png media/images/blog/2012/07/mutt-send-1.png media/images/blog/2012/07/what-the-mutt.png media/images/blog/2012/10/keychain-1.png media/images/blog/2012/10/keychain-2.png media/images/blog/2012/10/keychain-3.png media/images/blog/2012/10/mutt-attachments.png media/images/blog/2012/10/mutt-contacts-1.png media/images/blog/2012/10/mutt-index.png media/images/blog/2012/10/mutt-pager.png media/images/blog/2012/10/mutt-ready-to-send.png media/images/blog/2012/10/mutt-send-1.png media/images/blog/2012/10/mutt-urls.png media/images/blog/2012/10/what-the-mutt.png

Changes

--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.ffignore	Sun Sep 30 01:31:40 2012 -0400
@@ -0,0 +1,1 @@
+deploy
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/content/blog/2012/10/the-homely-mutt.html	Sun Sep 30 01:31:40 2012 -0400
@@ -0,0 +1,1319 @@
+    {% extends "_post.html" %}
+
+    {% hyde
+        title: "The Homely Mutt"
+        snip: "Sparrow's dead?  Why not try Mutt?"
+        created: 2012-10-01 10:00:00
+        flattr: true
+    %}
+
+{% block article %}
+
+Now that [Sparrow][] is [effectively dead][sparrow-dead] 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 [Mutt][] a try.
+
+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.
+
+In this post I'll show you how to set up Mutt on OS X like I do.
+
+[Sparrow]: http://sparrowmailapp.com/
+[sparrow-dead]: http://www.theverge.com/2012/7/20/3172222/google-buys-sparrow-mail
+[Mutt]: http://www.mutt.org/
+
+[TOC]
+
+How I Use Email
+---------------
+
+This setup is going to be specific to the way I work with email.  Notably:
+
+* I have a Google Apps account that provides my steve@stevelosh.com email
+  address.
+* I have many other email addresses, but they all simply forward to my main one.
+* All mail I send comes from steve@stevelosh.com.
+* I store my contacts in the OS X address book.
+* All email comes into my inbox (or to a folder for a specific mailing list).
+* 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.
+* I sometimes read email offline and mark it for deletion, then sync that
+  deletion back to the server once I get online again.
+* Sometimes I write email without an internet connection and send it once I get
+  connected again.
+
+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.
+
+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".
+
+Other Guides and Resources
+--------------------------
+
+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:
+
+* <http://thomas.pelletier.im/2010/10/low-memory-mail-client/>
+* <http://www.andrews-corner.org/mutt.html>
+* <http://jstorimer.com/shells/2010/01/19/using-mutt-with-gmail-on-osx.html>
+* <http://www.vijaykiran.com/2010/01/27/mutt-for-gmail-imap-on-mac-os-x/>
+* <http://hynek.me/articles/my-mutt-gmail-setup/>
+* <https://wiki.archlinux.org/index.php/Mutt>
+* <http://linsec.ca/Using_mutt_on_OS_X>
+* <http://www.mutt.org/doc/manual/manual.html>
+* <http://pbrisbin.com/posts/two_accounts_in_mutt>
+* <http://upsilon.cc/~zack/blog/posts/2011/01/how_to_use_Notmuch_with_Mutt/>
+
+Overview
+--------
+
+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.
+
+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.
+
+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:
+
+![Diagram](/media/images{{ parent_url }}/what-the-mutt.png)
+
+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.
+
+Getting Email
+-------------
+
+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.
+
+### Why Local Email?
+
+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.
+
+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 [Backblaze][] that's yet another free backup.
+
+In this setup **all of your email is stored as plain text**.  If you want it
+encrypted just use OS X's full-disk encryption and you're set.
+
+I use [offlineimap][] 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.
+
+[offlineimap]: http://offlineimap.org/
+[Backblaze]: http://www.backblaze.com/partner/af3574
+
+### The Alternative
+
+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 [this guide][roma] instead.
+
+You'll probably still find the other sections of this post interesting though.
+
+[roma]: http://empt1e.blogspot.com/2009/10/using-mutt-with-gmail-imap-complete.html
+
+### Installing offlineimap
+
+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.
+
+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:
+
+    :::text
+    git clone git://github.com/spaetz/offlineimap.git
+    cd offlineimap
+    git checkout 679c491c56c981961e18aa43b31955900491d7a3
+    python setup.py install
+
+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.
+
+### Configuring offlineimap
+
+Once you've got offlineimap installed, you'll need to create
+a `~/.offlineimaprc` 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:
+
+    :::text
+    [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',
+                                                 ]
+
+It's kind of long, so let's go through it line by line and see what's going on.
+
+    :::text
+    [general]
+    ui = TTY.TTYUI
+    accounts = SteveLosh
+    pythonfile=~/.mutt/offlineimap.py
+    fsync = False
+
+First we tell offlineimap to use the `TTY.TTYUI` 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.
+
+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.
+
+The `pythonfile` 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.
+
+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).
+
+    :::text
+    [Account SteveLosh]
+    localrepository = SteveLosh-Local
+    remoterepository = SteveLosh-Remote
+    status_backend = sqlite
+
+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.
+
+Hey, I titled the entry "The *Homely* Mutt" for a reason.
+
+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 `brew install sqlite`.
+
+    :::text
+    [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)
+
+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
+[Maildir format][maildir] because it plays nicely with Mutt (and tons of other
+stuff).
+
+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 *all*
+of it.
+
+I think offlineimap needs the `~/.mail` 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 `mkdir ~/.mail`.
+
+Next we have the craziest part of the configuration: name translation.
+
+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.
+
+Also, Gmail doesn't actually use *folders* 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.
+
+User-created labels in Gmail (like "Mercurial" or "Clients") will appear as
+folders with those names through IMAP.
+
+Built-in, special Gmail folders have names that start with `[Gmail]/`.  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.
+
+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.
+
+So the "Sent Mail" folder in your Gmail account will be synced to
+`~/.mail/steve-stevelosh.com/sent`.  Cool.
+
+(No, I don't know what would happen if you created a label called `[Gmail]/All
+Mail` in Gmail.  If you try please let me know, but I take no responsibility if
+it ends with all your email being deleted.)
+
+[maildir]: https://en.wikipedia.org/wiki/Maildir
+
+    :::text
+    [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',
+                                                 ]
+
+Finally, the home stretch.  The last section described the folder on our local
+hard drive, and this one describes our Gmail account.
+
+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.
+
+Next is the type.  Luckily offlineimap provides a `Gmail` type that handles
+a lot of the craziness that is Gmail's IMAP setup.  Nice.
+
+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 `@gmail.com` in the username.  I don't know.  If one doesn't
+work, just try the other.
+
+Next we have `remotepasseval`.  This is a bit of Python code (drink!) that
+should return the password for the account.
+
+What is this `get_keychain_pass` function?  Well, remember when we saw the
+`pythonfile` 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.
+
+Next we set `realdelete` 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.
+
+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.
+
+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.
+
+Finally, we have `folderfilter`.  This is a Python function (drink!) that takes
+a **remote** folder name and returns `True` if that folder should be synced, or
+`False` if it should **not** 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.
+
+### Retrieving Passwords
+
+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.
+
+If you don't care too much about security, you *can* 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.
+
+First, you need to add your Gmail password into your OS X keychain.  Open the
+Keychain Access app and press the `+` button:
+
+![Keychain 1](/media/images{{ parent_url }}/keychain-1.png)
+
+Then fill out the form.  The "Keychain Item Name" should be
+`http://imap.gmail.com`.  The "Account Name" should be your email address.  The
+password should be your password:
+
+![Keychain 2](/media/images{{ parent_url }}/keychain-2.png)
+
+Press "Add".  Now repeat the process for the SMTP server.  The "Keychain Item
+Name" should be `smtp://smtp.gmail.com`.  The "Account Name" should be your
+email address.  The password should be your password:
+
+![Keychain 3](/media/images{{ parent_url }}/keychain-3.png)
+
+Now we need to create the `offlineimap.py` file we pointed offlineimap to
+earlier.  It needs to contain the `get_keychain_pass` function, which takes an
+`account` and `server` and return the password.  Here's the file I'm using:
+
+    :::python
+    #!/usr/bin/python
+    import re, subprocess
+    def get_keychain_pass(account=None, server=None):
+        params = {
+            'security': '/usr/bin/security',
+            'command': 'find-internet-password',
+            'account': account,
+            'server': server,
+            'keychain': '/Users/sjl/Library/Keychains/login.keychain',
+        }
+        command = "sudo -u sjl %(security)s -v %(command)s -g -a %(account)s -s %(server)s %(keychain)s" % params
+        output = subprocess.check_output(command, shell=True, stderr=subprocess.STDOUT)
+        outtext = [l for l in output.splitlines()
+                   if l.startswith('password: ')][0]
+
+        return re.match(r'password: "(.*)"', outtext).group(1)
+
+In a nutshell, it uses `/usr/bin/security` to retrieve the password.  Read
+through the code if you're curious.
+
+This is not *completely* secure, but it's better than having your password in
+a plaintext file in your home directory.
+
+Whew!  Time to actually run this thing and pull down our email!
+
+### Running offlineimap
+
+Assuming everything is in place, open a terminal and run offlineimap:
+
+    offlineimap
+
+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.
+
+**If there's an error, stop and figure out what went wrong**.  Remember,
+offlineimap is a *two-way* sync, so there's always the possibility it'll eat
+your email if you seriously mess something up!  I wish it had a
+`--dont-touch-remote` option you could use as a safety net for the original
+sync, but it doesn't, so be careful!
+
+In the future you can use `offlineimap -q` to run it in "quick mode".  It'll
+perform fewer checks but will generally be much faster.
+
+If you want to set up offlineimap to run every 5 minutes or so, you can use
+`launchd`.  `cron` does not work for some reason.  I'm not entirely sure why.
+
+Personally I actually *like* 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.
+
+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, `Ctrl-c` it, put the laptop to sleep in the middle of a run, or `kill -9`
+it, and it still won't lose emails.  On the next sync it'll fix anything that's
+missing.
+
+Mutt!
+-----
+
+Now that you've got your email on your computer, it's finally time to start
+using Mutt itself!
+
+### Installing
+
+Mutt can be installed in a bunch of different ways, but the easiest is through
+Homebrew:
+
+    :::text
+    brew install mutt --sidebar-patch
+
+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.
+
+That's pretty much it for installation, but don't get too relaxed because you're
+far from done.
+
+### Configuring
+
+Mutt is *very* 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.
+
+Mutt settings are kept in a `~/.muttrc` file.  If this file doesn't exist Mutt
+will look for `~/.mutt/muttrc` (note the lack of a dot in the filename), so you
+can put it there if you prefer.
+
+Let's start by creating a basic `~/.muttrc` piece by piece (a lot of this was
+taken from [this article][pris]).
+
+Once you've got a bit of Mutt under your belt you'll want to read [the
+documentation][muttdoc] for these settings, but for now just use them to keep
+things sane.
+
+[pris]: http://pbrisbin.com/posts/two_accounts_in_mutt
+[muttdoc]: http://www.mutt.org/doc/manual/manual-6.html
+
+    :::text
+    # 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
+
+Here we tell Mutt where to find the various folders it needs.
+
+    :::text
+    # 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
+
+These are some basic options to make Mutt behave a bit more sanely.
+
+    :::text
+    # Sidebar Patch --------------------------------------
+    set sidebar_delim   = '  │'
+    set sidebar_visible = yes
+    set sidebar_width   = 24
+    color sidebar_new color221 color233
+
+These options are specific to the sidebar patch.
+
+    :::text
+    # 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 )?───"
+
+This gives us a pretty status bar with the information we care about (and none
+of the stuff we don't).
+
+    :::text
+    # Header Options -------------------------------------
+    ignore *                                # ignore all headers
+    unignore from: to: cc: date: subject:   # show only these
+    hdr_order from: to: cc: date: subject:  # and in this order
+
+These options hide some of the extra email headers we don't care about when
+viewing and composing email.
+
+Now it's time to fill on our account details:
+
+    :::text
+    # 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"
+
+Most of those should be self-explanatory.  Fill in the appropriate values for
+your mail folder(s).
+
+We'll add more as we go through the next few sections, but that's enough to get
+us started.
+
+### Running
+
+Now that you've got Mutt configured you can run it:
+
+    :::sh
+    mutt
+
+I like to always be in my `~/Desktop` 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 `cd`s there for me before running Mutt:
+
+    :::sh
+    alias mutt 'cd ~/Desktop && mutt'
+
+If you run the [new fish shell][fish], this is going to cause problems later
+(long story, but it's related to the `read` builtin).  Do yourself a favor and
+head those confusing issues off at the pass with a fish function:
+
+    :::text
+    function mutt
+        bash --login -c 'cd ~/Desktop; /usr/local/bin/mutt' $argv;
+    end
+
+Remember that if you use another shell like this you'll want to set up any
+aliases and your `PATH` for that shell properly (probably identically to your
+main shell).
+
+[fish]: http://ridiculousfish.com/shell/
+
+Reading Email
+-------------
+
+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.
+
+### The Index
+
+When viewing a folder, Mutt presents you with a list of your email.  This view
+is called the "index":
+
+![Mutt's Index](/media/images{{ parent_url }}/mutt-index.png)
+
+This entry isn't meant be a guide to setting up Mutt on OS X.  For a full guide
+on how to *use* Mutt, you can Google around for some tutorials, or just learn as
+you go with `?`.  The `?` key will show you a list of all the keys you can use
+wherever you currently are, and what they do.
+
+Let's add a few lines to our `~/.muttrc` to make the index view behave a bit
+more nicely:
+
+    :::text
+    # 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]+\])?: *)?(\[[^]]+\] *)?)*"
+
+I won't go into what those do here.  You can read the documentation if you're
+curious.
+
+Quit and rerun Mutt to see your changes.  Mutt is a very lightweight program so
+this should be fast.
+
+Let's also add a few key bindings in the index to make it easier to use:
+
+    :::text
+    # 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"
+
+Remember to quit and rerun Mutt for them to take effect.
+
+We're going to use `j` and `k` to move around, so we may as well support Vim
+keys like `gg` and `G` too.  We'll use `R` for reply all, since that comes in
+handy fairly often.  `Ctrl-R` will mark all messages in the current folder as
+read.
+
+Don't worry if you don't understand how all these bindings and macros work right
+now.  You can read the documentation later.
+
+The `tab` 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.
+
+**Note:** 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.
+
+The `space` key will toggle collapsing of threads, which can be convenient when
+viewing mailing lists (or any conversations with many messages).
+
+The `o` and `O` 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.  `o` will sync only the inbox (fast), and `O` will sync
+everything (much slower).
+
+Finally we rebind `C` and `M` to perform the same operations they usually do,
+but in a more user-friendly manner.
+
+While we're at it, let's add a way to navigate around the sidebar so we can
+switch folders:
+
+    :::text
+    # Sidebar Navigation ---------------------------------
+    bind index,pager <down>   sidebar-next
+    bind index,pager <up>     sidebar-prev
+    bind index,pager <right>  sidebar-open
+
+We're binding the `up` and `down` arrow keys to switch between folders, and
+`right` to "enter" a folder.  Give it a try.
+
+We don't need the arrows because we can navigate with `j` and `k`, but if you
+prefer to rebind them to something else feel free.
+
+Practice moving between folders and around in the list, then we'll move on to
+actually reading emails.
+
+### The Pager
+
+Press `return` in the index to open the selected email.  This view is called the
+pager:
+
+![Mutt's Pager](/media/images{{ parent_url }}/mutt-pager.png)
+
+Like before, let's add a few settings:
+
+    :::text
+    # 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
+
+This is a good, sane starting point.  And now for a few extra key bindings:
+
+    :::text
+    # 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
+
+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.
+
+Don't worry about the last one -- that's to make sure Mutt treats attachments
+properly.
+
+Go ahead and try reading some emails.  Remember that `?` will always give you
+a list of keys and their functions.
+
+### Attachments
+
+Now that we're all set for reading plain text email, it's time to deal with
+attachments.
+
+When you're in the pager view reading an email with attachments, you can press
+`v` to view a list of them:
+
+![Attachment List](/media/images{{ parent_url }}/mutt-attachments.png)
+
+Scroll through the list with `j` and `k` and press `return` to view one.  But
+first we need to tell Mutt how to view things that aren't text!
+
+For that we need to create a `~/.mutt/mailcap` file.  Here's a sample to get you
+started:
+
+    :::text
+    # 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 "-"
+
+The `view_attachment.sh` script is from [here][attachment].  Here's a link to
+[my copy][attachment-mirror] in case that site ever goes down.  Grab the script,
+chmod it to executable, and stick it in `~/.mutt`.
+
+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).
+
+Now you can press `return` to open an attachment in the proper program.
+
+[attachment]: http://linsec.ca/Using_mutt_on_OS_X#mailcap
+[attachment-mirror]: https://bitbucket.org/sjl/dotfiles/src/tip/mutt/view_attachment.sh
+
+### URLs
+
+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!
+
+We're going to use a small helper program called urlview to make it easy to open
+URLs in email.  First, install it with `brew install urlview`.  Then make
+a `~/.urlview` file with the following contents:
+
+    :::text
+    COMMAND open %s
+
+This tells urlview what command to use to open a URL.  We're just going to use
+the OS X `open` command to do the right thing.
+
+Next, add the following line to your `~/.muttrc`:
+
+    :::text
+    macro pager \Cu "|urlview<enter>" "call urlview to open links"
+
+Now when you're reading an email with links in it you can press `Ctrl-u` to open
+urlview.  You'll see a screen like this:
+
+![urlview screen](/media/images{{ parent_url }}/mutt-urls.png)
+
+Navigate with `j`, `k`, `gg`, `G`, or `/` and press `return` 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 `return` one more time to actually
+open it in your default browser.
+
+That about wraps it up for reading email.  Now it's time to write some!
+
+Writing Email
+-------------
+
+Writing email is one of the best parts of Mutt.  First let's add a few settings
+to get things nice and sane:
+
+    :::text
+    # 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
+
+You can reply to an email with `r` in the index or pager, or start a fresh one
+with `m`.
+
+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 `~/.muttrc`:
+
+    :::text
+    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.
+
+Any command that takes a filename and doesn't return until you're done can be
+used here.
+
+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.
+
+Once you save the email in your editor and close it, Mutt will present you with
+a menu that looks like this:
+
+![Sending Screen](/media/images{{ parent_url }}/mutt-send-1.png)
+
+You can press `e` to go back and edit the mail, `a` to add attachments, and so
+on (the options are listed at the top of the screen).
+
+Before we can continue we need to tell Mutt how to send email.  Press `q` to
+discard the email for now.
+
+Sending Email
+-------------
+
+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.
+
+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.
+
+Second, we want a method that won't require our password in a plaintext config
+file.
+
+Go ahead and install the `msmtp` program through Homebrew with `brew install
+msmtp`.
+
+Next we're going to need to create a `~/.msmtprc` file with the following
+contents:
+
+    :::text
+    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
+
+`msmtp` will look in your keychain for your SMTP password, which we added
+earlier.  No plaintext passwords!
+
+The other "interesting" bit here is the `tls_trust_file`.  We're going to be
+connecting to Gmail's SMTP server over SSL, and `msmtp` needs to know if it can
+trust the certificate that the server on the other end is sending back.
+
+Copy the following and paste it into the path `tls_trust_file` is set to:
+
+    :::text
+    -----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-----
+
+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.
+
+Now we need to tell Mutt to use msmtp.  Add the following to your `~/.muttrc`
+file:
+
+    :::text
+    set from     = "steve@stevelosh.com"
+    set sendmail = "/usr/local/bin/msmtp -a stevelosh"
+    set sendmail_wait = 0
+    unset record
+
+The `-a stevelosh` will need to change to whatever you named your account in the
+msmtp config.
+
+The `unset record` 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.
+
+The `sendmail_wait` 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.
+
+Now you can send email!  Awesome!
+
+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):
+
+![Sending Screen](/media/images{{ parent_url }}/mutt-send-1.png)
+
+The keys you need are listed along the top.  Pressing `y` now will invoke msmtp
+and send your email!
+
+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.
+
+Postponing Drafts
+-----------------
+
+Sometimes I like to read and respond to email without an internet connection,
+then actually send the replies when I get back to civilization.
+
+Since all of my email is stored locally, reading it offline is trivial.
+
+To reply or compose offline, I use Mutt's "postpone" feature.  First I've added
+the following line to my `~/.muttrc`:
+
+    :::text
+    bind compose p postpone-message
+
+Now when I'm at the sending screen instead of pressing `y` to send I can press
+`p` to postpone the message.  This places the message in the drafts folder.
+
+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.
+
+If you want to edit your drafts (postponed messages) locally, you need to
+"recall" them.  I have the following line in my `~/.muttrc` to set the key to
+the same one as I used to postpone it in the first place:
+
+    :::text
+    bind index p recall-message
+
+You can also use `m` to start writing a new email, and mutt will prompt you if
+there are existing postponed messages.
+
+Once you hit `p` (or `m` and then select "yes") Mutt will show you a list of the
+postponed messages.  Select one and press `return` to start editing it again.
+If there's only one it will skip this step and simply open it for you
+immediately.
+
+Once you're done editing you can either postpone it again or send it as usual.
+
+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 *too* bad, and
+it's great to be able to work offline like this.
+
+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.
+
+Contacts
+--------
+
+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.
+
+### Autocompleting
+
+Unfortunately I've got some bad news for you: you're going to need to install
+XCode.
+
+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.
+
+Okay, now that you've got XCode you can install the `contacts` program through
+Homebrew with `brew install contacts`.
+
+`contacts` 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
+`~/.mutt/muttrc`:
+
+    :::text
+    set query_command = "contacts -Sf '%eTOKEN%n' '%s' | sed -e 's/TOKEN/\t/g'"
+    bind editor <Tab> complete-query
+    bind editor ^T    complete
+
+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:
+
+![Contacts](/media/images{{ parent_url }}/mutt-contacts-1.png)
+
+You can use `j` and `k` to select an item, press return to complete it.  Press
+`q` 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.
+
+If there's only one item in the list Mutt won't bother showing you this screen
+and will just complete it right away.
+
+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.
+
+### Adding Contacts
+
+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?
+
+For this I use a little script that [Simone Manganelli][simx] wrote for me
+called `addcontact`.  You can [get it here][addcontact] and stick it in your
+`$PATH` somewhere.  It's just a command-line utility that you can use like this:
+
+    :::text
+    $ addcontact Steve Losh steve@stevelosh.com
+    $ addcontact "Steve Losh" steve@stevelosh.com
+    $ addcontact Steve Losh work steve@pculture.org
+
+As you can see, it's pretty flexible.
+
+**Note:** 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.
+
+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 `!` 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.
+
+[simx]: http://simx.me/
+[addcontact]: https://bitbucket.org/sjl/dotfiles/src/tip/bin/addcontact
+
+Searching Email
+---------------
+
+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.
+
+### Vanilla Searching
+
+There are two main ways to search your email in Mutt: plain searching and
+"limiting".
+
+Plain searching is done with the `/` key.  It works similarly to Vim or less:
+you press `/`, type your query, and press enter to perform the search.  You can
+use `n` to move to the next match, and the search will loop around to the top if
+it hits the bottom.
+
+I have the following lines in my `~/.muttrc` to bind `N` to go to the *previous*
+match:
+
+    :::text
+    bind index N search-opposite
+    bind pager N search-opposite
+
+Normally the `N` 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.
+
+Two things about queries:
+
+1. They are regular expressions (actually it's more powerful than than, see [the
+   documentation][patterns] for more information).
+2. They only search the To and Subject fields (*not* the message bodies!).
+
+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 `j` or `k` 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.
+
+[patterns]: http://www.mutt.org/doc/manual/manual-4.html#ss4.2
+
+### Vanilla Limiting
+
+Limiting is the other way Mutt provides for searching mail.  It's done with the
+`l` key by default.
+
+Like `/`, `l` will ask you for a pattern.  But instead of simply moving you to
+the next message that matches the pattern, Mutt will *hide* messages that don't
+match.
+
+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 `/`, tag things, and so on as
+you normally would.  It's really quite nice once you get used to it.
+
+To remove the limiting once you're done, you can limit to the special value
+`all`.  I've added a line to my `~/.muttrc` so I can do that with a single key:
+
+    :::text
+    macro index a "<limit>all\n" "show all messages (undo limit)"
+
+**Note:** This shadows the `create-alias` function which I never use.
+
+Limit queries work exactly like search queries, so you can do powerful stuff
+like `~f arthur ~C honza ~s api` ("limit to messages from 'arthur', to or cced
+to 'honza', containing 'api' in the subject").
+
+### Full-Text Searching
+
+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.
+
+First is the simple, slow way.  You can use `~B` 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.
+
+I use a program called [notmuch][] to index and search my email.  It's blazingly
+fast and works pretty well.
+
+First, install it with `brew install notmuch`.  Now you need
+a `~/.notmuch-config` file.  Run `notmuch setup` 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
+(`~/.mail/steve-stevelosh.com` in my setup).
+
+Now run `notmuch new` 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.
+
+You'll want to reindex when your email changes, so add a hook in your
+`~/.offlineimaprc` to run `notmuch new` whenever offlineimap syncs your email:
+
+    :::text
+    [Account SteveLosh]
+    localrepository = SteveLosh-Local
+    remoterepository = SteveLosh-Remote
+    status_backend = sqlite
+    postsynchook = notmuch new
+
+Now you can use `notmuch search foo` to search your mail for "foo" from the
+command line.  Try a couple of queries and make sure it works.
+
+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.
+
+A quick overview of how this is going to work:
+
+1. You'll press a key in Mutt to activate searching.
+2. You'll type your query and press return.
+3. Your mail will be searched with `notmuch`.
+4. Τhe resulting messages will be symlinked into a temporary maildir folder.
+5. That temporary folder will be opened in Mutt.
+
+First get [mutt-notmuch-py][].  The original `mutt-notmuch` is a Perl script
+with many external requirements that are a pain to install, and it doesn't work
+on OS X.  `mutt-notmuch-py` is a Python script with zero external requirements.
+
+`mutt-notmuch-py` is going to handle steps 2 through 4 in the list.  Get the
+script into your `$PATH` somehow and then run it:
+
+    :::text
+    $ mutt-notmuch-py -G ~/.mail/temporary/search
+    Query: foo
+
+    $ ls ~/.mail/temporary/search
+    cur new
+
+The `-G` 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.
+
+Now we need to handle the first and last points in the list.  I have the
+following mapping in my `~/.muttrc`:
+
+    :::text
+    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)"
+
+That's a lot to take in, so let's see how it works piece by piece:
+
+    :::text
+    macro index S
+
+We're going to use the `S` key to perform a full search of all of our mail.
+
+    :::text
+    <enter-command>unset wait_key<enter>
+
+Unset the `wait_key` Mutt option to prevent Mutt from asking us to press a key
+after the search has finished.
+
+    :::text
+    <shell-escape>mutt-notmuch-py -G ~/.mail/temporary/search<enter>
+
+Run `mutt-notmuch-py`.  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.
+
+    :::text
+    <change-folder-readonly>+temporary/search<enter>
+
+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).
+
+Finally, let's get this search results folder in our sidebar so we can see where
+we are visually at all times:
+
+    :::text
+    mailboxes +steve-stevelosh.com/INBOX \
+              +steve-stevelosh.com/vim \
+              +steve-stevelosh.com/clojure \
+              ...
+              +temporary/search \
+
+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.
+
+[notmuch]: http://notmuchmail.org/
+[mutt-notmuch-py]: https://github.com/honza/mutt-notmuch-py
+
+Conclusion
+----------
+
+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.
+
+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.
+
+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.
+
+{% endblock article %}
Binary file media/images/blog/2012/07/keychain-1.png has changed
Binary file media/images/blog/2012/07/keychain-2.png has changed
Binary file media/images/blog/2012/07/keychain-3.png has changed
Binary file media/images/blog/2012/07/mutt-contacts-1.png has changed
Binary file media/images/blog/2012/07/mutt-quotes-1.png has changed
Binary file media/images/blog/2012/07/mutt-send-1.png has changed
Binary file media/images/blog/2012/07/what-the-mutt.png has changed
Binary file media/images/blog/2012/10/keychain-1.png has changed
Binary file media/images/blog/2012/10/keychain-2.png has changed
Binary file media/images/blog/2012/10/keychain-3.png has changed
Binary file media/images/blog/2012/10/mutt-attachments.png has changed
Binary file media/images/blog/2012/10/mutt-contacts-1.png has changed
Binary file media/images/blog/2012/10/mutt-index.png has changed
Binary file media/images/blog/2012/10/mutt-pager.png has changed
Binary file media/images/blog/2012/10/mutt-ready-to-send.png has changed
Binary file media/images/blog/2012/10/mutt-send-1.png has changed
Binary file media/images/blog/2012/10/mutt-urls.png has changed
Binary file media/images/blog/2012/10/what-the-mutt.png has changed