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__ |