| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
1
|
|
|
1
|
|
49317
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
47
|
|
|
2
|
|
|
|
|
|
|
package Mail::LocalDelivery; |
|
3
|
|
|
|
|
|
|
{ |
|
4
|
|
|
|
|
|
|
$Mail::LocalDelivery::VERSION = '0.305'; |
|
5
|
|
|
|
|
|
|
} |
|
6
|
|
|
|
|
|
|
# ABSTRACT: Deliver mail to a local mailbox |
|
7
|
|
|
|
|
|
|
|
|
8
|
1
|
|
|
1
|
|
5
|
use Carp; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
54
|
|
|
9
|
1
|
|
|
1
|
|
759
|
use Email::Abstract; |
|
|
1
|
|
|
|
|
54020
|
|
|
|
1
|
|
|
|
|
30
|
|
|
10
|
1
|
|
|
1
|
|
9
|
use File::Basename; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
75
|
|
|
11
|
1
|
|
|
1
|
|
6
|
use Fcntl ':flock'; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
150
|
|
|
12
|
1
|
|
|
1
|
|
4
|
use Mail::Internet; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
20
|
|
|
13
|
1
|
|
|
1
|
|
2091
|
use Sys::Hostname; (my $HOSTNAME = hostname) =~ s/\..*//; |
|
|
1
|
|
|
|
|
1188
|
|
|
|
1
|
|
|
|
|
93
|
|
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
my $debuglevel = 0; |
|
16
|
|
|
|
|
|
|
our $ASSUME_MSGPREFIX = 0; |
|
17
|
|
|
|
|
|
|
|
|
18
|
1
|
|
|
1
|
|
7
|
use constant DEFERRED => 75; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
63
|
|
|
19
|
1
|
|
|
1
|
|
4
|
use constant REJECTED => 100; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
40
|
|
|
20
|
1
|
|
|
1
|
|
17
|
use constant DELIVERED => 0; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
3234
|
|
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub _debug { |
|
24
|
34
|
|
|
34
|
|
51
|
my ($self, $priority, $what) = @_; |
|
25
|
34
|
50
|
|
|
|
119
|
return $self->{logger}->($priority, $what) if $self->{logger}; |
|
26
|
0
|
0
|
|
|
|
0
|
return if $debuglevel < $priority; |
|
27
|
0
|
|
|
|
|
0
|
chomp $what; |
|
28
|
0
|
|
|
|
|
0
|
chomp $what; |
|
29
|
0
|
|
|
|
|
0
|
my ($subroutine) = (caller(1))[3]; |
|
30
|
0
|
|
|
|
|
0
|
$subroutine =~ s/(.*):://; |
|
31
|
0
|
|
|
|
|
0
|
my ($line) = (caller(0))[2]; |
|
32
|
|
|
|
|
|
|
|
|
33
|
0
|
|
|
|
|
0
|
warn "$line($subroutine): $what\n"; |
|
34
|
|
|
|
|
|
|
} |
|
35
|
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub new { |
|
38
|
1
|
|
|
1
|
1
|
2194
|
my $class = shift; |
|
39
|
1
|
|
|
|
|
2
|
my $stuff = shift; |
|
40
|
|
|
|
|
|
|
|
|
41
|
1
|
|
|
|
|
5
|
my %opts = @_; |
|
42
|
|
|
|
|
|
|
|
|
43
|
1
|
|
|
|
|
6
|
my $self = { |
|
44
|
|
|
|
|
|
|
interpolate_strftime => 0, |
|
45
|
|
|
|
|
|
|
one_for_all => 0, |
|
46
|
|
|
|
|
|
|
%opts, |
|
47
|
|
|
|
|
|
|
}; |
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# What sort of stuff do we have? |
|
50
|
1
|
50
|
33
|
|
|
2
|
if (eval { $stuff->isa('Mail::Internet') }) { |
|
|
1
|
50
|
|
|
|
13
|
|
|
|
|
50
|
|
|
|
|
|
|
51
|
0
|
|
|
|
|
0
|
$self->{email} = $stuff; |
|
52
|
1
|
|
|
|
|
10
|
} elsif (my $email = eval { Email::Abstract->new($self); }) { |
|
53
|
0
|
|
|
|
|
0
|
$self->{email} = $email->cast('Mail::Internet'); |
|
54
|
|
|
|
|
|
|
} elsif (ref $stuff eq "ARRAY" or ref $stuff eq "GLOB") { |
|
55
|
1
|
|
|
|
|
55
|
$self->{email} = Mail::Internet->new($stuff); |
|
56
|
|
|
|
|
|
|
} else { |
|
57
|
0
|
|
|
|
|
0
|
croak |
|
58
|
|
|
|
|
|
|
"Data was neither a mail object or a reference to something I understand"; |
|
59
|
|
|
|
|
|
|
} |
|
60
|
|
|
|
|
|
|
|
|
61
|
1
|
|
|
|
|
1645
|
my $default_maildir = ((getpwuid($>))[7]) . "/Maildir/"; |
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
my $default_mbox = $ENV{MAIL} |
|
64
|
|
|
|
|
|
|
|| (-d File::Spec->catdir($default_maildir, 'new') ? $default_maildir : ()) |
|
65
|
1
|
|
0
|
|
|
7
|
|| ((grep { -d $_ } qw(/var/spool/mail/ /var/mail/))[0] . getpwuid($>)); |
|
66
|
|
|
|
|
|
|
|
|
67
|
1
|
|
|
|
|
3
|
$self->{default_mbox} = $default_mbox; |
|
68
|
1
|
|
33
|
|
|
6
|
$self->{emergency} ||= $default_mbox; |
|
69
|
|
|
|
|
|
|
|
|
70
|
1
|
|
|
|
|
7
|
return bless $self => $class; |
|
71
|
|
|
|
|
|
|
} |
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub _nifty_interpolate { |
|
75
|
|
|
|
|
|
|
# perform ~user and %Y%m%d strftime interpolation |
|
76
|
3
|
|
|
3
|
|
3
|
my $self = shift; |
|
77
|
3
|
|
|
|
|
6
|
my @out = @_; |
|
78
|
3
|
|
|
|
|
148
|
my @localtime = localtime; |
|
79
|
|
|
|
|
|
|
|
|
80
|
3
|
50
|
33
|
|
|
17
|
if ($self->{interpolate_strftime} and grep { /%/ } @out) { |
|
|
0
|
|
|
|
|
0
|
|
|
81
|
0
|
|
|
|
|
0
|
require POSIX; |
|
82
|
0
|
|
|
|
|
0
|
POSIX->import(qw(strftime)); |
|
83
|
0
|
|
|
|
|
0
|
@out = map { strftime($_, @localtime) } @out; |
|
|
0
|
|
|
|
|
0
|
|
|
84
|
|
|
|
|
|
|
} |
|
85
|
|
|
|
|
|
|
|
|
86
|
2
|
|
|
|
|
24
|
@out = map { |
|
87
|
3
|
|
|
|
|
8
|
s{^~/} {((getpwuid($>))[7])."/"}e; |
|
|
0
|
|
|
|
|
0
|
|
|
88
|
2
|
|
|
|
|
5
|
s{^~(\w+)/}{((getpwnam($1))[7])."/"}e; |
|
|
0
|
|
|
|
|
0
|
|
|
89
|
2
|
|
|
|
|
6
|
$_ |
|
90
|
|
|
|
|
|
|
} @out; |
|
91
|
|
|
|
|
|
|
|
|
92
|
3
|
|
|
|
|
7
|
return @out; |
|
93
|
|
|
|
|
|
|
} |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
sub deliver { |
|
96
|
3
|
|
|
3
|
1
|
3233
|
my $self = shift; |
|
97
|
|
|
|
|
|
|
|
|
98
|
3
|
|
|
|
|
10
|
my @files = $self->_nifty_interpolate(@_); |
|
99
|
3
|
100
|
|
|
|
8
|
@files = ($self->{default_mbox}) if not @files; |
|
100
|
|
|
|
|
|
|
|
|
101
|
3
|
|
|
|
|
4
|
my @actually_saved_to_files; |
|
102
|
|
|
|
|
|
|
|
|
103
|
3
|
|
|
|
|
14
|
$self->_debug(2, "delivering to @files"); |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# from man procmailrc: |
|
106
|
|
|
|
|
|
|
# If it is a directory, the mail will be delivered to a newly created, |
|
107
|
|
|
|
|
|
|
# guaranteed to be unique file named $MSGPRE- FIX* in the specified |
|
108
|
|
|
|
|
|
|
# directory. If the mailbox name ends in "/.", then this directory is |
|
109
|
|
|
|
|
|
|
# presumed to be an MH folder; i.e., procmail will use the next |
|
110
|
|
|
|
|
|
|
# number it finds available. If the mailbox name ends in "/", then |
|
111
|
|
|
|
|
|
|
# this directory is presumed to be a maildir folder; i.e., proc- mail will |
|
112
|
|
|
|
|
|
|
# deliver the message to a file in a subdirectory named "tmp" and rename |
|
113
|
|
|
|
|
|
|
# it to be inside a subdirectory named "new". If the mailbox is |
|
114
|
|
|
|
|
|
|
# specified to be an MH folder or maildir folder, procmail will create |
|
115
|
|
|
|
|
|
|
# the neces- sary directories if they don't exist, rather than treat the |
|
116
|
|
|
|
|
|
|
# mailbox as a non-existent filename. When procmail is delivering to |
|
117
|
|
|
|
|
|
|
# directories, you can specify multiple direc- tories to deliver to |
|
118
|
|
|
|
|
|
|
# (procmail will do so utilising hardlinks). |
|
119
|
|
|
|
|
|
|
# |
|
120
|
|
|
|
|
|
|
# for now we will support maildir and mbox delivery. |
|
121
|
|
|
|
|
|
|
# MH delivery and MSGPREFIX delivery remain todo. |
|
122
|
|
|
|
|
|
|
|
|
123
|
3
|
|
|
|
|
20
|
my %deliver_types = ( |
|
124
|
|
|
|
|
|
|
mbox => [], |
|
125
|
|
|
|
|
|
|
maildir => [], |
|
126
|
|
|
|
|
|
|
mh => [], |
|
127
|
|
|
|
|
|
|
msgprefix => [], |
|
128
|
|
|
|
|
|
|
); |
|
129
|
|
|
|
|
|
|
|
|
130
|
3
|
|
|
|
|
5
|
for my $file (@files) { |
|
131
|
3
|
|
|
|
|
8
|
my $mailbox_type = $self->_mailbox_type($file); |
|
132
|
3
|
|
|
|
|
3
|
push @{ $deliver_types{$mailbox_type} }, $file; |
|
|
3
|
|
|
|
|
7
|
|
|
133
|
3
|
|
|
|
|
37
|
$self->_debug(3, "$file is of type $mailbox_type"); |
|
134
|
|
|
|
|
|
|
} |
|
135
|
|
|
|
|
|
|
|
|
136
|
3
|
|
|
|
|
25
|
foreach my $deliver_type (sort keys %deliver_types) { |
|
137
|
12
|
100
|
|
|
|
13
|
next if not @{ $deliver_types{$deliver_type} }; |
|
|
12
|
|
|
|
|
30
|
|
|
138
|
3
|
|
|
|
|
14
|
my $deliver_handler = "_deliver_to_$deliver_type"; |
|
139
|
3
|
|
|
|
|
5
|
$self->_debug(3, |
|
140
|
|
|
|
|
|
|
"calling deliver handler " |
|
141
|
3
|
|
|
|
|
14
|
. "$deliver_handler(@{$deliver_types{$deliver_type}})" |
|
142
|
|
|
|
|
|
|
); |
|
143
|
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
# Don't try to deliver to things for which we have no delivery method. |
|
145
|
3
|
50
|
|
|
|
21
|
next unless $self->can($deliver_handler); |
|
146
|
|
|
|
|
|
|
|
|
147
|
3
|
|
|
|
|
11
|
push @actually_saved_to_files, |
|
148
|
3
|
|
|
|
|
4
|
$self->$deliver_handler(@{ $deliver_types{$deliver_type} }); |
|
149
|
|
|
|
|
|
|
} |
|
150
|
|
|
|
|
|
|
|
|
151
|
3
|
100
|
|
|
|
9
|
if (@actually_saved_to_files == 0) { |
|
152
|
|
|
|
|
|
|
# in this section you will often see |
|
153
|
|
|
|
|
|
|
# $!=DEFERRED; die("unable to write to @files or to $emergency"); |
|
154
|
|
|
|
|
|
|
# we say this instead of |
|
155
|
|
|
|
|
|
|
# exit DEFERRED; |
|
156
|
|
|
|
|
|
|
# because we want to be able to trap the die message inside an eval {} |
|
157
|
|
|
|
|
|
|
# for testing purposes. |
|
158
|
|
|
|
|
|
|
|
|
159
|
1
|
|
|
|
|
3
|
my $emergency = $self->{emergency}; |
|
160
|
1
|
50
|
|
|
|
2
|
if (not defined $emergency) { |
|
161
|
0
|
|
|
|
|
0
|
return; |
|
162
|
|
|
|
|
|
|
} else { |
|
163
|
1
|
50
|
|
|
|
4
|
if (grep ($emergency eq $_, @files)) { # already tried that mailbox |
|
164
|
0
|
|
|
|
|
0
|
return; |
|
165
|
|
|
|
|
|
|
} else { |
|
166
|
1
|
|
|
|
|
3
|
my $deliver_type = $self->_mailbox_type($emergency); |
|
167
|
1
|
|
|
|
|
4
|
my $deliver_handler = "_deliver_to_$deliver_type"; |
|
168
|
1
|
|
|
|
|
15
|
@actually_saved_to_files = $self->$deliver_handler($emergency); |
|
169
|
1
|
50
|
|
|
|
5
|
return if not @actually_saved_to_files; |
|
170
|
|
|
|
|
|
|
} |
|
171
|
|
|
|
|
|
|
} |
|
172
|
|
|
|
|
|
|
} |
|
173
|
3
|
|
|
|
|
14
|
return @actually_saved_to_files; |
|
174
|
|
|
|
|
|
|
} |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub _mailbox_type { |
|
177
|
4
|
|
|
4
|
|
6
|
my $self = shift; |
|
178
|
4
|
|
|
|
|
5
|
my $file = shift; |
|
179
|
|
|
|
|
|
|
|
|
180
|
4
|
50
|
|
|
|
10
|
return 'maildir' if $file =~ /\/$/; |
|
181
|
4
|
50
|
|
|
|
10
|
return 'mh' if $file =~ /\/\.$/; |
|
182
|
|
|
|
|
|
|
|
|
183
|
4
|
100
|
|
|
|
63
|
if (-d $file) { |
|
184
|
2
|
50
|
33
|
|
|
45
|
return 'maildir' if (-d "$file/tmp" and -d "$file/new"); |
|
185
|
2
|
50
|
|
|
|
7
|
if (exists($self->{ASSUME_MSGPREFIX})) { |
|
186
|
0
|
0
|
|
|
|
0
|
return 'msgprefix' if $self->{ASSUME_MSGPREFIX}; |
|
187
|
0
|
|
|
|
|
0
|
return "maildir"; |
|
188
|
|
|
|
|
|
|
} |
|
189
|
2
|
50
|
|
|
|
5
|
return 'msgprefix' if $ASSUME_MSGPREFIX; |
|
190
|
2
|
|
|
|
|
6
|
return 'maildir'; |
|
191
|
|
|
|
|
|
|
} |
|
192
|
2
|
|
|
|
|
5
|
return 'mbox'; |
|
193
|
|
|
|
|
|
|
} |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub _get_opt { |
|
196
|
2
|
|
|
2
|
|
3
|
my ($self, $arg) = @_; |
|
197
|
|
|
|
|
|
|
|
|
198
|
2
|
|
|
|
|
3
|
my $opt; |
|
199
|
|
|
|
|
|
|
|
|
200
|
2
|
50
|
|
|
|
20
|
if (ref $arg->[0] eq 'HASH') { |
|
|
|
50
|
|
|
|
|
|
|
201
|
0
|
0
|
|
|
|
0
|
Carp::carp "prepending arguments is deprecated; append them instead" |
|
202
|
|
|
|
|
|
|
unless @$arg == 1; |
|
203
|
0
|
|
|
|
|
0
|
$opt = shift @$arg; |
|
204
|
|
|
|
|
|
|
} elsif (ref $arg->[-1] eq 'HASH') { |
|
205
|
0
|
|
|
|
|
0
|
$opt = pop @$arg; |
|
206
|
|
|
|
|
|
|
} |
|
207
|
|
|
|
|
|
|
|
|
208
|
2
|
|
50
|
|
|
12
|
return $opt || {}; |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
sub _deliver_to_mbox { |
|
212
|
2
|
|
|
2
|
|
3
|
my $self = shift; |
|
213
|
2
|
|
|
|
|
6
|
my $opt = $self->_get_opt(\@_); |
|
214
|
|
|
|
|
|
|
|
|
215
|
2
|
|
|
|
|
4
|
my @saved_to = (); |
|
216
|
|
|
|
|
|
|
|
|
217
|
2
|
|
|
|
|
4
|
foreach my $file (@_) { |
|
218
|
|
|
|
|
|
|
# auto-create the parent dir. |
|
219
|
2
|
100
|
|
|
|
68
|
if (my $mkdir_error = $self->_mkdir_p(dirname($file))) { |
|
220
|
1
|
|
|
|
|
3
|
$self->_debug(0, $mkdir_error); |
|
221
|
1
|
|
|
|
|
4
|
next; |
|
222
|
|
|
|
|
|
|
} |
|
223
|
|
|
|
|
|
|
|
|
224
|
1
|
|
|
|
|
6
|
my $error = $self->_write_message( |
|
225
|
|
|
|
|
|
|
$file, |
|
226
|
|
|
|
|
|
|
{ |
|
227
|
|
|
|
|
|
|
need_lock => 1, |
|
228
|
|
|
|
|
|
|
need_from => 1, |
|
229
|
|
|
|
|
|
|
extra_newline => 1 |
|
230
|
|
|
|
|
|
|
} |
|
231
|
|
|
|
|
|
|
); |
|
232
|
|
|
|
|
|
|
|
|
233
|
1
|
50
|
|
|
|
5
|
if (not $error) { |
|
234
|
1
|
|
|
|
|
4
|
push @saved_to, $file; |
|
235
|
|
|
|
|
|
|
} else { |
|
236
|
0
|
|
|
|
|
0
|
$self->_debug(1, $error); |
|
237
|
|
|
|
|
|
|
} |
|
238
|
|
|
|
|
|
|
} |
|
239
|
|
|
|
|
|
|
|
|
240
|
2
|
|
|
|
|
8
|
return @saved_to; |
|
241
|
|
|
|
|
|
|
} |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
sub _write_message { |
|
244
|
3
|
|
|
3
|
|
5
|
my $self = shift; |
|
245
|
3
|
|
|
|
|
5
|
my $file = shift; |
|
246
|
3
|
|
50
|
|
|
7
|
my $write_opts = shift || {}; |
|
247
|
|
|
|
|
|
|
|
|
248
|
3
|
50
|
|
|
|
10
|
$write_opts->{need_from} = 1 if not defined $write_opts->{need_from}; |
|
249
|
3
|
50
|
|
|
|
7
|
$write_opts->{need_lock} = 1 if not defined $write_opts->{need_lock}; |
|
250
|
3
|
100
|
|
|
|
9
|
$write_opts->{extra_newline} = 0 if not defined $write_opts->{extra_newline}; |
|
251
|
|
|
|
|
|
|
|
|
252
|
3
|
|
|
|
|
7
|
$self->_debug(3, "writing to $file; options @{[%$write_opts]}"); |
|
|
3
|
|
|
|
|
20
|
|
|
253
|
|
|
|
|
|
|
|
|
254
|
3
|
50
|
|
|
|
556
|
unless (open(FH, ">>$file")) { |
|
255
|
0
|
|
|
|
|
0
|
return "Couldn't open $file: $!"; |
|
256
|
|
|
|
|
|
|
} |
|
257
|
|
|
|
|
|
|
|
|
258
|
3
|
100
|
|
|
|
10
|
if ($write_opts->{need_lock}) { |
|
259
|
1
|
|
|
|
|
5
|
my $lock_error = $self->_audit_get_lock(\*FH, $file); |
|
260
|
1
|
50
|
|
|
|
3
|
return $lock_error if $lock_error; |
|
261
|
|
|
|
|
|
|
} |
|
262
|
|
|
|
|
|
|
|
|
263
|
3
|
|
|
|
|
21
|
seek FH, 0, 2; |
|
264
|
|
|
|
|
|
|
|
|
265
|
3
|
50
|
66
|
|
|
23
|
if ( |
|
266
|
|
|
|
|
|
|
not $write_opts->{need_from} |
|
267
|
|
|
|
|
|
|
and $self->{email}->head->header->[0] =~ /^From\s/ |
|
268
|
|
|
|
|
|
|
) { |
|
269
|
0
|
|
|
|
|
0
|
$self->_debug(3, "mbox From line found, stripping because we're maildir"); |
|
270
|
0
|
|
|
|
|
0
|
$self->{email}->head->delete("From "); |
|
271
|
0
|
|
|
|
|
0
|
$self->{email}->unescape_from; |
|
272
|
|
|
|
|
|
|
} |
|
273
|
|
|
|
|
|
|
|
|
274
|
3
|
100
|
66
|
|
|
272
|
if ( |
|
275
|
|
|
|
|
|
|
$write_opts->{need_from} |
|
276
|
|
|
|
|
|
|
and $self->{email}->head->header->[0] !~ /^From\s/ |
|
277
|
|
|
|
|
|
|
) { |
|
278
|
1
|
|
|
|
|
128
|
$self->_debug(3, "No mbox From line, making one up."); |
|
279
|
1
|
50
|
|
|
|
5
|
if (exists $ENV{UFLINE}) { |
|
280
|
0
|
|
|
|
|
0
|
$self->_debug( |
|
281
|
|
|
|
|
|
|
3, |
|
282
|
|
|
|
|
|
|
"Looks qmail, but preline not run, prepending UFLINE, RPLINE, DTLINE" |
|
283
|
|
|
|
|
|
|
); |
|
284
|
0
|
|
|
|
|
0
|
print FH $ENV{UFLINE}; |
|
285
|
0
|
|
|
|
|
0
|
print FH $ENV{RPLINE}; |
|
286
|
0
|
|
|
|
|
0
|
print FH $ENV{DTLINE}; |
|
287
|
|
|
|
|
|
|
} else { |
|
288
|
1
|
|
50
|
|
|
5
|
my $from = ( |
|
289
|
|
|
|
|
|
|
$self->{email}->get('Return-path') |
|
290
|
|
|
|
|
|
|
|| $self->{email}->get('Sender') |
|
291
|
|
|
|
|
|
|
|| $self->{email}->get('Reply-To') |
|
292
|
|
|
|
|
|
|
|| |
|
293
|
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# It might not be insane to include From header here. |
|
295
|
|
|
|
|
|
|
# -- rjbs, 2006-07-25 |
|
296
|
|
|
|
|
|
|
'root@localhost' |
|
297
|
|
|
|
|
|
|
); |
|
298
|
1
|
|
|
|
|
86
|
chomp $from; |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
# comment -> email@address |
|
301
|
1
|
50
|
|
|
|
4
|
$from = $1 if $from =~ /<(.*?)>/; |
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
# email@address (comment) -> email@address |
|
304
|
1
|
|
|
|
|
35
|
$from =~ s/\s*\(.*\)\s*//; |
|
305
|
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
# if any whitespace remains, get rid of it. |
|
307
|
1
|
|
|
|
|
3
|
$from =~ s/\s+//g; |
|
308
|
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
# strip timezone. |
|
310
|
1
|
|
|
|
|
29
|
(my $fromtime = localtime) =~ s/(:\d\d) \S+ (\d{4})$/$1 $2/; |
|
311
|
|
|
|
|
|
|
|
|
312
|
1
|
|
|
|
|
14
|
print FH "From $from $fromtime\n"; |
|
313
|
|
|
|
|
|
|
} |
|
314
|
|
|
|
|
|
|
} |
|
315
|
|
|
|
|
|
|
|
|
316
|
3
|
|
|
|
|
8
|
$self->_debug(4, "printing self as mbox string."); |
|
317
|
3
|
50
|
|
|
|
16
|
print FH $self->{email}->as_string or return "couldn't write to $file: $!"; |
|
318
|
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
# extra \n added because mutt seems to like a "\n\nFrom " in mbox files |
|
320
|
3
|
50
|
100
|
|
|
97
|
print FH "\n" |
|
321
|
|
|
|
|
|
|
if $write_opts->{extra_newline} |
|
322
|
|
|
|
|
|
|
or return "couldn't write to $file: $!"; |
|
323
|
|
|
|
|
|
|
|
|
324
|
1
|
50
|
|
|
|
3
|
if ($write_opts->{need_lock}) { |
|
325
|
1
|
50
|
|
|
|
38
|
flock(FH, LOCK_UN) or return "Couldn't unlock $file"; |
|
326
|
|
|
|
|
|
|
} |
|
327
|
|
|
|
|
|
|
|
|
328
|
1
|
50
|
|
|
|
19
|
close FH or return "Couldn't close $file after writing: $!"; |
|
329
|
1
|
|
|
|
|
4
|
$self->_debug(4, "returning success."); |
|
330
|
1
|
|
|
|
|
4
|
return 0; # success |
|
331
|
|
|
|
|
|
|
} |
|
332
|
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
# ---------------------------------------------------------- |
|
334
|
|
|
|
|
|
|
# NOT IMPLEMENTED |
|
335
|
|
|
|
|
|
|
# ---------------------------------------------------------- |
|
336
|
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
#sub _deliver_to_mh { my $self = shift; my @saved_to=(); } |
|
338
|
|
|
|
|
|
|
#sub _deliver_to_msgprefix { my $self = shift; my @saved_to=(); } |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
# variables for _deliver_to_maildir |
|
341
|
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
my $maildir_time = 0; |
|
343
|
|
|
|
|
|
|
my $maildir_counter = 0; |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
sub _deliver_to_maildir { |
|
346
|
2
|
|
|
2
|
|
4
|
my $self = shift; |
|
347
|
2
|
|
|
|
|
4
|
my @saved_to = (); |
|
348
|
|
|
|
|
|
|
|
|
349
|
2
|
|
|
|
|
9
|
$self->_debug(3, "will write to @_"); |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
# since mutt won't add a lines tag to maildir messages, we'll add it here |
|
352
|
2
|
100
|
100
|
|
|
16
|
unless (length($self->{email}->get("Lines") || '')) { |
|
353
|
1
|
|
|
|
|
46
|
my $num_lines = @{ $self->{email}->body }; |
|
|
1
|
|
|
|
|
4
|
|
|
354
|
1
|
|
|
|
|
16
|
$self->{email}->head->add("Lines", $num_lines); |
|
355
|
1
|
|
|
|
|
126
|
$self->_debug(4, "Adding Lines: $num_lines header"); |
|
356
|
|
|
|
|
|
|
} |
|
357
|
|
|
|
|
|
|
|
|
358
|
2
|
100
|
|
|
|
70
|
if ($maildir_time != time) { |
|
359
|
1
|
|
|
|
|
2
|
$maildir_time = time; |
|
360
|
1
|
|
|
|
|
1
|
$maildir_counter = 0; |
|
361
|
|
|
|
|
|
|
} else { |
|
362
|
1
|
|
|
|
|
2
|
$maildir_counter++; |
|
363
|
|
|
|
|
|
|
} |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
# write the tmp file. |
|
366
|
|
|
|
|
|
|
# hardlink to all the new files. |
|
367
|
|
|
|
|
|
|
# unlink the temp file. |
|
368
|
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
# write the tmp file in the first writable maildir directory. |
|
370
|
|
|
|
|
|
|
|
|
371
|
2
|
|
|
|
|
3
|
my $tmp_path; |
|
372
|
2
|
|
|
|
|
6
|
foreach my $file (my @maildirs = @_) { |
|
373
|
|
|
|
|
|
|
|
|
374
|
2
|
|
|
|
|
4
|
$file =~ s/\/$//; |
|
375
|
2
|
50
|
|
|
|
7
|
my $tmpdir = $self->{one_for_all} ? $file : "$file/tmp"; |
|
376
|
|
|
|
|
|
|
|
|
377
|
2
|
|
|
|
|
3
|
my $msg_file; |
|
378
|
2
|
|
|
|
|
3
|
do { |
|
379
|
2
|
|
|
|
|
9
|
$msg_file = join ".", |
|
380
|
|
|
|
|
|
|
($maildir_time, $$ . "_$maildir_counter", $HOSTNAME); |
|
381
|
2
|
|
|
|
|
42
|
$maildir_counter++; |
|
382
|
|
|
|
|
|
|
} while (-e "$tmpdir/$msg_file"); |
|
383
|
|
|
|
|
|
|
|
|
384
|
2
|
|
|
|
|
5
|
$tmp_path = "$tmpdir/$msg_file"; |
|
385
|
2
|
|
|
|
|
8
|
$self->_debug(3, "writing to $tmp_path"); |
|
386
|
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
# auto-create the maildir. |
|
388
|
2
|
50
|
|
|
|
10
|
if ( |
|
|
|
50
|
|
|
|
|
|
|
389
|
6
|
|
|
|
|
20
|
my $mkdir_error = $self->_mkdir_p( |
|
390
|
|
|
|
|
|
|
$self->{one_for_all} |
|
391
|
|
|
|
|
|
|
? ($file) |
|
392
|
|
|
|
|
|
|
: map { "$file/$_" } qw(tmp new cur) |
|
393
|
|
|
|
|
|
|
) |
|
394
|
|
|
|
|
|
|
) |
|
395
|
|
|
|
|
|
|
{ |
|
396
|
0
|
|
|
|
|
0
|
$self->_debug(0, $mkdir_error); |
|
397
|
0
|
|
|
|
|
0
|
next; |
|
398
|
|
|
|
|
|
|
} |
|
399
|
|
|
|
|
|
|
|
|
400
|
2
|
|
|
|
|
12
|
my $error = $self->_write_message( |
|
401
|
|
|
|
|
|
|
$tmp_path, |
|
402
|
|
|
|
|
|
|
{ |
|
403
|
|
|
|
|
|
|
need_from => 0, |
|
404
|
|
|
|
|
|
|
need_lock => 0 |
|
405
|
|
|
|
|
|
|
} |
|
406
|
|
|
|
|
|
|
); |
|
407
|
|
|
|
|
|
|
|
|
408
|
2
|
50
|
|
|
|
8
|
last unless $error; # only write to the first writeable maildir |
|
409
|
|
|
|
|
|
|
|
|
410
|
2
|
|
|
|
|
4
|
$self->_debug(1, $error); |
|
411
|
2
|
|
|
|
|
98
|
unlink $tmp_path; |
|
412
|
2
|
|
|
|
|
3
|
$tmp_path = undef; |
|
413
|
2
|
|
|
|
|
6
|
next; |
|
414
|
|
|
|
|
|
|
} |
|
415
|
|
|
|
|
|
|
|
|
416
|
2
|
50
|
|
|
|
6
|
if (not $tmp_path) { |
|
417
|
2
|
|
|
|
|
7
|
return 0; |
|
418
|
|
|
|
|
|
|
} # unable to write to any of the specified maildirs. |
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
# |
|
421
|
|
|
|
|
|
|
# it is now in tmp/. hardlink to all the new/ destinations. |
|
422
|
|
|
|
|
|
|
# |
|
423
|
|
|
|
|
|
|
|
|
424
|
0
|
|
|
|
|
0
|
foreach my $file (my @maildirs = @_) { |
|
425
|
0
|
|
|
|
|
0
|
$file =~ s/\/$//; |
|
426
|
|
|
|
|
|
|
|
|
427
|
0
|
|
|
|
|
0
|
my $msg_file; |
|
428
|
0
|
0
|
|
|
|
0
|
my $newdir = $self->{one_for_all} ? $file : "$file/new"; |
|
429
|
0
|
|
|
|
|
0
|
$maildir_counter = 0; |
|
430
|
0
|
|
|
|
|
0
|
do { |
|
431
|
0
|
|
|
|
|
0
|
$msg_file = join ".", |
|
432
|
|
|
|
|
|
|
($maildir_time = time, $$ . "_$maildir_counter", $HOSTNAME); |
|
433
|
0
|
|
|
|
|
0
|
$maildir_counter++; |
|
434
|
|
|
|
|
|
|
} while (-e "$newdir/$msg_file"); |
|
435
|
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
# auto-create the maildir. |
|
437
|
0
|
0
|
|
|
|
0
|
if ( |
|
|
|
0
|
|
|
|
|
|
|
438
|
0
|
|
|
|
|
0
|
my $mkdir_error = $self->_mkdir_p( |
|
439
|
|
|
|
|
|
|
$self->{one_for_all} |
|
440
|
|
|
|
|
|
|
? ($file) |
|
441
|
|
|
|
|
|
|
: map { "$file/$_" } qw(tmp new cur) |
|
442
|
|
|
|
|
|
|
) |
|
443
|
|
|
|
|
|
|
) |
|
444
|
|
|
|
|
|
|
{ |
|
445
|
0
|
|
|
|
|
0
|
$self->_debug(0, $mkdir_error); |
|
446
|
0
|
|
|
|
|
0
|
next; |
|
447
|
|
|
|
|
|
|
} |
|
448
|
|
|
|
|
|
|
|
|
449
|
0
|
|
|
|
|
0
|
my $new_path = "$newdir/$msg_file"; |
|
450
|
0
|
|
|
|
|
0
|
$self->_debug(3, "maildir: hardlinking to $new_path"); |
|
451
|
|
|
|
|
|
|
|
|
452
|
0
|
0
|
|
|
|
0
|
if (link $tmp_path, $new_path) { push @saved_to, $new_path; } |
|
|
0
|
|
|
|
|
0
|
|
|
453
|
|
|
|
|
|
|
else { |
|
454
|
0
|
|
|
|
|
0
|
require Errno; |
|
455
|
0
|
|
|
|
|
0
|
import Errno qw(EXDEV); |
|
456
|
0
|
0
|
|
|
|
0
|
if ($! == &EXDEV) |
|
457
|
|
|
|
|
|
|
{ # Invalid cross-device link, see /usr/**/include/*/errno.h |
|
458
|
0
|
|
|
|
|
0
|
$self->_debug(0, "Couldn't link $tmp_path to $new_path: $!"); |
|
459
|
0
|
|
|
|
|
0
|
$self->_debug(0, "attempting direct maildir delivery to $new_path..."); |
|
460
|
0
|
|
|
|
|
0
|
push @saved_to, $self->_deliver_to_maildir($file); |
|
461
|
0
|
|
|
|
|
0
|
next; |
|
462
|
|
|
|
|
|
|
} else { |
|
463
|
0
|
|
|
|
|
0
|
$self->_debug(0, "Couldn't link $tmp_path to $new_path: $!"); |
|
464
|
|
|
|
|
|
|
} |
|
465
|
|
|
|
|
|
|
} |
|
466
|
|
|
|
|
|
|
} |
|
467
|
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
# unlink the temp file |
|
469
|
0
|
0
|
|
|
|
0
|
unlink $tmp_path or $self->_debug(1, "Couldn't unlink $tmp_path: $!"); |
|
470
|
0
|
|
|
|
|
0
|
return @saved_to; |
|
471
|
|
|
|
|
|
|
} |
|
472
|
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
sub _audit_get_lock { |
|
474
|
1
|
|
|
1
|
|
2
|
my $self = shift; |
|
475
|
1
|
|
|
|
|
2
|
my $FH = shift; |
|
476
|
1
|
|
|
|
|
2
|
my $file = shift; |
|
477
|
1
|
|
|
|
|
4
|
$self->_debug(4, " attempting to lock file $file"); |
|
478
|
|
|
|
|
|
|
|
|
479
|
1
|
|
|
|
|
4
|
for (1 .. 10) { |
|
480
|
1
|
50
|
|
|
|
12
|
if (flock($FH, LOCK_EX)) { |
|
481
|
1
|
|
|
|
|
4
|
$self->_debug(4, " successfully locked file $file"); |
|
482
|
1
|
|
|
|
|
4
|
return; |
|
483
|
|
|
|
|
|
|
} else { |
|
484
|
0
|
0
|
|
|
|
0
|
sleep $_ and next; |
|
485
|
|
|
|
|
|
|
} |
|
486
|
|
|
|
|
|
|
} |
|
487
|
|
|
|
|
|
|
|
|
488
|
0
|
|
|
|
|
0
|
$self->_debug(1, my $errstr = "Couldn't get exclusive lock on $file"); |
|
489
|
0
|
|
|
|
|
0
|
return $errstr; |
|
490
|
|
|
|
|
|
|
} |
|
491
|
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
sub _mkdir_p { |
|
493
|
4
|
|
|
4
|
|
7
|
my $self = shift; |
|
494
|
4
|
50
|
|
|
|
11
|
return unless @_; |
|
495
|
|
|
|
|
|
|
|
|
496
|
4
|
|
|
|
|
6
|
foreach my $path (@_) { |
|
497
|
8
|
100
|
|
|
|
140
|
next if -d $path; |
|
498
|
7
|
|
|
|
|
28
|
$self->_debug(4, "$path doesn't exist, creating."); |
|
499
|
7
|
|
|
|
|
23
|
eval { File::Path::mkpath($path, 0, 0755) }; |
|
|
7
|
|
|
|
|
1193
|
|
|
500
|
7
|
100
|
|
|
|
23
|
return $@ if $@; |
|
501
|
|
|
|
|
|
|
} |
|
502
|
|
|
|
|
|
|
|
|
503
|
3
|
|
|
|
|
12
|
return; |
|
504
|
|
|
|
|
|
|
} |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
1; |
|
507
|
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
__END__ |