#!PERL_COMMAND
-# $Cambridge: exim/src/src/exipick.src,v 1.4 2005/03/22 15:07:42 ph10 Exp $
+# $Cambridge: exim/src/src/exipick.src,v 1.8 2005/12/15 17:58:23 jetmore Exp $
# This variable should be set by the building process to Exim's spool directory.
my $spool = 'SPOOL_DIRECTORY';
use Getopt::Long;
my($p_name) = $0 =~ m|/?([^/]+)$|;
-my $p_version = "20050225.0";
+my $p_version = "20051215.3";
my $p_usage = "Usage: $p_name [--help|--version] (see --help for details)";
my $p_cp = <<EOM;
Copyright (c) 2003-2005 John Jetmore <jj33\@pobox.com>
'or' => \$G::or, # 'or' the criteria
'f:s' => \$G::qgrep_f, # from regexp
'r:s' => \$G::qgrep_r, # recipient regexp
- #'s:s' => \$G::qgrep_s, # match against size field
+ 's:s' => \$G::qgrep_s, # match against size field
'y:s' => \$G::qgrep_y, # message younger than (secs)
'o:s' => \$G::qgrep_o, # message older than (secs)
'z' => \$G::qgrep_z, # frozen only
'show-tests' => \$G::show_tests # display tests as applied to each message
) || exit(1);
-push(@ARGV, "\$sender_address =~ /$G::qgrep_f/") if ($G::qgrep_f);
-push(@ARGV, "\$recipients =~ /$G::qgrep_r/") if ($G::qgrep_r);
-push(@ARGV, "\$message_age < $G::qgrep_y") if ($G::qgrep_y);
-push(@ARGV, "\$message_age > $G::qgrep_o") if ($G::qgrep_o);
-push(@ARGV, "\$deliver_freeze") if ($G::qgrep_z);
-push(@ARGV, "!\$deliver_freeze") if ($G::qgrep_x);
+push(@ARGV, "\$sender_address =~ /$G::qgrep_f/") if ($G::qgrep_f);
+push(@ARGV, "\$recipients =~ /$G::qgrep_r/") if ($G::qgrep_r);
+push(@ARGV, "\$shown_message_size eq $G::qgrep_s") if ($G::qgrep_s);
+push(@ARGV, "\$message_age < $G::qgrep_y") if ($G::qgrep_y);
+push(@ARGV, "\$message_age > $G::qgrep_o") if ($G::qgrep_o);
+push(@ARGV, "\$deliver_freeze") if ($G::qgrep_z);
+push(@ARGV, "!\$deliver_freeze") if ($G::qgrep_x);
$G::mailq_bp = $G::mailq_bp; # shut up -w
$G::and = $G::and; # shut up -w
-$G::msg_ids = {};
+$G::msg_ids = {}; # short circuit when crit is only MID
$G::caseless = $G::caseful ? 0 : 1; # nocase by default, case if both
-@G::recipients_crit = ();
+@G::recipients_crit = (); # holds per-recip criteria
$spool = $G::spool if ($G::spool);
my $count_only = 1 if ($G::mailq_bpc || $G::qgrep_c);
my $unsorted = 1 if ($G::mailq_bpr || $G::mailq_bpra || $G::mailq_bpru);
my $msg = get_all_msgs($spool, $unsorted);
my $crit = process_criteria(\@ARGV);
my $e = Exim::SpoolFile->new();
-my $tcount = 0 if ($count_only);
-my $mcount = 0 if ($count_only);
+my $tcount = 0 if ($count_only); # holds count of all messages
+my $mcount = 0 if ($count_only); # holds count of matching messages
$e->set_undelivered_only(1) if ($G::mailq_bpru || $G::mailq_bpu);
$e->set_show_generated(1) if ($G::mailq_bpra || $G::mailq_bpa);
$e->output_long() if ($G::qgrep_l);
push(@local_crit, \%t);
}
}
- if ($G::show_tests) { print $e->get_var('message_id'), "\n"; }
+ if ($G::show_tests) { print $e->get_var('message_exim_id'), "\n"; }
CRITERIA:
foreach my $c (@$crit, @local_crit) {
my $var = $e->get_var($c->{var});
else { next(MSG); }
}
}
- next(MSG) if (scalar(@$crit, @local_crit) > 0 && !$match);
+
+ # skip this message if any criteria were supplied and it didn't match
+ next(MSG) if ((scalar(@$crit) || scalar(@local_crit)) && !$match);
if ($count_only) {
$mcount++;
} elsif (/^(.*?)\s+(eq|ne)\s+(.*)$/) {
#print STDERR "found as string cmp\n";
my $var = lc($1); my $op = $2; my $val = $3;
+ $val =~ s|^(['"])(.*)\1$|$2|;
push(@c, { var => $var, cmp => "(\"\$var\" $op \"$val\") ? 1 : 0" });
- if ($var eq 'message_id' && $op eq "eq") {
+ if (($var eq 'message_id' || $var eq 'message_exim_id') && $op eq "eq") {
#print STDERR "short circuit @c[-1]->{cmp} $val\n";
$G::msg_ids->{$val} = 1;
}
package Exim::SpoolFile;
-$Exim::SpoolFile::ACL_C_MAX = 10;
-#$Exim::SpoolFile::ACL_M_MAX = 10;
+# versions 4.61 and higher will not need these variables anymore, but they
+# are left for handling legacy installs
+$Exim::SpoolFile::ACL_C_MAX_LEGACY = 10;
+#$Exim::SpoolFile::ACL_M_MAX _LEGACY= 10;
sub new {
my $class = shift;
$self->{_output_idonly} = 0;
$self->{_output_brief} = 0;
$self->{_output_flatq} = 0;
- $self->{_show_vars} = {};
+ $self->{_show_vars} = [];
$self->_reset();
return($self);
my $s = shift;
foreach my $v (split(/\s*,\s*/, $s)) {
- $self->{_show_vars}{$v}++;
+ push(@{$self->{_show_vars}}, $v);
}
}
# accepts a variable with or without leading '$' or trailing ':'
sub get_var {
my $self = shift;
- my $var = shift;
+ my $var = lc(shift);
$var =~ s/^\$//;
$var =~ s/:$//;
chomp($_ = <I>);
return(0) if ($self->{_message}.'-H' ne $_);
$self->{_vars}{message_id} = $self->{_message};
+ $self->{_vars}{message_exim_id} = $self->{_message};
# line 2
chomp($_ = <I>);
- return(0) if (!/^(.+)\s(\d+)\s(\d+)$/);
+ return(0) if (!/^(.+)\s(\-?\d+)\s(\-?\d+)$/);
$self->{_vars}{originator_login} = $1;
$self->{_vars}{originator_uid} = $2;
$self->{_vars}{originator_gid} = $3;
if ($tag eq '-acl') {
my $t;
return(0) if ($arg !~ /^(\d+)\s(\d+)$/);
- if ($1 < $Exim::SpoolFile::ACL_C_MAX) {
+ if ($1 < $Exim::SpoolFile::ACL_C_MAX_LEGACY) {
$t = "acl_c$1";
} else {
- $t = "acl_m" . ($1 - $Exim::SpoolFile::ACL_C_MAX);
+ $t = "acl_m" . ($1 - $Exim::SpoolFile::ACL_C_MAX_LEGACY);
}
read(I, $self->{_vars}{$t}, $2+1) || return(0);
chomp($self->{_vars}{$t});
+ } elsif ($tag eq '-aclc') {
+ return(0) if ($arg !~ /^(\d+)\s(\d+)$/);
+ my $t = "acl_c$1";
+ read(I, $self->{_vars}{$t}, $2+1) || return(0);
+ chomp($self->{_vars}{$t});
+ } elsif ($tag eq '-aclm') {
+ return(0) if ($arg !~ /^(\d+)\s(\d+)$/);
+ my $t = "acl_m$1";
+ read(I, $self->{_vars}{$t}, $2+1) || return(0);
+ chomp($self->{_vars}{$t});
} elsif ($tag eq '-local') {
$self->{_vars}{sender_local} = 1;
} elsif ($tag eq '-localerror') {
my $bytes = $_;
return(0) if (read(I, $_, $bytes) != $bytes);
chomp(); # may regret this later
+ $self->{_vars}{message_linecount} += scalar(split(/\n/)) if ($t ne '*');
# build the $header_ variable, following exim's rules (sort of)
if (/^([^ :]+):(.*)$/s) {
my $v = "header_" . lc($1);
$self->{_vars}{message_size} += $self->{_vars}{message_body_size} + 1;
}
+ $self->{_vars}{message_linecount} += $self->{_vars}{body_linecount};
+
+ my $i = $self->{_vars}{message_size};
+ if ($i == 0) { $i = ""; }
+ elsif ($i < 1024) { $i = sprintf("%d", $i); }
+ elsif ($i < 10*1024) { $i = sprintf("%.1fK", $i / 1024); }
+ elsif ($i < 1024*1024) { $i = sprintf("%dK", ($i+512)/1024); }
+ elsif ($i < 10*1024*1024) { $i = sprintf("%.1fM", $i/(1024*1024)); }
+ else { $i = sprintf("%dM", ($i + 512 * 1024)/(1024*1024)); }
+ $self->{_vars}{shown_message_size} = $i;
+
return(1);
}
return if ($self->{_delivered});
if ($self->{_output_idonly}) {
- print $fh $self->{_message}, "\n";
+ print $fh $self->{_message};
+ foreach my $v (@{$self->{_show_vars}}) {
+ print $fh " $v='", $self->get_var($v), "'";
+ }
+ print $fh "\n";
return;
}
else { printf $fh "%2dh ", $i; }
} else { printf $fh "%2dm ", $i; }
- $i = $self->{_vars}{message_size};
- if ($i == 0) { $i = " "; }
- elsif ($i < 1024) { $i = sprintf("%5d", $i); }
- elsif ($i < 10*1024) { $i = sprintf("%4.1fK", $i / 1024); }
- elsif ($i < 1024*1024) { $i = sprintf("%4dK", ($i+512)/1024); }
- elsif ($i < 10*1024*1024) { $i = sprintf("%4.1fM", $i/(1024*1024)); }
- else { $i = sprintf("%4dM", ($i + 512 * 1024)/(1024*1024)); }
- print $fh "$i ";
+ if ($self->{_output_flatq} && $self->{_show_vars}) {
+ print $fh join(';',
+ map { "$_='".$self->get_var($_)."'" }
+ (@{$self->{_show_vars}})
+ );
+ } else {
+ printf $fh "%5s", $self->{_vars}{shown_message_size};
+ }
+ print $fh " ";
}
print $fh "$self->{_message} ";
print $fh "From: " if ($self->{_output_brief});
print $fh " *** frozen ***" if ($self->{_vars}{deliver_freeze});
print $fh "\n";
- foreach my $v (keys(%{$self->{_show_vars}})) {
+ foreach my $v (@{$self->{_show_vars}}) {
printf $fh " %25s = '%s'\n", $v, $self->get_var($v);
}
push(@r, $r);
}
print $fh " To: ", join(';', @r);
+ if ($self->{_show_vars} && scalar(@{$self->{_show_vars}})) {
+ print $fh " Vars: ", join(';',
+ map { "$_='".$self->get_var($_)."'" }
+ (@{$self->{_show_vars}})
+ );
+ }
} elsif ($self->{_output_flatq}) {
print $fh " *** frozen ***" if ($self->{_vars}{deliver_freeze});
my @r = ();
By default criteria using the '=' operator are caseless. Specifying this option make them respect case.
+=item --show-vars <variable>[,<variable>...]
+
+Cause the value of each specified variable to be displayed for every message dispayed. For instance, the command "exipick --show-vars '$sender_ident' 'sender_host_address eq 127.0.01'" will show the ident string for every message submitted via localhost. How exactly the variable value is diplayed changes according to what output format you specify.
+
+=item --show-rules
+
+If specified the internal representation of each message criteria is shown. This is primarily used for debugging purposes.
+
+==item --show-tests
+
+If specified, for every message (regardless of matching criteria) the criteria's actual value is shown and the compiled internal eval is shown. This is used primarily for debugging purposes.
+
+=item --flatq
+
+Change format of output so that every message is on a single line. Useful for parsing with tools such as sed, awk, cut, etc.
+
=item The -bp* options all control how much information is displayed and in what manner. They all match the functionality of the options of the same name in Exim. Briefly:
=item -bp display the matching messages in 'mailq' format.
=item -r <regexp> Same as '$recipients = <regexp>'
+=item -s <string> Same as '$shown_message_size eq <string>'
+
=item -y <seconds> Same as '$message_age < <seconds>'
=item -o <seconds> Same as '$message_age > <seconds>'
The size of the body in bytes.
+=item . $message_linecount
+
+The number of lines in the entire message (body and headers).
+
=item . $message_size
The size of the message in bytes.
The number of Received: header lines in the message.
-=item + $received_time
+=item . $received_time
The epoch time at which the message was received.
A concatenation of all the header lines except for lines added by routers or transports.
-=item . $message_id
+=item . $message_exim_id, $message_id
-The unique message id that is used by Exim to identify the message.
+The unique message id that is used by Exim to identify the message. $message_id is deprecated as of Exim 4.53.
=item + $originator_login
The identification received in response to an RFC 1413 request for remote messages, the login name of the user that called Exim for locally generated messages.
+=item + $shown_message_size
+
+This non-standard variable contains the formatted size string. That is, for a message whose $message_size is 66566 bytes, $shown_message_size is 65K.
+
=item . $smtp_active_hostname
The value of the active host name when the message was received, as specified by the "smtp_active_hostname" option.