Bug 609: Add -C option to exiqgrep
[exim.git] / src / src / exiqgrep.src
1 #!PERL_COMMAND
2
3 # Utility for searching and displaying queue information.
4 # Written by Matt Hubbard 15 August 2002
5
6 # Except when they appear in comments, the following placeholders in this
7 # source are replaced when it is turned into a runnable script:
8 #
9 # BIN_DIRECTORY
10 # PERL_COMMAND
11
12 # PROCESSED_FLAG
13
14
15 # Routine for extracting the UTC timestamp from message ID
16 # lifted from eximstat utility
17
18 # Version 1.2
19
20 use strict;
21 use Getopt::Std;
22
23 # Have this variable point to your exim binary.
24 my $exim = 'BIN_DIRECTORY/exim';
25 my $eargs = '-bpu';
26 my %id;
27 my %opt;
28 my $count = 0;
29 my $mcount = 0;
30 my @tab62 =
31   (0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,     # 0-9
32    0,10,11,12,13,14,15,16,17,18,19,20,  # A-K
33   21,22,23,24,25,26,27,28,29,30,31,32,  # L-W
34   33,34,35, 0, 0, 0, 0, 0,              # X-Z
35    0,36,37,38,39,40,41,42,43,44,45,46,  # a-k
36   47,48,49,50,51,52,53,54,55,56,57,58,  # l-w
37   59,60,61);                            # x-z
38
39 my $base;
40 if ($^O eq 'darwin') { # aka MacOS X
41   $base = 36;
42  } else {
43   $base = 62;
44 };
45
46 getopts('hf:r:y:o:s:C:zxlibRca',\%opt);
47 if ($opt{h}) { &help; exit;}
48 if ($opt{a}) { $eargs = '-bp'; }
49 if ($opt{C}) { $eargs .= ' -C '.$opt{C}; }
50
51 # Read message queue output into hash
52 &collect();
53 # Identify which messages match selection criteria
54 &selection();
55 # Print matching data according to display option.
56 &display();
57 exit;
58
59
60 sub help() {
61         print <<'EOF'
62 Exim message queue display utility.
63
64         -h              This help message.
65         -C              Specify which exim.conf to use.
66
67 Selection criteria:
68         -f <regexp>     Match sender address sender (field is "< >" wrapped)
69         -r <regexp>     Match recipient address
70         -s <regexp>     Match against the size field from long output
71         -y <seconds>    Message younger than
72         -o <seconds>    Message older than
73         -z              Frozen messages only (exclude non-frozen)
74         -x              Non-frozen messages only (exclude frozen)
75
76 [ NB: for regexps, provided string sits in /<string>/ ]
77
78 Display options:
79         -c              Display match count
80         -l              Long Format [Default]
81         -i              Message IDs only
82         -b              Brief Format
83         -R              Reverse order
84         -a              All recipients (including delivered)
85 EOF
86 }
87
88 sub collect() {
89         open(QUEUE,"$exim $eargs |") or die("Error openning pipe: $!\n");
90         while(<QUEUE>) {
91                 chomp();
92                 my $line = $_;
93                 #Should be 1st line of record, if not error.
94                 if ($line =~ /^\s*(\w+)\s+((?:\d+(?:\.\d+)?[A-Z]?)?)\s*(\w{6}-\w{6}-\w{2})\s+(<.*?>)/) {
95                         my $msg = $3;
96                         $id{$msg}{age} = $1;
97                         $id{$msg}{size} = $2;
98                         $id{$msg}{from} = $4;
99                         $id{$msg}{birth} = &msg_utc($msg);
100                         $id{$msg}{ages} = time - $id{$msg}{birth};
101                         if ($line =~ /\*\*\* frozen \*\*\*$/) {
102                                 $id{$msg}{frozen} = 1;
103                         } else {
104                                 $id{$msg}{frozen} = 0;
105                         }
106                         while(<QUEUE> =~ /\s+(.*?\@.*)$/) {
107                                 push(@{$id{$msg}{rcpt}},$1);
108                         }
109                         # Increment message counter.
110                         $count++;
111                 } else {
112                         print STDERR "Line mismatch: $line\n"; exit 1;
113                 }
114         }
115         close(QUEUE) or die("Error closing pipe: $!\n");
116 }
117
118 sub selection() {
119         foreach my $msg (keys(%id)) {
120                 if ($opt{f}) {
121                         # Match sender address
122                         next unless ($id{$msg}{from} =~ /$opt{f}/i);
123                 }
124                 if ($opt{r}) {
125                         # Match any recipient address
126                         my $match = 0;
127                         foreach my $rcpt (@{$id{$msg}{rcpt}}) {
128                                 $match++ if ($rcpt =~ /$opt{r}/i);
129                         }
130                         next unless ($match);
131                 }
132                 if ($opt{s}) {
133                         # Match against the size string.
134                         next unless ($id{$msg}{size} =~ /$opt{s}/);
135                 }
136                 if ($opt{y}) {
137                         # Match younger than
138                         next unless ($id{$msg}{ages} < $opt{y});
139                 }
140                 if ($opt{o}) {
141                         # Match older than
142                         next unless ($id{$msg}{ages} > $opt{o});
143                 }
144                 if ($opt{z}) {
145                         # Exclude non frozen
146                         next unless ($id{$msg}{frozen});
147                 }
148                 if ($opt{x}) {
149                         # Exclude frozen
150                         next if ($id{$msg}{frozen});
151                 }
152                 # Here's what we do to select the record.
153                 # Should only get this far if the message passed all of
154                 # the active tests.
155                 $id{$msg}{d} = 1;
156                 # Increment match counter.
157                 $mcount++;
158         }
159 }
160
161 sub display() {
162         if ($opt{c}) {
163                 printf("%d matches out of %d messages\n",$mcount,$count);
164                 exit;
165         }
166         foreach my $msg (sort { $opt{R} ? $id{$b}{birth} <=> $id{$a}{birth} : $id{$a}{birth} <=> $id{$b}{birth} } keys(%id) ) {
167                 if (exists($id{$msg}{d})) {
168                         if ($opt{i}) {
169                                 # Just the msg ID
170                                 print $msg, "\n";
171                         } elsif ($opt{b}) {
172                                 # Brief format
173                                 printf("%s From: %s To: %s\n",$msg,$id{$msg}{from},join(';',@{$id{$msg}{rcpt}}))
174                         } else {
175                                 # Otherwise Long format attempted duplication of original format.
176                                 printf("%3s %5s %s %s%s\n",$id{$msg}{age},$id{$msg}{size},$msg,$id{$msg}{from},$id{$msg}{frozen} ? " *** frozen ***" : "");
177                                 foreach my $rcpt (@{$id{$msg}{rcpt}}) {
178                                         printf("          %s\n",$rcpt);
179                                 }
180                                 print "\n";
181                         }
182                 }
183         }
184 }
185
186 sub report() {
187         foreach my $msg (keys(%id)) {
188                 print "$id{$msg}{birth} $msg\tAge: $id{$msg}{age}\tSize: $id{$msg}{size}\tFrom: $id{$msg}{from}\tTo: " . join(" ",@{$id{$msg}{rcpt}}). "\n";
189         }
190 }
191
192 sub msg_utc() {
193         my $id = substr((pop @_), 0, 6);
194         my $s = 0;
195         my @c = split(//, $id);
196         while($#c >= 0) { $s = $s * $base + $tab62[ord(shift @c) - ord('0')] }
197         return $s;
198 }