X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=contrib%2Fnotmuch-mutt%2Fnotmuch-mutt;h=d1e2c084b4208f093f97a47e81169da28a65d9ad;hp=97fb2d85160cb9a495de68c31103671beeae3fc0;hb=HEAD;hpb=e3e03fb718de9790bc9b7a78210f42559bff24e5 diff --git a/contrib/notmuch-mutt/notmuch-mutt b/contrib/notmuch-mutt/notmuch-mutt index 97fb2d85..b81252c8 100755 --- a/contrib/notmuch-mutt/notmuch-mutt +++ b/contrib/notmuch-mutt/notmuch-mutt @@ -2,7 +2,7 @@ # # notmuch-mutt - notmuch (of a) helper for Mutt # -# Copyright: © 2011-2015 Stefano Zacchiroli +# Copyright: © 2011-2015 Stefano Zacchiroli # License: GNU General Public License (GPL), version 3 or above # # See the bottom of this file for more documentation. @@ -12,11 +12,12 @@ use strict; use warnings; use File::Path; +use File::Basename; +use File::Find; use Getopt::Long qw(:config no_getopt_compat); use Mail::Header; use Mail::Box::Maildir; use Pod::Usage; -use String::ShellQuote; use Term::ReadLine; use Digest::SHA; @@ -25,9 +26,53 @@ 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"; +sub die_dir($$) { + my ($maildir, $error) = @_; + die "notmuch-mutt: search cache maildir $maildir $error\n". + "Please ensure that the notmuch-mutt search cache Maildir\n". + "contains no subfolders or real mail data, only symlinks to mail\n"; +} + +sub die_subdir($$$) { + my ($maildir, $subdir, $error) = @_; + die_dir($maildir, "subdir $subdir $error"); +} -# create an empty maildir (if missing) or empty an existing maildir" -sub empty_maildir($) { +# check that the search cache maildir is that and not a real maildir +# otherwise there could be data loss when the search cache is emptied +sub check_search_cache_maildir($) { + my ($maildir) = (@_); + + return unless -e $maildir; + + -d $maildir or die_dir($maildir, 'is not a directory'); + + opendir(my $mdh, $maildir) or die_dir($maildir, "cannot be opened: $!"); + my @contents = grep { !/^\.\.?$/ } readdir $mdh; + closedir $mdh; + + my @required = ('cur', 'new', 'tmp'); + foreach my $d (@required) { + -l "$maildir/$d" and die_dir($maildir, "contains symlink $d"); + -e "$maildir/$d" or die_subdir($maildir, $d, 'is missing'); + -d "$maildir/$d" or die_subdir($maildir, $d, 'is not a directory'); + find(sub { + $_ eq '.' and return; + $_ eq '..' and return; + -l $_ or die_subdir($maildir, $d, "contains non-symlink $_"); + }, "$maildir/$d"); + } + + my %required = map { $_ => 1 } @required; + foreach my $d (@contents) { + -l "$maildir/$d" and die_dir( $maildir, "contains symlink $d"); + -d "$maildir/$d" or die_dir( $maildir, "contains non-directory $d"); + exists($required{$d}) or die_dir( $maildir, "contains directory $d"); + } +} + +# create an empty search cache maildir (if missing) or empty existing one +sub empty_search_cache_maildir($) { my ($maildir) = (@_); rmtree($maildir) if (-d $maildir); my $folder = new Mail::Box::Maildir(folder => $maildir, @@ -41,16 +86,18 @@ sub search($$$) { my ($maildir, $remove_dups, $query) = @_; my $dup_option = ""; - $query = shell_quote($query); - - if ($remove_dups) { - $dup_option = "--duplicate=1"; + my @args = qw/notmuch search --output=files/; + push @args, "--duplicate=1" if $remove_dups; + push @args, $query; + + check_search_cache_maildir($maildir); + empty_search_cache_maildir($maildir); + open my $pipe, '-|', @args or die "Running @args failed: $!\n"; + while (<$pipe>) { + chomp; + my $ln = "$maildir/cur/" . basename $_; + symlink $_, "$ln" or warn "Failed to symlink '$_', '$ln': $!\n"; } - - empty_maildir($maildir); - system("notmuch search --output=files $dup_option $query" - . " | sed -e 's: :\\\\ :g'" - . " | xargs -r -I searchoutput ln -s searchoutput $maildir/cur/"); } sub prompt($$) { @@ -91,7 +138,7 @@ sub get_message_id() { $mid = $1; } else { # Message-ID header not found, synthesize a message id # based on SHA1, as notmuch would do. See: - # http://git.notmuchmail.org/git/notmuch/blob/HEAD:/lib/sha1.c + # https://git.notmuchmail.org/git/notmuch/blob/HEAD:/lib/sha1.c my $sha = Digest::SHA->new(1); $sha->add($_) foreach(@headers); $sha->addfile(\*STDIN); @@ -119,21 +166,23 @@ sub thread_action($$@) { my $mid = get_message_id(); if (! defined $mid) { - empty_maildir($results_dir); die "notmuch-mutt: cannot find Message-Id, abort.\n"; } - my $search_cmd = 'notmuch search --output=threads ' . shell_quote("id:$mid"); - my $tid = `$search_cmd`; # get thread id - chomp($tid); - search($results_dir, $remove_dups, $tid); + $mid =~ s/ //g; # notmuch strips spaces before storing Message-Id + $mid =~ s/"/""""/g; # escape all double quote characters twice + + search($results_dir, $remove_dups, qq{thread:"{id:""$mid""}"}); } sub tag_action(@) { my $mid = get_message_id(); defined $mid or die "notmuch-mutt: cannot find Message-Id, abort.\n"; - system("notmuch", "tag", @_, "--", "id:$mid"); + $mid =~ s/ //g; # notmuch strips spaces before storing Message-Id + $mid =~ s/"/""/g; # escape all double quote characters + + system("notmuch", "tag", @_, "--", qq{id:"$mid"}); } sub die_usage() {