Subject: | connection closes while waiting for folder lock |
Hi. I seem to have found a problem.
Using dovecot 1.2.9 on CentOS 5.3 with Perl 5.8.8.
I run `lockfile -ml` and then access my mail via IMAP with Thunderbird
(Icedove). It sits there waiting while accessing the mail. When I run
`lockfile -mu` on the server, Thunderbird continues fine and downloads
the messages on the same connection. So, I do not think this is a
problem with dovecot 1.2.9, though it might be, I will ask on the list.
With Mail::IMAPClient, the same process fails. message_string() returns
undef. body_string() completely borks.
This used to work in my more complicated test scripts. So I don't know,
maybe it is something that changed in dovecot or the dovecot options,
but I'm having trouble figuring out what. Perl in CentOS often behaves
strangely.
I wrote a test script to demonstrate this, attached, as well as debug
info. The test script only works in unix/linux with an imap server set
to respect dotlock files. Does it happen for you?
Thanks! --mark--
Subject: | mail_imapclient_bug.t |
#!/usr/bin/perl
# this test demonstrates a bug in Mail::IMAPClient without use of any DeSpam modules
use strict;
use warnings FATAL => 'all';
use English '-no_match_vars';
use FindBin;
my $imap_logfile = "/tmp/imap_debug.$FindBin::Script.log";
use Test::More;
use YAML;
use IO::File;
#exit(0);
$ENV{PATH} = '/sbin:/usr/sbin:/bin:/usr/bin';
use Mail::Sendmail;
use Mail::IMAPClient;
use User::pwent;
use File::Slurp;
use Term::ReadLine;
use POSIX;
#plan skip_all => 'Unimplemented';
plan tests => 6;
my $pw = getpwuid($REAL_USER_ID);
my $username = $pw->name;
verify_continue();
my $inbox_file = "/var/spool/mail/$username";
diag("inbox: $inbox_file");
clear_mailbox($inbox_file);
use Mail::Sendmail;
sendmail(
To => "$username\@localhost",
From => "$username\@localhost",
Subject => "test message",
Message => "foobar\n",
) || die "cannot send test mail: $Mail::Sendmail::error\n";
do { diag("sleeping for delivery..."); sleep 2 } until -s $inbox_file;
my $inbox = read_file($inbox_file) || die "could not read test mail: $OS_ERROR\n";
#diag($inbox);
# here's how we check if the message is right:
my $patmatch = qr{ ^ Subject: \s+ test \s+ message \r?\n .* ^ foobar \r?\n }mxs;
ok( $inbox =~ $patmatch, 'test message arrived' );
# get imap connection, try to connect while inbox is not locked
my $imap = get_mail_imapclient($username);
# can we read the mail?
$imap->select('INBOX');
my @msg_uids = $imap->messages();
diag("message uids in box: \n".Dump(\@msg_uids));
my $try_1 = $imap->message_string($msg_uids[0]);
ok( $try_1 =~ $patmatch, 'try 1 matches message' );
$imap->close;
$imap->logout;
undef $imap;
# lock inbox
system("lockfile -ml") == 0 || die "cannot `lockfile -ml`: $OS_ERROR\n";
ok(-e "$inbox_file.lock", 'lockfile present after `lockfile -ml`');
# install a sig handler to remove it nicely if we bail:
install_handler();
# while inbox is locked, try connection, setting alarm to remove lockfile.
local $SIG{ALRM} = sub {
system("lockfile -mu") == 0 || die "cannot `lockfile -mu`: $OS_ERROR\n";
ok(!-e "$inbox_file.lock", 'alarmsub lock gone after `lockfile -mu`');
};
diag("setting alarm to remove lockfile in 4 seconds...");
alarm 4;
# get imap connection, try to connect while inbox is locked
$imap = get_mail_imapclient($username);
# can we read the mail?
$imap->select('INBOX');
@msg_uids = $imap->messages();
diag("message uids in box: \n".Dump(\@msg_uids));
diag("when alarm is triggered, read should continue here.");
diag("it does not, message_string is undefined and body_string causes error.");
my $try_2 = $imap->message_string($msg_uids[0]);
ok( $try_2 && $try_2 =~ $patmatch, 'try 2 matches message' );
my $body_try = $imap->body_string($msg_uids[0]);
ok( $body_try && $body_try =~ m{ foobar }mxs, 'get something from body_string too' );
$imap->close;
$imap->logout;
undef $imap;
exit(0);
#####################
sub get_mail_imapclient {
my ($username) = @_;
diag("opening $imap_logfile");
my $imap = Mail::IMAPClient->new(
Server => 'localhost',
User => $username,
Password => get_password(),
Uid => 1,
Debug => 1,
Timeout => 60,
Debug_fh => IO::File->new(">>$imap_logfile")
|| die "cannot open $imap_logfile: $OS_ERROR",
);
return $imap;
}
my $password;
sub get_password {
my $term = Term::ReadLine->new($PROGRAM_NAME);
my $attribs = $term->Attribs;
$attribs->{redisplay_function} = $attribs->{shadow_redisplay};
ENTER_PASS:
while (1) {
last ENTER_PASS if $password;
print "Enter password for this account:\n";
chomp( $password = $term->readline("password > ") );
}
return $password;
}
sub verify_continue {
print "# this will completely erase your account's mail. continue? [y/N] ";
chomp(my $answ = <STDIN>);
exit if !$answ || lc $answ ne 'y';
print "# no really, it will clear the inbox for user '$username' on this system.\n";
print "# continue? [y/N] ";
chomp(my $answ2 = <STDIN>);
exit if !$answ2 || lc $answ2 ne 'y';
return;
}
# install the sig handler AFTER getting the lock file
sub unlock {
my $signame = shift;
warn "caught signal SIG$signame, exiting...\n" if $signame;
system("lockfile -mu") == 0 || die "cannot `lockfile -mu`: $OS_ERROR\n";
}
sub install_handler {
my $sigset = POSIX::SigSet->new();
my $action = POSIX::SigAction->new(
'unlock',
$sigset,
&POSIX::SA_NODEFER,
);
POSIX::sigaction($_, $action) for # see `man 7 signal`
&POSIX::SIGHUP, # kill -1
&POSIX::SIGINT, # types ctl-c
&POSIX::SIGQUIT, # types something (??)
&POSIX::SIGKILL, # doesn't trap this one (-9) or output is lost
&POSIX::SIGABRT, # (??)
&POSIX::SIGPIPE, # broken pipe - bad rsync ipc?
&POSIX::SIGTERM, # regular kill (-15)
;
}
sub clear_mailbox {
my ($file) = @_;
open my $fh, '>', $file || die "cannot clear $file: $OS_ERROR\n";
close $file;
}
Subject: | imap_debug.mail_imapclient_bug.t.log |
Message body is not shown because it is too large.