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