## # autofile.pl - auto-file messages in flail # # Time-stamp: <2006-07-02 12:16:38 attila@stalphonsos.com> # $Id: autofile.pl,v 1.5 2006/07/13 17:59:11 attila Exp $ # # Copyright (C) 2006 by Sean Levy . # All Rights Reserved. # This file is released under a BSD license. See the LICENSE # file that should've come with the flail distribution. ## # This is the code I use to automatically file messages in my # inbox based on regular expressions. The $AUTO_FILE hashref # (of which there is an example in autofile_config.pl) contains # the data used by this code to decode what to do with messages. # # A message can match more than once, in which case it will # be filed more than once. # # The two main entry points are autofile and automark, which # are typically used like this from the flail command line: # # map all { autofile() } # # I define an alias in dot.flailrc called autofile which does # just this. You can also see what autofile will do by using # automark, which just marks matching messages instead of # mv'ing them, but also spits out all of the matches for each # message. # # This .pl file, along with a suitably tweaked autofile_config.pl # should be installed somewhere in your @INC. The sample # dot.flailrc puts a directory called ~/.flail on the front of @INC # if it exists, with the intention of it being a convenient # place to stick exactly this kind of stuff. ## use vars qw($AUTO_FILE); sub say; sub is_before { my($h,$d) = @_; my $dh = parsedate($h->get("Date")); my $dt = parsedate($d); return ($dh < $dt)? 1: 0; } sub is_after { my($h,$d) = @_; my $dh = parsedate($h->get("Date")); my $dt = parsedate($d); return ($dh >= $dt)? 1: 0; } sub is_about { my($h,$p) = @_; my $s = $h->get("Subject"); return ($s =~ /$p/)? 1: 0; } sub is_from { my($h,$p) = @_; foreach my $x (qw(From Sender)) { my $f = $h->get($x); return 1 if $f =~ /$p/i; } return 0; } sub is_to { my($h,$p) = @_; foreach my $x (qw(To Cc Bcc)) { my $f = $h->get($x); return 1 if $f =~ /$p/i; } return 0; } sub push_autofile_result { my($results,$tag,$foo,$k) = @_; my $flags = undef; ($k,$flags) = ($1,{ map { $_ => 1 } split(/,/, $2) }) if ($k =~ /^(.*)\[([\w\.]+)\]$/); push(@$results, [ $tag, $foo, $k, $flags ]); } sub suss_autofile_by_headers { return unless defined($AUTO_FILE); my $h = shift(@_); my @headers = ( grep { $_ !~ /^[: ]/ } keys %$AUTO_FILE ); my @results = (); say "autofile_by_headers: @headers"; foreach my $tag (@headers) { my $hash = $AUTO_FILE->{$tag}; my @hdrs = split(/,/, $tag); foreach my $hdr (@hdrs) { my $n = $h->count($hdr); my $j = 0; say "autofile: examining $n $hdr headers"; while ($j < $n) { my $v = $h->get($hdr,$j); ++$j; $v = addresschomp($v); my @tmp = split(/,/, $v); say "autofile: $j/$n=$v"; foreach my $foo (@tmp) { $foo = addresschomp($foo); study($foo); ### Maybe? say "autofile: studied $hdr=$foo"; foreach my $k (keys %$hash) { my $re = $hash->{$k}; say "autofile: $k: /$re/"; push_autofile_result(\@results,$hdr,$foo,$k) if ($foo =~ /$re/i); } } } } } return @results; } sub dump_autofile_results { foreach my $auto (@_) { my($header,$value,$folder,$flags) = @$auto; my $flagstr = ''; $flagstr = ' ('.join(',', sort { $a cmp $b } keys %$flags) . ')' if defined($flags); print "autofile: $header = $value => $folder$flagstr\n"; } } sub suss_autofile_by_content { return unless defined($AUTO_FILE); say "autofile: args=@_\n"; my $h = shift(@_); my $chash = $AUTO_FILE->{':Content'}; return () unless defined($chash); my @results = (); my $body = $h->body(); if (!defined($body)) { warn("<2>no body in $h? trying $M\n"); $body = $M->body() if defined($M); if (!defined($body)) { warn("<2>no body no how\n"); return (); } } $body = join("\n", @$body) if ref($body) eq 'ARRAY'; study($body); foreach my $k (keys %$chash) { my $re = $chash->{$k}; if ($body =~ /$re/) { my $captured = []; foreach my $i (1 .. 9) { last unless defined(${$i}); push(@$captured, ${$i}); } $captured = undef unless @$captured > 0; push_autofile_result(\@results,':Content',$captured,$k); } } return @results; } sub autofile { my $h = $H; my @auto = ( suss_autofile_by_headers($H), suss_autofile_by_content($M) ); return unless @auto > 0; my %filed = (); foreach my $af (@auto) { next unless defined($af); ## ?? my($header,$value,$folder,$flags) = @$af; my $flagstr = ''; $flagstr = ' ('.join(',', sort { $a cmp $b } keys %$flags) . ')' if defined($flags); say "autofile: $header = $value => $folder$flagstr"; next unless defined($folder); my $to = $h->get("To"); $to = addresschomp($to); my $subj = $h->get("Subject"); $subj = psychochomp($subj); unless (defined($flags) && $flags->{'quiet'}) { if ($header ne ':Content') { print "[$N: $header:$value => $to: $subj => $folder$flagstr]\n"; } else { print "[$N: Content Match: $to: $subj => $folder$flagstr]\n"; } } flail_eval("mv $N $folder") unless $filed{$folder}; ## API needs work, man ++$filed{$folder}; } } sub automark { my @auto = ( suss_autofile_by_headers($H), suss_autofile_by_content($M), ); return unless @auto > 0; flail_eval("mark $N"); my $to = $H->get("To"); $to = addresschomp($to); my $subj = $H->get("Subject"); $subj = psychochomp($subj); foreach my $af (@auto) { next unless defined($af); my($header,$value,$folder,$flags) = @$af; unless (defined($flags) && $flags->{'quiet'}) { my $flagstr = ''; $flagstr = ' ('.join(',', sort { $a cmp $b } keys %$flags) . ')' if defined($flags); if ($header ne ':Content') { print "[$N: Marked ($folder): $header:$value => $to: $subj$flagstr]\n"; } else { print "[$N: Marked for content ($folder): $to: $subj$flagstr]\n"; } } } } 1; __END__ # Local variables: # mode: perl # indent-tabs-mode: nil # tab-width: 4 # perl-indent-level: 4 # End: