File Coverage

blib/lib/SyslogScan/DeliveryIterator.pm
Criterion Covered Total %
statement 4 7 57.1
branch n/a
condition n/a
subroutine 2 3 66.6
pod n/a
total 6 10 60.0


line stmt bran cond sub pod time code
1             # DeliveryIterator: an object which scans a syslog for
2             # successful sendmail deliveries.
3              
4             # TODO: add documentation
5             # TODO: use undef value rather than END_OF_TIME
6              
7             package main;
8              
9             require 'timelocal.pl';
10              
11             package SyslogScan::DeliveryIterator;
12              
13             $VERSION = 0.30;
14 0     0     sub Version { $VERSION };
15              
16 4     4   2793 use SyslogScan::Delivery;
  4         7  
  4         116  
17 4     4   3092 use SyslogScan::SendmailUtil;
  0            
  0            
18             use SyslogScan::ParseDate;
19              
20             use Carp;
21             use strict;
22              
23             # internal subroutines
24             my ($pValidateRule, $pParseDate);
25              
26             # time_t start and end values
27             my $END_OF_TIME = 4294967295; # 2 ** 32 - 1;
28             my $START_OF_TIME = 0;
29              
30             sub new
31             {
32             my $type = shift;
33             my %deliveryRule = @_;
34              
35             my $Rule = {};
36             my $self = { Rule => $Rule,
37             seenFromLine => {},
38             syslogList => [] };
39              
40             bless $self, $type;
41              
42             &$pValidateRule(\%deliveryRule);
43            
44             $$Rule{startDate} = $deliveryRule{startDate} || $START_OF_TIME;
45             $$Rule{endDate} = $deliveryRule{endDate} || $END_OF_TIME;
46              
47             $$Rule{unknownSender} = $deliveryRule{unknownSender};
48             $$Rule{unknownSize} = $deliveryRule{unknownSize};
49              
50             # User should call setDefaultYear() directly instead of through here.
51             # But, for backwards compatibility:
52             if (defined $deliveryRule{defaultYear})
53             {
54             &SyslogScan::ParseDate::setDefaultYear($deliveryRule{defaultYear});
55             warn "setting default year in DeliveryIterator deprecated"
56             unless $::gbQuiet;
57             }
58             # $$Rule{defaultYear} = $deliveryRule{defaultYear};
59              
60             $$Rule{startDate} = &SyslogScan::ParseDate::parseDate($$Rule{startDate});
61             $$Rule{endDate} = &SyslogScan::ParseDate::parseDate($$Rule{endDate});
62              
63             # $$Rule{startDate} = &$pParseDate($$Rule{startDate});
64             # $$Rule{endDate} = &$pParseDate($$Rule{endDate});
65            
66             my $paFileName = $deliveryRule{syslogList};
67             if (defined $paFileName)
68             {
69             ref($paFileName) eq "ARRAY" or
70             die "fileNameList is not an array, stopped";
71             my $fileName;
72              
73             foreach $fileName (@$paFileName)
74             {
75             $self -> appendSyslog($fileName);
76             }
77             }
78            
79             return $self;
80             }
81              
82             sub next
83             {
84             my $self = shift;
85             my $fh;
86              
87             return $self -> _nextInFileHandle($fh)
88             if defined($fh = shift);
89            
90             while (1)
91             {
92             if (!$$self{fileHandle})
93             {
94             my $fileName = shift(@{$$self{syslogList}});
95             defined($fileName) or return undef;
96              
97             open(SYSLOG,$fileName) or die "could not open $fileName: $!";
98             $$self{fileHandle} = \*SYSLOG;
99             }
100             my $next = $self -> _nextInFileHandle($$self{fileHandle});
101             return $next if defined($next);
102             close($$self{fileHandle});
103             undef($$self{fileHandle});
104             }
105             die;
106             }
107            
108             sub appendSyslog
109             {
110             my $self = shift;
111             my $fileName = shift;
112            
113             push(@{$$self{syslogList}},$fileName);
114             return 0;
115             }
116              
117             sub _transferToDelivery
118             {
119             my $self = shift;
120             my $pLogLine = shift;
121              
122             my $pRule = $$self{Rule};
123              
124             my $lineClass = ref $pLogLine;
125             die "sanity check of class failed for $lineClass"
126             unless ($lineClass =~ /^SyslogScan::SendmailLine/);
127            
128             my $date = "$$pLogLine{month} $$pLogLine{day} " .
129             "$$pLogLine{'time'}";
130            
131             my $dateValue = $pLogLine -> unix_time;
132             # my $dateValue = &$pParseDate($date, $$pRule{defaultYear});
133             die "invalid date and time: $date" unless $dateValue > 0;
134            
135             defined $$pRule{endDate} || die "no start date defined";
136             if ($dateValue > $$pRule{endDate})
137             {
138             return undef;
139             }
140            
141             # for debugging purposes
142             my $id = $$pLogLine{'messageID'};
143            
144             if ($lineClass =~ /From/)
145             {
146             $self -> _storeFromLine($pLogLine);
147             return undef;
148             }
149            
150             if ($lineClass =~ /Clone/)
151             {
152             $self -> _storeCloneLine($pLogLine);
153             return undef;
154             }
155            
156             if ($dateValue < $$pRule{startDate})
157             {
158             return undef;
159             }
160            
161             my $pAttrHash = $$pLogLine{'attrHash'};
162             die "could not access hash" unless defined $pAttrHash;
163             return undef unless ($$pAttrHash{'stat'} =~ /^Sent/);
164            
165             my @receiverList = @{$$pLogLine{'toList'}};
166            
167             my $instance = $self -> _recallInstanceAndIncrement($pLogLine);
168             my $size = $self -> _recallSize($pLogLine);
169             my $sender = $self -> _recallSender($pLogLine);
170            
171             if (! defined $size)
172             {
173             #never saw the associated From: line
174             print STDERR "could not find sender for msg id $id to @receiverList\n"
175             unless $::gbQuiet;
176             $size = $$pRule{unknownSize}; # may be undefined
177             $sender = $$pRule{unknownSender}; # may be undefined
178             }
179            
180             return new SyslogScan::Delivery ( Date => $date,
181             Size => $size,
182             Id => $id,
183             Sender => $sender,
184             ReceiverList => \@receiverList,
185             Instance => $instance );
186             }
187              
188              
189             sub _nextInFileHandle
190             {
191             my $self = shift;
192             my $fh = shift;
193              
194             my $pLogLine;
195             while ($pLogLine = &SyslogScan::SendmailUtil::getNextMailTransfer($fh))
196             {
197             my $delivery = $self -> _transferToDelivery($pLogLine);
198             return $delivery if defined($delivery);
199             }
200             return undef; #at EOF
201             }
202              
203             # internal routines for storing and retrieving the information
204             # in 'From:' lines.
205              
206             # A 'mini-fromline' is a three-element array of (size, sender,
207             # instance), which stores the all the information we need about a
208             # formerly-seen from-line in a compact form. By putting it in a
209             # compact form, the already-seen table only takes up about 2 mb in our
210             # environment when chomping through 3 days of mail, rather than 20 mb
211             # or more like it would take up if we stored the full SendmailLineFrom
212             # object.
213              
214             sub _storeFromLine
215             {
216             my $self = shift;
217             my $pFromLine = shift;
218              
219             my $pSeenFromLine = $$self{seenFromLine};
220             my $pFromHash = $$pFromLine{attrHash};
221             my $id = $$pFromLine{messageID};
222              
223             my $miniFromLine;
224             if (defined $$pSeenFromLine{$id})
225             {
226             print STDERR "already saw from-line with id $id\n"
227             unless $::gbQuiet;
228              
229             # We should probably use a Rule same as for messages where
230             # the sender is unknown... but, this does not happen very
231             # often, so no big deal.
232             $miniFromLine = [0,"duplicate",0];
233             }
234             else
235             {
236             # to save on memory, we do not store the whole line
237             $miniFromLine = [ $$pFromHash{'size'}, $$pFromHash{'from'},0 ];
238             }
239             $$pSeenFromLine{$id} = $miniFromLine;
240             }
241              
242             sub _storeCloneLine
243             {
244             my $self = shift;
245             my $pCloneLine = shift;
246              
247             my $pSeenFromLine = $$self{seenFromLine};
248             my $id = $$pCloneLine{messageID};
249              
250             my $miniFromLine;
251             if (defined $$pSeenFromLine{$id})
252             {
253             print STDERR "already saw from-line with id $id\n"
254             unless $::gbQuiet;
255             $miniFromLine = [0,"duplicate",0];
256             }
257             else
258             {
259             my $originalID = $$pCloneLine{clonedFrom};
260             defined($originalID) || die "originalID undefined for ID $id";
261             $miniFromLine = $$pSeenFromLine{$originalID};
262             }
263             $$pSeenFromLine{$id} = $miniFromLine;
264             }
265              
266             sub _getIncrementAmount
267             {
268             my $self = shift;
269             my $pToLine = shift;
270             my $receiverCount = @{$$pToLine{'toList'}};
271             die "no receivers" unless $receiverCount > 0;
272             return $receiverCount;
273             }
274              
275             sub _recallFromLine
276             {
277             my $self = shift;
278             my $pToLine = shift;
279              
280             my $id = $$pToLine{messageID};
281             my $pSeenFromLine = $$self{seenFromLine};
282             my $miniFromLine = $$pSeenFromLine{$id};
283              
284             return $miniFromLine; # undefined if we did not see from-line
285             }
286              
287             sub _recallSize
288             {
289             my $self = shift;
290             my $fromLine = $self -> _recallFromLine(@_);
291             return $$fromLine[0];
292             }
293              
294             sub _recallSender
295             {
296             my $self = shift;
297             my $fromLine = $self -> _recallFromLine(@_);
298             return $$fromLine[1];
299             }
300              
301             sub _recallInstanceAndIncrement
302             {
303             my $self = shift;
304             my $fromLine = $self -> _recallFromLine(@_);
305             my $incrementAmount = $self -> _getIncrementAmount(@_);
306            
307             $$fromLine[2] = 0 if ! defined($$fromLine[2]);
308             $$fromLine[2] += $incrementAmount;
309              
310             #instance number starts at 1
311             return $$fromLine[2] + 1 - $incrementAmount;
312             }
313              
314             my $ONE_MONTH = 30*24*60*60;
315             my $ELEVEN_MONTH = 11 * $ONE_MONTH;
316              
317             my @LEGAL_KEY_LIST = qw( startDate endDate syslogList unknownSender
318             unknownSize defaultYear );
319              
320             $pValidateRule = sub {
321             my $rule = shift;
322             my $myKey;
323             foreach $myKey (keys %$rule)
324             {
325             confess("illegal key for delivery rule: $myKey")
326             unless grep ($_ eq $myKey, @LEGAL_KEY_LIST);
327              
328             # sanity check
329             die "internal error: wrong kind of reference"
330             unless $myKey
331             }
332             };
333              
334             my @gMonthList = qw ( jan feb mar apr may jun jul aug sep oct nov dec );
335             my %gMonthTable = ();
336             my ($i, $month);
337             foreach $month (@gMonthList)
338             {
339             $gMonthTable{$month} = $i++;
340             }
341              
342             1;
343              
344             __END__