#!/usr/bin/perl -w # # notmuch-mutt - notmuch (of a) helper for Mutt # # Copyright: © 2011-2012 Stefano Zacchiroli # License: GNU General Public License (GPL), version 3 or above # # See the bottom of this file for more documentation. # A manpage can be obtained by running "pod2man notmuch-mutt > notmuch-mutt.1" use strict; use warnings; use File::Path; use Getopt::Long qw(:config no_getopt_compat); use Mail::Internet; use Mail::Box::Maildir; use Pod::Usage; use String::ShellQuote; use Term::ReadLine; my $xdg_cache_dir = "$ENV{HOME}/.cache"; $xdg_cache_dir = $ENV{XDG_CACHE_HOME} if $ENV{XDG_CACHE_HOME}; my $cache_dir = "$xdg_cache_dir/notmuch/mutt"; # create an empty maildir (if missing) or empty an existing maildir" sub empty_maildir($) { my ($maildir) = (@_); rmtree($maildir) if (-d $maildir); my $folder = new Mail::Box::Maildir(folder => $maildir, create => 1); $folder->close(); } # search($maildir, $query) # search mails according to $query with notmuch; store results in $maildir sub search($$) { my ($maildir, $query) = @_; $query = shell_quote($query); empty_maildir($maildir); system("notmuch search --output=files $query" . " | sed -e 's: :\\\\ :g'" . " | xargs --no-run-if-empty ln -s -t $maildir/cur/"); } sub prompt($$) { my ($text, $default) = @_; my $query = ""; my $term = Term::ReadLine->new( "notmuch-mutt" ); my $histfile = "$cache_dir/history"; $term->ornaments( 0 ); $term->unbind_key( ord( "\t" ) ); $term->MinLine( 3 ); $histfile = $ENV{MUTT_NOTMUCH_HISTFILE} if $ENV{MUTT_NOTMUCH_HISTFILE}; $term->ReadHistory($histfile) if (-r $histfile); while (1) { chomp($query = $term->readline($text, $default)); if ($query eq "?") { system("man", "notmuch-search-terms"); } else { $term->WriteHistory($histfile); return $query; } } } sub get_message_id() { my $mail = Mail::Internet->new(\*STDIN); $mail->head->get("message-id") =~ /^<(.*)>$/; # get message-id return $1; } sub search_action($$@) { my ($interactive, $results_dir, @params) = @_; if (! $interactive) { search($results_dir, join(' ', @params)); } else { my $query = prompt("search ('?' for man): ", join(' ', @params)); if ($query ne "") { search($results_dir,$query); } } } sub thread_action(@) { my ($results_dir, @params) = @_; my $mid = get_message_id(); my $search_cmd = 'notmuch search --output=threads ' . shell_quote("id:$mid"); my $tid = `$search_cmd`; # get thread id chomp($tid); search($results_dir, $tid); } sub tag_action(@) { my $mid = get_message_id(); system("notmuch tag " . shell_quote(join(' ', @_)) . " id:$mid"); } sub die_usage() { my %podflags = ( "verbose" => 1, "exitval" => 2 ); pod2usage(%podflags); } sub main() { mkpath($cache_dir) unless (-d $cache_dir); my $results_dir = "$cache_dir/results"; my $interactive = 0; my $help_needed = 0; my $getopt = GetOptions( "h|help" => \$help_needed, "o|output-dir=s" => \$results_dir, "p|prompt" => \$interactive); if (! $getopt || $#ARGV < 0) { die_usage() }; my ($action, @params) = ($ARGV[0], @ARGV[1..$#ARGV]); foreach my $param (@params) { $param =~ s/folder:=/folder:/g; } if ($help_needed) { die_usage(); } elsif ($action eq "search" && $#ARGV == 0 && ! $interactive) { print STDERR "Error: no search term provided\n\n"; die_usage(); } elsif ($action eq "search") { search_action($interactive, $results_dir, @params); } elsif ($action eq "thread") { thread_action($results_dir, @params); } elsif ($action eq "tag") { tag_action(@params); } else { die_usage(); } } main(); __END__ =head1 NAME notmuch-mutt - notmuch (of a) helper for Mutt =head1 SYNOPSIS =over =item B [I