line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Mail::POP3::Folder::mbox; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our @ISA = qw(Mail::POP3::Folder); |
4
|
|
|
|
|
|
|
|
5
|
4
|
|
|
4
|
|
29
|
use strict; |
|
4
|
|
|
|
|
25
|
|
|
4
|
|
|
|
|
133
|
|
6
|
4
|
|
|
4
|
|
27
|
use IO::File; |
|
4
|
|
|
|
|
11
|
|
|
4
|
|
|
|
|
678
|
|
7
|
4
|
|
|
4
|
|
30
|
use Fcntl ':flock'; |
|
4
|
|
|
|
|
10
|
|
|
4
|
|
|
|
|
6899
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
my $CRLF = "\015\012"; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
sub new { |
12
|
|
|
|
|
|
|
my ( |
13
|
2
|
|
|
2
|
1
|
10
|
$class, |
14
|
|
|
|
|
|
|
$user_name, |
15
|
|
|
|
|
|
|
$password, |
16
|
|
|
|
|
|
|
$user_id, |
17
|
|
|
|
|
|
|
$mailgroup, |
18
|
|
|
|
|
|
|
$spoolfile, |
19
|
|
|
|
|
|
|
$message_start, |
20
|
|
|
|
|
|
|
$message_end, |
21
|
|
|
|
|
|
|
) = @_; |
22
|
2
|
|
|
|
|
4
|
my $self = {}; |
23
|
2
|
|
|
|
|
9
|
bless $self, $class; |
24
|
2
|
|
|
|
|
17
|
$self->{CLIENT_USER_ID} = $user_id; |
25
|
2
|
|
|
|
|
8
|
$self->{MAILGROUP} = $mailgroup; |
26
|
2
|
|
|
|
|
5
|
$self->{SPOOLFILE} = $spoolfile; |
27
|
2
|
|
|
|
|
4
|
$self->{MESSAGE_START} = $message_start; |
28
|
2
|
|
|
|
|
5
|
$self->{MESSAGE_END} = $message_end; |
29
|
2
|
|
|
|
|
7
|
$self->{MESSAGECNT} = 0; |
30
|
2
|
|
|
|
|
4
|
$self->{MSG2OCTETS} = {}; |
31
|
2
|
|
|
|
|
6
|
$self->{MSG2UIDL} = {}; |
32
|
2
|
|
|
|
|
5
|
$self->{MSG2FROMLINE} = {}; |
33
|
2
|
|
|
|
|
2
|
$self->{MSG2TEXT} = {}; |
34
|
2
|
|
|
|
|
4
|
$self->{TOTALOCTETS} = 0; |
35
|
2
|
|
|
|
|
4
|
$self->{DELMESSAGECNT} = 0; |
36
|
2
|
|
|
|
|
3
|
$self->{DELTOTALOCTETS} = 0; |
37
|
2
|
|
|
|
|
6
|
$self; |
38
|
|
|
|
|
|
|
} |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
sub _start_new_message { |
41
|
6
|
|
|
6
|
|
16
|
my ($self, $newmessageno, $fromline) = @_; |
42
|
|
|
|
|
|
|
# Hold the "From ..." line to put back if message is not retrieved |
43
|
6
|
|
|
|
|
35
|
$self->{MSG2FROMLINE}->{$newmessageno} = $fromline; |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub _close_old_message { |
47
|
6
|
|
|
6
|
|
14
|
my ($self, $oldmessageno, $messageuidl, $messageoctets) = @_; |
48
|
6
|
50
|
|
|
|
12
|
return if $oldmessageno <= 0; |
49
|
6
|
|
|
|
|
9
|
$self->{TOTALOCTETS} += $messageoctets; |
50
|
6
|
|
|
|
|
13
|
$self->{MSG2OCTETS}->{$oldmessageno} = $messageoctets; |
51
|
6
|
|
|
|
|
14
|
$self->{MSG2UIDL}->{$oldmessageno} = $messageuidl; |
52
|
|
|
|
|
|
|
} |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# assume that mbox is good, no error-checking at start |
55
|
|
|
|
|
|
|
# not sure the message_end is necessary, is that for MMDF? |
56
|
|
|
|
|
|
|
sub _list_messages { |
57
|
2
|
|
|
2
|
|
3
|
my $self = shift; |
58
|
2
|
50
|
|
|
|
4
|
if (!-s $self->_spoolfile) { |
59
|
|
|
|
|
|
|
# no mail |
60
|
0
|
|
|
|
|
0
|
return; |
61
|
|
|
|
|
|
|
} |
62
|
2
|
|
|
|
|
4
|
my $seen_message_end = 1; # the end of fake "message 0"... |
63
|
2
|
|
|
|
|
3
|
my $messagecnt = 0; |
64
|
2
|
|
|
|
|
4
|
my $messageoctets = 0; |
65
|
2
|
|
|
|
|
3
|
my $messageuidl = ''; |
66
|
2
|
|
|
|
|
5
|
local *MDROP; |
67
|
2
|
|
|
|
|
7
|
open MDROP, $self->_spoolfile; |
68
|
2
|
|
|
|
|
128
|
while () { |
69
|
74
|
|
|
|
|
182
|
$self->_lock_update; |
70
|
74
|
|
|
|
|
324
|
s/\r|\n//g; |
71
|
74
|
100
|
100
|
|
|
236
|
if ($seen_message_end and /$self->{MESSAGE_START}/) { |
72
|
|
|
|
|
|
|
# tick over |
73
|
6
|
|
|
|
|
26
|
$self->_close_old_message( |
74
|
|
|
|
|
|
|
$messagecnt, |
75
|
|
|
|
|
|
|
$messageuidl, |
76
|
|
|
|
|
|
|
$messageoctets, |
77
|
|
|
|
|
|
|
); |
78
|
6
|
|
|
|
|
24
|
$messagecnt++; |
79
|
6
|
|
|
|
|
16
|
$messageuidl = ''; |
80
|
6
|
|
|
|
|
9
|
$messageoctets = 0; |
81
|
6
|
|
|
|
|
17
|
$self->_start_new_message($messagecnt, $_); |
82
|
|
|
|
|
|
|
} else { |
83
|
68
|
|
|
|
|
84
|
$seen_message_end = 0; |
84
|
68
|
100
|
|
|
|
236
|
if (/$self->{MESSAGE_END}/) { |
85
|
8
|
|
|
|
|
11
|
$seen_message_end = 1; |
86
|
|
|
|
|
|
|
} |
87
|
68
|
100
|
66
|
|
|
162
|
if (/^Message-Id:\s*(.+)/ and not $messageuidl) { |
88
|
|
|
|
|
|
|
# only take first Message-ID; cf such a header appearing in body |
89
|
6
|
|
|
|
|
18
|
$messageuidl = $1; |
90
|
|
|
|
|
|
|
} |
91
|
68
|
|
|
|
|
162
|
$self->_push_message($messagecnt, $_); |
92
|
68
|
|
|
|
|
224
|
$messageoctets += length ($_.$CRLF); |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
# catch the last one |
96
|
2
|
|
|
|
|
8
|
$self->_close_old_message($messagecnt, $messageuidl, $messageoctets); |
97
|
2
|
|
|
|
|
11
|
$self->{MESSAGECNT} = $messagecnt; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
sub _spoolfile { |
101
|
10
|
|
|
10
|
|
16
|
my $self = shift; |
102
|
10
|
|
|
|
|
62
|
$self->{SPOOLFILE}; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
sub lock_acquire { |
106
|
2
|
|
|
2
|
1
|
10
|
my $self = shift; |
107
|
2
|
|
|
|
|
18
|
my $lockfile = $self->_lock_filename; |
108
|
2
|
50
|
|
|
|
93
|
return if -f $lockfile; |
109
|
2
|
|
|
|
|
11
|
$self->{LINE} = 0; |
110
|
2
|
50
|
|
|
|
15
|
$self->{LOCK_FH} = IO::File->new( |
111
|
|
|
|
|
|
|
">$lockfile" |
112
|
|
|
|
|
|
|
) or die "open >$lockfile: $!\n"; |
113
|
2
|
50
|
|
|
|
184
|
unless (flock $self->{LOCK_FH}, LOCK_EX|LOCK_NB) { |
114
|
0
|
|
|
|
|
0
|
unlink $lockfile; |
115
|
0
|
|
|
|
|
0
|
return; |
116
|
|
|
|
|
|
|
} |
117
|
2
|
|
|
|
|
19
|
chmod 0600, $lockfile; |
118
|
2
|
|
|
|
|
21
|
chown $self->{CLIENT_USER_ID}, $self->{MAILGROUP}, $lockfile; |
119
|
2
|
|
|
|
|
9
|
my $oldfh = select $self->{LOCK_FH}; |
120
|
2
|
|
|
|
|
7
|
$| = 1; |
121
|
2
|
|
|
|
|
4
|
select $oldfh; |
122
|
2
|
|
|
|
|
19
|
$self->_lock_refresh; |
123
|
|
|
|
|
|
|
# stat the file to get its size, this is checked before closing |
124
|
|
|
|
|
|
|
# the mailbox. |
125
|
|
|
|
|
|
|
# If the size has changed the lock may have been compromised, so a |
126
|
|
|
|
|
|
|
# backup is then made. |
127
|
2
|
|
|
|
|
15
|
my @filestat = stat $self->_spoolfile; |
128
|
2
|
|
|
|
|
6
|
$self->{MAILBOX_TIMESTAMP_OPEN} = $filestat[9]; |
129
|
|
|
|
|
|
|
# set effective UID to user for the rest of the session; |
130
|
2
|
|
|
|
|
7
|
$> = $self->{CLIENT_USER_ID}; |
131
|
2
|
|
|
|
|
14
|
$self->_list_messages; |
132
|
2
|
50
|
|
|
|
8
|
die unless $self->{LOCK_FH}; |
133
|
2
|
|
|
|
|
10
|
1; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
sub lock_release { |
137
|
2
|
|
|
2
|
1
|
6
|
my $self = shift; |
138
|
2
|
|
|
|
|
17
|
close $self->{LOCK_FH}; |
139
|
2
|
|
|
|
|
17
|
$> = 0; |
140
|
2
|
|
|
|
|
11
|
unlink $self->_lock_filename; |
141
|
2
|
|
|
|
|
108
|
$> = $self->{CLIENT_USER_ID}; |
142
|
|
|
|
|
|
|
} |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
sub _lock_filename { |
145
|
4
|
|
|
4
|
|
11
|
my ($self) = @_; |
146
|
4
|
|
|
|
|
17
|
"$self->{SPOOLFILE}.lock"; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
sub _lock_refresh { |
150
|
|
|
|
|
|
|
# This is to update the m time on the .lock mbox lock file. |
151
|
|
|
|
|
|
|
# It may seem paranoid but I have seen lock files removed by impatient |
152
|
|
|
|
|
|
|
# MDA's, so the file is written-to, unbuffered, as often as is |
153
|
|
|
|
|
|
|
# practicable. |
154
|
25
|
|
|
25
|
|
40
|
my $self = shift; |
155
|
25
|
|
|
|
|
133
|
$> = 0; |
156
|
25
|
50
|
|
|
|
71
|
die unless $self->{LOCK_FH}; |
157
|
25
|
|
|
|
|
79
|
seek $self->{LOCK_FH}, 0, SEEK_SET; |
158
|
25
|
|
|
|
|
82
|
$self->{LOCK_FH}->print("\0"); |
159
|
25
|
|
|
|
|
354
|
$> = $self->{CLIENT_USER_ID}; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
sub _lock_update { |
163
|
174
|
|
|
174
|
|
256
|
my $self = shift; |
164
|
174
|
50
|
|
|
|
424
|
if (++$self->{LINE} == 1000) { |
165
|
0
|
|
|
|
|
0
|
$self->_lock_refresh; |
166
|
0
|
|
|
|
|
0
|
$self->{LINE} = 0; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub _push_message_line { |
171
|
0
|
|
|
0
|
|
0
|
my ($self, $messagecnt, $data) = @_; |
172
|
0
|
|
|
|
|
0
|
push @{ $self->{MSG2TEXT}->{$messagecnt} }, "$data$CRLF"; |
|
0
|
|
|
|
|
0
|
|
173
|
|
|
|
|
|
|
} |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub _set_message_line { |
176
|
0
|
|
|
0
|
|
0
|
my ($self, $messagecnt, $lineno, $data) = @_; |
177
|
0
|
|
|
|
|
0
|
$self->{MSG2TEXT}->{$messagecnt}->[$lineno] = $data; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub _get_message_line { |
181
|
0
|
|
|
0
|
|
0
|
my ($self, $messagecnt, $lineno) = @_; |
182
|
0
|
|
|
|
|
0
|
$self->{MSG2TEXT}->{$messagecnt}->[$lineno]; |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
sub _unshift_message_line { |
186
|
0
|
|
|
0
|
|
0
|
my ($self, $messagecnt, $data) = @_; |
187
|
0
|
|
|
|
|
0
|
unshift @{ $self->{MSG2TEXT}->{$messagecnt} }, "$data$CRLF"; |
|
0
|
|
|
|
|
0
|
|
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
sub _list_message_line { |
191
|
0
|
|
|
0
|
|
0
|
my ($self, $messagecnt) = @_; |
192
|
0
|
|
|
|
|
0
|
@{ $self->{MSG2TEXT}->{$messagecnt} }; |
|
0
|
|
|
|
|
0
|
|
193
|
|
|
|
|
|
|
} |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
sub octets { |
196
|
4
|
|
|
4
|
1
|
82
|
my ($self, $message) = @_; |
197
|
4
|
100
|
|
|
|
14
|
if (defined $message) { |
198
|
2
|
|
|
|
|
13
|
$self->{MSG2OCTETS}->{$message}; |
199
|
|
|
|
|
|
|
} else { |
200
|
2
|
|
|
|
|
7
|
$self->{TOTALOCTETS} - $self->{DELTOTALOCTETS}; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
} |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
sub messages { |
205
|
2
|
|
|
2
|
1
|
90
|
my ($self) = @_; |
206
|
2
|
|
|
|
|
6
|
$self->{MESSAGECNT} - $self->{DELMESSAGECNT}; |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# Build message arrays or write the next line to disk |
210
|
|
|
|
|
|
|
sub _push_message { |
211
|
0
|
|
|
0
|
|
0
|
my ($self, $messagecnt, $data) = @_; |
212
|
0
|
|
|
|
|
0
|
$self->_push_message_line($messagecnt, $data); |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# $message starts at 1 |
216
|
|
|
|
|
|
|
# returns number of bytes |
217
|
|
|
|
|
|
|
sub top { |
218
|
0
|
|
|
0
|
1
|
0
|
my ($self, $message, $output_fh, $body_lines) = @_; |
219
|
0
|
|
|
|
|
0
|
my $top_bytes = 0; |
220
|
0
|
|
|
|
|
0
|
my $rows = (scalar $self->_list_message_line($message)) -1; |
221
|
0
|
0
|
|
|
|
0
|
$body_lines = $rows if $body_lines > $rows; |
222
|
0
|
|
|
|
|
0
|
my $cnt = 0; |
223
|
0
|
|
|
|
|
0
|
for my $line ($self->_list_message_line($message)) { |
224
|
0
|
|
|
|
|
0
|
$top_bytes += length($line); |
225
|
0
|
|
|
|
|
0
|
++$cnt; |
226
|
0
|
|
|
|
|
0
|
$self->_lock_update; |
227
|
0
|
|
|
|
|
0
|
$output_fh->print($line); |
228
|
0
|
0
|
|
|
|
0
|
last if $line =~ /^\s*$/; |
229
|
|
|
|
|
|
|
} |
230
|
0
|
|
|
|
|
0
|
for my $lineno ($cnt..(($cnt + $body_lines) -1)) { |
231
|
0
|
|
|
|
|
0
|
$self->_lock_update; |
232
|
0
|
|
|
|
|
0
|
my $line = $self->_get_message_line($message, $lineno); |
233
|
0
|
|
|
|
|
0
|
$top_bytes += length($line); |
234
|
0
|
|
|
|
|
0
|
$line =~ s/^\./\.\./o; |
235
|
0
|
|
|
|
|
0
|
$output_fh->print($line); |
236
|
|
|
|
|
|
|
} |
237
|
0
|
|
|
|
|
0
|
$top_bytes; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
sub flush_delete { |
241
|
2
|
|
|
2
|
1
|
5
|
my ($self) = @_; |
242
|
2
|
|
|
|
|
9
|
my $spool_mtime = (stat $self->_spoolfile)[9]; |
243
|
2
|
50
|
|
|
|
13
|
if ($spool_mtime != $self->{MAILBOX_TIMESTAMP_OPEN}) { |
244
|
0
|
|
|
|
|
0
|
die "spool lock error\n"; |
245
|
|
|
|
|
|
|
} |
246
|
2
|
|
|
|
|
7
|
my $spoolfile = $self->_spoolfile; |
247
|
2
|
50
|
|
|
|
12
|
open MDROP, '>' . $spoolfile or die "open >$spoolfile: $!\n"; |
248
|
2
|
|
|
|
|
171
|
foreach my $cnt (1..$self->{MESSAGECNT}) { |
249
|
6
|
100
|
|
|
|
18
|
if (!$self->is_deleted($cnt)) { |
250
|
4
|
|
|
|
|
41
|
print MDROP "$self->{MSG2FROMLINE}->{$cnt}\n"; |
251
|
4
|
|
|
|
|
19
|
$self->retrieve($cnt, \*MDROP, 1); |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
} |
254
|
2
|
|
|
|
|
1739
|
close MDROP; |
255
|
|
|
|
|
|
|
} |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
sub retrieve { |
258
|
0
|
|
|
0
|
1
|
0
|
my ($self, $message, $output_fh, $mbox_destined) = @_; |
259
|
0
|
|
|
|
|
0
|
for my $line ($self->_list_message_line($message)) { |
260
|
0
|
0
|
|
|
|
0
|
$line =~ s/^\./\.\./o unless $mbox_destined; |
261
|
0
|
0
|
|
|
|
0
|
$line =~ s/\r$// if $mbox_destined; |
262
|
0
|
|
|
|
|
0
|
$self->_lock_update; |
263
|
0
|
|
|
|
|
0
|
$output_fh->print($line); |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
sub uidl { |
268
|
1
|
|
|
1
|
1
|
3
|
my ($self, $message) = @_; |
269
|
1
|
|
|
|
|
7
|
$self->{MSG2UIDL}->{$message}; |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
sub uidl_list { |
273
|
4
|
|
|
4
|
1
|
751
|
my ($self, $output_fh) = @_; |
274
|
4
|
|
|
|
|
17
|
for (1..$self->{MESSAGECNT}) { |
275
|
12
|
|
|
|
|
75
|
$self->_lock_refresh; |
276
|
12
|
100
|
|
|
|
37
|
if (!$self->is_deleted($_)) { |
277
|
10
|
|
|
|
|
44
|
$output_fh->print("$_ $self->{MSG2UIDL}->{$_}$CRLF"); |
278
|
|
|
|
|
|
|
} |
279
|
|
|
|
|
|
|
} |
280
|
4
|
|
|
|
|
45
|
$output_fh->print(".$CRLF"); |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
sub is_deleted { |
284
|
27
|
|
|
27
|
1
|
62
|
my ($self, $message) = @_; |
285
|
27
|
|
|
|
|
107
|
$self->{DELETE}->{$message}; |
286
|
|
|
|
|
|
|
} |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
sub delete { |
289
|
3
|
|
|
3
|
1
|
33
|
my ($self, $message) = @_; |
290
|
3
|
|
|
|
|
11
|
$self->_lock_refresh; |
291
|
3
|
|
|
|
|
8
|
$self->{DELETE}->{$message} = 1; |
292
|
3
|
|
|
|
|
7
|
$self->{DELMESSAGECNT} += 1; |
293
|
3
|
|
|
|
|
12
|
$self->{DELTOTALOCTETS} += $self->{MSG2OCTETS}->{$message}; |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
sub is_valid { |
297
|
7
|
|
|
7
|
1
|
26
|
my ($self, $msg) = @_; |
298
|
7
|
|
|
|
|
23
|
$self->_lock_refresh; |
299
|
7
|
50
|
33
|
|
|
60
|
$msg > 0 and $msg <= $self->{MESSAGECNT} and !$self->is_deleted($msg); |
300
|
|
|
|
|
|
|
} |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
sub reset { |
303
|
1
|
|
|
1
|
1
|
4
|
my $self = shift; |
304
|
1
|
|
|
|
|
7
|
$self->_lock_refresh; |
305
|
1
|
|
|
|
|
6
|
$self->{DELETE} = {}; |
306
|
1
|
|
|
|
|
5
|
$self->{DELMESSAGECNT} = 0; |
307
|
1
|
|
|
|
|
4
|
$self->{DELTOTALOCTETS} = 0; |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
1; |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
__END__ |