File Coverage

blib/lib/Log/Fine/Handle/Email.pm
Criterion Covered Total %
statement 71 108 65.7
branch 18 60 30.0
condition 11 53 20.7
subroutine 15 18 83.3
pod 1 1 100.0
total 116 240 48.3


line stmt bran cond sub pod time code
1              
2             =head1 NAME
3              
4             Log::Fine::Handle::Email - Email log messages to one or more addresses
5              
6             =head1 SYNOPSIS
7              
8             Provides messaging to one or more email addresses.
9              
10             use Email::Sender::Simple qw(sendmail);
11             use Email::Sender::Transport::SMTP qw();
12             use Log::Fine;
13             use Log::Fine::Handle::Email;
14             use Log::Fine::Levels::Syslog;
15              
16             # Get a new logger
17             my $log = Log::Fine->logger("foo");
18              
19             # Create a formatter object for subject line
20             my $subjfmt = Log::Fine::Formatter::Template
21             ->new( name => 'template1',
22             template => "%%LEVEL%% : The angels have my blue box" );
23              
24             # Create a formatted msg template
25             my $msgtmpl = <
26             The program, $0, has encountered the following error condition:
27              
28             %%MSG%% at %%TIME%%
29              
30             Contact Operations at 1-800-555-5555 immediately!
31             EOF
32              
33             my $bodyfmt = Log::Fine::Formatter::Template
34             ->new( name => 'template2',
35             template => $msgtmpl );
36              
37             # Create an Email Handle
38             my $handle = Log::Fine::Handle::Email
39             ->new( name => 'email0',
40             mask => LOGMASK_EMERG | LOGMASK_ALERT | LOGMASK_CRIT,
41             subject_formatter => $subjfmt,
42             body_formatter => $bodyfmt,
43             header_from => "alerts@example.com",
44             header_to => [ "critical_alerts@example.com" ],
45             envelope =>
46             { to => [ "critical_alerts@example.com" ],
47             from => "alerts@example.com",
48             transport =>
49             Email::Sender::Transport::SMTP->new({ host => 'smtp.example.com' }),
50             }
51             );
52              
53             # Register the handle
54             $log->registerHandle($handle);
55              
56             # Log something
57             $log->log(CRIT, "Beware the weeping angels");
58              
59             =head1 DESCRIPTION
60              
61             Log::Fine::Handle::Email provides formatted message delivery to one or
62             more email addresses. The intended use is for programs that need to
63             alert a user in the event of a critical condition. Conceivably, the
64             destination address could be a pager or cell phone.
65              
66             =head2 Implementation Details
67              
68             Log::Fine::Handle::Email uses the L framework for
69             delivery of emails. Users who wish to use Log::Fine::Handle::Email
70             are I encouraged to read the following documentation:
71              
72             =over
73              
74             =item * L
75              
76             =item * L
77              
78             =item * L
79              
80             =back
81              
82             Be especially mindful of the following environment variables as they
83             will take precedence when defining a transport:
84              
85             =over
86              
87             =item * C
88              
89             =item * C
90              
91             =item * C
92              
93             =back
94              
95             See L for further details.
96              
97             =head2 Email Address Validation
98              
99             Log::Fine::Handle::Email will validate each email addresses prior to
100             use. Upon initilization, L::F::H::E will search for and use the
101             following email address validation modules in the following order of
102             preference:
103              
104             =over
105              
106             =item * L
107              
108             =item * L
109              
110             =back
111              
112             Should neither Mail::RFC822::Address nor Email::Valid be found, then a
113             default regex will be used which should work for most instances. See
114             L for special considerations.
115              
116             =head2 Constructor Parameters
117              
118             The following parameters can be passed to
119             Log::Fine::Handle::Email->new();
120              
121             =over
122              
123             =item * name
124              
125             [optional] Name of this object (see L). Will be auto-set if
126             not specified.
127              
128             =item * mask
129              
130             Mask to set the handle to (see L)
131              
132             =item * subject_formatter
133              
134             A Log::Fine::Formatter object. Will be used to format the Email
135             Subject Line.
136              
137             =item * body_formatter
138              
139             A Log::Fine::Formatter object. Will be used to format the body of the
140             message.
141              
142             =item * header_from
143              
144             String containing text to be placed in "From" header of generated
145             email.
146              
147             =item * header_to
148              
149             String containing text to be placed in "To" header of generated email.
150             Optionally, this can be an array ref containing multiple addresses
151              
152             =item * envelope
153              
154             [optional] hash ref containing envelope information for email:
155              
156             =over 8
157              
158             =item + to
159              
160             array ref containing one or more destination addresses
161              
162             =item + from
163              
164             String containing email sender
165              
166             =item + transport
167              
168             An L object. See L
169             for further details.
170              
171             =back
172              
173             =back
174              
175             =cut
176              
177 2     2   124752 use strict;
  2         3  
  2         54  
178 2     2   6 use warnings;
  2         4  
  2         55  
179              
180             package Log::Fine::Handle::Email;
181              
182 2     2   33 use 5.008_003; # Email::Sender requires Moose which requires 5.8.3
  2         4  
183              
184 2     2   7 use base qw( Log::Fine::Handle );
  2         2  
  2         434  
185              
186 2     2   8 use Carp qw(carp);
  2         3  
  2         82  
187              
188             #use Data::Dumper;
189 2     2   761 use Email::Sender::Simple qw(try_to_sendmail);
  2         182190  
  2         11  
190 2     2   474 use Email::Simple;
  2         29  
  2         37  
191 2     2   6 use Log::Fine;
  2         3  
  2         27  
192 2     2   6 use Log::Fine::Formatter;
  2         3  
  2         38  
193 2     2   4 use Sys::Hostname;
  2         2  
  2         287  
194              
195             BEGIN {
196              
197             # Set email address validation routine depending on what
198             # module is installed on this system
199 2     2   5 my @modules = ('Mail::RFC822::Address', 'Email::Valid', 'Default');
200              
201 2         4 foreach my $module (@modules) {
202              
203 2 50       10 if ($module eq 'Default') {
204 0         0 *_isValid = \&_validate_default;
205 0         0 carp 'Using default email validation. ' . 'Consider Mail::RFC822::Address\n';
206 0         0 last;
207             }
208              
209 2         98 eval "{ require $module }";
210              
211 2 50       522 unless ($@) {
212 2         6 my $sub = '_validate_' . lc($module);
213 2         6 $sub =~ s/\:\:/_/g;
214              
215 2         2 *_isValid = \&{$sub};
  2         12  
216 2         1553 last;
217             }
218              
219             # Reset $@ just in case
220 0         0 undef $@;
221              
222             }
223              
224             }
225              
226             our $VERSION = $Log::Fine::Handle::VERSION;
227              
228             # --------------------------------------------------------------------
229              
230             =head1 METHODS
231              
232             =head2 msgWrite
233              
234             Sends given message via Email::Sender module. Note that
235             L will be called should there be a failure of
236             delivery.
237              
238             See L
239              
240             =cut
241              
242             sub msgWrite
243             {
244              
245 1     1 1 2 my $self = shift;
246 1         2 my $lvl = shift;
247 1         1 my $msg = shift;
248 1         2 my $skip = shift;
249              
250             my $email =
251             Email::Simple->create(
252             header => [ To => $self->{header_to},
253             From => $self->{header_from},
254             Subject => $self->{subject_formatter}->format($lvl, "", $skip),
255             ],
256 1         7 body => $self->{body_formatter}->format($lvl, $msg, $skip),
257             );
258              
259             # Set X-Mailer
260 1         2803 $email->header_set("X-Mailer", sprintf("%s ver %s", ref $self, $VERSION));
261              
262             $self->_error("Unable to deliver email: $_")
263 1 50       33 unless (try_to_sendmail($email, $self->{envelope}));
264              
265             } # msgWrite()
266              
267             # --------------------------------------------------------------------
268              
269             ##
270             # Initializes our object
271              
272             sub _init
273             {
274              
275 1     1   2 my $self = shift;
276              
277             # Perform any necessary upper class initializations
278 1         7 $self->SUPER::_init();
279              
280             # Make sure envelope is defined
281 1   50     10 $self->{envelope} ||= {};
282              
283             # Validate From address
284 1 50 33     10 if (not defined $self->{header_from}) {
    50          
285             $self->{header_from} =
286 0         0 printf("%s@%s", $self->_userName(), $self->_hostName());
287             } elsif (defined $self->{header_from}
288             and not $self->_isValid($self->{header_from})) {
289 0         0 $self->_fatal("{header_from} must be a valid RFC 822 Email Address");
290             }
291              
292             # Validate To address
293             $self->_fatal( "{header_to} must be either an array ref containing "
294             . "valid email addresses or a string representing a "
295             . "valid email address")
296 1 50       1116 unless (defined $self->{header_to});
297              
298             # Check for array ref
299 1 50       6 if (ref $self->{header_to} eq "ARRAY") {
    50          
300              
301 0 0       0 if ($self->_isValid($self->{header_to})) {
302 0         0 $self->{header_to} = join(",", @{ $self->{header_to} });
  0         0  
303             } else {
304 0         0 $self->_fatal("{header_to} must contain valid " . "RFC 822 email addresses");
305             }
306              
307             } elsif (not $self->_isValid($self->{header_to})) {
308 0         0 $self->_fatal("{header_to} must contain a valid " . "RFC 822 email address");
309             }
310              
311             # Validate subject formatter
312             $self->_fatal("{subject_formatter} must be a valid " . "Log::Fine::Formatter object")
313             unless ( defined $self->{subject_formatter}
314             and ref $self->{subject_formatter}
315             and UNIVERSAL::can($self->{subject_formatter}, 'isa')
316 1 50 33     32 and $self->{subject_formatter}->isa("Log::Fine::Formatter"));
      33        
      33        
317              
318             # Validate body formatter
319             $self->_fatal(
320             "{body_formatter} must be a valid " . "Log::Fine::Formatter object : " . ref $self->{body_formatter}
321             || "{undef}")
322             unless ( defined $self->{body_formatter}
323             and ref $self->{body_formatter}
324             and UNIVERSAL::can($self->{body_formatter}, 'isa')
325 1 50 0     18 and $self->{body_formatter}->isa("Log::Fine::Formatter"));
      33        
      33        
      33        
326              
327             # Grab a ref to envelope
328 1         2 my $envelope = $self->{envelope};
329              
330             # Check Envelope Transport
331 1 50       3 if (defined $envelope->{transport}) {
332 0         0 my $transtype = ref $envelope->{transport};
333 0 0       0 $self->_fatal(
334             "{envelope}->{transport} must be a valid " . "Email::Sender::Transport object : $transtype")
335             unless ($transtype =~ /^Email\:\:Sender\:\:Transport/);
336             }
337              
338             # Check Envelope To
339 1 50       3 if (defined $envelope->{to}) {
340             $self->_fatal( "{envelope}->{to} must be an "
341             . "array ref containing one or more valid "
342             . "RFC 822 email addresses")
343             unless (ref $envelope->{to} eq "ARRAY"
344 0 0 0     0 and $self->_isValid($envelope->{to}));
345             }
346              
347             # Check envelope from
348 1 50 33     3 if (defined $envelope->{from} and $envelope->{from} =~ /\w/) {
349             $self->_fatal("{envelope}->{from} must be a " . "valid RFC 822 Email Address")
350 0 0       0 unless $self->_isValid($envelope->{from});
351             } else {
352 1         3 $envelope->{from} = $self->{header_from};
353             }
354              
355             # Validate subject formatter
356             $self->_fatal("{subject_formatter} must be a valid " . "Log::Fine::Formatter object")
357             unless (defined $self->{subject_formatter}
358 1 50 33     6 and $self->{subject_formatter}->isa("Log::Fine::Formatter"));
359              
360             # Validate body formatter
361             $self->_fatal(
362             "{body_formatter} must be a valid " . "Log::Fine::Formatter object : " . ref $self->{body_formatter}
363             || "{undef}")
364             unless (defined $self->{body_formatter}
365 1 50 0     7 and $self->{body_formatter}->isa("Log::Fine::Formatter"));
      33        
366              
367 1         2 return $self;
368              
369             } # _init()
370              
371             ##
372             # Getter/Setter for hostname
373              
374             sub _hostName
375             {
376              
377 0     0   0 my $self = shift;
378              
379             # Should {_fullHost} be already cached, then return it,
380             # otherwise get hostname, cache it, and return
381             $self->{_fullHost} = hostname() || "{undef}"
382 0 0 0     0 unless (defined $self->{_fullHost} and $self->{_fullHost} =~ /\w/);
      0        
383              
384 0         0 return $self->{_fullHost};
385              
386             } # _hostName()
387              
388             ##
389             # Getter/Setter for user name
390              
391             sub _userName
392             {
393              
394 0     0   0 my $self = shift;
395              
396             # Should {_userName} be already cached, then return it,
397             # otherwise get the user name, cache it, and return
398 0 0 0     0 if (defined $self->{_userName} and $self->{_userName} =~ /\w/) {
    0          
399 0         0 return $self->{_userName};
400             } elsif ($self->{use_effective_id}) {
401             $self->{_userName} =
402             ($^O eq "MSWin32")
403 0 0 0     0 ? $ENV{EUID} || 0
      0        
404             : getpwuid($>) || "nobody";
405             } else {
406 0   0     0 $self->{_userName} = getlogin() || getpwuid($<) || "nobody";
407             }
408              
409 0         0 return $self->{_userName};
410              
411             } # _userName()
412              
413             ##
414             # Default email address checker
415             #
416             # Parameters:
417             #
418             # - addy : either a scalar containing a string to check or an array
419             # ref containing one or more strings to check
420             #
421             # Returns:
422             #
423             # 1 on success, undef otherwise
424              
425             sub _validate_default
426             {
427              
428 1     1   369 my $self = shift;
429 1         1 my $addy = shift;
430              
431 1 50       4 if (ref $addy eq "ARRAY") {
432 0         0 foreach my $address (@{$addy}) {
  0         0  
433             return undef
434 0 0       0 unless $address =~
435             /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+\@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
436             }
437             } else {
438             return undef
439 1 50       46 unless ($addy =~
440             /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+\@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/
441             );
442             }
443              
444 1         5 return 1;
445              
446             } # _validate_default()
447              
448             ##
449             # Validate email address via Email::Valid
450             #
451             # Parameters:
452             #
453             # - addy : either a scalar containing a string to check or an array
454             # ref containing one or more strings to check
455             #
456             # Returns:
457             #
458             # 1 on success, undef otherwise
459              
460             sub _validate_email_valid
461             {
462              
463 0     0   0 my $self = shift;
464 0         0 my $addy = shift;
465              
466 0         0 my $validator = Email::Valid->new();
467              
468 0 0       0 if (ref $addy eq 'ARRAY') {
469 0         0 foreach my $address (@{$addy}) {
  0         0  
470 0 0       0 return undef unless $validator->address($address);
471             }
472             } else {
473 0 0       0 return undef unless $validator->address($addy);
474             }
475              
476 0         0 return 1;
477              
478             } # _validate_email_valid()
479              
480             ##
481             # Validate email address via Mail::RFC822::Address
482             #
483             # Parameters:
484             #
485             # - addy : either a scalar containing a string to check or an array
486             # ref containing one or more strings to check
487             #
488             # Returns:
489             #
490             # 1 on success, undef otherwise
491              
492             sub _validate_mail_rfc822_address
493             {
494              
495 2     2   3 my $self = shift;
496 2         3 my $addy = shift;
497              
498 2 50       4 if (ref $addy eq "ARRAY") {
499 0         0 return Mail::RFC822::Address::validlist($addy);
500             } else {
501 2         6 return Mail::RFC822::Address::valid($addy);
502             }
503              
504             } # _validate_mail_rfc822_address()
505              
506             =head1 CAVEATS
507              
508             Note that the L module does not use the same checking
509             algorithms as L. Email addresses considered
510             valid under one module may not be considered valid under the other.
511             For example, under Mail::RFC822::Address, C is
512             considered a valid address while Email::Valid will reject it.
513             Consider researching each module prior to making a determination as to
514             which is acceptable for your environment and needs.
515              
516             =head1 BUGS
517              
518             Please report any bugs or feature requests to
519             C, or through the web interface at
520             L.
521             I will be notified, and then you'll automatically be notified of progress on
522             your bug as I make changes.
523              
524             =head1 SUPPORT
525              
526             You can find documentation for this module with the perldoc command.
527              
528             perldoc Log::Fine
529              
530             You can also look for information at:
531              
532             =over 4
533              
534             =item * AnnoCPAN: Annotated CPAN documentation
535              
536             L
537              
538             =item * CPAN Ratings
539              
540             L
541              
542             =item * RT: CPAN's request tracker
543              
544             L
545              
546             =item * Search CPAN
547              
548             L
549              
550             =back
551              
552             =head1 AUTHOR
553              
554             Christopher M. Fuhrman, C<< >>
555              
556             =head1 SEE ALSO
557              
558             L, L, L
559              
560             =head1 COPYRIGHT & LICENSE
561              
562             Copyright (c) 2011-2013 Christopher M. Fuhrman,
563             All rights reserved.
564              
565             This program is free software licensed under the...
566              
567             The BSD License
568              
569             The full text of the license can be found in the
570             LICENSE file included with this module.
571              
572             =cut
573              
574             1; # End of Log::Fine::Handle::Email