December 25, 2015

Notmuch and Addressbook handling

I always had a problem with managing my email contacts, not because I'm lazy, but because tooling for that was/is really crude. In most cases (actually, in all cases AFAIK) I'd have to do it manually, by marking target address so the mail client knows what to store or do the hard work, open addressbook editor and fill necessary details.

For example, in Mutt (which I used before) I'd have to press 'a' on desired address to be added on Mutt alias list. In Thunderbird, it is a totally different story: there are two contact lists - one you manually manage and the other is populated by Thunderbird by reading your recipient list.

The problem with all of these approaches is that I'd have to think in advance if a particular email address is important for me, so I can do appropriate steps and save it. Of course, Thunderbird's Collected addresses can help here, but will not work unless you send an email.

As I switched a couple of months ago to Notmuch, tool that made me finally productive in handling multiple mail accounts and the large volume of emails, the natural way was to explore offering in contact management. Which should not be that hard, since Notmuch index pretty much all important fields from the mail.

After inspecting a couple of alternatives, I settled up with notmuch-addrlookup.c, a tiny project that was written in C/glib combo. Setup was simple - compiled it first and added this to Emacs init file:

(setq notmuch-address-command "/usr/local/bin/notmuch-addrlookup")
(notmuch-address-message-insinuate)

and suddenly autocompletion for all known mail addresses from all my accounts started to work automatically. Imagine how I was happy.

Looking for a particular contact, but you can't remember mail address? No problem:

$ notmuch-addrlookup John

John Doe <john.doe@...>
John Malkovich <...>
...

Find all collegues from your Foo company? Simple:

$ notmuch-addrlookup foo.com

Black Baz <baz@foo.com>
John Done <doe@foo.com>
Michael Foo <foo@foo.com>
...

you get the point. However, sometimes you'd like to reorder search results (e.g. John Doe should be the first item when you do a lookup for John) or you'd like to find address not shown in search results.

Wait a second, didn't I said before it will search for all mail addresses? Well, not actually all addresses stored in mail headers, but only those set in To and From fields with some additional logic. This excludes CC and BCC fields.

To make it clearer, let's peek into the code of notmuch-addrlookup.c file, or even better, let's just see relevant part where notmuch query is constructed:

...
  /* Pass 1: looks at all the "from:" addresses with the address book tag */
  GString *qs = g_string_new ("tag:");
  g_string_append (qs, notmuch_user_abook_tag);
  if (name)
    g_string_append_printf (qs, " and from:%s*", name);
  gchar *sbuf = g_string_free (qs, FALSE);
  queries[0] = notmuch_query_create (db, sbuf);
  g_free (sbuf);

  /* Pass 2: looks at all "to:" addresses sent from the primary e-mail */
  qs = g_string_new ("");
  if (name)
    g_string_append_printf (qs, "to:%s*", name);
  if (notmuch_user_email)
    g_string_append_printf (qs, " from:%s", notmuch_user_email);
  sbuf = g_string_free (qs, FALSE);
  queries[1] = notmuch_query_create (db, sbuf);
  g_free (sbuf);

  /* Pass 3: With only a few hits, add all the "from:" addresses */
  if (notmuch_query_count_messages (queries[0]) +
      notmuch_query_count_messages (queries[1]) < 10)
    {
      qs = g_string_new ("");
      if (name)
        g_string_append_printf (qs, "from:%s*", name);
      sbuf = g_string_free (qs, FALSE);
      queries[2] = notmuch_query_create (db, sbuf);
      g_free (sbuf);
    }
...

So, we have three passes (actually, they are three queries, executed one after the other) and to explain them better, let's search for all John contacts. We will have these queries then:

  1. tag:<addressbook-tag> and from:John*
  2. to:John* and from:<account-email-address>
  3. from:John*

Query 1) will lookup for all emails with tag <addressbook-tag> and from anyone starting with John name.

Query 2) will search all emails sent to any John, but only from your primary email (the mail you've set under primary_email variable in notmuch config file).

Query 3) is executed unless we have more than 10 results from the first two queries and will lookup for all emails from any John.

As you can see, if you use one of the accounts not associated as your primary email and you are sending to someone for the first time, receiver address will not be shown. Or, if found (using other queries) it will be shown in relevance order, calculated by Xapian (notmuch indexing engine).

What if you'd like a certain address to be shown always (within the scope of searched term), maybe in top search results? In that case, <addressbook-tag> tag is for that.

By default, this tag in notmuch-addrlookup.c has name addressbook and if you tag your mail with it, no matter what account you've used while sending, notmuch-addrlookup.c will show it among the first results.

In case you don't like addressbook tag name and you'd like to change it to something better, just set addrbook_tag variable in notmuch config (make sure it is under [user] section), like:

[user]
name=Your name
primary_email=your email
...
addrbook_tag=something_cool

Can it be simpler?

To be honest, tagging emails for everything (e.g. for deleting or moving them to the different folders and now for managing your addressbook) is a bit strange concept, especially for the starters, but after some period of adjustment or better say, when you grok it, it becomes indispensable tool for effective email management.

At least, that is the case for me ;)

Tags: misc emacs