File Coverage

blib/lib/Email/LocalDelivery/Mbox.pm
Criterion Covered Total %
statement 70 73 95.8
branch 12 26 46.1
condition 2 5 40.0
subroutine 15 15 100.0
pod 0 3 0.0
total 99 122 81.1


line stmt bran cond sub pod time code
1 1     1   5 use strict;
  1         1  
  1         21  
2 1     1   4 use warnings;
  1         2  
  1         28  
3             package Email::LocalDelivery::Mbox 1.201;
4             # ABSTRACT: deliver mail to an mbox
5              
6             #pod =head1 INSTEAD...
7             #pod
8             #pod Instead, consider using L.
9             #pod
10             #pod =cut
11              
12 1     1   4 use File::Path;
  1         8  
  1         36  
13 1     1   14 use File::Basename;
  1         2  
  1         45  
14 1     1   306 use Email::Simple 1.998; # needed for ->header_obj
  1         3193  
  1         21  
15 1     1   5 use Fcntl ':flock';
  1         1  
  1         100  
16 1     1   5 use Symbol qw(gensym);
  1         2  
  1         604  
17              
18             sub deliver {
19             # The slightly convoluted method of unrolling the stack is intended to limit
20             # the scope of which a large string at $_[1] might be in memory before being
21             # constructed into an Email::Simple. -- rjbs, 2007-05-25
22 1     1 0 2 my $class = shift;
23              
24 1         1 my $email;
25 1 50       1 if (eval { $_[0]->isa('Email::Simple') }) {
  1         7  
26 0         0 $email = shift;
27             } else {
28 1         1 my $text = shift;
29 1         4 $email = Email::Simple->new(\$text); # requires Email::Simple 1.998 or so
30             }
31              
32 1         115 my @files = @_;
33              
34 1         1 my @rv;
35              
36 1         2 for my $file (@files) {
37 1 50       2 my $fh = $class->_open_fh($file) or next;
38 1 50       4 print $fh "\n" if tell($fh) > 0;
39 1         2 print $fh $class->_from_line($email);
40 1         4 print $fh $class->_escape_from_body($email);
41              
42             # This will make streaming a bit more annoying. -- rjbs, 2007-05-25
43 1 50       30 print $fh "\n" unless $email->as_string =~ /\n$/;
44              
45 1 50       31 $class->_close_fh($fh) || next;
46 1         4 push @rv, $file;
47             }
48 1         10 return @rv;
49             }
50              
51             sub _open_fh {
52 1     1   2 my ($class, $file) = @_;
53 1         35 my $dir = dirname($file);
54 1 0 33     13 return if !-d $dir and not mkpath($dir);
55              
56 1         4 my $fh = gensym;
57 1 50       79 open $fh, ">> $file" or return;
58 1 50       6 $class->getlock($fh) || return;
59 1         6 seek $fh, 0, 2;
60 1         4 return $fh;
61             }
62              
63             sub _close_fh {
64 1     1   2 my ($class, $fh) = @_;
65 1 50       2 $class->unlock($fh) || return;
66 1 50       11 close $fh or return;
67 1         3 return 1;
68             }
69              
70             sub _escape_from_body {
71 1     1   2 my ($class, $email) = @_;
72              
73 1         3 my $body = $email->body;
74 1         12 $body =~ s/^(From )/>$1/gm;
75              
76 1         2 return $email->header_obj->as_string . $email->crlf . $body;
77             }
78              
79             sub _from_line {
80 1     1   2 my ($class, $email) = @_;
81              
82             # The qmail way.
83 1 50       2 return $ENV{UFLINE} . $ENV{RPLINE} . $ENV{DTLINE} if exists $ENV{UFLINE};
84              
85             # The boring way.
86 1         2 return _from_line_boring($email);
87             }
88              
89             sub _from_line_boring {
90 1     1   1 my $mail = shift;
91 1   50     3 my $from = $mail->header("Return-path")
92             || $mail->header("Sender")
93             || $mail->header("Reply-To")
94             || $mail->header("From")
95             || 'root@localhost';
96 1 50       96 $from = $1 if $from =~ /<(.*?)>/; # comment -> email@address
97 1         2 $from =~ s/\s*\(.*\)\s*//; # email@address (comment) -> email@address
98 1         2 $from =~ s/\s+//g; # if any whitespace remains, get rid of it.
99              
100 1         31 my $fromtime = localtime;
101 1         5 $fromtime =~ s/(:\d\d) \S+ (\d{4})$/$1 $2/; # strip timezone.
102 1         13 return "From $from $fromtime\n";
103             }
104              
105             sub getlock {
106 1     1 0 2 my ($class, $fh) = @_;
107 1         2 for (1 .. 10) {
108 1 50       12 return 1 if flock($fh, LOCK_EX | LOCK_NB);
109 0         0 sleep $_;
110             }
111 0         0 return 0;
112             }
113              
114             sub unlock {
115 1     1 0 1 my ($class, $fh) = @_;
116 1         37 flock($fh, LOCK_UN);
117             }
118              
119             1;
120              
121             __END__