3 # notmuch-mutt - notmuch (of a) helper for Mutt
5 # Copyright: © 2011-2012 Stefano Zacchiroli <zack@upsilon.cc>
6 # License: GNU General Public License (GPL), version 3 or above
8 # See the bottom of this file for more documentation.
9 # A manpage can be obtained by running "pod2man notmuch-mutt > notmuch-mutt.1"
15 use Getopt::Long qw(:config no_getopt_compat);
17 use Mail::Box::Maildir;
19 use String::ShellQuote;
23 my $xdg_cache_dir = "$ENV{HOME}/.cache";
24 $xdg_cache_dir = $ENV{XDG_CACHE_HOME} if $ENV{XDG_CACHE_HOME};
25 my $cache_dir = "$xdg_cache_dir/notmuch/mutt";
28 # create an empty maildir (if missing) or empty an existing maildir"
29 sub empty_maildir($) {
31 rmtree($maildir) if (-d $maildir);
32 my $folder = new Mail::Box::Maildir(folder => $maildir,
37 # search($maildir, $query)
38 # search mails according to $query with notmuch; store results in $maildir
40 my ($maildir, $query) = @_;
41 $query = shell_quote($query);
43 empty_maildir($maildir);
44 system("notmuch search --output=files $query"
45 . " | sed -e 's: :\\\\ :g'"
46 . " | xargs --no-run-if-empty ln -s -t $maildir/cur/");
50 my ($text, $default) = @_;
52 my $term = Term::ReadLine->new( "notmuch-mutt" );
53 my $histfile = "$cache_dir/history";
55 $term->ornaments( 0 );
56 $term->unbind_key( ord( "\t" ) );
58 $histfile = $ENV{MUTT_NOTMUCH_HISTFILE} if $ENV{MUTT_NOTMUCH_HISTFILE};
59 $term->ReadHistory($histfile) if (-r $histfile);
61 chomp($query = $term->readline($text, $default));
63 system("man", "notmuch-search-terms");
65 $term->WriteHistory($histfile);
71 sub get_message_id() {
72 my $mail = Mail::Internet->new(\*STDIN);
73 $mail->head->get("message-id") =~ /^<(.*)>$/; # get message-id
77 sub search_action($$@) {
78 my ($interactive, $results_dir, @params) = @_;
81 search($results_dir, join(' ', @params));
83 my $query = prompt("search ('?' for man): ", join(' ', @params));
85 search($results_dir,$query);
90 sub thread_action(@) {
91 my ($results_dir, @params) = @_;
93 my $mid = get_message_id();
94 my $search_cmd = 'notmuch search --output=threads ' . shell_quote("id:$mid");
95 my $tid = `$search_cmd`; # get thread id
98 search($results_dir, $tid);
102 my $mid = get_message_id();
104 system("notmuch tag "
105 . shell_quote(join(' ', @_))
110 my %podflags = ( "verbose" => 1,
112 pod2usage(%podflags);
116 mkpath($cache_dir) unless (-d $cache_dir);
118 my $results_dir = "$cache_dir/results";
122 my $getopt = GetOptions(
123 "h|help" => \$help_needed,
124 "o|output-dir=s" => \$results_dir,
125 "p|prompt" => \$interactive);
126 if (! $getopt || $#ARGV < 0) { die_usage() };
127 my ($action, @params) = ($ARGV[0], @ARGV[1..$#ARGV]);
129 foreach my $param (@params) {
130 $param =~ s/folder:=/folder:/g;
135 } elsif ($action eq "search" && $#ARGV == 0 && ! $interactive) {
136 print STDERR "Error: no search term provided\n\n";
138 } elsif ($action eq "search") {
139 search_action($interactive, $results_dir, @params);
140 } elsif ($action eq "thread") {
141 thread_action($results_dir, @params);
142 } elsif ($action eq "tag") {
155 notmuch-mutt - notmuch (of a) helper for Mutt
161 =item B<notmuch-mutt> [I<OPTION>]... search [I<SEARCH-TERM>]...
163 =item B<notmuch-mutt> [I<OPTION>]... thread < I<MAIL>
165 =item B<notmuch-mutt> [I<OPTION>]... tag [I<TAGS>]... < I<MAIL>
171 notmuch-mutt is a frontend to the notmuch mail indexer capable of populating
172 a maildir with search results.
180 =item --output-dir DIR
182 Store search results as (symlink) messages under maildir DIR. Beware: DIR will
183 be overwritten. (Default: F<~/.cache/notmuch/mutt/results/>)
189 Instead of using command line search terms, prompt the user for them (only for
196 Show usage information and exit.
200 =head1 INTEGRATION WITH MUTT
202 notmuch-mutt can be used to integrate notmuch with the Mutt mail user agent
203 (unsurprisingly, given the name). To that end, you should define macros like
204 the following in your Mutt configuration (usually one of: F<~/.muttrc>,
205 F</etc/Muttrc>, or a configuration snippet under F</etc/Muttrc.d/>):
208 "<enter-command>unset wait_key<enter><shell-escape>notmuch-mutt --prompt search<enter><change-folder-readonly>~/.cache/notmuch/mutt/results<enter>" \
209 "notmuch: search mail"
211 "<enter-command>unset wait_key<enter><pipe-message>notmuch-mutt thread<enter><change-folder-readonly>~/.cache/notmuch/mutt/results<enter><enter-command>set wait_key<enter>" \
212 "notmuch: reconstruct thread"
214 "<enter-command>unset wait_key<enter><pipe-message>notmuch-mutt tag -inbox<enter>" \
215 "notmuch: remove message from inbox"
217 The first macro (activated by <F8>) prompts the user for notmuch search terms
218 and then jump to a temporary maildir showing search results. The second macro
219 (activated by <F9>) reconstructs the thread corresponding to the current mail
220 and show it as search results. The third macro (activated by <F6>) removes the
221 tag C<inbox> from the current message; by changing C<-inbox> this macro may be
222 customised to add or remove tags appropriate to the users notmuch work-flow.
224 To keep notmuch index current you should then periodically run C<notmuch
225 new>. Depending on your local mail setup, you might want to do that via cron,
226 as a hook triggered by mail retrieval, etc.
234 Copyright: (C) 2011-2012 Stefano Zacchiroli <zack@upsilon.cc>
236 License: GNU General Public License (GPL), version 3 or higher