File Coverage

blib/lib/Mail/POP3Client.pm
Criterion Covered Total %
statement 16 544 2.9
branch 0 330 0.0
condition 0 103 0.0
subroutine 6 64 9.3
pod 31 53 58.4
total 53 1094 4.8


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   56648 use strict;
  2         13  
  2         49  
15 2     2   7 use warnings;
  2         3  
  2         37  
16 2     2   8 use Carp;
  2         3  
  2         133  
17 2     2   859 use IO::Socket qw(SOCK_STREAM);
  2         36961  
  2         7  
18              
19 2     2   376 use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
  2         4  
  2         11032  
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.20';
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 66 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__