File Coverage

blib/lib/CPAN/Testers/Data/Uploads/Mailer.pm
Criterion Covered Total %
statement 118 127 92.9
branch 29 40 72.5
condition 6 8 75.0
subroutine 21 21 100.0
pod 2 2 100.0
total 176 198 88.8


line stmt bran cond sub pod time code
1             package CPAN::Testers::Data::Uploads::Mailer;
2              
3 8     8   581979 use warnings;
  8         20  
  8         264  
4 8     8   42 use strict;
  8         12  
  8         424  
5             $|++;
6              
7             our $VERSION = '0.06';
8              
9             #----------------------------------------------------------------------------
10              
11             =head1 NAME
12              
13             CPAN::Testers::Data::Uploads::Mailer - Verify CPAN uploads and mails reports
14              
15             =head1 SYNOPSIS
16              
17             my $mailer = CPAN::Testers::Data::Uploads::Mailer->new();
18             $mailer->process();
19              
20             =head1 DESCRIPTION
21              
22             Reads the uploads log, then generates and emails the bad uploads report to
23             the appropriate authors.
24              
25             =cut
26              
27             # -------------------------------------
28             # Library Modules
29              
30 8     8   6624 use Email::Simple;
  8         52588  
  8         247  
31 8     8   75 use File::Basename;
  8         12  
  8         955  
32 8     8   49 use File::Path;
  8         16  
  8         476  
33 8     8   7699 use File::Slurp;
  8         122056  
  8         641  
34 8     8   8748 use Getopt::ArgvFile default=>1;
  8         51924  
  8         57  
35 8     8   77616 use Getopt::Long;
  8         163457  
  8         105  
36 8     8   11185 use IO::File;
  8         88115  
  8         1501  
37 8     8   20611 use Template;
  8         211074  
  8         308  
38 8     8   12131 use Time::Piece;
  8         110694  
  8         54  
39              
40             # -------------------------------------
41             # Variables
42              
43             # the following will have the emails mailed to them too
44             my @ADMINS = ('barbie@missbarbell.co.uk');
45             my %ADMINS = map {$_ => 1} @ADMINS;
46              
47             my %default = (
48             source => 'logs/uploads.log',
49             lastfile => 'logs/uploads-mailer.txt',
50             logfile => 'logs/uploads-mailer.log',
51             debug => 0, # if set to 1 will not send mails
52             test => 1, # if set to 1 will only send to @ADMINS
53             help => 0,
54             version => 0
55             );
56              
57             #my $HOW = 'blah';
58             my $HOW = '/usr/sbin/sendmail -bm';
59             my $HEAD = 'To: EMAIL
60             From: Barbie
61             Subject: SUBJECT
62             Date: DATE
63              
64             ';
65              
66             my $archive = qr/\b(rar|tgs|tbz|zip|tar|pm|gz|bz2|tz)$/i; # known archive formats posted
67             my $accept = qr/\.(?:(?:tar\.|t)(?:gz|bz2)|zip)$/i; # valid archives
68             my $ignore = qr/
69             \.(pl|sh) | # ignore scripts
70             \.(gif|png|jpg) | # ... images
71             \.(readme|meta|yml|json|changelog) | # ... package files
72             \.(asc|pdf|ppm|patch|pod|txt) $ # ... docs and patches
73             /xi;
74              
75              
76             #----------------------------------------------------------------------------
77             # The Application Programming Interface
78              
79             sub new {
80 9     9 1 19318 my $class = shift;
81              
82 9         22 my $self = {};
83 9         26 bless $self, $class;
84              
85 9         37 $self->_init_options(@_);
86 6         21 return $self;
87             }
88              
89             sub process {
90 2     2 1 11 my $self = shift;
91 2         4 my $source = $self->{options}{source};
92 2         8 my $lastid = $self->_last_id();
93 2         2 my $last_id = $lastid;
94              
95 2         6 $self->{mail}{layout} = 'mail-bad-uploads.eml';
96              
97 2 50       14 my $fh = IO::File->new($source) or die "Cannot open file [$source]: $!";
98 2         183 while(<$fh>) {
99 2154         1844 chomp;
100              
101             #... [1281307] subject=CPAN Upload: A/AP/APLA/update_db_schema.pl
102 2154         3257 my ($id,$path,$cpan,$dist) = m!\.\.\. \[(\d+)\] subject=CPAN Upload: (\w/\w{2}/(\w+)/(.*))!;
103 2154 100 100     6336 next unless($id && $id > $lastid);
104 260         271 $last_id = $id;
105              
106 260 50       375 next unless(defined $cpan); # must have a PAUSE id
107 260 100       2623 next if($dist =~ /$accept|$ignore/); # accepted valid archives or ignored extensions
108              
109 1 50       8 if($dist !~ /$archive/) { # not a known archive format
110 0         0 $self->{mail}{others} .= "$id,$path\n";
111 0         0 next;
112             }
113 1         5 $self->{mail}{authors}{$cpan.'@cpan.org'} = 1;
114 1         6 $self->{mail}{uploads} .= "$id,$path\n";
115             }
116              
117 2         12 $self->_send_mail();
118              
119 2         36 $self->_last_id($last_id);
120             }
121              
122             #----------------------------------------------------------------------------
123             # Private Methods
124              
125             sub _send_mail {
126 2     2   4 my $self = shift;
127              
128 2 100 66     13 return unless(defined $self->{mail}{authors} && keys %{$self->{mail}{authors}});
  1         8  
129              
130 1         4 my $DATE = _emaildate();
131 1         171 $DATE =~ s/\s+$//;
132              
133 1         5 my %tvars = (
134             date => $DATE,
135             uploads => $self->{mail}{uploads},
136             );
137              
138 1         2 my @recipients;
139 1 50       6 push @recipients, (keys %{$self->{mail}{authors}}) unless($self->{options}{test});
  0         0  
140 1         2 push @recipients, @ADMINS;
141              
142 1         2 for my $addr (@recipients) {
143 1         3 $tvars{email} = $addr;
144 1 50 50     9 $tvars{others} = $ADMINS{$addr} ? $self->{mail}{others}||'' : '';
145              
146 1         5 my $body = _create_mail($self->{mail}{layout},\%tvars);
147              
148 1         101 my $cmd = qq!| $HOW $addr!;
149              
150 1 50       8 if($self->{options}{debug}) {
151 1         7 $self->_log("$DATE: NULL: $addr");
152 1         66 $self->_log("$body");
153             } else {
154 0 0       0 if(my $fh = IO::File->new($cmd)) {
155 0         0 print $fh $body;
156 0         0 $fh->close;
157 0         0 $self->_log("$DATE: PASS: $addr");
158             } else {
159 0         0 $self->_log("$DATE: FAIL: $addr");
160             }
161             }
162             }
163             }
164              
165             sub _create_mail {
166 1     1   2 my $layout = shift;
167 1         2 my $tvars = shift;
168 1         2 my $body;
169              
170 1         6 my %config = ( # provide config info
171             RELATIVE => 1,
172             ABSOLUTE => 1,
173             INCLUDE_PATH => './templates',
174             INTERPOLATE => 0,
175             POST_CHOMP => 1,
176             TRIM => 1,
177             );
178              
179 1         24 my $parser = Template->new(\%config); # initialise parser
180 1 50       22697 $parser->process($layout,$tvars,\$body) # parse the template
181             or die $parser->error();
182              
183 1         171 return $body;
184             }
185              
186             # Format date and time into one that conforms to the RFCs.
187              
188             sub _emaildate {
189 1     1   7 my $t = localtime;
190 1         113 return $t->strftime("%a, %d %b %Y %H:%M:%S %z");
191             }
192              
193             sub _last_id {
194 4     4   8 my $self = shift;
195 4         7 my ( $id ) = @_;
196              
197 4 100       68 overwrite_file( $self->{options}{lastfile}, 0 ) unless -f $self->{options}{lastfile};
198              
199 4 100       231 if (defined $id) {
200 2         13 overwrite_file( $self->{options}{lastfile}, $id );
201             } else {
202 2         9 $id = read_file($self->{options}{lastfile});
203             }
204              
205 4         445 return $id;
206             }
207              
208             sub _log {
209 2     2   3 my $self = shift;
210 2         3 my $msg = shift;
211 2         5 my $logfile = $self->{options}{logfile};
212 2 50       7 return unless($logfile);
213              
214 2 50       12 my $fh = IO::File->new($logfile,'a+') or die "Cannot write to file [$logfile]: $!\n";
215 2         271 print $fh "$msg\n";
216 2         14 $fh->close;
217             }
218              
219             sub _defined_or {
220 63     63   81 my $self = shift;
221 63         114 while(@_) {
222 161         192 my $value = shift;
223 161 100       634 return $value if(defined $value);
224             }
225              
226 0         0 return;
227             }
228              
229             sub _init_options {
230 9     9   17 my $self = shift;
231 9         25 my %opts = @_;
232              
233 9         15 my %options;
234 9         52 GetOptions( \%options,
235             'source|s=s',
236             'logfile=s',
237             'lastfile=s',
238             'test|t!',
239             'debug|d!',
240             'help|h',
241             'version|v'
242             );
243              
244 9         4401 $self->{options} = ();
245 9         75 $self->{options}{$_} = $self->_defined_or($options{$_}, $opts{$_}, $default{$_}) for(keys %default);
246              
247 9 100       46 _help(1) if($self->{options}{help});
248 8 100       61 _help(0) if($self->{options}{version});
249              
250 7 100       159 unless(-f $self->{options}{source}) {
251 1         108 print "No uploads source log file [$self->{options}{source}] found\n\n";
252 1         3 _help(1);
253             }
254              
255 6         473 mkpath(dirname($self->{options}{lastfile}));
256 6         262 mkpath(dirname($self->{options}{logfile}));
257             }
258              
259             sub _help {
260 3     3   27 my $full = shift;
261              
262 3 100       8 if($full) {
263 2         71 print <
264              
265             Usage: $0 \\
266             [--logfile=] [--source=] [--lastfile=] \\
267             [--test] [--debug] [-h] [-v]
268              
269             --logfile log file from cpanstats-verify
270             --source results output file
271             --lastfile last NNTP ID mailed out
272             --test send mails to admin only
273             --debug do not send mails
274             -h this help screen
275             -v program version
276              
277             HERE
278              
279             }
280              
281 3         71 print "$0 v$VERSION\n";
282 3         12 exit(0);
283             }
284              
285             __END__