Expand the documentation about the new submission mode behaviour.
[exim.git] / src / src / exipick.src
CommitLineData
059ec3d9 1#!PERL_COMMAND
bf759a8b 2# $Cambridge: exim/src/src/exipick.src,v 1.4 2005/03/22 15:07:42 ph10 Exp $
059ec3d9
PH
3
4# This variable should be set by the building process to Exim's spool directory.
5my $spool = 'SPOOL_DIRECTORY';
6
7use strict;
8use Getopt::Long;
9
10my($p_name) = $0 =~ m|/?([^/]+)$|;
bf759a8b 11my $p_version = "20050225.0";
059ec3d9
PH
12my $p_usage = "Usage: $p_name [--help|--version] (see --help for details)";
13my $p_cp = <<EOM;
bf759a8b 14 Copyright (c) 2003-2005 John Jetmore <jj33\@pobox.com>
059ec3d9
PH
15
16 This program is free software; you can redistribute it and/or modify
17 it under the terms of the GNU General Public License as published by
18 the Free Software Foundation; either version 2 of the License, or
19 (at your option) any later version.
20
21 This program is distributed in the hope that it will be useful,
22 but WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 GNU General Public License for more details.
25
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29EOM
30ext_usage(); # before we do anything else, check for --help
31
bf759a8b
PH
32$| = 1; # unbuffer STDOUT
33
059ec3d9
PH
34Getopt::Long::Configure("bundling_override");
35GetOptions(
bf759a8b
PH
36 'spool:s' => \$G::spool, # exim spool dir
37 'bp' => \$G::mailq_bp, # List the queue (noop - default)
38 'bpa' => \$G::mailq_bpa, # ... with generated address as well
39 'bpc' => \$G::mailq_bpc, # ... but just show a count of messages
40 'bpr' => \$G::mailq_bpr, # ... do not sort
41 'bpra' => \$G::mailq_bpra, # ... with generated addresses, unsorted
42 'bpru' => \$G::mailq_bpru, # ... only undelivered addresses, unsorted
43 'bpu' => \$G::mailq_bpu, # ... only undelivered addresses
44 'and' => \$G::and, # 'and' the criteria (default)
45 'or' => \$G::or, # 'or' the criteria
46 'f:s' => \$G::qgrep_f, # from regexp
47 'r:s' => \$G::qgrep_r, # recipient regexp
48 #'s:s' => \$G::qgrep_s, # match against size field
49 'y:s' => \$G::qgrep_y, # message younger than (secs)
50 'o:s' => \$G::qgrep_o, # message older than (secs)
51 'z' => \$G::qgrep_z, # frozen only
52 'x' => \$G::qgrep_x, # non-frozen only
53 'c' => \$G::qgrep_c, # display match count
54 'l' => \$G::qgrep_l, # long format (default)
55 'i' => \$G::qgrep_i, # message ids only
56 'b' => \$G::qgrep_b, # brief format
57 'flatq' => \$G::flatq, # brief format
58 'caseful' => \$G::caseful, # in '=' criteria, respect case
59 'caseless' => \$G::caseless, # ...ignore case (default)
60 'show-vars:s' => \$G::show_vars, # display the contents of these vars
61 'show-rules' => \$G::show_rules, # display compiled match rules
62 'show-tests' => \$G::show_tests # display tests as applied to each message
059ec3d9
PH
63) || exit(1);
64
65push(@ARGV, "\$sender_address =~ /$G::qgrep_f/") if ($G::qgrep_f);
66push(@ARGV, "\$recipients =~ /$G::qgrep_r/") if ($G::qgrep_r);
67push(@ARGV, "\$message_age < $G::qgrep_y") if ($G::qgrep_y);
68push(@ARGV, "\$message_age > $G::qgrep_o") if ($G::qgrep_o);
69push(@ARGV, "\$deliver_freeze") if ($G::qgrep_z);
70push(@ARGV, "!\$deliver_freeze") if ($G::qgrep_x);
bf759a8b
PH
71$G::mailq_bp = $G::mailq_bp; # shut up -w
72$G::and = $G::and; # shut up -w
73$G::msg_ids = {};
74$G::caseless = $G::caseful ? 0 : 1; # nocase by default, case if both
75@G::recipients_crit = ();
76$spool = $G::spool if ($G::spool);
77my $count_only = 1 if ($G::mailq_bpc || $G::qgrep_c);
78my $unsorted = 1 if ($G::mailq_bpr || $G::mailq_bpra || $G::mailq_bpru);
79my $msg = get_all_msgs($spool, $unsorted);
80my $crit = process_criteria(\@ARGV);
81my $e = Exim::SpoolFile->new();
82my $tcount = 0 if ($count_only);
83my $mcount = 0 if ($count_only);
84$e->set_undelivered_only(1) if ($G::mailq_bpru || $G::mailq_bpu);
85$e->set_show_generated(1) if ($G::mailq_bpra || $G::mailq_bpa);
86$e->output_long() if ($G::qgrep_l);
87$e->output_idonly() if ($G::qgrep_i);
88$e->output_brief() if ($G::qgrep_b);
89$e->output_flatq() if ($G::flatq);
059ec3d9 90$e->set_show_vars($G::show_vars) if ($G::show_vars);
bf759a8b 91$e->set_spool($spool);
059ec3d9
PH
92
93MSG:
94foreach my $m (@$msg) {
af66f652
PH
95 next if (scalar(keys(%$G::msg_ids)) && !$G::or
96 && !$G::msg_ids->{$m->{message}});
059ec3d9
PH
97 if (!$e->parse_message($m->{message})) {
98 warn "Couldn't parse $m->{message}: ".$e->error()."\n";
99 next(MSG);
100 }
101 $tcount++;
102 my $match = 0;
bf759a8b
PH
103 my @local_crit = ();
104 foreach my $c (@G::recipients_crit) { # handle each_recip* vars
105 foreach my $addr (split(/, /, $e->get_var($c->{var}))) {
106 my %t = ( 'cmp' => $c->{cmp}, 'var' => $c->{var} );
107 $t{cmp} =~ s/"?\$var"?/'$addr'/;
108 push(@local_crit, \%t);
109 }
110 }
111 if ($G::show_tests) { print $e->get_var('message_id'), "\n"; }
059ec3d9 112 CRITERIA:
bf759a8b 113 foreach my $c (@$crit, @local_crit) {
059ec3d9
PH
114 my $var = $e->get_var($c->{var});
115 my $ret = eval($c->{cmp});
bf759a8b
PH
116 if ($G::show_tests) {
117 printf " %25s = '%s'\n %25s => $ret\n",$c->{var},$var,$c->{cmp},$ret;
118 }
059ec3d9
PH
119 if ($@) {
120 print STDERR "Error in eval '$c->{cmp}': $@\n";
121 next(MSG);
122 } elsif ($ret) {
123 $match = 1;
124 if ($G::or) { last(CRITERIA); }
125 else { next(CRITERIA); }
126 } else { # no match
127 if ($G::or) { next(CRITERIA); }
bf759a8b 128 else { next(MSG); }
059ec3d9
PH
129 }
130 }
bf759a8b 131 next(MSG) if (scalar(@$crit, @local_crit) > 0 && !$match);
059ec3d9
PH
132
133 if ($count_only) {
134 $mcount++;
135 } else {
136 $e->print_message(\*STDOUT);
137 }
138}
139
140if ($G::mailq_bpc) {
141 print "$tcount\n";
142} elsif ($G::qgrep_c) {
143 print "$mcount matches out of $tcount messages\n";
144}
145
146exit;
147
148sub process_criteria {
149 my $a = shift;
150 my @c = ();
151 my $e = 0;
152
153 foreach (@$a) {
154 foreach my $t ('@') { s/$t/\\$t/g; } # '$'
155 if (/^(.*?)\s+(<=|>=|==|!=|<|>)\s+(.*)$/) {
156 #print STDERR "found as integer\n";
157 my $v = $1; my $o = $2; my $n = $3;
158 if ($n =~ /^([\d\.]+)M$/) { $n = $1 * 1024 * 1024; }
159 elsif ($n =~ /^([\d\.]+)K$/) { $n = $1 * 1024; }
160 elsif ($n =~ /^([\d\.]+)B?$/) { $n = $1; }
161 elsif ($n =~ /^([\d\.]+)d$/) { $n = $1 * 60 * 60 * 24; }
162 elsif ($n =~ /^([\d\.]+)h$/) { $n = $1 * 60 * 60; }
163 elsif ($n =~ /^([\d\.]+)m$/) { $n = $1 * 60; }
164 elsif ($n =~ /^([\d\.]+)s?$/) { $n = $1; }
165 else {
166 print STDERR "Expression $_ did not parse: numeric comparison with ",
167 "non-number\n";
168 $e = 1;
169 next;
170 }
171 push(@c, { var => lc($v), cmp => "(\$var $o $n) ? 1 : 0" });
172 } elsif (/^(.*?)\s+(=~|!~)\s+(.*)$/) {
173 #print STDERR "found as string regexp\n";
174 push(@c, { var => lc($1), cmp => "(\"\$var\" $2 $3) ? 1 : 0" });
175 } elsif (/^(.*?)\s+=\s+(.*)$/) {
176 #print STDERR "found as bare string regexp\n";
af66f652
PH
177 my $case = $G::caseful ? '' : 'i';
178 push(@c, { var => lc($1), cmp => "(\"\$var\" =~ /$2/$case) ? 1 : 0" });
059ec3d9
PH
179 } elsif (/^(.*?)\s+(eq|ne)\s+(.*)$/) {
180 #print STDERR "found as string cmp\n";
af66f652
PH
181 my $var = lc($1); my $op = $2; my $val = $3;
182 push(@c, { var => $var, cmp => "(\"\$var\" $op \"$val\") ? 1 : 0" });
183 if ($var eq 'message_id' && $op eq "eq") {
184 #print STDERR "short circuit @c[-1]->{cmp} $val\n";
185 $G::msg_ids->{$val} = 1;
186 }
059ec3d9
PH
187 } elsif (/^(!)?(\S+)$/) {
188 #print STDERR "found as boolean\n";
189 push(@c, { var => lc($2), cmp => "($1\$var) ? 1 : 0" });
190 } else {
191 print STDERR "Expression $_ did not parse\n";
192 $e = 1;
193 }
bf759a8b
PH
194 # support the each_* psuedo variables. Steal the criteria off of the
195 # queue for special processing later
196 if ($c[-1]{var} =~ /^each_(recipients(_(un)?del)?)$/) {
197 my $var = $1;
198 push(@G::recipients_crit,pop(@c));
199 $G::recipients_crit[-1]{var} = $var; # remove each_ from the variable
200 }
059ec3d9
PH
201 }
202
203 exit(1) if ($e);
204
205 if ($G::show_rules) { foreach (@c) { print "$_->{var}\t$_->{cmp}\n"; } }
206
207 return(\@c);
208}
209
210sub get_all_msgs {
211 my $d = shift() . '/input';
212 my $u = shift;
213 my @m = ();
214
215 opendir(D, "$d") || die "Couldn't opendir $d: $!\n";
216 foreach my $e (grep !/^\./, readdir(D)) {
217 if ($e =~ /^[a-zA-Z0-9]$/) {
218 opendir(DD, "$d/$e") || next;
219 foreach my $f (grep !/^\./, readdir(DD)) {
220 push(@m, { message => $1, path => "$e/$1" }) if ($f =~ /^(.{16})-H$/);
221 }
222 closedir(DD);
223 } elsif ($e =~ /^(.{16})-H$/) {
224 push(@m, { message => $1, path => $1 });
225 }
226 }
227 closedir(D);
228
229 return($u ? \@m : [ sort { $a->{message} cmp $b->{message} } @m ]);
230}
231
232BEGIN {
233
234package Exim::SpoolFile;
235
236$Exim::SpoolFile::ACL_C_MAX = 10;
237#$Exim::SpoolFile::ACL_M_MAX = 10;
238
239sub new {
240 my $class = shift;
241 my $self = {};
242 bless($self, $class);
243
244 $self->{_spool_dir} = '';
245 $self->{_undelivered_only} = 0;
246 $self->{_show_generated} = 0;
247 $self->{_output_long} = 1;
248 $self->{_output_idonly} = 0;
249 $self->{_output_brief} = 0;
250 $self->{_output_flatq} = 0;
251 $self->{_show_vars} = {};
252
253 $self->_reset();
254 return($self);
255}
256
257sub output_long {
258 my $self = shift;
259
260 $self->{_output_long} = 1;
261 $self->{_output_idonly} = 0;
262 $self->{_output_brief} = 0;
263 $self->{_output_flatq} = 0;
264}
265
266sub output_idonly {
267 my $self = shift;
268
269 $self->{_output_long} = 0;
270 $self->{_output_idonly} = 1;
271 $self->{_output_brief} = 0;
272 $self->{_output_flatq} = 0;
273}
274
275sub output_brief {
276 my $self = shift;
277
278 $self->{_output_long} = 0;
279 $self->{_output_idonly} = 0;
280 $self->{_output_brief} = 1;
281 $self->{_output_flatq} = 0;
282}
283
284sub output_flatq {
285 my $self = shift;
286
287 $self->{_output_long} = 0;
288 $self->{_output_idonly} = 0;
289 $self->{_output_brief} = 0;
290 $self->{_output_flatq} = 1;
291}
292
293sub set_show_vars {
294 my $self = shift;
295 my $s = shift;
296
297 foreach my $v (split(/\s*,\s*/, $s)) {
298 $self->{_show_vars}{$v}++;
299 }
300}
301
302sub set_show_generated {
303 my $self = shift;
304 $self->{_show_generated} = shift;
305}
306
307sub set_undelivered_only {
308 my $self = shift;
309 $self->{_undelivered_only} = shift;
310}
311
312sub error {
313 my $self = shift;
314 return $self->{_error};
315}
316
317sub _error {
318 my $self = shift;
319 $self->{_error} = shift;
320 return(undef);
321}
322
323sub _reset {
324 my $self = shift;
325
326 $self->{_error} = '';
327 $self->{_delivered} = 0;
328 $self->{_message} = '';
329 $self->{_path} = '';
330 $self->{_vars} = {};
331
332 $self->{_numrecips} = 0;
333 $self->{_udel_tree} = {};
334 $self->{_del_tree} = {};
335 $self->{_recips} = {};
336
337 return($self);
338}
339
340sub parse_message {
341 my $self = shift;
8e669ac1 342
059ec3d9
PH
343 $self->_reset();
344 $self->{_message} = shift || return(0);
345 return(0) if (!$self->{_spool_dir});
346 if (!$self->_find_path()) {
347 # assume the message was delivered from under us and ignore
348 $self->{_delivered} = 1;
349 return(1);
350 }
351 $self->_parse_header() || return(0);
352
353 return(1);
354}
355
356sub _find_path {
357 my $self = shift;
358
359 return(0) if (!$self->{_message});
360 return(0) if (!$self->{_spool_dir});
361
362 foreach my $f ('', substr($self->{_message}, 5, 1).'/') {
363 if (-f $self->{_spool_dir} . "/input/$f" . $self->{_message} . '-H') {
364 $self->{_path} = $self->{_spool_dir} . "/input/$f";
365 return(1);
366 }
367 }
368 return(0);
369}
370
371sub set_spool {
372 my $self = shift;
373 $self->{_spool_dir} = shift;
374}
375
376# accepts a variable with or without leading '$' or trailing ':'
377sub get_var {
378 my $self = shift;
379 my $var = shift;
380
381 $var =~ s/^\$//;
382 $var =~ s/:$//;
383
384 $self->_parse_body()
385 if ($var eq 'message_body' && !$self->{_vars}{message_body});
386
387 return $self->{_vars}{$var};
388}
389
390sub _parse_body {
391 my $self = shift;
392 my $f = $self->{_path} . '/' . $self->{_message} . '-D';
393
394 open(I, "<$f") || return($self->_error("Couldn't open $f: $!"));
395 chomp($_ = <I>);
396 return(0) if ($self->{_message}.'-D' ne $_);
397
398 $self->{_vars}{message_body} = join('', <I>);
399 close(I);
400 $self->{_vars}{message_body} =~ s/\n/ /g;
401 $self->{_vars}{message_body} =~ s/\000/ /g;
402 return(1);
403}
404
405sub _parse_header {
406 my $self = shift;
407 my $f = $self->{_path} . '/' . $self->{_message} . '-H';
408
409 open(I, "<$f") || return($self->_error("Couldn't open $f: $!"));
410 chomp($_ = <I>);
411 return(0) if ($self->{_message}.'-H' ne $_);
412 $self->{_vars}{message_id} = $self->{_message};
413
414 # line 2
415 chomp($_ = <I>);
bf759a8b 416 return(0) if (!/^(.+)\s(\d+)\s(\d+)$/);
059ec3d9
PH
417 $self->{_vars}{originator_login} = $1;
418 $self->{_vars}{originator_uid} = $2;
419 $self->{_vars}{originator_gid} = $3;
420
421 # line 3
422 chomp($_ = <I>);
423 return(0) if (!/^<(.*)>$/);
424 $self->{_vars}{sender_address} = $1;
425 $self->{_vars}{sender_address_domain} = $1;
426 $self->{_vars}{sender_address_local_part} = $1;
427 $self->{_vars}{sender_address_domain} =~ s/^.*\@//;
428 $self->{_vars}{sender_address_local_part} =~ s/^(.*)\@.*$/$1/;
429
430 # line 4
431 chomp($_ = <I>);
432 return(0) if (!/^(\d+)\s(\d+)$/);
433 $self->{_vars}{received_time} = $1;
434 $self->{_vars}{warning_count} = $2;
435 $self->{_vars}{message_age} = time() - $self->{_vars}{received_time};
436
437 while (<I>) {
438 chomp();
439 if (/^(-\S+)\s*(.*$)/) {
440 my $tag = $1;
441 my $arg = $2;
442 if ($tag eq '-acl') {
443 my $t;
444 return(0) if ($arg !~ /^(\d+)\s(\d+)$/);
445 if ($1 < $Exim::SpoolFile::ACL_C_MAX) {
446 $t = "acl_c$1";
447 } else {
448 $t = "acl_m" . ($1 - $Exim::SpoolFile::ACL_C_MAX);
449 }
450 read(I, $self->{_vars}{$t}, $2+1) || return(0);
451 chomp($self->{_vars}{$t});
452 } elsif ($tag eq '-local') {
453 $self->{_vars}{sender_local} = 1;
454 } elsif ($tag eq '-localerror') {
455 $self->{_vars}{local_error_message} = 1;
456 } elsif ($tag eq '-local_scan') {
457 $self->{_vars}{local_scan_data} = $arg;
bf759a8b
PH
458 } elsif ($tag eq '-spam_score_int') {
459 $self->{_vars}{spam_score_int} = $arg;
460 $self->{_vars}{spam_score} = $arg / 10;
461 } elsif ($tag eq '-bmi_verdicts') {
462 $self->{_vars}{bmi_verdicts} = $arg;
463 } elsif ($tag eq '-host_lookup_deferred') {
464 $self->{_vars}{host_lookup_deferred} = 1;
059ec3d9
PH
465 } elsif ($tag eq '-host_lookup_failed') {
466 $self->{_vars}{host_lookup_failed} = 1;
467 } elsif ($tag eq '-body_linecount') {
468 $self->{_vars}{body_linecount} = $arg;
bf759a8b
PH
469 } elsif ($tag eq '-body_zerocount') {
470 $self->{_vars}{body_zerocount} = $arg;
059ec3d9
PH
471 } elsif ($tag eq '-frozen') {
472 $self->{_vars}{deliver_freeze} = 1;
473 $self->{_vars}{deliver_frozen_at} = $arg;
bf759a8b
PH
474 } elsif ($tag eq '-allow_unqualified_recipient') {
475 $self->{_vars}{allow_unqualified_recipient} = 1;
476 } elsif ($tag eq '-allow_unqualified_sender') {
477 $self->{_vars}{allow_unqualified_sender} = 1;
059ec3d9
PH
478 } elsif ($tag eq '-deliver_firsttime') {
479 $self->{_vars}{deliver_firsttime} = 1;
480 $self->{_vars}{first_delivery} = 1;
481 } elsif ($tag eq '-manual_thaw') {
482 $self->{_vars}{deliver_manual_thaw} = 1;
483 $self->{_vars}{manually_thawed} = 1;
484 } elsif ($tag eq '-auth_id') {
485 $self->{_vars}{authenticated_id} = $arg;
486 } elsif ($tag eq '-auth_sender') {
487 $self->{_vars}{authenticated_sender} = $arg;
488 } elsif ($tag eq '-sender_set_untrusted') {
489 $self->{_vars}{sender_set_untrusted} = 1;
490 } elsif ($tag eq '-tls_certificate_verified') {
491 $self->{_vars}{tls_certificate_verified} = 1;
492 } elsif ($tag eq '-tls_cipher') {
493 $self->{_vars}{tls_cipher} = $arg;
494 } elsif ($tag eq '-tls_peerdn') {
495 $self->{_vars}{tls_peerdn} = $arg;
496 } elsif ($tag eq '-host_address') {
497 $self->{_vars}{sender_host_port} = $self->_get_host_and_port(\$arg);
498 $self->{_vars}{sender_host_address} = $arg;
499 } elsif ($tag eq '-interface_address') {
500 $self->{_vars}{interface_port} = $self->_get_host_and_port(\$arg);
501 $self->{_vars}{interface_address} = $arg;
bf759a8b
PH
502 } elsif ($tag eq '-active_hostname') {
503 $self->{_vars}{smtp_active_hostname} = $arg;
059ec3d9
PH
504 } elsif ($tag eq '-host_auth') {
505 $self->{_vars}{sender_host_authenticated} = $arg;
506 } elsif ($tag eq '-host_name') {
507 $self->{_vars}{sender_host_name} = $arg;
508 } elsif ($tag eq '-helo_name') {
509 $self->{_vars}{sender_helo_name} = $arg;
510 } elsif ($tag eq '-ident') {
511 $self->{_vars}{sender_ident} = $arg;
512 } elsif ($tag eq '-received_protocol') {
513 $self->{_vars}{received_protocol} = $arg;
514 } elsif ($tag eq '-N') {
515 $self->{_vars}{dont_deliver} = 1;
059ec3d9
PH
516 } else {
517 # unrecognized tag, save it for reference
518 $self->{$tag} = $arg;
519 }
520 } else {
521 last;
522 }
523 }
524
8e669ac1 525 # when we drop out of the while loop, we have the first line of the
059ec3d9
PH
526 # delivered tree in $_
527 do {
528 if ($_ eq 'XX') {
529 ; # noop
530 } elsif ($_ =~ s/^[YN][YN]\s+//) {
531 $self->{_del_tree}{$_} = 1;
532 } else {
533 return(0);
534 }
535 chomp($_ = <I>);
536 } while ($_ !~ /^\d+$/);
537
538 $self->{_numrecips} = $_;
539 $self->{_vars}{recipients_count} = $self->{_numrecips};
540 for (my $i = 0; $i < $self->{_numrecips}; $i++) {
541 chomp($_ = <I>);
542 return(0) if (/^$/);
543 my $addr = '';
544 if (/^(.*)\s\d+,(\d+),\d+$/) {
545 #print STDERR "exim3 type (untested): $_\n";
546 $self->{_recips}{$1} = { pno => $2 };
547 $addr = $1;
548 } elsif (/^(.*)\s(\d+)$/) {
549 #print STDERR "exim4 original type (untested): $_\n";
550 $self->{_recips}{$1} = { pno => $2 };
551 $addr = $1;
552 } elsif (/^(.*)\s(.*)\s(\d+),(\d+)#1$/) {
553 #print STDERR "exim4 new type #1 (untested): $_\n";
554 return($self->_error("incorrect format: $_")) if (length($2) != $3);
555 $self->{_recips}{$1} = { pno => $4, errors_to => $2 };
556 $addr = $1;
557 } elsif (/^.*#(\d+)$/) {
bf759a8b 558 #print STDERR "exim4 #$1 style (unimplemented): $_\n";
059ec3d9
PH
559 $self->_error("exim4 #$1 style (unimplemented): $_");
560 } else {
561 #print STDERR "default type: $_\n";
562 $self->{_recips}{$_} = {};
563 $addr = $_;
564 }
565 $self->{_udel_tree}{$addr} = 1 if (!$self->{_del_tree}{$addr});
566 }
af66f652
PH
567 $self->{_vars}{recipients} = join(', ', keys(%{$self->{_recips}}));
568 $self->{_vars}{recipients_del} = join(', ', keys(%{$self->{_del_tree}}));
569 $self->{_vars}{recipients_undel} = join(', ', keys(%{$self->{_udel_tree}}));
570 $self->{_vars}{recipients_undel_count} = scalar(keys(%{$self->{_udel_tree}}));
571 $self->{_vars}{recipients_del_count} = 0;
572 foreach my $r (keys %{$self->{_del_tree}}) {
573 next if (!$self->{_recips}{$r});
574 $self->{_vars}{recipients_del_count}++;
575 }
059ec3d9
PH
576
577 # blank line
578 $_ = <I>;
579 return(0) if (!/^$/);
580
581 # start reading headers
582 while (read(I, $_, 3) == 3) {
583 my $t = getc(I);
584 return(0) if (!length($t));
585 while ($t =~ /^\d$/) {
586 $_ .= $t;
587 $t = getc(I);
588 }
589 # ok, right here $t contains the header flag and $_ contains the number of
590 # bytes to read. If we ever use the header flag, grab it here.
591 $self->{_vars}{message_size} += $_ if ($t ne '*');
592 $t = getc(I); # strip the space out of the file
593 my $bytes = $_;
594 return(0) if (read(I, $_, $bytes) != $bytes);
595 chomp(); # may regret this later
596 # build the $header_ variable, following exim's rules (sort of)
597 if (/^([^ :]+):(.*)$/s) {
598 my $v = "header_" . lc($1);
599 my $d = $2;
600 $d =~ s/^\s*//;
601 $d =~ s/\s*$//;
602 $self->{_vars}{$v} .= (defined($self->{_vars}{$v}) ? "\n" : '') . $d;
603 $self->{_vars}{received_count}++ if ($v eq 'header_received');
604 }
605 # push header onto $message_headers var, following exim's rules
606 $self->{_vars}{message_headers} .=
607 (defined($self->{_vars}{message_headers}) ? "\n" : '') . $_;
608 }
609 close(I);
610
611 if (length($self->{_vars}{"header_reply-to"}) > 0) {
612 $self->{_vars}{reply_address} = $self->{_vars}{"header_reply-to"};
613 } else {
614 $self->{_vars}{reply_address} = $self->{_vars}{header_from};
615 }
616
617 $self->{_vars}{message_body_size} =
618 (stat($self->{_path}.'/'.$self->{_message}.'-D'))[7] - 19;
619 if ($self->{_vars}{message_body_size} < 0) {
620 $self->{_vars}{message_size} = 0;
621 } else {
622 $self->{_vars}{message_size} += $self->{_vars}{message_body_size} + 1;
623 }
624
625 return(1);
8e669ac1 626}
059ec3d9
PH
627
628# mimic exim's host_extract_port function - receive a ref to a scalar,
629# strip it of port, return port
630sub _get_host_and_port {
631 my $self = shift;
632 my $host = shift; # scalar ref, be careful
633
634 if ($$host =~ /^\[([^\]]+)\](?:\:(\d+))?$/) {
635 $$host = $1;
636 return($2 || 0);
637 } elsif ($$host =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:\.(\d+))?$/) {
638 $$host = $1;
639 return($2 || 0);
640 } elsif ($$host =~ /^([\d\:]+)(?:\.(\d+))?$/) {
641 $$host = $1;
642 return($2 || 0);
643 }
644 # implicit else
645 return(0);
646}
647
648sub print_message {
649 my $self = shift;
650 my $fh = shift || \*STDOUT;
651 return if ($self->{_delivered});
652
653 if ($self->{_output_idonly}) {
654 print $fh $self->{_message}, "\n";
655 return;
656 }
8e669ac1 657
059ec3d9
PH
658 if ($self->{_output_long} || $self->{_output_flatq}) {
659 my $i = int($self->{_vars}{message_age} / 60);
660 if ($i > 90) {
661 $i = int(($i+30)/60);
662 if ($i > 72) { printf $fh "%2dd ", int(($i+12)/24); }
663 else { printf $fh "%2dh ", $i; }
664 } else { printf $fh "%2dm ", $i; }
665
666 $i = $self->{_vars}{message_size};
667 if ($i == 0) { $i = " "; }
668 elsif ($i < 1024) { $i = sprintf("%5d", $i); }
669 elsif ($i < 10*1024) { $i = sprintf("%4.1fK", $i / 1024); }
670 elsif ($i < 1024*1024) { $i = sprintf("%4dK", ($i+512)/1024); }
671 elsif ($i < 10*1024*1024) { $i = sprintf("%4.1fM", $i/(1024*1024)); }
672 else { $i = sprintf("%4dM", ($i + 512 * 1024)/(1024*1024)); }
673 print $fh "$i ";
674 }
675 print $fh "$self->{_message} ";
676 print $fh "From: " if ($self->{_output_brief});
677 print $fh "<$self->{_vars}{sender_address}>";
678
679 if ($self->{_output_long}) {
680 print $fh " ($self->{_vars}{originator_login})"
681 if ($self->{_vars}{sender_set_untrusted});
8e669ac1 682
059ec3d9
PH
683 # XXX exim contains code here to print spool format errors
684 print $fh " *** frozen ***" if ($self->{_vars}{deliver_freeze});
685 print $fh "\n";
686
687 foreach my $v (keys(%{$self->{_show_vars}})) {
688 printf $fh " %25s = '%s'\n", $v, $self->get_var($v);
689 }
8e669ac1 690
059ec3d9
PH
691 foreach my $r (keys %{$self->{_recips}}) {
692 next if ($self->{_del_tree}{$r} && $self->{_undelivered_only});
693 printf $fh " %s %s\n", $self->{_del_tree}{$r} ? "D" : " ", $r;
694 }
695 if ($self->{_show_generated}) {
696 foreach my $r (keys %{$self->{_del_tree}}) {
697 next if ($self->{_recips}{$r});
698 printf $fh " +D %s\n", $r;
699 }
700 }
701 } elsif ($self->{_output_brief}) {
702 my @r = ();
703 foreach my $r (keys %{$self->{_recips}}) {
704 next if ($self->{_del_tree}{$r});
705 push(@r, $r);
706 }
707 print $fh " To: ", join(';', @r);
708 } elsif ($self->{_output_flatq}) {
709 print $fh " *** frozen ***" if ($self->{_vars}{deliver_freeze});
710 my @r = ();
711 foreach my $r (keys %{$self->{_recips}}) {
712 next if ($self->{_del_tree}{$r});
713 push(@r, $r);
714 }
715 print $fh " ", join(' ', @r);
716 }
717
718 print $fh "\n";
719}
720
721sub dump {
722 my $self = shift;
723
724 foreach my $k (sort keys %$self) {
725 my $r = ref($self->{$k});
726 if ($r eq 'ARRAY') {
727 printf "%20s <<EOM\n", $k;
728 print @{$self->{$k}}, "EOM\n";
729 } elsif ($r eq 'HASH') {
730 printf "%20s <<EOM\n", $k;
731 foreach (sort keys %{$self->{$k}}) {
732 printf "%20s %s\n", $_, $self->{$k}{$_};
733 }
734 print "EOM\n";
735 } else {
736 printf "%20s %s\n", $k, $self->{$k};
737 }
738 }
739}
740
741} # BEGIN
742
743sub ext_usage {
744 if ($ARGV[0] =~ /^--help$/i) {
745 require Config;
746 $ENV{PATH} .= ":" unless $ENV{PATH} eq "";
747 $ENV{PATH} = "$ENV{PATH}$Config::Config{'installscript'}";
748 #exec("perldoc", "-F", "-U", $0) || exit 1;
749 $< = $> = 1 if ($> == 0 || $< == 0);
750 exec("perldoc", $0) || exit 1;
751 # make parser happy
752 %Config::Config = ();
753 } elsif ($ARGV[0] =~ /^--version$/i) {
754 print "$p_name version $p_version\n\n$p_cp\n";
755 } else {
756 return;
757 }
758
759 exit(0);
760}
761
762__END__
763
764=head1 NAME
765
766exipick - display messages from Exim queue based on a variety of criteria
767
768=head1 USAGE
769
770exipick [--help|--version] | [-spool <spool>] [-and|-or] [-bp|-bpa|-bpc|-bpr|-bpra|-bpru|-bpu] [<criterion> [<criterion> ...]]
771
772=head1 DESCRIPTION
773
bf759a8b 774exipick is designed to display the contents of a Exim mail spool based on user-specified criteria. It is designed to mimic the output of 'exim -bp' (or any of the other -bp* options) and Exim's spec.txt should be used to learn more about the exact format of the output. The criteria are formed by creating comparisons against characteristics of the messages, for instance $message_size, $sender_helo_name, or $message_headers.
059ec3d9
PH
775
776=head1 OPTIONS
777
778=over 4
779
bf759a8b 780=item --spool
059ec3d9 781
bf759a8b 782The path to Exim's spool directory. In general usage you should set the $spool variable in the script to your site's main spool directory (and if exipick was installed from the Exim distribution, this is done by default), but this option is useful for alternate installs, or installs on NFS servers, etc.
059ec3d9 783
bf759a8b 784=item --and
059ec3d9
PH
785
786A message will be displayed only if it matches all of the specified criteria. This is the default.
787
bf759a8b 788=item --or
059ec3d9
PH
789
790A message will be displayed if it matches any of the specified criteria.
791
af66f652
PH
792=item --caseful
793
794By default criteria using the '=' operator are caseless. Specifying this option make them respect case.
795
059ec3d9
PH
796=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:
797
798=item -bp display the matching messages in 'mailq' format.
799
800=item -bpa ... with generated addresses as well.
801
802=item -bpc ... just show a count of messages.
803
804=item -bpr ... do not sort.
805
806=item -bpra ... with generated addresses, unsorted.
807
808=item -bpru ... only undelivered addresses, unsorted.
809
810=item -bpu ... only undelivered addresses.
811
812Please see Exim's spec.txt for details on the format and information displayed with each option.
813
814=item The following options are included for compatibility with the 'exiqgrep' utility:
815
816=item -f <regexp> Same as '$sender_address = <regexp>'
817
818=item -r <regexp> Same as '$recipients = <regexp>'
819
820=item -y <seconds> Same as '$message_age < <seconds>'
821
822=item -o <seconds> Same as '$message_age > <seconds>'
823
824=item -z Same as '$deliver_freeze'
825
826=item -x Same as '!$deliver_freeze'
827
828=item -c Display count of matches only
829
830=item -l Display in long format (default)
831
832=item -i Display message IDs only
833
834=item -b Display brief format only
835
836Please see the 'exiqgrep' documentation for more details on the behaviour and output format produced by these options
837
838=item <criterion>
839
bf759a8b 840The criteria are used to determine whether or not a given message should be displayed. The criteria are built using variables containing information about the individual messages (see VARIABLES section for list and descriptions of available variables). Each criterion is evaluated for each message in the spool and if all (by default) criteria match or (if --or option is specified) any criterion matches, the message is displayed. See VARIABLE TYPES for explanation of types of variables and the evaluations that can be performed on them and EXAMPLES section for complete examples.
059ec3d9
PH
841
842The format of a criterion is explained in detail below, but a key point to make is that the variable being compared must always be on the left side of the comparison.
843
844If no criteria are provided all messages in the queue are displayed (in this case the output of exipick should be identical to the output of 'exim -bp')
845
846=item --help
847
848This screen.
849
850=item --version
851
852Version info.
853
854=back
855
856=head1 VARIABLE TYPES
857
858Although there are variable types defined, they are defined only by the type of data that gets put into them. They are internally typeless. Because of this it is perfectly legal to perform a numeric comparison against a string variable, although the results will probably be meaningless.
859
860=over 4
861
862=item NUMERIC
863
864Variable of the numeric type can be of integer or float. Valid comparisons are <, <=, >, >=, ==, and !=.
865
bf759a8b 866The numbers specified in the criteria can have a suffix of d, h, m, s, M, K, or B, in which case the number will be mulitplied by 86400, 3600, 60, 1, 1048576, 1024, or 1 respectively. These suffixes are case sensitive. While these are obviously designed to aid in date and size calculations, they are not restricted to variables of their respective types. That is, though it's odd it's legal to create a criterion of a message being around for 3 kiloseconds: '$message_age >= 3K'.
059ec3d9
PH
867
868=item BOOLEAN
869
bf759a8b 870Variables of the boolean type are very easy to use in criteria. The format is either the variable by itself or the variable negated with a ! sign. For instance, '$deliver_freeze' matches if the message in question is frozen, '!$deliver_freeze' matches if message is not frozen.
059ec3d9
PH
871
872=item STRING
873
bf759a8b 874String variables are basically defined as those that are neither numeric nor boolean and can contain any data. The string operators are =, eq, ne, =~, and !~. With the exception of '=', the operators all match the functionality of the like-named perl operators.
059ec3d9 875
bf759a8b 876The simplest form is a bare string regular expression, represented by the operator '='. The value used for the comparison will be evaluated as a regular expression and can be as simple or as complex as desired. For instance '$sender_helo_name = example' on the simple end or '$sender_helo_name = ^aol\.com$' on the more complex end. This comparison is caseless by default, but see the --caseful option to change this.
059ec3d9 877
bf759a8b 878Slightly more complex is the string comparison with the operators 'eq' and 'ne' for equal and not equal, respectively. '$sender_helo_name eq hotmail.com' is true for messages with the exact helo string "hotmail.com", while '$sender_helo_name ne hotmail.com' is true for any message with a helo string other than "hotmail.com".
059ec3d9 879
bf759a8b 880The most complex and the most flexible format are straight regular expressions with the operators '=~' and '!~'. The value in the criteria is expected to be a correctly formatted perl regular expression B<including the regexp delimiters (usually //)>. The criterion '$sender_helo_name !~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/' matches for any message which does not have an IP address for its helo string.
059ec3d9
PH
881
882=back
883
884=head1 VARIABLES
885
bf759a8b 886With a few exceptions the available variables match Exim's internal expansion variables in both name and exact contents. There are a few notable additions and format deviations which are noted below. Although a brief explanation is offered below, Exim's spec.txt should be consulted for full details. It is important to remember that not every variable will be defined for every message. For example, $sender_host_port is not defined for messages not received from a remote host.
059ec3d9
PH
887
888In the list below, '.' denotes standard messages with contents matching Exim's variable, '#' denotes standard variables with non-standard contents, and '+' denotes a non-standard variable.
889
890=head2 Boolean variables
891
892=over 4
893
bf759a8b 894=item + $allow_unqualified_recipient
059ec3d9
PH
895
896TRUE if unqualified recipient addresses are permitted in header lines.
897
bf759a8b 898=item + $allow_unqualified_sender
059ec3d9
PH
899
900TRUE if unqualified sender addresses are permitted in header lines.
901
bf759a8b 902=item + $deliver_freeze
059ec3d9 903
bf759a8b 904TRUE if the message is currently frozen.
059ec3d9 905
bf759a8b 906=item . $first_delivery
059ec3d9 907
bf759a8b 908TRUE if the message has never been deferred.
059ec3d9 909
bf759a8b 910=item . $manually_thawed
059ec3d9
PH
911
912TRUE when the message has been manually thawed.
913
bf759a8b 914=item + $dont_deliver
059ec3d9
PH
915
916TRUE if, under normal circumstances, Exim will not try to deliver the message.
917
bf759a8b
PH
918=item . $host_lookup_deferred
919
920TRUE if there was an attempt to look up the host's name from its IP address, but an error occurred that during the attempt.
921
922=item . $host_lookup_failed
059ec3d9 923
bf759a8b 924TRUE if there was an attempt to look up the host's name from its IP address, but the attempt returned a negative result.
059ec3d9 925
bf759a8b 926=item + $local_error_message
059ec3d9
PH
927
928TRUE if the message is a locally-generated error message.
929
bf759a8b 930=item + $sender_local
059ec3d9
PH
931
932TRUE if the message was locally generated.
933
bf759a8b 934=item + $sender_set_untrusted
059ec3d9
PH
935
936TRUE if the envelope sender of this message was set by an untrusted local caller.
937
bf759a8b 938=item . $tls_certificate_verified
059ec3d9
PH
939
940TRUE if a TLS certificate was verified when the message was received.
941
942=back
943
944=head2 Numeric variables
945
946=over 4
947
bf759a8b 948=item . $body_linecount
059ec3d9
PH
949
950The number of lines in the message's body.
951
bf759a8b 952=item . $body_zerocount
059ec3d9
PH
953
954The number of binary zero bytes in the message's body.
955
bf759a8b 956=item + $deliver_frozen_at
059ec3d9
PH
957
958The epoch time at which message was frozen.
959
bf759a8b 960=item . $interface_port
059ec3d9
PH
961
962The local port number if network-originated messages.
963
bf759a8b 964=item . $message_age
059ec3d9
PH
965
966The number of seconds since the message was received.
967
bf759a8b 968=item . $message_body_size
059ec3d9
PH
969
970The size of the body in bytes.
971
bf759a8b 972=item . $message_size
059ec3d9
PH
973
974The size of the message in bytes.
975
bf759a8b 976=item . $originator_gid
059ec3d9
PH
977
978The group id under which the process that called Exim was running as when the message was received.
979
bf759a8b 980=item . $originator_uid
059ec3d9
PH
981
982The user id under which the process that called Exim was running as when the message was received.
983
bf759a8b 984=item . $received_count
059ec3d9
PH
985
986The number of Received: header lines in the message.
987
bf759a8b 988=item + $received_time
059ec3d9
PH
989
990The epoch time at which the message was received.
991
bf759a8b 992=item . $recipients_count
059ec3d9 993
af66f652
PH
994The number of envelope recipients for the message.
995
bf759a8b 996=item + $recipients_del_count
af66f652
PH
997
998The number of envelope recipients for the message which have already been delivered. Note that this is the count of original recipients to which the message has been delivered. It does not include generated addresses so it is possible that this number will be less than the number of addresses in the recipients_del string.
999
bf759a8b 1000=item + $recipients_undel_count
af66f652
PH
1001
1002The number of envelope recipients for the message which have not yet been delivered.
059ec3d9 1003
bf759a8b 1004=item . $sender_host_port
059ec3d9
PH
1005
1006The port number that was used on the remote host for network-originated messages.
1007
bf759a8b 1008=item + $warning_count
059ec3d9
PH
1009
1010The number of delay warnings which have been sent for this message.
1011
1012=back
1013
1014=head2 String variables
1015
1016=over 4
1017
bf759a8b 1018=item . $acl_c0-$acl_c9, $acl_m0-$acl_m9
059ec3d9
PH
1019
1020User definable variables.
1021
bf759a8b 1022=item . $authenticated_id
059ec3d9
PH
1023
1024Optional saved information from authenticators, or the login name of the calling process for locally submitted messages.
1025
bf759a8b 1026=item . $authenticated_sender
059ec3d9
PH
1027
1028The value of AUTH= param for smtp messages, or a generated value from the calling processes login and qualify domain for locally submitted messages.
1029
bf759a8b
PH
1030=item + $bmi_verdicts
1031
1032I honestly don't know what the format of this variable is. It only exists if you have Exim compiled with WITH_CONTENT_SCAN and EXPERIMENTAL_BRIGHTMAIL (and, you know, pay Symantec/Brightmail a bunch of money for the client libs and a server to use them with).
1033
1034=item + $each_recipients
1035
1036This is a psuedo variable which allows you to apply a criterion against each address in $recipients individually. This allows you to create criteria against which every individual recipient is tested. For instance, '$recipients =~ /aol.com/' will match if any of the recipient addresses contain the string "aol.com". However, with the criterion '$each_recipients =~ /@aol.com$/', a message will only match if B<every> recipient matches that pattern. Note that this obeys --and or --or being set. Using it with --or is very similar to just matching against $recipients, but with the added benefit of being able to use anchors at the beginning and end of each recipient address.
1037
1038=item + $each_recipients_del
1039
1040Like $each_recipients, but for the $recipients_del variable.
1041
1042=item + $each_recipients_undel
1043
1044Like $each_recipients, but for the $recipients_undel variable.
1045
1046=item # $header_*
059ec3d9
PH
1047
1048The value of the same named message header, for example header_to or header_reply-to. These variables are really closer to Exim's rheader_* variables, with the exception that leading and trailing space is removed.
1049
bf759a8b 1050=item . $interface_address
059ec3d9
PH
1051
1052The address of the local IP interface for network-originated messages.
1053
bf759a8b 1054=item . $local_scan_data
059ec3d9
PH
1055
1056The text returned by the local_scan() function when a message is received.
1057
bf759a8b 1058=item # $message_body
059ec3d9
PH
1059
1060The message's body. Unlike Exim's variable of the same name, this variable contains the entire message body. The logic behind this is that the message body is not read unless it is specifically referenced, so under normal circumstances it is not a penalty, but when you need the entire body you need the entire body. Like Exim's copy, newlines and nulls are replaced by spaces.
1061
bf759a8b 1062=item . $message_headers
059ec3d9
PH
1063
1064A concatenation of all the header lines except for lines added by routers or transports.
1065
bf759a8b 1066=item . $message_id
059ec3d9
PH
1067
1068The unique message id that is used by Exim to identify the message.
1069
bf759a8b 1070=item + $originator_login
059ec3d9
PH
1071
1072The login of the process which called Exim.
1073
bf759a8b 1074=item . $received_protocol
059ec3d9
PH
1075
1076The name of the protocol by which the message was received.
1077
bf759a8b 1078=item # $recipients
059ec3d9
PH
1079
1080The list of envelope recipients for a message. Unlike Exim's version, this variable always contains every envelope recipient of the message. The recipients are separated by a comma and a space.
1081
bf759a8b 1082=item + $recipients_del
059ec3d9 1083
af66f652 1084The list of delivered envelope recipients for a message. This non-standard variable is in the same format as recipients and contains the list of already-delivered recipients including any generated addresses.
059ec3d9 1085
bf759a8b 1086=item + $recipients_undel
059ec3d9
PH
1087
1088The list of undelivered envelope recipients for a message. This non-standard variable is in the same format as recipients and contains the list of undelivered recipients.
1089
bf759a8b 1090=item . $reply_address
059ec3d9
PH
1091
1092The contents of the Reply-To: header line if one exists and it is not empty, or otherwise the contents of the From: header line.
1093
bf759a8b 1094=item . $sender_address
059ec3d9
PH
1095
1096The sender's address that was received in the message's envelope. For bounce messages, the value of this variable is the empty string.
1097
bf759a8b 1098=item . $sender_address_domain
059ec3d9 1099
bf759a8b 1100The domain part of $sender_address.
059ec3d9 1101
bf759a8b 1102=item . $sender_address_local_part
059ec3d9 1103
bf759a8b 1104The local part of $sender_address.
059ec3d9 1105
bf759a8b 1106=item . $sender_helo_name
059ec3d9
PH
1107
1108The HELO or EHLO value supplied for smtp or bsmtp messages.
1109
bf759a8b 1110=item . $sender_host_address
059ec3d9
PH
1111
1112The remote host's IP address.
1113
bf759a8b 1114=item . $sender_host_authenticated
059ec3d9
PH
1115
1116The name of the authenticator driver which successfully authenticated the client from which the message was received.
1117
bf759a8b 1118=item . $sender_host_name
059ec3d9
PH
1119
1120The remote host's name as obtained by looking up its IP address.
1121
bf759a8b 1122=item . $sender_ident
059ec3d9
PH
1123
1124The 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.
1125
bf759a8b
PH
1126=item . $smtp_active_hostname
1127
1128The value of the active host name when the message was received, as specified by the "smtp_active_hostname" option.
1129
1130=item . $spam_score
1131
1132The spam score of the message, for example '3.4' or '30.5'. (Requires exiscan or WITH_CONTENT_SCAN)
1133
1134=item . $spam_score_int
1135
1136The spam score of the message, multiplied by ten, as an integer value. For instance '34' or '305'. (Requires exiscan or WITH_CONTENT_SCAN)
1137
1138=item . $tls_cipher
059ec3d9
PH
1139
1140The cipher suite that was negotiated for encrypted SMTP connections.
1141
bf759a8b 1142=item . $tls_peerdn
059ec3d9
PH
1143
1144The value of the Distinguished Name of the certificate if Exim is configured to request one.
1145
1146=back
1147
1148=head1 EXAMPLES
1149
1150=over 4
1151
bf759a8b 1152=item exipick '$deliver_freeze'
059ec3d9
PH
1153
1154Display only frozen messages.
1155
bf759a8b 1156=item exipick '$received_protocol eq asmtp' '$message_age < 20m'
059ec3d9 1157
bf759a8b 1158Display only messages which were delivered over an authenticated smtp session in the last 20 minutes.
059ec3d9 1159
bf759a8b 1160=item exipick -bpc '$message_size > 200K'
059ec3d9
PH
1161
1162Display a count of messages in the queue which are over 200 kilobytes in size.
1163
bf759a8b 1164=item exipick -or '$sender_helo_name =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/' '$sender_helo_name = _'
059ec3d9
PH
1165
1166Display message which have a HELO string which either is an IP address or contains an underscore.
1167
1168=back
1169
1170=head1 REQUIREMENTS
1171
bf759a8b 1172None that I know of, except an Exim installation. Your life will also be a lot easier if you set $spool at the top of the script to your install's spool directory (assuming this was not done automatically by the Exim install process).
059ec3d9
PH
1173
1174=head1 ACKNOWLEDGEMENTS
1175
1176Although I conceived of the concept for this program independently, the name 'exipick' was taken from the Exim WishList and was suggested by Jeffrey Goldberg.
1177
1178Thank you to Philip Hazel for writing Exim. Of course this program exists because of Exim, but more specifically the message parsing code is based on Exim's and some of this documentation was copy/pasted from Exim's.
1179
1180=head1 CONTACT
1181
1182=over 4
1183
1184=item EMAIL: proj-exipick@jetmore.net
1185
1186=item HOME: jetmore.org/john/code/#exipick
1187
1188=back
1189
1190=cut