File Coverage

blib/lib/Log/Procmail.pm
Criterion Covered Total %
statement 109 110 99.0
branch 45 52 86.5
condition 7 9 77.7
subroutine 19 19 100.0
pod 6 6 100.0
total 186 196 94.9


line stmt bran cond sub pod time code
1             package Log::Procmail;
2             $Log::Procmail::VERSION = '0.14';
3             require 5.005;
4 3     3   117380 use strict;
  3         25  
  3         75  
5 3     3   1263 use IO::File;
  3         22641  
  3         259  
6 3     3   1327 use IO::Select;
  3         4651  
  3         113  
7 3     3   16 use Carp;
  3         6  
  3         117  
8 3     3   1392 use UNIVERSAL ();
  3         47  
  3         75  
9              
10 3     3   14 use vars qw/ $VERSION /;
  3         5  
  3         2763  
11             local $^W = 1;
12              
13             $VERSION = '0.12';
14              
15             my %month;
16             @month{qw/ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec /} = ( 0 .. 11 );
17              
18             my $DATE = qr/(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ([ \d]\d) (\d\d):(\d\d):(\d\d) .*(\d\d\d\d)/;
19              
20             sub new {
21 10     10 1 791 my $class = shift;
22 10         45 return bless {
23             fh => new IO::File,
24             files => [@_],
25             errors => 0,
26             buffer => [],
27             }, $class;
28             }
29              
30             sub next {
31 91     91 1 16793 my $log = shift; # who needs $self?
32              
33             # try to read a record (3 lines)
34 91         155 my $fh = $log->fh();
35             READ:
36             {
37 92         106 my $read;
  96         105  
38              
39             # ensure we can read past the previous end of file
40 96 100       187 $fh->clearerr if $fh->eof;
41             LINE:
42 96         1022 while (<$fh>) {
43 214         265 $read++;
44              
45             # should carp if doesn't get what's expected
46             # (From, then Subject, then Folder)
47              
48             # From create a new Abstract
49 214 100       831 /^From (.+?) +($DATE)$/o && do {
50 50         65 push @{$log->{buffer}}, Log::Procmail::Abstract->new();
  50         123  
51              
52             # assert: $read == 1;
53 50         117 $log->{buffer}[-1]->from($1);
54 50         104 $log->{buffer}[-1]->date($2);
55              
56             # return ASAP
57 50 100       65 last READ if @{$log->{buffer}} > 1;
  50         88  
58 49         154 next LINE;
59             };
60              
61             # assert: $read == 2;
62 164 100       378 /^ Subject: (.*)/i && do {
63 1         3 push @{$log->{buffer}}, Log::Procmail::Abstract->new()
64 49 100       53 unless @{$log->{buffer}};
  49         93  
65 49         98 $log->{buffer}[0]->subject($1);
66 49         124 next LINE;
67             };
68              
69             # procmail tabulates with tabs and spaces... :-(
70             # assert: $read == 3;
71             # Folder means the end of this record
72 115 100       273 /^ Folder: (.*?)\s+(\d+)$/ && do {
73 0         0 push @{$log->{buffer}}, Log::Procmail::Abstract->new()
74 50 50       58 unless @{$log->{buffer}};
  50         87  
75              
76             # assert: $read == 3;
77 50         116 $log->{buffer}[0]->folder($1);
78 50         101 $log->{buffer}[0]->size($2);
79 50         76 last READ;
80             };
81              
82             # fall through: some error message
83             # shall we ignore it?
84 65 100       133 next LINE unless $log->{errors};
85              
86             # or return it?
87 38         49 chomp;
88 38         41 push @{$log->{buffer}}, $_;
  38         83  
89 38         58 last LINE;
90             }
91              
92             # in case we couldn't read the first line
93 45 100 100     93 if ( !$read or @{$log->{buffer}} == 0 ) {
  39         109  
94              
95             # return ASAP
96 7 50       9 last READ if @{$log->{buffer}};
  7         16  
97              
98             # go to next file
99 7 100       16 redo READ if $log->_open_next;
100              
101             # unless it's the last one
102 3         9 return;
103             }
104             }
105              
106             # we have an abstract
107 89         100 my $rec = shift @{$log->{buffer}};
  89         133  
108 89 100       287 if(UNIVERSAL::isa( $rec, 'Log::Procmail::Abstract')) {
109             # the folder field is required
110 51 100       69 goto READ unless defined $rec->folder;
111 50         87 $rec->{source} = $log->{source};
112             }
113              
114 88         217 return $rec;
115             }
116              
117             sub push {
118 3     3 1 430 my $log = shift;
119 3         4 push @{ $log->{files} }, @_;
  3         10  
120             }
121              
122             sub errors {
123 6     6 1 1011 my $self = shift;
124 6 100       26 return @_ ? $self->{errors} = shift() : $self->{errors};
125             }
126              
127             sub fh {
128 95     95 1 133 my $log = shift;
129 95 100       216 $log->_open_next unless $log->{fh}->opened();
130 95         509 return $log->{fh};
131             }
132              
133             sub select {
134 3     3 1 906 my $log = shift;
135 3         7 $log->fh(); # make sure the file is correctly opened and select is updated
136 3         10 return $log->{select};
137             }
138              
139             # *internal method*
140             # opens a file or replace the old filehandle by the new one
141             # push() can therefore accept refs to typeglobs, IO::Handle, or filenames
142             sub _open_next {
143 16     16   113 my ( $log ) = @_;
144 16         21 my $file;
145              
146 16 100       18 if ( @{ $log->{files} } ) {
  16         36  
147 13         14 $file = shift @{ $log->{files} };
  13         27  
148 3         8 } else { return 0 };
149              
150 13 100 66     56 if ( ref $file eq 'GLOB' ) {
    100          
151 1         4 $log->{fh} = *$file{IO};
152 1 50       3 carp "Closed filehandle $log->{fh}" unless $log->{fh}->opened;
153             }
154             elsif ( ref $file && $file->isa('IO::Handle') ) {
155 1         3 $log->{fh} = $file;
156             }
157             else {
158 11 50       34 $log->{fh}->open($file) or carp "Can't open $file: $!";
159             }
160 13         526 $log->{source} = $file;
161             $log->{select} = ( grep $^O eq $_, qw(MSWin32 NetWare dos VMS riscos beos) )
162             ? undef
163 13 50       111 : IO::Select->new( $log->{fh} );
164 13         632 1;
165             }
166              
167             sub DESTROY {
168 10     10   1488 my $self = shift;
169 10 100 66     117 if ( $self->{fh} && $self->{fh}->opened ) { $self->{fh}->close }
  9         79  
170             }
171              
172             #
173             # a small class for the abstracts themselves
174             #
175             package Log::Procmail::Abstract;
176             $Log::Procmail::Abstract::VERSION = '0.14';
177 3     3   36 use Carp;
  3         5  
  3         293  
178              
179             sub new {
180 52     52   146 my $class = shift;
181 52         151 return bless {@_}, $class;
182             }
183              
184             for my $attr (qw( from date subject size folder source ) ) {
185 3     3   18 no strict 'refs';
  3         13  
  3         547  
186             *$attr = sub {
187 408     408   1304 my $self = shift;
188 408 100       1170 @_ ? $self->{$attr} = shift: $self->{$attr};
189             }
190             }
191              
192             sub ymd {
193 3     3   6 my $self = shift;
194 3 50       10 croak("Log::Procmail::Abstract::ymd cannot be used to set the date")
195             if @_;
196 3 50       8 return undef unless defined $self->{date};
197 3         154 $self->{date} =~ /^$DATE$/o;
198 3 100       19 return undef unless $1;
199 2         28 return sprintf( "%04d%02d%02d$3$4$5", $6, $month{$1} + 1, $2 );
200             }
201              
202             1;
203              
204             __END__