line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
#****************************************************************************** |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# Description: POP3Client module - acts as interface to POP3 server |
4
|
|
|
|
|
|
|
# Author: Sean Dowd |
5
|
|
|
|
|
|
|
# |
6
|
|
|
|
|
|
|
# Copyright (c) 1999-2022 Sean Dowd. All rights reserved. |
7
|
|
|
|
|
|
|
# This module is free software; you can redistribute it and/or modify |
8
|
|
|
|
|
|
|
# it under the same terms as Perl itself. |
9
|
|
|
|
|
|
|
# |
10
|
|
|
|
|
|
|
#****************************************************************************** |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
package Mail::POP3Client; |
13
|
|
|
|
|
|
|
|
14
|
2
|
|
|
2
|
|
57626
|
use strict; |
|
2
|
|
|
|
|
16
|
|
|
2
|
|
|
|
|
57
|
|
15
|
2
|
|
|
2
|
|
8
|
use warnings; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
53
|
|
16
|
2
|
|
|
2
|
|
7
|
use Carp; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
161
|
|
17
|
2
|
|
|
2
|
|
862
|
use IO::Socket qw(SOCK_STREAM); |
|
2
|
|
|
|
|
42602
|
|
|
2
|
|
|
|
|
9
|
|
18
|
|
|
|
|
|
|
|
19
|
2
|
|
|
2
|
|
507
|
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
11551
|
|
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
require Exporter; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
@ISA = qw(Exporter); |
24
|
|
|
|
|
|
|
# Items to export into callers namespace by default. Note: do not export |
25
|
|
|
|
|
|
|
# names by default without a very good reason. Use EXPORT_OK instead. |
26
|
|
|
|
|
|
|
# Do not simply export all your public functions/methods/constants. |
27
|
|
|
|
|
|
|
@EXPORT = qw(); |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
$VERSION = '2.21'; |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
# Preloaded methods go here. |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
#****************************************************************************** |
35
|
|
|
|
|
|
|
#* constructor |
36
|
|
|
|
|
|
|
#* new Mail::POP3Client( USER => user, |
37
|
|
|
|
|
|
|
#* PASSWORD => pass, |
38
|
|
|
|
|
|
|
#* HOST => host, |
39
|
|
|
|
|
|
|
#* AUTH_MODE => [BEST|APOP|CRAM-MD5|PASS], |
40
|
|
|
|
|
|
|
#* TIMEOUT => 30, |
41
|
|
|
|
|
|
|
#* LOCALADDR => 'xxx.xxx.xxx.xxx[:xx]', |
42
|
|
|
|
|
|
|
#* DEBUG => 1 ); |
43
|
|
|
|
|
|
|
#* OR (deprecated) |
44
|
|
|
|
|
|
|
#* new Mail::POP3Client( user, pass, host [, port, debug, auth_mode, local_addr]) |
45
|
|
|
|
|
|
|
#****************************************************************************** |
46
|
|
|
|
|
|
|
sub new |
47
|
|
|
|
|
|
|
{ |
48
|
0
|
|
|
0
|
1
|
0
|
my $classname = shift; |
49
|
0
|
|
|
|
|
0
|
my $self = { |
50
|
|
|
|
|
|
|
DEBUG => 0, |
51
|
|
|
|
|
|
|
SERVER => "pop3", |
52
|
|
|
|
|
|
|
PORT => 110, |
53
|
|
|
|
|
|
|
COUNT => -1, |
54
|
|
|
|
|
|
|
SIZE => -1, |
55
|
|
|
|
|
|
|
ADDR => "", |
56
|
|
|
|
|
|
|
STATE => 'DEAD', |
57
|
|
|
|
|
|
|
MESG => 'OK', |
58
|
|
|
|
|
|
|
BANNER => '', |
59
|
|
|
|
|
|
|
MESG_ID => '', |
60
|
|
|
|
|
|
|
AUTH_MODE => 'BEST', |
61
|
|
|
|
|
|
|
EOL => "\015\012", |
62
|
|
|
|
|
|
|
TIMEOUT => 60, |
63
|
|
|
|
|
|
|
STRIPCR => 0, |
64
|
|
|
|
|
|
|
LOCALADDR => undef, |
65
|
|
|
|
|
|
|
SOCKET => undef, |
66
|
|
|
|
|
|
|
USESSL => 0, |
67
|
|
|
|
|
|
|
}; |
68
|
0
|
|
|
|
|
0
|
$self->{tranlog} = (); |
69
|
0
|
0
|
|
|
|
0
|
$^O =~ /MacOS/i && ($self->{STRIPCR} = 1); |
70
|
0
|
|
|
|
|
0
|
bless( $self, $classname ); |
71
|
0
|
|
|
|
|
0
|
$self->_init( @_ ); |
72
|
|
|
|
|
|
|
|
73
|
0
|
0
|
0
|
|
|
0
|
if ( defined($self->User()) && defined($self->Pass()) ) |
74
|
|
|
|
|
|
|
{ |
75
|
0
|
|
|
|
|
0
|
$self->Connect(); |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
0
|
|
|
|
|
0
|
return $self; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
#****************************************************************************** |
84
|
|
|
|
|
|
|
#* initialize - check for old-style params |
85
|
|
|
|
|
|
|
#****************************************************************************** |
86
|
|
|
|
|
|
|
sub _init { |
87
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# if it looks like a hash |
90
|
0
|
0
|
0
|
|
|
0
|
if ( @_ && (scalar( @_ ) % 2 == 0) ) |
91
|
|
|
|
|
|
|
{ |
92
|
|
|
|
|
|
|
# ... and smells like a hash... |
93
|
0
|
|
|
|
|
0
|
my %hashargs = @_; |
94
|
0
|
0
|
0
|
|
|
0
|
if ( ( defined($hashargs{USER}) && |
|
|
|
0
|
|
|
|
|
95
|
|
|
|
|
|
|
defined($hashargs{PASSWORD}) ) || |
96
|
|
|
|
|
|
|
defined($hashargs{HOST}) |
97
|
|
|
|
|
|
|
) |
98
|
|
|
|
|
|
|
{ |
99
|
|
|
|
|
|
|
# ... then it must be a hash! Push all values into my internal hash. |
100
|
0
|
|
|
|
|
0
|
foreach my $key ( keys %hashargs ) |
101
|
|
|
|
|
|
|
{ |
102
|
0
|
|
|
|
|
0
|
$self->{$key} = $hashargs{$key}; |
103
|
|
|
|
|
|
|
} |
104
|
|
|
|
|
|
|
} |
105
|
0
|
|
|
|
|
0
|
else {$self->_initOldStyle( @_ );} |
106
|
|
|
|
|
|
|
} |
107
|
0
|
|
|
|
|
0
|
else {$self->_initOldStyle( @_ );} |
108
|
|
|
|
|
|
|
} |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
#****************************************************************************** |
111
|
|
|
|
|
|
|
#* initialize using the old positional parameter style new - deprecated |
112
|
|
|
|
|
|
|
#****************************************************************************** |
113
|
|
|
|
|
|
|
sub _initOldStyle { |
114
|
0
|
|
|
0
|
|
0
|
my $self = shift; |
115
|
0
|
|
|
|
|
0
|
$self->User( shift ); |
116
|
0
|
|
|
|
|
0
|
$self->Pass( shift ); |
117
|
0
|
|
|
|
|
0
|
my $host = shift; |
118
|
0
|
0
|
|
|
|
0
|
$host && $self->Host( $host ); |
119
|
0
|
|
|
|
|
0
|
my $port = shift; |
120
|
0
|
0
|
|
|
|
0
|
$port && $self->Port( $port ); |
121
|
0
|
|
|
|
|
0
|
my $debug = shift; |
122
|
0
|
0
|
|
|
|
0
|
$debug && $self->Debug( $debug ); |
123
|
0
|
|
|
|
|
0
|
my $auth_mode = shift; |
124
|
0
|
0
|
|
|
|
0
|
$auth_mode && ($self->{AUTH_MODE} = $auth_mode); |
125
|
0
|
|
|
|
|
0
|
my $localaddr = shift; |
126
|
0
|
0
|
|
|
|
0
|
$localaddr && ($self->{LOCALADDR} = $localaddr); |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
#****************************************************************************** |
130
|
|
|
|
|
|
|
#* What version are we? |
131
|
|
|
|
|
|
|
#****************************************************************************** |
132
|
|
|
|
|
|
|
sub Version { |
133
|
1
|
|
|
1
|
1
|
68
|
return $VERSION; |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
#****************************************************************************** |
138
|
|
|
|
|
|
|
#* Is the socket alive? |
139
|
|
|
|
|
|
|
#****************************************************************************** |
140
|
|
|
|
|
|
|
sub Alive |
141
|
|
|
|
|
|
|
{ |
142
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
143
|
0
|
|
|
|
|
|
$me->State =~ /^AUTHORIZATION$|^TRANSACTION$/i; |
144
|
|
|
|
|
|
|
} # end Alive |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
#****************************************************************************** |
148
|
|
|
|
|
|
|
#* What's the frequency Kenneth? |
149
|
|
|
|
|
|
|
#****************************************************************************** |
150
|
|
|
|
|
|
|
sub State |
151
|
|
|
|
|
|
|
{ |
152
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
153
|
0
|
0
|
|
|
|
|
my $stat = shift or return $me->{STATE}; |
154
|
0
|
|
|
|
|
|
$me->{STATE} = $stat; |
155
|
|
|
|
|
|
|
} # end Stat |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
#****************************************************************************** |
159
|
|
|
|
|
|
|
#* Got anything to say? |
160
|
|
|
|
|
|
|
#****************************************************************************** |
161
|
|
|
|
|
|
|
sub Message |
162
|
|
|
|
|
|
|
{ |
163
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
164
|
0
|
0
|
|
|
|
|
my $msg = shift or return $me->{MESG}; |
165
|
0
|
|
|
|
|
|
$me->{MESG} = $msg; |
166
|
|
|
|
|
|
|
} # end Message |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
#****************************************************************************** |
170
|
|
|
|
|
|
|
#* set/query debugging |
171
|
|
|
|
|
|
|
#****************************************************************************** |
172
|
|
|
|
|
|
|
sub Debug |
173
|
|
|
|
|
|
|
{ |
174
|
0
|
|
|
0
|
0
|
|
my $me = shift; |
175
|
0
|
0
|
|
|
|
|
my $debug = shift or return $me->{DEBUG}; |
176
|
0
|
|
|
|
|
|
$me->{DEBUG} = $debug; |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
} # end Debug |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
#****************************************************************************** |
182
|
|
|
|
|
|
|
#* set/query the port number |
183
|
|
|
|
|
|
|
#****************************************************************************** |
184
|
|
|
|
|
|
|
sub Port |
185
|
|
|
|
|
|
|
{ |
186
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
187
|
0
|
0
|
|
|
|
|
my $port = shift or return $me->{PORT}; |
188
|
|
|
|
|
|
|
|
189
|
0
|
|
|
|
|
|
$me->{PORT} = $port; |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
} # end port |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
#****************************************************************************** |
195
|
|
|
|
|
|
|
#* set the host |
196
|
|
|
|
|
|
|
#****************************************************************************** |
197
|
|
|
|
|
|
|
sub Host |
198
|
|
|
|
|
|
|
{ |
199
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
200
|
0
|
0
|
|
|
|
|
my $host = shift or return $me->{HOST}; |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
# $me->{INTERNET_ADDR} = inet_aton( $host ) or |
203
|
|
|
|
|
|
|
# $me->Message( "Could not inet_aton: $host, $!") and return; |
204
|
0
|
|
|
|
|
|
$me->{HOST} = $host; |
205
|
|
|
|
|
|
|
} # end host |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
#****************************************************************************** |
208
|
|
|
|
|
|
|
#* set the local address |
209
|
|
|
|
|
|
|
#****************************************************************************** |
210
|
|
|
|
|
|
|
sub LocalAddr |
211
|
|
|
|
|
|
|
{ |
212
|
0
|
|
|
0
|
0
|
|
my $me = shift; |
213
|
0
|
0
|
|
|
|
|
my $addr = shift or return $me->{LOCALADDR}; |
214
|
|
|
|
|
|
|
|
215
|
0
|
|
|
|
|
|
$me->{LOCALADDR} = $addr; |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
#****************************************************************************** |
220
|
|
|
|
|
|
|
#* query the socket to use as a file handle - allows you to set the |
221
|
|
|
|
|
|
|
#* socket too to allow SSL (thanks to Jamie LeTual) |
222
|
|
|
|
|
|
|
#****************************************************************************** |
223
|
|
|
|
|
|
|
sub Socket { |
224
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
225
|
0
|
0
|
|
|
|
|
my $socket = shift or return $me->{'SOCKET'}; |
226
|
0
|
|
|
|
|
|
$me->{'SOCKET'} = $socket; |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
sub AuthMode { |
230
|
0
|
|
|
0
|
0
|
|
my $me = shift; |
231
|
0
|
|
|
|
|
|
my $mode = shift; |
232
|
0
|
0
|
|
|
|
|
return $me->{'AUTH_MODE'} unless $mode; |
233
|
0
|
|
|
|
|
|
$me->{'AUTH_MODE'} = $mode; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
#****************************************************************************** |
237
|
|
|
|
|
|
|
#* set/query the USER |
238
|
|
|
|
|
|
|
#****************************************************************************** |
239
|
|
|
|
|
|
|
sub User |
240
|
|
|
|
|
|
|
{ |
241
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
242
|
0
|
0
|
|
|
|
|
my $user = shift or return $me->{USER}; |
243
|
0
|
|
|
|
|
|
$me->{USER} = $user; |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
} # end User |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
#****************************************************************************** |
249
|
|
|
|
|
|
|
#* set/query the password |
250
|
|
|
|
|
|
|
#****************************************************************************** |
251
|
|
|
|
|
|
|
sub Pass |
252
|
|
|
|
|
|
|
{ |
253
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
254
|
0
|
0
|
|
|
|
|
my $pass = shift or return $me->{PASSWORD}; |
255
|
0
|
|
|
|
|
|
$me->{PASSWORD} = $pass; |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
} # end Pass |
258
|
|
|
|
|
|
|
|
259
|
0
|
|
|
0
|
0
|
|
sub Password { Pass(@_); } |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
#****************************************************************************** |
262
|
|
|
|
|
|
|
#* |
263
|
|
|
|
|
|
|
#****************************************************************************** |
264
|
|
|
|
|
|
|
sub Count |
265
|
|
|
|
|
|
|
{ |
266
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
267
|
0
|
|
|
|
|
|
my $c = shift; |
268
|
0
|
0
|
0
|
|
|
|
if (defined $c and length($c) > 0) { |
269
|
0
|
|
|
|
|
|
$me->{COUNT} = $c; |
270
|
|
|
|
|
|
|
} else { |
271
|
0
|
|
|
|
|
|
return $me->{COUNT}; |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
} # end Count |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
#****************************************************************************** |
278
|
|
|
|
|
|
|
#* set/query the size of the mailbox |
279
|
|
|
|
|
|
|
#****************************************************************************** |
280
|
|
|
|
|
|
|
sub Size |
281
|
|
|
|
|
|
|
{ |
282
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
283
|
0
|
|
|
|
|
|
my $c = shift; |
284
|
0
|
0
|
0
|
|
|
|
if (defined $c and length($c) > 0) { |
285
|
0
|
|
|
|
|
|
$me->{SIZE} = $c; |
286
|
|
|
|
|
|
|
} else { |
287
|
0
|
|
|
|
|
|
return $me->{SIZE}; |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
} # end Size |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
#****************************************************************************** |
294
|
|
|
|
|
|
|
#* |
295
|
|
|
|
|
|
|
#****************************************************************************** |
296
|
|
|
|
|
|
|
sub EOL { |
297
|
0
|
|
|
0
|
0
|
|
my $me = shift; |
298
|
0
|
|
|
|
|
|
return $me->{'EOL'}; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
#****************************************************************************** |
303
|
|
|
|
|
|
|
#* |
304
|
|
|
|
|
|
|
#****************************************************************************** |
305
|
|
|
|
|
|
|
sub Close |
306
|
|
|
|
|
|
|
{ |
307
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
# only send the QUIT message is the socket is still connected. Some |
310
|
|
|
|
|
|
|
# POP3 servers close the socket after a failed authentication. It |
311
|
|
|
|
|
|
|
# is unclear whether the RFC allows this or not, so we'll attempt to |
312
|
|
|
|
|
|
|
# check the condition of the socket before sending data here. |
313
|
0
|
0
|
0
|
|
|
|
if ($me->Alive() && $me->Socket() && $me->Socket()->connected() ) { |
|
|
|
0
|
|
|
|
|
314
|
0
|
|
|
|
|
|
$me->_sockprint( "QUIT", $me->EOL ); |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
# from Patrick Bourdon - need this because some servers do not |
317
|
|
|
|
|
|
|
# delete in all cases. RFC says server can respond (in UPDATE |
318
|
|
|
|
|
|
|
# state only, otherwise always OK). |
319
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
320
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
321
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for QUIT"); |
322
|
|
|
|
|
|
|
# XXX: Should add the following? |
323
|
|
|
|
|
|
|
#$me->State('DEAD'); |
324
|
0
|
|
|
|
|
|
undef $me->{SOCKET}; |
325
|
0
|
|
|
|
|
|
return 0; |
326
|
|
|
|
|
|
|
} |
327
|
0
|
|
|
|
|
|
$me->Message( $line ); |
328
|
0
|
0
|
0
|
|
|
|
close( $me->Socket() ) or $me->Message("close failed: $!") and do { |
329
|
0
|
|
|
|
|
|
undef $me->{SOCKET}; |
330
|
0
|
|
|
|
|
|
return 0; |
331
|
|
|
|
|
|
|
}; |
332
|
0
|
|
|
|
|
|
$me->State('DEAD'); |
333
|
0
|
|
|
|
|
|
undef $me->{SOCKET}; |
334
|
0
|
0
|
|
|
|
|
$line =~ /^\+OK/i || return 0; |
335
|
|
|
|
|
|
|
} |
336
|
0
|
|
|
|
|
|
1; |
337
|
|
|
|
|
|
|
} # end Close |
338
|
|
|
|
|
|
|
|
339
|
0
|
|
|
0
|
0
|
|
sub close { Close(@_); } |
340
|
0
|
|
|
0
|
0
|
|
sub logout { Close(@_); } |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
#****************************************************************************** |
343
|
|
|
|
|
|
|
#* |
344
|
|
|
|
|
|
|
#****************************************************************************** |
345
|
|
|
|
|
|
|
sub DESTROY |
346
|
|
|
|
|
|
|
{ |
347
|
0
|
|
|
0
|
|
|
my $me = shift; |
348
|
0
|
|
|
|
|
|
$me->Close; |
349
|
|
|
|
|
|
|
} # end DESTROY |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
#****************************************************************************** |
353
|
|
|
|
|
|
|
#* Connect to the specified POP server |
354
|
|
|
|
|
|
|
#****************************************************************************** |
355
|
|
|
|
|
|
|
sub Connect |
356
|
|
|
|
|
|
|
{ |
357
|
0
|
|
|
0
|
1
|
|
my ($me, $host, $port) = @_; |
358
|
|
|
|
|
|
|
|
359
|
0
|
0
|
|
|
|
|
$host and $me->Host($host); |
360
|
0
|
0
|
|
|
|
|
$port and $me->Port($port); |
361
|
|
|
|
|
|
|
|
362
|
0
|
|
|
|
|
|
$me->Close(); |
363
|
|
|
|
|
|
|
|
364
|
0
|
|
|
|
|
|
my $s = $me->{SOCKET}; |
365
|
0
|
0
|
|
|
|
|
$s || do { |
366
|
0
|
0
|
|
|
|
|
if ( $me->{USESSL} ) { |
367
|
0
|
0
|
|
|
|
|
if ( $me->Port() == 110 ) { $me->Port( 995 ); } |
|
0
|
|
|
|
|
|
|
368
|
0
|
|
|
|
|
|
eval { |
369
|
0
|
|
|
|
|
|
require IO::Socket::SSL; |
370
|
|
|
|
|
|
|
}; |
371
|
0
|
0
|
0
|
|
|
|
$@ and $me->Message("Could not load IO::Socket::SSL: $@") and return 0; |
372
|
|
|
|
|
|
|
$s = IO::Socket::SSL->new( PeerAddr => $me->Host(), |
373
|
|
|
|
|
|
|
PeerPort => $me->Port(), |
374
|
|
|
|
|
|
|
Proto => "tcp", |
375
|
|
|
|
|
|
|
Type => SOCK_STREAM, |
376
|
|
|
|
|
|
|
LocalAddr => $me->LocalAddr(), |
377
|
|
|
|
|
|
|
Timeout => $me->{TIMEOUT} ) |
378
|
0
|
0
|
0
|
|
|
|
or $me->Message( "could not connect SSL socket [$me->{HOST}, $me->{PORT}]: $!" ) |
379
|
|
|
|
|
|
|
and return 0; |
380
|
0
|
|
|
|
|
|
$me->{SOCKET} = $s; |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
} else { |
383
|
|
|
|
|
|
|
$s = IO::Socket::INET->new( PeerAddr => $me->Host(), |
384
|
|
|
|
|
|
|
PeerPort => $me->Port(), |
385
|
|
|
|
|
|
|
Proto => "tcp", |
386
|
|
|
|
|
|
|
Type => SOCK_STREAM, |
387
|
|
|
|
|
|
|
LocalAddr => $me->LocalAddr(), |
388
|
|
|
|
|
|
|
Timeout => $me->{TIMEOUT} ) |
389
|
0
|
0
|
0
|
|
|
|
or |
390
|
|
|
|
|
|
|
$me->Message( "could not connect socket [$me->{HOST}, $me->{PORT}]: $!" ) |
391
|
|
|
|
|
|
|
and |
392
|
|
|
|
|
|
|
return 0; |
393
|
0
|
|
|
|
|
|
$me->{SOCKET} = $s; |
394
|
|
|
|
|
|
|
} |
395
|
|
|
|
|
|
|
}; |
396
|
|
|
|
|
|
|
|
397
|
0
|
|
|
|
|
|
$s->autoflush( 1 ); |
398
|
|
|
|
|
|
|
|
399
|
0
|
0
|
0
|
|
|
|
defined(my $msg = $me->_sockread()) or $me->Message("Could not read") and return 0; |
400
|
0
|
|
|
|
|
|
chomp $msg; |
401
|
0
|
|
|
|
|
|
$me->{BANNER}= $msg; |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
# add check for servers that return -ERR on connect (not in RFC1939) |
404
|
0
|
|
|
|
|
|
$me->Message($msg); |
405
|
0
|
0
|
|
|
|
|
$msg =~ /^\+OK/i || return 0; |
406
|
|
|
|
|
|
|
|
407
|
0
|
|
|
|
|
|
my $atom = qr([-_\w!#$%&'*+/=?^`{|}~]+); |
408
|
0
|
0
|
|
|
|
|
$me->{MESG_ID}= $1 if ($msg =~/(<$atom(?:\.$atom)*\@$atom(?:\.$atom)*>)/o); |
409
|
0
|
|
|
|
|
|
$me->Message($msg); |
410
|
|
|
|
|
|
|
|
411
|
0
|
|
|
|
|
|
$me->State('AUTHORIZATION'); |
412
|
0
|
0
|
0
|
|
|
|
defined($me->User()) and defined($me->Pass()) and $me->Login(); |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
} # end Connect |
415
|
|
|
|
|
|
|
|
416
|
0
|
|
|
0
|
0
|
|
sub connect { Connect(@_); } |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
#****************************************************************************** |
419
|
|
|
|
|
|
|
#* login to the POP server. If the AUTH_MODE is set to BEST, and the server |
420
|
|
|
|
|
|
|
#* appears to support APOP, it will try APOP, if that fails, then it will try |
421
|
|
|
|
|
|
|
#* SASL CRAM-MD5 if the server appears to support it, and finally PASS. |
422
|
|
|
|
|
|
|
#* If the AUTH_MODE is set to APOP, and the server appears to support APOP, it |
423
|
|
|
|
|
|
|
#* will use APOP or it will fail to log in. Likewise, for AUTH_MODE CRAM-MD5, |
424
|
|
|
|
|
|
|
#* no PASS-fallback is made. Otherwise password is sent in clear text. |
425
|
|
|
|
|
|
|
#****************************************************************************** |
426
|
|
|
|
|
|
|
sub Login |
427
|
|
|
|
|
|
|
{ |
428
|
0
|
|
|
0
|
1
|
|
my $me= shift; |
429
|
0
|
0
|
|
|
|
|
return 1 if $me->State eq 'TRANSACTION'; # Already logged in |
430
|
|
|
|
|
|
|
|
431
|
0
|
0
|
|
|
|
|
if ($me->{AUTH_MODE} eq 'BEST') { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
432
|
0
|
|
|
|
|
|
my $retval; |
433
|
0
|
0
|
|
|
|
|
if ($me->{MESG_ID}) { |
434
|
0
|
|
|
|
|
|
$retval = $me->Login_APOP(); |
435
|
0
|
0
|
|
|
|
|
return($retval) if ($me->State eq 'TRANSACTION'); |
436
|
|
|
|
|
|
|
} |
437
|
0
|
|
|
|
|
|
my $has_cram_md5 = 0; |
438
|
0
|
|
|
|
|
|
foreach my $capa ($me->Capa()) { |
439
|
0
|
0
|
0
|
|
|
|
$capa =~ /^SASL.*?\sCRAM-MD5\b/ and $has_cram_md5 = 1 and last; |
440
|
|
|
|
|
|
|
} |
441
|
0
|
0
|
|
|
|
|
if ($has_cram_md5) { |
442
|
0
|
|
|
|
|
|
$retval = $me->Login_CRAM_MD5(); |
443
|
0
|
0
|
|
|
|
|
return($retval) if ($me->State() eq 'TRANSACTION'); |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
elsif ($me->{AUTH_MODE} eq 'APOP') { |
447
|
0
|
0
|
|
|
|
|
return(0) if (!$me->{MESG_ID}); # fail if the server does not support APOP |
448
|
0
|
|
|
|
|
|
return($me->Login_APOP()); |
449
|
|
|
|
|
|
|
} |
450
|
|
|
|
|
|
|
elsif ($me->{AUTH_MODE} eq 'CRAM-MD5') { |
451
|
0
|
|
|
|
|
|
return($me->Login_CRAM_MD5()); |
452
|
|
|
|
|
|
|
} |
453
|
|
|
|
|
|
|
elsif ($me->{AUTH_MODE} ne 'PASS') { |
454
|
0
|
|
|
|
|
|
$me->Message("Programing error. AUTH_MODE (".$me->{AUTH_MODE}.") not BEST | APOP | CRAM-MD5 | PASS."); |
455
|
0
|
|
|
|
|
|
return(0); |
456
|
|
|
|
|
|
|
} |
457
|
0
|
|
|
|
|
|
return($me->Login_Pass()); |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
|
460
|
0
|
|
|
0
|
0
|
|
sub login { Login(@_); } |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
#****************************************************************************** |
463
|
|
|
|
|
|
|
#* login to the POP server using APOP (md5) authentication. |
464
|
|
|
|
|
|
|
#****************************************************************************** |
465
|
|
|
|
|
|
|
sub Login_APOP |
466
|
|
|
|
|
|
|
{ |
467
|
0
|
|
|
0
|
0
|
|
my $me = shift; |
468
|
0
|
|
|
|
|
|
eval { |
469
|
0
|
|
|
|
|
|
require Digest::MD5; |
470
|
|
|
|
|
|
|
}; |
471
|
0
|
0
|
0
|
|
|
|
$@ and $me->Message("APOP failed: $@") and return 0; |
472
|
|
|
|
|
|
|
|
473
|
0
|
|
|
|
|
|
my $hash = Digest::MD5::md5_hex($me->{MESG_ID} . $me->Pass()); |
474
|
|
|
|
|
|
|
|
475
|
0
|
0
|
|
|
|
|
$me->_checkstate('AUTHORIZATION', 'APOP') or return 0; |
476
|
0
|
|
|
|
|
|
$me->_sockprint( "APOP " , $me->User , ' ', $hash, $me->EOL ); |
477
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
478
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
479
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for APOP"); |
480
|
0
|
|
|
|
|
|
$me->State('AUTHORIZATION'); |
481
|
0
|
|
|
|
|
|
return 0; |
482
|
|
|
|
|
|
|
} |
483
|
0
|
|
|
|
|
|
chomp $line; |
484
|
0
|
|
|
|
|
|
$me->Message($line); |
485
|
|
|
|
|
|
|
# some servers will close here... |
486
|
0
|
0
|
|
|
|
|
$me->NOOP() || do { |
487
|
0
|
|
|
|
|
|
$me->State('DEAD'); |
488
|
0
|
|
|
|
|
|
undef $me->{SOCKET}; |
489
|
0
|
|
|
|
|
|
$me->Message("APOP failed: server has closed the socket"); |
490
|
0
|
|
|
|
|
|
return 0; |
491
|
|
|
|
|
|
|
}; |
492
|
|
|
|
|
|
|
|
493
|
0
|
0
|
0
|
|
|
|
$line =~ /^\+OK/ or $me->Message("APOP failed: $line") and return 0; |
494
|
0
|
|
|
|
|
|
$me->State('TRANSACTION'); |
495
|
|
|
|
|
|
|
|
496
|
0
|
0
|
|
|
|
|
$me->POPStat() or return 0; |
497
|
|
|
|
|
|
|
} |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
#****************************************************************************** |
501
|
|
|
|
|
|
|
#* login to the POP server using CRAM-MD5 (RFC 2195) authentication. |
502
|
|
|
|
|
|
|
#****************************************************************************** |
503
|
|
|
|
|
|
|
sub Login_CRAM_MD5 |
504
|
|
|
|
|
|
|
{ |
505
|
0
|
|
|
0
|
0
|
|
my $me = shift; |
506
|
|
|
|
|
|
|
|
507
|
0
|
|
|
|
|
|
eval { |
508
|
0
|
|
|
|
|
|
require Digest::HMAC_MD5; |
509
|
0
|
|
|
|
|
|
require MIME::Base64; |
510
|
|
|
|
|
|
|
}; |
511
|
0
|
0
|
0
|
|
|
|
$@ and $me->Message("AUTH CRAM-MD5 failed: $@") and return 0; |
512
|
|
|
|
|
|
|
|
513
|
0
|
0
|
|
|
|
|
$me->_checkstate('AUTHORIZATION', 'AUTH') or return 0; |
514
|
0
|
|
|
|
|
|
$me->_sockprint('AUTH CRAM-MD5', $me->EOL()); |
515
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
516
|
0
|
|
|
|
|
|
chomp $line; |
517
|
0
|
|
|
|
|
|
$me->Message($line); |
518
|
|
|
|
|
|
|
|
519
|
0
|
0
|
|
|
|
|
if ($line =~ /^\+ (.+)$/) { |
520
|
|
|
|
|
|
|
|
521
|
0
|
|
|
|
|
|
my $hmac = |
522
|
|
|
|
|
|
|
Digest::HMAC_MD5::hmac_md5_hex(MIME::Base64::decode($1), $me->Pass()); |
523
|
0
|
|
|
|
|
|
(my $response = MIME::Base64::encode($me->User() . " $hmac")) =~ s/[\r\n]//g; |
524
|
0
|
|
|
|
|
|
$me->_sockprint($response, $me->EOL()); |
525
|
|
|
|
|
|
|
|
526
|
0
|
|
|
|
|
|
$line = $me->_sockread(); |
527
|
0
|
|
|
|
|
|
chomp $line; |
528
|
0
|
|
|
|
|
|
$me->Message($line); |
529
|
0
|
0
|
0
|
|
|
|
$line =~ /^\+OK/ or |
530
|
|
|
|
|
|
|
$me->Message("AUTH CRAM-MD5 failed: $line") and return 0; |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
} else { |
533
|
0
|
0
|
|
|
|
|
$me->Message("AUTH CRAM-MD5 failed: $line") and return 0; |
534
|
|
|
|
|
|
|
} |
535
|
|
|
|
|
|
|
|
536
|
0
|
|
|
|
|
|
$me->State('TRANSACTION'); |
537
|
|
|
|
|
|
|
|
538
|
0
|
0
|
|
|
|
|
$me->POPStat() or return 0; |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
#****************************************************************************** |
543
|
|
|
|
|
|
|
#* login to the POP server using simple (cleartext) authentication. |
544
|
|
|
|
|
|
|
#****************************************************************************** |
545
|
|
|
|
|
|
|
sub Login_Pass |
546
|
|
|
|
|
|
|
{ |
547
|
0
|
|
|
0
|
0
|
|
my $me = shift; |
548
|
|
|
|
|
|
|
|
549
|
0
|
0
|
|
|
|
|
$me->_checkstate('AUTHORIZATION', 'USER') or return 0; |
550
|
0
|
|
|
|
|
|
$me->_sockprint( "USER " , $me->User() , $me->EOL ); |
551
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
552
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
553
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for USER"); |
554
|
0
|
|
|
|
|
|
$me->State('AUTHORIZATION'); |
555
|
0
|
|
|
|
|
|
return 0; |
556
|
|
|
|
|
|
|
} |
557
|
0
|
|
|
|
|
|
chomp $line; |
558
|
0
|
|
|
|
|
|
$me->Message($line); |
559
|
0
|
0
|
0
|
|
|
|
$line =~ /^\+/ or $me->Message("USER failed: $line") and $me->State('AUTHORIZATION') |
|
|
|
0
|
|
|
|
|
560
|
|
|
|
|
|
|
and return 0; |
561
|
|
|
|
|
|
|
|
562
|
0
|
|
|
|
|
|
$me->_sockprint( "PASS " , $me->Pass() , $me->EOL ); |
563
|
0
|
|
|
|
|
|
$line = $me->_sockread(); |
564
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
565
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for PASS"); |
566
|
0
|
|
|
|
|
|
$me->State('AUTHORIZATION'); |
567
|
0
|
|
|
|
|
|
return 0; |
568
|
|
|
|
|
|
|
} |
569
|
0
|
|
|
|
|
|
chomp $line; |
570
|
0
|
|
|
|
|
|
$me->Message($line); |
571
|
0
|
0
|
0
|
|
|
|
$line =~ /^\+OK/ or $me->Message("PASS failed: $line") and $me->State('AUTHORIZATION') |
|
|
|
0
|
|
|
|
|
572
|
|
|
|
|
|
|
and return 0; |
573
|
|
|
|
|
|
|
|
574
|
0
|
|
|
|
|
|
$me->State('TRANSACTION'); |
575
|
|
|
|
|
|
|
|
576
|
0
|
0
|
|
|
|
|
($me->POPStat() >= 0) or return 0; |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
} # end Login |
579
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
|
581
|
|
|
|
|
|
|
#****************************************************************************** |
582
|
|
|
|
|
|
|
#* Get the Head of a message number. If you give an optional number |
583
|
|
|
|
|
|
|
#* of lines you will get the first n lines of the body also. This |
584
|
|
|
|
|
|
|
#* allows you to preview a message. |
585
|
|
|
|
|
|
|
#****************************************************************************** |
586
|
|
|
|
|
|
|
sub Head |
587
|
|
|
|
|
|
|
{ |
588
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
589
|
0
|
|
|
|
|
|
my $num = shift; |
590
|
0
|
|
|
|
|
|
my $lines = shift; |
591
|
0
|
|
0
|
|
|
|
$lines ||= 0; |
592
|
0
|
0
|
|
|
|
|
$lines =~ /\d+/ || ($lines = 0); |
593
|
|
|
|
|
|
|
|
594
|
0
|
|
|
|
|
|
my $header = ''; |
595
|
|
|
|
|
|
|
|
596
|
0
|
0
|
|
|
|
|
$me->_checkstate('TRANSACTION', 'TOP') or return; |
597
|
0
|
|
|
|
|
|
$me->_sockprint( "TOP $num $lines", $me->EOL ); |
598
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
599
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
600
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for TOP"); |
601
|
0
|
|
|
|
|
|
return; |
602
|
|
|
|
|
|
|
} |
603
|
0
|
|
|
|
|
|
chomp $line; |
604
|
0
|
0
|
0
|
|
|
|
$line =~ /^\+OK/ or $me->Message("Bad return from TOP: $line") and return; |
605
|
0
|
0
|
|
|
|
|
$line =~ /^\+OK (\d+) / and my $buflen = $1; |
606
|
|
|
|
|
|
|
|
607
|
0
|
|
|
|
|
|
while (1) { |
608
|
0
|
|
|
|
|
|
$line = $me->_sockread(); |
609
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
610
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for TOP"); |
611
|
0
|
|
|
|
|
|
return; |
612
|
|
|
|
|
|
|
} |
613
|
0
|
0
|
|
|
|
|
last if $line =~ /^\.\s*$/; |
614
|
0
|
|
|
|
|
|
$line =~ s/^\.\././; |
615
|
0
|
|
|
|
|
|
$header .= $line; |
616
|
|
|
|
|
|
|
} |
617
|
|
|
|
|
|
|
|
618
|
0
|
0
|
|
|
|
|
return wantarray ? split(/\r?\n/, $header) : $header; |
619
|
|
|
|
|
|
|
} # end Head |
620
|
|
|
|
|
|
|
|
621
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
#****************************************************************************** |
623
|
|
|
|
|
|
|
#* Get the header and body of a message |
624
|
|
|
|
|
|
|
#****************************************************************************** |
625
|
|
|
|
|
|
|
sub HeadAndBody |
626
|
|
|
|
|
|
|
{ |
627
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
628
|
0
|
|
|
|
|
|
my $num = shift; |
629
|
0
|
|
|
|
|
|
my $mandb = ''; |
630
|
|
|
|
|
|
|
|
631
|
0
|
0
|
|
|
|
|
$me->_checkstate('TRANSACTION', 'RETR') or return; |
632
|
0
|
|
|
|
|
|
$me->_sockprint( "RETR $num", $me->EOL ); |
633
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
634
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
635
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for RETR"); |
636
|
0
|
|
|
|
|
|
return; |
637
|
|
|
|
|
|
|
} |
638
|
0
|
|
|
|
|
|
chomp $line; |
639
|
0
|
0
|
0
|
|
|
|
$line =~ /^\+OK/ or $me->Message("Bad return from RETR: $line") and return; |
640
|
0
|
0
|
|
|
|
|
$line =~ /^\+OK (\d+) / and my $buflen = $1; |
641
|
|
|
|
|
|
|
|
642
|
0
|
|
|
|
|
|
while (1) { |
643
|
0
|
|
|
|
|
|
$line = $me->_sockread(); |
644
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
645
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for RETR"); |
646
|
0
|
|
|
|
|
|
return; |
647
|
|
|
|
|
|
|
} |
648
|
0
|
0
|
|
|
|
|
last if $line =~ /^\.\s*$/; |
649
|
|
|
|
|
|
|
# convert any '..' at the start of a line to '.' |
650
|
0
|
|
|
|
|
|
$line =~ s/^\.\././; |
651
|
0
|
|
|
|
|
|
$mandb .= $line; |
652
|
|
|
|
|
|
|
} |
653
|
|
|
|
|
|
|
|
654
|
0
|
0
|
|
|
|
|
return wantarray ? split(/\r?\n/, $mandb) : $mandb; |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
} # end HeadAndBody |
657
|
|
|
|
|
|
|
|
658
|
0
|
|
|
0
|
0
|
|
sub message_string { HeadAndBody(@_); } |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
#****************************************************************************** |
661
|
|
|
|
|
|
|
#* get the head and body of a message and write it to a file handle. |
662
|
|
|
|
|
|
|
#* Sends the raw data: does no CR/NL stripping or mapping. |
663
|
|
|
|
|
|
|
#****************************************************************************** |
664
|
|
|
|
|
|
|
sub HeadAndBodyToFile |
665
|
|
|
|
|
|
|
{ |
666
|
0
|
|
|
0
|
1
|
|
local ($, , $\); |
667
|
0
|
|
|
|
|
|
my $me = shift; |
668
|
0
|
|
|
|
|
|
my $fh = shift; |
669
|
0
|
|
|
|
|
|
my $num = shift; |
670
|
0
|
|
|
|
|
|
my $body = ''; |
671
|
|
|
|
|
|
|
|
672
|
0
|
0
|
|
|
|
|
$me->_checkstate('TRANSACTION', 'RETR') or return; |
673
|
0
|
|
|
|
|
|
$me->_sockprint( "RETR $num", $me->EOL ); |
674
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
675
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
676
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for RETR"); |
677
|
0
|
|
|
|
|
|
return 0; |
678
|
|
|
|
|
|
|
} |
679
|
0
|
|
|
|
|
|
chomp $line; |
680
|
0
|
0
|
0
|
|
|
|
$line =~ /^\+OK/ or $me->Message("Bad return from RETR: $line") and return 0; |
681
|
0
|
0
|
|
|
|
|
$line =~ /^\+OK (\d+) / and my $buflen = $1; |
682
|
|
|
|
|
|
|
|
683
|
0
|
|
|
|
|
|
while (1) { |
684
|
0
|
|
|
|
|
|
$line = $me->_sockread(); |
685
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
686
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for RETR"); |
687
|
0
|
|
|
|
|
|
return 0; |
688
|
|
|
|
|
|
|
} |
689
|
0
|
0
|
|
|
|
|
last if $line =~ /^\.\s*$/; |
690
|
|
|
|
|
|
|
# convert any '..' at the start of a line to '.' |
691
|
0
|
|
|
|
|
|
$line =~ s/^\.\././; |
692
|
0
|
|
|
|
|
|
print $fh $line; |
693
|
|
|
|
|
|
|
} |
694
|
0
|
|
|
|
|
|
return 1; |
695
|
|
|
|
|
|
|
} # end BodyToFile |
696
|
|
|
|
|
|
|
|
697
|
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
#****************************************************************************** |
700
|
|
|
|
|
|
|
#* get the body of a message |
701
|
|
|
|
|
|
|
#****************************************************************************** |
702
|
|
|
|
|
|
|
sub Body |
703
|
|
|
|
|
|
|
{ |
704
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
705
|
0
|
|
|
|
|
|
my $num = shift; |
706
|
0
|
|
|
|
|
|
my $body = ''; |
707
|
|
|
|
|
|
|
|
708
|
0
|
0
|
|
|
|
|
$me->_checkstate('TRANSACTION', 'RETR') or return; |
709
|
0
|
|
|
|
|
|
$me->_sockprint( "RETR $num", $me->EOL ); |
710
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
711
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
712
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for RETR"); |
713
|
0
|
|
|
|
|
|
return; |
714
|
|
|
|
|
|
|
} |
715
|
0
|
|
|
|
|
|
chomp $line; |
716
|
0
|
0
|
0
|
|
|
|
$line =~ /^\+OK/ or $me->Message("Bad return from RETR: $line") and return; |
717
|
0
|
0
|
|
|
|
|
$line =~ /^\+OK (\d+) / and my $buflen = $1; |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
# skip the header |
720
|
0
|
|
|
|
|
|
do { |
721
|
0
|
|
|
|
|
|
$line = $me->_sockread(); |
722
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
723
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for RETR"); |
724
|
0
|
|
|
|
|
|
return; |
725
|
|
|
|
|
|
|
} |
726
|
0
|
|
|
|
|
|
$line =~ s/[\r\n]//g; |
727
|
|
|
|
|
|
|
} until $line =~ /^(\s*|\.)$/; |
728
|
0
|
0
|
|
|
|
|
$line =~ /^\.\s*$/ && return; # we found a header only! Lotus Notes seems to do this. |
729
|
|
|
|
|
|
|
|
730
|
0
|
|
|
|
|
|
while (1) { |
731
|
0
|
|
|
|
|
|
$line = $me->_sockread(); |
732
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
733
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for RETR"); |
734
|
0
|
|
|
|
|
|
return; |
735
|
|
|
|
|
|
|
} |
736
|
0
|
0
|
|
|
|
|
last if $line =~ /^\.\s*$/; |
737
|
|
|
|
|
|
|
# convert any '..' at the start of a line to '.' |
738
|
0
|
|
|
|
|
|
$line =~ s/^\.\././; |
739
|
0
|
|
|
|
|
|
$body .= $line; |
740
|
|
|
|
|
|
|
} |
741
|
|
|
|
|
|
|
|
742
|
0
|
0
|
|
|
|
|
return wantarray ? split(/\r?\n/, $body) : $body; |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
} # end Body |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
#****************************************************************************** |
748
|
|
|
|
|
|
|
#* get the body of a message and write it to a file handle. Sends the raw data: |
749
|
|
|
|
|
|
|
#* does no CR/NL stripping or mapping. |
750
|
|
|
|
|
|
|
#****************************************************************************** |
751
|
|
|
|
|
|
|
sub BodyToFile |
752
|
|
|
|
|
|
|
{ |
753
|
0
|
|
|
0
|
1
|
|
local ($, , $\); |
754
|
0
|
|
|
|
|
|
my $me = shift; |
755
|
0
|
|
|
|
|
|
my $fh = shift; |
756
|
0
|
|
|
|
|
|
my $num = shift; |
757
|
0
|
|
|
|
|
|
my $body = ''; |
758
|
|
|
|
|
|
|
|
759
|
0
|
0
|
|
|
|
|
$me->_checkstate('TRANSACTION', 'RETR') or return; |
760
|
0
|
|
|
|
|
|
$me->_sockprint( "RETR $num", $me->EOL ); |
761
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
762
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
763
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for RETR"); |
764
|
0
|
|
|
|
|
|
return; |
765
|
|
|
|
|
|
|
} |
766
|
0
|
|
|
|
|
|
chomp $line; |
767
|
0
|
0
|
0
|
|
|
|
$line =~ /^\+OK/ or $me->Message("Bad return from RETR: $line") and return; |
768
|
0
|
0
|
|
|
|
|
$line =~ /^\+OK (\d+) / and my $buflen = $1; |
769
|
|
|
|
|
|
|
|
770
|
|
|
|
|
|
|
# skip the header |
771
|
0
|
|
|
|
|
|
do { |
772
|
0
|
|
|
|
|
|
$line = $me->_sockread(); |
773
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
774
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for RETR"); |
775
|
0
|
|
|
|
|
|
return; |
776
|
|
|
|
|
|
|
} |
777
|
0
|
|
|
|
|
|
$line =~ s/[\r\n]//g; |
778
|
|
|
|
|
|
|
} until $line =~ /^(\s*|\.)$/; |
779
|
0
|
0
|
|
|
|
|
$line =~ /^\.\s*$/ && return; # we found a header only! Lotus Notes seems to do this. |
780
|
|
|
|
|
|
|
|
781
|
0
|
|
|
|
|
|
while (1) { |
782
|
0
|
|
|
|
|
|
$line = $me->_sockread(); |
783
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
784
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for RETR"); |
785
|
0
|
|
|
|
|
|
return; |
786
|
|
|
|
|
|
|
} |
787
|
0
|
|
|
|
|
|
chomp $line; |
788
|
0
|
0
|
|
|
|
|
last if $line =~ /^\.\s*$/; |
789
|
|
|
|
|
|
|
# convert any '..' at the start of a line to '.' |
790
|
0
|
|
|
|
|
|
$line =~ s/^\.\././; |
791
|
0
|
|
|
|
|
|
print $fh $line, "\n"; |
792
|
|
|
|
|
|
|
} |
793
|
|
|
|
|
|
|
} # end BodyToFile |
794
|
|
|
|
|
|
|
|
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
|
797
|
|
|
|
|
|
|
#****************************************************************************** |
798
|
|
|
|
|
|
|
#* handle a STAT command - returns the number of messages in the box |
799
|
|
|
|
|
|
|
#****************************************************************************** |
800
|
|
|
|
|
|
|
sub POPStat |
801
|
|
|
|
|
|
|
{ |
802
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
803
|
|
|
|
|
|
|
|
804
|
0
|
0
|
|
|
|
|
$me->_checkstate('TRANSACTION', 'STAT') or return -1; |
805
|
0
|
|
|
|
|
|
$me->_sockprint( "STAT", $me->EOL ); |
806
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
807
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
808
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for STAT"); |
809
|
0
|
|
|
|
|
|
return -1; |
810
|
|
|
|
|
|
|
} |
811
|
0
|
0
|
0
|
|
|
|
$line =~ /^\+OK/ or $me->Message("STAT failed: $line") and return -1; |
812
|
0
|
0
|
|
|
|
|
$line =~ /^\+OK (\d+) (\d+)/ and $me->Count($1), $me->Size($2); |
813
|
|
|
|
|
|
|
|
814
|
0
|
|
|
|
|
|
return $me->Count(); |
815
|
|
|
|
|
|
|
} |
816
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
#****************************************************************************** |
819
|
|
|
|
|
|
|
#* issue the LIST command |
820
|
|
|
|
|
|
|
#****************************************************************************** |
821
|
|
|
|
|
|
|
sub List { |
822
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
823
|
0
|
|
0
|
|
|
|
my $num = shift || ''; |
824
|
0
|
|
0
|
|
|
|
my $CMD = shift || 'LIST'; |
825
|
0
|
|
|
|
|
|
$CMD=~ tr/a-z/A-Z/; |
826
|
|
|
|
|
|
|
|
827
|
0
|
0
|
|
|
|
|
$me->Alive() or return; |
828
|
|
|
|
|
|
|
|
829
|
0
|
|
|
|
|
|
my @retarray = (); |
830
|
0
|
|
|
|
|
|
my $ret = ''; |
831
|
|
|
|
|
|
|
|
832
|
0
|
0
|
|
|
|
|
$me->_checkstate('TRANSACTION', $CMD) or return; |
833
|
0
|
0
|
|
|
|
|
$me->_sockprint($CMD, $num ? " $num" : '', $me->EOL()); |
834
|
|
|
|
|
|
|
|
835
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
836
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
837
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for LIST"); |
838
|
0
|
|
|
|
|
|
return; |
839
|
|
|
|
|
|
|
} |
840
|
0
|
0
|
0
|
|
|
|
$line =~ /^\+OK/ or $me->Message("$line") and return; |
841
|
0
|
0
|
|
|
|
|
if ($num) { |
842
|
0
|
|
|
|
|
|
$line =~ s/^\+OK\s*//; |
843
|
0
|
|
|
|
|
|
return $line; |
844
|
|
|
|
|
|
|
} |
845
|
0
|
|
|
|
|
|
while( defined( $line = $me->_sockread() ) ) { |
846
|
0
|
0
|
|
|
|
|
$line =~ /^\.\s*$/ and last; |
847
|
0
|
|
|
|
|
|
$ret .= $line; |
848
|
0
|
|
|
|
|
|
chomp $line; |
849
|
0
|
|
|
|
|
|
push(@retarray, $line); |
850
|
|
|
|
|
|
|
} |
851
|
0
|
0
|
|
|
|
|
if ($ret) { |
852
|
0
|
0
|
|
|
|
|
return wantarray ? @retarray : $ret; |
853
|
|
|
|
|
|
|
} |
854
|
|
|
|
|
|
|
} |
855
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
#****************************************************************************** |
857
|
|
|
|
|
|
|
#* issue the LIST command, but return results in an indexed array. |
858
|
|
|
|
|
|
|
#****************************************************************************** |
859
|
|
|
|
|
|
|
sub ListArray { |
860
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
861
|
0
|
|
0
|
|
|
|
my $num = shift || ''; |
862
|
0
|
|
0
|
|
|
|
my $CMD = shift || 'LIST'; |
863
|
0
|
|
|
|
|
|
$CMD=~ tr/a-z/A-Z/; |
864
|
|
|
|
|
|
|
|
865
|
0
|
0
|
|
|
|
|
$me->Alive() or return; |
866
|
|
|
|
|
|
|
|
867
|
0
|
|
|
|
|
|
my @retarray = (); |
868
|
0
|
|
|
|
|
|
my $ret = ''; |
869
|
|
|
|
|
|
|
|
870
|
0
|
0
|
|
|
|
|
$me->_checkstate('TRANSACTION', $CMD) or return; |
871
|
0
|
0
|
|
|
|
|
$me->_sockprint($CMD, $num ? " $num" : '', $me->EOL()); |
872
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
873
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
874
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for LIST"); |
875
|
0
|
|
|
|
|
|
return; |
876
|
|
|
|
|
|
|
} |
877
|
0
|
0
|
0
|
|
|
|
$line =~ /^\+OK/ or $me->Message("$line") and return; |
878
|
0
|
0
|
|
|
|
|
if ($num) { |
879
|
0
|
|
|
|
|
|
$line =~ s/^\+OK\s*//; |
880
|
0
|
|
|
|
|
|
return $line; |
881
|
|
|
|
|
|
|
} |
882
|
0
|
|
|
|
|
|
while( defined( $line = $me->_sockread() ) ) { |
883
|
0
|
0
|
|
|
|
|
$line =~ /^\.\s*$/ and last; |
884
|
0
|
|
|
|
|
|
$ret .= $line; |
885
|
0
|
|
|
|
|
|
chomp $line; |
886
|
0
|
|
|
|
|
|
my ($num, $uidl) = split('\s+', $line); |
887
|
0
|
|
|
|
|
|
$retarray[$num] = $uidl; |
888
|
|
|
|
|
|
|
} |
889
|
0
|
0
|
|
|
|
|
if ($ret) { |
890
|
0
|
0
|
|
|
|
|
return wantarray ? @retarray : $ret; |
891
|
|
|
|
|
|
|
} |
892
|
|
|
|
|
|
|
} |
893
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
|
895
|
|
|
|
|
|
|
#****************************************************************************** |
896
|
|
|
|
|
|
|
#* retrieve the given message number - uses HeadAndBody |
897
|
|
|
|
|
|
|
#****************************************************************************** |
898
|
|
|
|
|
|
|
sub Retrieve { |
899
|
0
|
|
|
0
|
1
|
|
return HeadAndBody( @_ ); |
900
|
|
|
|
|
|
|
} |
901
|
|
|
|
|
|
|
|
902
|
|
|
|
|
|
|
#****************************************************************************** |
903
|
|
|
|
|
|
|
#* retrieve the given message number to the given file handle- uses |
904
|
|
|
|
|
|
|
#* HeadAndBodyToFile |
905
|
|
|
|
|
|
|
#****************************************************************************** |
906
|
|
|
|
|
|
|
sub RetrieveToFile { |
907
|
0
|
|
|
0
|
1
|
|
return HeadAndBodyToFile( @_ ); |
908
|
|
|
|
|
|
|
} |
909
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
#****************************************************************************** |
912
|
|
|
|
|
|
|
#* implement the LAST command - see the rfc (1081) OBSOLETED by RFC |
913
|
|
|
|
|
|
|
#****************************************************************************** |
914
|
|
|
|
|
|
|
sub Last |
915
|
|
|
|
|
|
|
{ |
916
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
917
|
|
|
|
|
|
|
|
918
|
0
|
0
|
|
|
|
|
$me->_checkstate('TRANSACTION', 'LAST') or return; |
919
|
0
|
|
|
|
|
|
$me->_sockprint( "LAST", $me->EOL ); |
920
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
921
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
922
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for LAST"); |
923
|
0
|
|
|
|
|
|
return 0; |
924
|
|
|
|
|
|
|
} |
925
|
|
|
|
|
|
|
|
926
|
0
|
0
|
|
|
|
|
$line =~ /\+OK (\d+)\D*$/ and return $1; |
927
|
|
|
|
|
|
|
} |
928
|
|
|
|
|
|
|
|
929
|
|
|
|
|
|
|
|
930
|
|
|
|
|
|
|
#****************************************************************************** |
931
|
|
|
|
|
|
|
#* reset the deletion stat |
932
|
|
|
|
|
|
|
#****************************************************************************** |
933
|
|
|
|
|
|
|
sub Reset |
934
|
|
|
|
|
|
|
{ |
935
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
936
|
|
|
|
|
|
|
|
937
|
0
|
0
|
|
|
|
|
$me->_checkstate('TRANSACTION', 'RSET') or return; |
938
|
0
|
|
|
|
|
|
$me->_sockprint( "RSET", $me->EOL ); |
939
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
940
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
941
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for RSET"); |
942
|
0
|
|
|
|
|
|
return 0; |
943
|
|
|
|
|
|
|
} |
944
|
0
|
0
|
|
|
|
|
$line =~ /^\+OK/ and return 1; |
945
|
0
|
|
|
|
|
|
return 0; |
946
|
|
|
|
|
|
|
} |
947
|
|
|
|
|
|
|
|
948
|
|
|
|
|
|
|
|
949
|
|
|
|
|
|
|
#****************************************************************************** |
950
|
|
|
|
|
|
|
#* delete the given message number |
951
|
|
|
|
|
|
|
#****************************************************************************** |
952
|
|
|
|
|
|
|
sub Delete { |
953
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
954
|
0
|
|
0
|
|
|
|
my $num = shift || return; |
955
|
|
|
|
|
|
|
|
956
|
0
|
0
|
|
|
|
|
$me->_checkstate('TRANSACTION', 'DELE') or return; |
957
|
0
|
|
|
|
|
|
$me->_sockprint( "DELE $num", $me->EOL ); |
958
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
959
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
960
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for DELE"); |
961
|
0
|
|
|
|
|
|
return 0; |
962
|
|
|
|
|
|
|
} |
963
|
0
|
|
|
|
|
|
$me->Message($line); |
964
|
0
|
0
|
|
|
|
|
$line =~ /^\+OK/ && return 1; |
965
|
0
|
|
|
|
|
|
return 0; |
966
|
|
|
|
|
|
|
} |
967
|
|
|
|
|
|
|
|
968
|
0
|
|
|
0
|
0
|
|
sub delete_message { Delete(@_); } |
969
|
|
|
|
|
|
|
|
970
|
|
|
|
|
|
|
#****************************************************************************** |
971
|
|
|
|
|
|
|
#* UIDL - submitted by Dion Almaer (dion@member.com) |
972
|
|
|
|
|
|
|
#****************************************************************************** |
973
|
|
|
|
|
|
|
sub Uidl |
974
|
|
|
|
|
|
|
{ |
975
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
976
|
0
|
|
0
|
|
|
|
my $num = shift || ''; |
977
|
|
|
|
|
|
|
|
978
|
0
|
0
|
|
|
|
|
$me->Alive() or return; |
979
|
|
|
|
|
|
|
|
980
|
0
|
|
|
|
|
|
my @retarray = (); |
981
|
0
|
|
|
|
|
|
my $ret = ''; |
982
|
|
|
|
|
|
|
|
983
|
0
|
0
|
|
|
|
|
$me->_checkstate('TRANSACTION', 'UIDL') or return; |
984
|
0
|
0
|
|
|
|
|
$me->_sockprint('UIDL', $num ? " $num" : '', $me->EOL()); |
985
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
986
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
987
|
0
|
|
|
|
|
|
$me->Message("Socket read failed for UIDL"); |
988
|
0
|
|
|
|
|
|
return; |
989
|
|
|
|
|
|
|
} |
990
|
0
|
0
|
0
|
|
|
|
$line =~ /^\+OK/ or $me->Message($line) and return; |
991
|
0
|
0
|
|
|
|
|
if ($num) { |
992
|
0
|
|
|
|
|
|
$line =~ s/^\+OK\s*//; |
993
|
0
|
|
|
|
|
|
return $line; |
994
|
|
|
|
|
|
|
} |
995
|
0
|
|
|
|
|
|
while( defined( $line = $me->_sockread() ) ) { |
996
|
0
|
0
|
|
|
|
|
$line =~ /^\.\s*$/ and last; |
997
|
0
|
|
|
|
|
|
$ret .= $line; |
998
|
0
|
|
|
|
|
|
chomp $line; |
999
|
0
|
|
|
|
|
|
my ($num, $uidl) = split('\s+', $line); |
1000
|
0
|
|
|
|
|
|
$retarray[$num] = $uidl; |
1001
|
|
|
|
|
|
|
} |
1002
|
0
|
0
|
|
|
|
|
if ($ret) { |
1003
|
0
|
0
|
|
|
|
|
return wantarray ? @retarray : $ret; |
1004
|
|
|
|
|
|
|
} |
1005
|
|
|
|
|
|
|
} |
1006
|
|
|
|
|
|
|
|
1007
|
|
|
|
|
|
|
|
1008
|
|
|
|
|
|
|
#****************************************************************************** |
1009
|
|
|
|
|
|
|
#* CAPA - query server capabilities, see RFC 2449 |
1010
|
|
|
|
|
|
|
#****************************************************************************** |
1011
|
|
|
|
|
|
|
sub Capa { |
1012
|
|
|
|
|
|
|
|
1013
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
1014
|
|
|
|
|
|
|
|
1015
|
|
|
|
|
|
|
# no state check here, all are allowed |
1016
|
|
|
|
|
|
|
|
1017
|
0
|
0
|
|
|
|
|
$me->Alive() or return; |
1018
|
|
|
|
|
|
|
|
1019
|
0
|
|
|
|
|
|
my @retarray = (); |
1020
|
0
|
|
|
|
|
|
my $ret = ''; |
1021
|
|
|
|
|
|
|
|
1022
|
0
|
|
|
|
|
|
$me->_sockprint('CAPA', $me->EOL()); |
1023
|
|
|
|
|
|
|
|
1024
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
1025
|
0
|
0
|
0
|
|
|
|
$line =~ /^\+OK/ or $me->Message($line) and return; |
1026
|
|
|
|
|
|
|
|
1027
|
0
|
|
|
|
|
|
while(defined($line = $me->_sockread())) { |
1028
|
0
|
0
|
|
|
|
|
$line =~ /^\.\s*$/ and last; |
1029
|
0
|
|
|
|
|
|
$ret .= $line; |
1030
|
0
|
|
|
|
|
|
chomp $line; |
1031
|
0
|
|
|
|
|
|
push(@retarray, $line); |
1032
|
|
|
|
|
|
|
} |
1033
|
|
|
|
|
|
|
|
1034
|
0
|
0
|
|
|
|
|
if ($ret) { |
1035
|
0
|
0
|
|
|
|
|
return wantarray ? @retarray : $ret; |
1036
|
|
|
|
|
|
|
} |
1037
|
|
|
|
|
|
|
} |
1038
|
|
|
|
|
|
|
|
1039
|
|
|
|
|
|
|
#****************************************************************************** |
1040
|
|
|
|
|
|
|
#* XTND - submitted by Chris Moates (six@mox.net) |
1041
|
|
|
|
|
|
|
#****************************************************************************** |
1042
|
|
|
|
|
|
|
sub Xtnd { |
1043
|
0
|
|
|
0
|
0
|
|
my $me = shift; |
1044
|
0
|
|
0
|
|
|
|
my $xtndarg = shift || ''; |
1045
|
|
|
|
|
|
|
|
1046
|
0
|
0
|
|
|
|
|
if ($xtndarg eq '') { |
1047
|
0
|
|
|
|
|
|
$me->Message("XTND requires a string argument"); |
1048
|
0
|
|
|
|
|
|
return; |
1049
|
|
|
|
|
|
|
} |
1050
|
|
|
|
|
|
|
|
1051
|
0
|
|
|
|
|
|
my $s = $me->Socket(); |
1052
|
0
|
0
|
|
|
|
|
$me->_checkstate('TRANSACTION', 'XTND') or return; |
1053
|
0
|
0
|
|
|
|
|
$me->Alive() or return; |
1054
|
|
|
|
|
|
|
|
1055
|
0
|
|
|
|
|
|
$me->_sockprint( "XTND $xtndarg", $me->EOL ); |
1056
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
1057
|
0
|
0
|
0
|
|
|
|
$line =~ /^\+OK/ or $me->Message($line) and return; |
1058
|
0
|
|
|
|
|
|
$line =~ s/^\+OK\s*//; |
1059
|
0
|
|
|
|
|
|
return $line; |
1060
|
|
|
|
|
|
|
} |
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
#****************************************************************************** |
1063
|
|
|
|
|
|
|
#* UTF8 - submitted by eady@galionlibrary.org |
1064
|
|
|
|
|
|
|
#****************************************************************************** |
1065
|
|
|
|
|
|
|
sub UTF8 { |
1066
|
0
|
|
|
0
|
1
|
|
my $me = shift; |
1067
|
0
|
0
|
|
|
|
|
if (grep { /^UTF8 USER/ } $me->Capa()) { |
|
0
|
|
|
|
|
|
|
1068
|
|
|
|
|
|
|
# my $sock = $me->Socket(); # Is this needed? Xtnd() does it... |
1069
|
0
|
0
|
|
|
|
|
if ($me->Alive()) { |
1070
|
0
|
|
|
|
|
|
$me->_sockprint("UTF8" . $me->EOL()); |
1071
|
0
|
|
|
|
|
|
my $result = $me->_sockread(); |
1072
|
0
|
|
|
|
|
|
$result = s/\r?\n$//; |
1073
|
0
|
0
|
0
|
|
|
|
$result =~ /^\+OK/ or $me->Message($result) and return; |
1074
|
0
|
|
|
|
|
|
$result =~ s/^\+OK\s*//; |
1075
|
0
|
|
0
|
|
|
|
$result ||= "[inferred: UTF-8 mode enabled]"; |
1076
|
0
|
|
|
|
|
|
return $result; |
1077
|
|
|
|
|
|
|
} |
1078
|
|
|
|
|
|
|
} |
1079
|
0
|
|
|
|
|
|
return; |
1080
|
|
|
|
|
|
|
} |
1081
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
#****************************************************************************** |
1083
|
|
|
|
|
|
|
#* NOOP - used to check socket |
1084
|
|
|
|
|
|
|
#****************************************************************************** |
1085
|
|
|
|
|
|
|
sub NOOP { |
1086
|
0
|
|
|
0
|
0
|
|
my $me = shift; |
1087
|
|
|
|
|
|
|
|
1088
|
0
|
|
|
|
|
|
my $s = $me->Socket(); |
1089
|
0
|
0
|
|
|
|
|
$me->Alive() or return 0; |
1090
|
|
|
|
|
|
|
|
1091
|
0
|
|
|
|
|
|
$me->_sockprint( "NOOP", $me->EOL ); |
1092
|
0
|
|
|
|
|
|
my $line = $me->_sockread(); |
1093
|
|
|
|
|
|
|
# defined( $line ) or return 0; |
1094
|
0
|
0
|
|
|
|
|
$line =~ /^\+OK/ or return 0; |
1095
|
0
|
|
|
|
|
|
return 1; |
1096
|
|
|
|
|
|
|
} |
1097
|
|
|
|
|
|
|
|
1098
|
|
|
|
|
|
|
|
1099
|
|
|
|
|
|
|
#****************************************************************************** |
1100
|
|
|
|
|
|
|
#* Mail::IMAPClient compatibility functions (wsnyder@wsnyder.org) |
1101
|
|
|
|
|
|
|
#****************************************************************************** |
1102
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
# Empty stubs |
1104
|
|
|
|
0
|
0
|
|
sub Peek {} |
1105
|
|
|
|
0
|
0
|
|
sub Uid {} |
1106
|
|
|
|
|
|
|
|
1107
|
|
|
|
|
|
|
# Pop doesn't have concept of different folders |
1108
|
0
|
|
|
0
|
0
|
|
sub folders { return ('INBOX'); } |
1109
|
0
|
|
|
0
|
0
|
|
sub Folder { return ('INBOX'); } |
1110
|
|
|
|
0
|
0
|
|
sub select {} |
1111
|
|
|
|
|
|
|
|
1112
|
|
|
|
|
|
|
# Message accessing |
1113
|
|
|
|
|
|
|
sub unseen { |
1114
|
0
|
|
|
0
|
0
|
|
my $me = shift; |
1115
|
0
|
|
|
|
|
|
my $count = $me->Count; |
1116
|
0
|
0
|
|
|
|
|
return () if !$count; |
1117
|
0
|
|
|
|
|
|
return ( 1..$count); |
1118
|
|
|
|
|
|
|
} |
1119
|
|
|
|
|
|
|
|
1120
|
|
|
|
|
|
|
#***************************************************************************** |
1121
|
|
|
|
|
|
|
#* Check the state before issuing a command |
1122
|
|
|
|
|
|
|
#***************************************************************************** |
1123
|
|
|
|
|
|
|
sub _checkstate |
1124
|
|
|
|
|
|
|
{ |
1125
|
0
|
|
|
0
|
|
|
my ($me, $state, $cmd) = @_; |
1126
|
0
|
|
|
|
|
|
my $currstate = $me->State(); |
1127
|
0
|
0
|
|
|
|
|
if ($currstate ne $state) { |
1128
|
0
|
|
|
|
|
|
$me->Message("POP3 command $cmd may be given only in the '$state' state " . |
1129
|
|
|
|
|
|
|
"(current state is '$currstate')."); |
1130
|
0
|
|
|
|
|
|
return 0; |
1131
|
|
|
|
|
|
|
} else { |
1132
|
0
|
|
|
|
|
|
return 1; |
1133
|
|
|
|
|
|
|
} |
1134
|
|
|
|
|
|
|
} |
1135
|
|
|
|
|
|
|
|
1136
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
#***************************************************************************** |
1138
|
|
|
|
|
|
|
#* funnel all read/write through here to allow easier debugging |
1139
|
|
|
|
|
|
|
#* (mitra@earth.path.net) |
1140
|
|
|
|
|
|
|
#***************************************************************************** |
1141
|
|
|
|
|
|
|
sub _sockprint |
1142
|
|
|
|
|
|
|
{ |
1143
|
0
|
|
|
0
|
|
|
local ($, , $\); |
1144
|
0
|
|
|
|
|
|
my $me = shift; |
1145
|
0
|
|
|
|
|
|
my $s = $me->Socket(); |
1146
|
0
|
0
|
|
|
|
|
$me->Debug and Carp::carp "POP3 -> ", @_; |
1147
|
0
|
|
|
|
|
|
my $outline = "@_"; |
1148
|
0
|
|
|
|
|
|
chomp $outline; |
1149
|
0
|
|
|
|
|
|
push(@{$me->{tranlog}}, $outline); |
|
0
|
|
|
|
|
|
|
1150
|
0
|
|
|
|
|
|
print $s @_; |
1151
|
|
|
|
|
|
|
} |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
sub _sockread |
1154
|
|
|
|
|
|
|
{ |
1155
|
0
|
|
|
0
|
|
|
my $me = shift; |
1156
|
0
|
|
|
|
|
|
my $line = $me->Socket()->getline(); |
1157
|
0
|
0
|
|
|
|
|
unless (defined $line) { |
1158
|
0
|
|
|
|
|
|
return; |
1159
|
|
|
|
|
|
|
} |
1160
|
|
|
|
|
|
|
|
1161
|
|
|
|
|
|
|
# Macs seem to leave CR's or LF's sitting on the socket. This |
1162
|
|
|
|
|
|
|
# removes them. |
1163
|
0
|
0
|
|
|
|
|
$me->{STRIPCR} && ($line =~ s/^[\r]+//); |
1164
|
|
|
|
|
|
|
|
1165
|
0
|
0
|
|
|
|
|
$me->Debug and Carp::carp "POP3 <- ", $line; |
1166
|
0
|
0
|
|
|
|
|
$line =~ /^[\\+\\-](OK|ERR)/i && do { |
1167
|
0
|
|
|
|
|
|
my $l = $line; |
1168
|
0
|
|
|
|
|
|
chomp $l; |
1169
|
0
|
|
|
|
|
|
push(@{$me->{tranlog}}, $l); |
|
0
|
|
|
|
|
|
|
1170
|
|
|
|
|
|
|
}; |
1171
|
0
|
|
|
|
|
|
return $line; |
1172
|
|
|
|
|
|
|
} |
1173
|
|
|
|
|
|
|
|
1174
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
# end package Mail::POP3Client |
1176
|
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
# Autoload methods go after =cut, and are processed by the autosplit program. |
1178
|
|
|
|
|
|
|
|
1179
|
|
|
|
|
|
|
1; |
1180
|
|
|
|
|
|
|
__END__ |