Mail-IMAPClient
view release on metacpan or search on metacpan
lib/Mail/IMAPClient.pm view on Meta::CPAN
# _{name} methods are undocumented and meant to be private.
require 5.008_001;
use strict;
use warnings;
package Mail::IMAPClient;
our $VERSION = '3.43';
use Mail::IMAPClient::MessageSet;
use IO::Socket qw(:crlf SOL_SOCKET SO_KEEPALIVE);
use IO::Select ();
use Carp qw(carp); #local $SIG{__WARN__} = \&Carp::cluck; #DEBUG
use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
use Errno qw(EAGAIN EBADF ECONNRESET EPIPE);
use List::Util qw(first min max sum);
use MIME::Base64 qw(encode_base64 decode_base64);
use File::Spec ();
use constant APPEND_BUFFER_SIZE => 1024 * 1024;
use constant {
Unconnected => 0,
Connected => 1, # connected; not logged in
Authenticated => 2, # logged in; no mailbox selected
Selected => 3, # mailbox selected
};
use constant {
INDEX => 0, # Array index for output line number
TYPE => 1, # Array index for line type (OUTPUT, INPUT, or LITERAL)
DATA => 2, # Array index for output line data
};
my %SEARCH_KEYS = map { ( $_ => 1 ) } qw(
ALL ANSWERED BCC BEFORE BODY CC DELETED DRAFT FLAGGED
FROM HEADER KEYWORD LARGER NEW NOT OLD ON OR RECENT
SEEN SENTBEFORE SENTON SENTSINCE SINCE SMALLER SUBJECT
TEXT TO UID UNANSWERED UNDELETED UNDRAFT UNFLAGGED
UNKEYWORD UNSEEN);
# modules require(d) during runtime when applicable
my %Load_Module = (
"Compress-Zlib" => "Compress::Zlib",
"INET" => "IO::Socket::INET",
"IP" => "IO::Socket::IP",
"SSL" => "IO::Socket::SSL",
"UNIX" => "IO::Socket::UNIX",
"BodyStructure" => "Mail::IMAPClient::BodyStructure",
"Envelope" => "Mail::IMAPClient::BodyStructure::Envelope",
"Thread" => "Mail::IMAPClient::Thread",
);
sub _load_module {
my $self = shift;
my $modkey = shift;
my $module = $Load_Module{$modkey} || $modkey;
my $err = do {
local ($@);
eval "require $module";
$@;
};
if ($err) {
$self->LastError("Unable to load '$module': $err");
return undef;
}
return $module;
}
sub _debug {
my $self = shift;
return unless $self->Debug;
my $text = join '', @_;
$text =~ s/$CRLF/\n /og;
$text =~ s/\s*$/\n/;
#use POSIX (); $text = POSIX::strftime("%F %T ", localtime).$text; #DEBUG
my $fh = $self->{Debug_fh} || \*STDERR;
print $fh $text;
}
BEGIN {
# set-up accessors
foreach my $datum (
qw(Authcallback Authmechanism Authuser Buffer Count Compress
Debug Debug_fh Domain Folder Ignoresizeerrors Keepalive
Maxappendstringlength Maxcommandlength Maxtemperrors
Password Peek Port Prewritemethod Proxy Ranges Readmethod
Readmoremethod Reconnectretry Server Showcredentials
Socketargs Ssl Starttls Supportedflags Timeout Uid User)
)
{
no strict 'refs';
*$datum = sub {
@_ > 1 ? ( $_[0]->{$datum} = $_[1] ) : $_[0]->{$datum};
};
}
}
sub LastError {
my $self = shift;
@_ or return $self->{LastError};
my $err = shift;
lib/Mail/IMAPClient.pm view on Meta::CPAN
$self->_debug( "LITERAL: received literal in line "
. "$current_line of length $expected_size; attempting to "
. "retrieve from the "
. length($iBuffer)
. " bytes in: $iBuffer<END_OF_iBuffer>" );
my $litstring;
if ( length $iBuffer >= $expected_size ) {
# already received all data
$litstring = substr $iBuffer, 0, $expected_size, '';
}
else { # literal data still to arrive
$litstring = $iBuffer;
$iBuffer = '';
my $litreadb = length($litstring);
my $temperrs = 0;
my $maxagain = $self->Maxtemperrors;
undef $maxagain if $maxagain and lc($maxagain) eq 'unlimited';
while ( $expected_size > $litreadb ) {
if ($timeout) {
my $rc = $self->_read_more( $socket, $timeout );
return undef unless ( $rc > 0 );
}
else { # 25 ms before retry
CORE::select( undef, undef, undef, 0.025 );
}
# $litstring is emptied when $literal_cbtype is GLOB
my $ret =
$self->_sysread( $socket, \$litstring,
$expected_size - $litreadb,
length($litstring) );
if ($timeout) {
if ( defined $ret ) {
$temperrs = 0;
}
else {
$emsg = "error while reading data from server: $!";
if ( $! == ECONNRESET ) {
$self->State(Unconnected);
}
elsif ( $! == EAGAIN ) {
if ( defined $maxagain
&& $temperrs++ >= $maxagain )
{
$emsg .= " ($temperrs)";
}
else {
undef $emsg;
next; # try again
}
}
}
}
# EOF: note IO::Socket::SSL does not support eof()
if ( defined $ret and $ret == 0 ) {
$emsg = "socket closed while reading data from server";
$self->State(Unconnected);
}
elsif ( defined $ret and $ret > 0 ) {
$litreadb += $ret;
# conserve memory when using literal_callback GLOB
if ( $literal_cbtype eq "GLOB" ) {
print $literal_callback $litstring;
$litstring = "" unless ($emsg);
}
}
$self->_debug( "Received ret="
. ( defined($ret) ? $ret : "<undef>" )
. " $litreadb of $expected_size" );
# save errors and return
if ($emsg) {
$self->LastError($emsg);
$self->_record(
$transno,
[
$self->_next_index($transno), "ERROR",
"$transno * NO $emsg"
]
);
$litstring = "" unless defined $litstring;
$self->_debug( "ERROR while processing LITERAL, "
. " buffer=\n"
. $litstring
. "<END>\n" );
return undef;
}
}
}
if ( defined $litstring ) {
if ( $literal_cbtype eq "GLOB" ) {
print $literal_callback $litstring;
}
elsif ( $literal_cbtype eq "CODE" ) {
$literal_callback->($litstring);
}
}
push @$oBuffer, [ $index++, 'LITERAL', $litstring ]
if ( $literal_cbtype ne "GLOB" );
}
}
$self->_debug( "Read: " . join "", map { "\t" . $_->[DATA] } @$oBuffer )
if ( $self->Debug );
@$oBuffer ? $oBuffer : undef;
}
sub _sysread {
my ( $self, $fh, $buf, $len, $off ) = @_;
my $rm = $self->Readmethod;
$rm ? $rm->(@_) : sysread( $fh, $$buf, $len, $off );
}
sub _read_more {
my $self = shift;
my $rm = $self->Readmoremethod;
$rm ? $rm->( $self, @_ ) : $self->__read_more(@_);
}
sub __read_more {
my $self = shift;
my $opt = ref( $_[0] ) eq "HASH" ? shift : {};
my ( $socket, $timeout ) = @_;
# IO::Socket::SSL buffers some data internally, so there might be some
# data available from the previous sysread of which the file-handle
# (used by select()) doesn't know of.
return 1 if $socket->isa("IO::Socket::SSL") && $socket->pending;
my $rvec = '';
vec( $rvec, fileno($socket), 1 ) = 1;
my $rc = CORE::select( $rvec, undef, $rvec, $timeout );
# fast track success
return $rc if $rc > 0;
# by default set an error on timeout
my $err_on_timeout =
exists $opt->{error_on_timeout} ? $opt->{error_on_timeout} : 1;
# $rc is 0 then we timed out
return $rc if !$rc and !$err_on_timeout;
# set the appropriate error and return
my $transno = $self->Transaction;
my $msg =
( $rc ? "error($rc)" : "timeout" )
. " waiting ${timeout}s for data from server"
. ( $! ? ": $!" : "" );
$self->LastError($msg);
$self->_record( $transno,
[ $self->_next_index($transno), "ERROR", "$transno * NO $msg" ] );
$self->_disconnect; # BUG: can not handle timeouts gracefully
return $rc;
}
sub _trans_index() {
sort { $a <=> $b } keys %{ $_[0]->{History} };
}
# all default to last transaction
sub _transaction(;$) {
@{ $_[0]->{History}{ $_[1] || $_[0]->Transaction } || [] };
}
sub _trans_data(;$) {
map { $_->[DATA] } $_[0]->_transaction( $_[1] );
}
sub _escaped_trans_data(;$) {
my ( $self, $trans ) = @_;
my @a;
my $prevwasliteral = 0;
foreach my $line ( $self->_transaction($trans) ) {
next unless defined $line;
my $data = $line->[DATA];
# literal is appended to previous data
if ( $self->_is_literal($line) ) {
$data = $self->Escape($data);
$a[-1] .= qq("$data");
$prevwasliteral = 1;
}
else {
if ($prevwasliteral) {
$a[-1] .= $data;
( run in 0.813 second using v1.01-cache-2.11-cpan-39bf76dae61 )