larch: migrating mail between IMAP accounts

I recently had to move several gigabytes of email (not my own, work-related) into Google Apps (Gmail). As best I can tell, the way most people do this is that they grit their teeth and they open up a graphical email client and drag folders one-by-one. It’s a one-off job for most people.

There were a couple of reasons I didn’t want to do that. One is that I was on my parents’ DSL connection at the time and pushing gigabytes of data through someone’s DSL is a violation of good guest principles, at least in Australia. The other is that we have over 500 folders in the account I’m talking about: that’s a lot of mouse pain.

Anyway, here’s your answer, if you are in the same position as me. After substantial searching, at least for this kind of tool, I came across larch, which is a Ruby command-line tool for IMAP-to-IMAP moves, most tested on Gmail and essentially designed for the “move my mail archives into Gmail” use-case. It’s much more mature than most of the one-off scripts people have thrown up on the ‘net. It certainly seemed robust over this volume of mail, although I did have to run it a couple of times to get past a few errors (it does not re-copy already copied mail, so re-runs are fast). It deserves more search juice.

If you wanted to keep two accounts permanently in sync, offlineimap would be the tool of choice, although the manual still seems to regard IMAP-to-IMAP syncing as not as robustly tested as its core mode of operation, which is IMAP-to-Maildir.

Creative Commons License
larch: migrating mail between IMAP accounts by Mary Gardiner is licensed under a Creative Commons Attribution 4.0 International License.

Viewing attachments when using mutt remotely

Yes, that’s right, I’m still in the dark ages and do not yet use Gmail for my email. Even though it has IMAP and everything. I still use Mutt.

I almost always use Mutt locally, using offlineimap to sync IMAP folders to local maildirs. This means I don’t usually have the problem of being unable to view non-text attachments. However, for the next little while I’ll be using Mutt on a remote connection.

Don Marti has one solution to this, which assumes that you are accessing the server with Mutt on it via SSH (probably true) and are easily able to create a tunnel to your local machine, which is trivial if you are using a commandline ssh client, but while you can do it with PuTTY I figured it was just annoying enough that I might not bother. (And I doubt you can do it at all with those web-based SSH clients.)

My alternative assumes instead that you have a webserver on the remote machine that has mutt on it. It then just copies the attachment to a web-accessible directory, and tells you the URL where you’ll be able to find the attachment. It’s thus a very trivial script (and I doubt very much it’s the only one out there), but perhaps using mine might save you fifteen minutes over coming up with your own, so here it is:

copy-to-dir.sh (in a bzr git repo)

Sample output is along these lines when you try to view an attachment in Mutt:

View attachment SOMEPDF.pdf at http://example.com/~user/SOMEPDF.pdf Press any key to continue, and delete viewable attachment

In order to use it, you need to:

  1. copy the script to the remote machine where you use mutt;
  2. make it executable;
  3. edit it to set the OUTPUTDIR and VIEWINGDIR variables to something that works for your setup;
  4. set up a custom mailcap file much like the one Don Marti gives, that is, put something like this in your ~/.mutt-mailcap:
     text/*; PATH-TO-SCRIPT/copy-to-dir.sh %s application/*; PATH-TO-SCRIPT/copy-to-dir.sh %s image/*; PATH-TO-SCRIPT/copy-to-dir.sh %s audio/*; PATH-TO-SCRIPT/copy-to-dir.sh %s
  5. set mailcap_path = ~/.mutt-mailcap in your ~/.muttrc file.

Something like this probably could work for Pine and other text-based email clients used remotely too, but I’m not sure how because I don’t use them. And if someone wants to document this in a way that assumes less pre-existing knowledge, go ahead.

Also, making your attachments web-accessible means that they are, well, web-accessible. I’ve set up a HTTP Auth-protected directory using https for this, you should think about your own setup too.

Creative Commons License
Viewing attachments when using mutt remotely by Mary Gardiner is licensed under a Creative Commons Attribution 4.0 International License.

Clean up IMAP folders

Per Matt Palmer’s blog entry OfflineIMAP and Deleting Folders users of any mail sorting recipe that creates new mail folders a lot tend to find that over time they accumulate a lot of mail folders for, eg, email lists they are no longer subscribed to. And most IMAP clients will waste time checking those folders for new mail all the time.

Matt wrote:

Now, of course, someone’s going to point me to a small script that finds all of your local empty folders and deletes them locally then issues an IMAP “delete folder” command on the server. But I had fun working all this out, so it’s not a complete waste.

I haven’t quite done this, I’ve just written a script that detects and deletes empty remote folders. (For me, offlineimap does not have the behaviour of creating new remote folders, so I haven’t bothered cleaning up local folders.)

It’s good: it’s speeding up my mail syncs a whole lot, deleting these old folders I haven’t received mail in for about five years. I’ve got full details and the script available for download (as you’d expect, it’s short): Python script to delete empty IMAP folders.

Creative Commons License
Clean up IMAP folders by Mary Gardiner is licensed under a Creative Commons Attribution 4.0 International License.

Attaching messages to outgoing mail in mutt

When I want to forward an email to someone as an attachment (usually because I want them to reply to it without having to snip gunky forward ‘headers’ in the body and preserving Message-ID and such) I can’t always forward-as-attachment, sometimes because I’m replying rather than starting a new mail, sometimes because I’m forwarding messages from multiple folders.

Up until today, I’ve resorted to all kinds of tricks to add other messages as attachments to mutt messages. One old favourite was copying them (shift+c) to a new maildir, attaching the individual files and manually setting their MIME type to message/rfc822. This does actually work but there is an easier way, the attach-message function, bound to shift+a by default.

On the screen where you normally add attachments, press shift+a. Navigate to the folder containing the messages you wish to attach (if they’re in different folders, just do this once per folder). Tag all the messages you want to attach (the default keybinding for tagging a message is ‘t’). Quit from the folder browser (‘q’). All tagged messages will be attached to the outgoing mail.

Creative Commons License
Attaching messages to outgoing mail in mutt by Mary Gardiner is licensed under a Creative Commons Attribution 4.0 International License.

Sending messages one-by-one with mutt

Here’s a feature of my mail client, mutt, that I wasn’t previously aware of: the ability to emulate the mail command if you invoke it with mutt -x.

This is my use-case: every so often, I want to email a bunch of people, typically for some kind of invitation thing. But I’d like to email them the same message one by one.

I don’t want to Cc them all because I know a whole lot of people who are addicted to group reply/reply to all, and also because Gmail itself is addicted to this: it interprets mutt’s Mail-Followup-To header as Reply-To, meaning if I included an email list in the Ccs and mutt has set a Mail-Followup-To to be that email list and all other recipients minus myself (it’s been told I’m subscribed to the list), all Gmail users will reply to the entire Cc list minus myself, which is exactly the reverse of what I want. And lastly, occasionally I don’t want to give them a complete list of exactly who is and isn’t privy to whatever is in this particular email.

The standard solution is then to Bcc them. But most of my social mailing lists don’t accept Bccs, some of my friends also don’t accept them, and I also have trouble remembering who I sent the mail to.

After that, the typical thing to do (on the UNIX-like commandline anyway), is something like this:

  1. Store the text of the message in messagebody.txt
  2. Store the recipient list in addresses.txt
  3. Run a script that goes pretty much like this:
    for address in `cat addresses.txt`
    do
    mail -s “Some subject line” $address < messagebody.txt
    done

But then the problem is that I don’t have the usual copy of the outgoing mail in my mutt outbox, because I sent it with mail, not with mutt. However, just now I checked the mutt man page, and saw this:

 OPTIONS  -x     Emulate the mailx compose mode.

So, that means I can do this, or something equivalent to this, and get exactly the behaviour I want (mails sent one-at-a-time with the recipient’s email in the To: field and a copy left in my mutt outbox):

for address in `cat addresses.txt`
do
mutt -x -s “Some subject line” $address < messagebody.txt
done

Creative Commons License
Sending messages one-by-one with mutt by Mary Gardiner is licensed under a Creative Commons Attribution 4.0 International License.