This commit is contained in:
Nick Bebout 2011-03-12 02:44:51 +00:00
parent 7299eabfd8
commit 95aab825e8
49 changed files with 885 additions and 234 deletions

View file

@ -1,17 +1,32 @@
RCS file: RCS/imapsync,v RCS file: RCS/imapsync,v
Working file: imapsync Working file: imapsync
head: 1.300 head: 1.303
branch: branch:
locks: strict locks: strict
gilles: 1.300 gilles: 1.303
access list: access list:
symbolic names: symbolic names:
keyword substitution: kv keyword substitution: kv
total revisions: 300; selected revisions: 300 total revisions: 303; selected revisions: 303
description: description:
---------------------------- ----------------------------
revision 1.300 locked by: gilles; revision 1.303 locked by: gilles;
date: 2010/01/20 04:12:52; author: gilles; state: Exp; lines: +13 -12
cosmetic changes.
----------------------------
revision 1.302
date: 2010/01/20 03:34:59; author: gilles; state: Exp; lines: +59 -51
Flags are now exactly synced from host1 to host2.
Previous releases just added flags, It was a wrong behavior
since when a \Seen flag is removed on host1 a sync have to
remove it on host2. imapsync is not imapadd.
----------------------------
revision 1.301
date: 2010/01/18 06:24:16; author: gilles; state: Exp; lines: +92 -23
Added TLSv1 support.
----------------------------
revision 1.300
date: 2010/01/16 03:34:37; author: gilles; state: Exp; lines: +250 -250 date: 2010/01/16 03:34:37; author: gilles; state: Exp; lines: +250 -250
Changed name of variables. "from" replaced by imap1 "to" by imap2. Changed name of variables. "from" replaced by imap1 "to" by imap2.
f_* replaced by h1_* f_* replaced by h1_*

View file

@ -5,6 +5,27 @@ Changes from 2.99_01 to 3.16 made by Mark Overmeer
Changes from 0.09 to 2.99_01 made by David Kernen Changes from 0.09 to 2.99_01 made by David Kernen
- Potential compatibility issues from 3.17+ highlighted with '*' - Potential compatibility issues from 3.17+ highlighted with '*'
version 3.21: Tue Sep 22 19:45:13 EDT 2009
- rt.cpan.org#49691: rewrite of fetch_hash to resolve several issues
[Robert Norris]
includes new tests via t/fetch_hash.t
- rt.cpan.org#48980: (enhancement) add support for XLIST extension
[Robert Norris]
- rt.cpan.org#49024: NIL personal name returned by *_addresses methods
[Dmitry Bigunyak]
- rt.cpan.org#49401: IMAPClient expunge fails (unless folder arg used)
[Gary Baluha]
- update/clarify close and expunge documentation a little
version 3.20: Fri Aug 21 17:40:40 EDT 2009
- added file/tests in t/simple.t
- added methods Rfc3501_date/Rfc3501_datetime
used by deprecated methods Rfc2060_date/Rfc2060_datetime
rt.cpan.org#48510: Rfc3501_date/Rfc3501_datetime methods do
not exist [sedmonds]
- login() hack to quote an empty password
rt.cpan.org#48107: Cannot LOGIN with empty password [skunk]
version 3.19: Fri Jun 19 14:59:15 EDT 2009 version 3.19: Fri Jun 19 14:59:15 EDT 2009
- *search() backwards compat: caller must quote single arg properly - *search() backwards compat: caller must quote single arg properly
rt.cpan.org#47044: $imap->search does not return [ekuemmer] rt.cpan.org#47044: $imap->search does not return [ekuemmer]

View file

@ -32,8 +32,10 @@ prepare_dist
sample.perldb sample.perldb
t/basic.t t/basic.t
t/bodystructure.t t/bodystructure.t
t/fetch_hash.t
t/messageset.t t/messageset.t
t/pod.t t/pod.t
t/simple.t
t/thread.t t/thread.t
test_template.txt test_template.txt
META.yml Module meta-data (added by MakeMaker) META.yml Module meta-data (added by MakeMaker)

View file

@ -1,7 +1,7 @@
# http://module-build.sourceforge.net/META-spec.html # http://module-build.sourceforge.net/META-spec.html
#XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX# #XXXXXXX This is a prototype!!! It will change in the future!!! XXXXX#
name: Mail-IMAPClient name: Mail-IMAPClient
version: 3.19 version: 3.21
version_from: lib/Mail/IMAPClient.pm version_from: lib/Mail/IMAPClient.pm
installdirs: site installdirs: site
requires: requires:

View file

@ -5,7 +5,7 @@ use strict;
use warnings; use warnings;
package Mail::IMAPClient; package Mail::IMAPClient;
our $VERSION = '3.19'; our $VERSION = '3.21';
use Mail::IMAPClient::MessageSet; use Mail::IMAPClient::MessageSet;
@ -139,10 +139,11 @@ my @dow = qw(Sun Mon Tue Wed Thu Fri Sat);
my @mnt = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec); my @mnt = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
sub Rfc822_date { sub Rfc822_date {
my $class = shift; #Date: Fri, 09 Jul 1999 13:10:55 -0000# my $class = shift;
my $date = $class =~ /^\d+$/ ? $class : shift; # method or function? my $date = $class =~ /^\d+$/ ? $class : shift; # method or function?
my @date = gmtime $date; my @date = gmtime($date);
#Date: Fri, 09 Jul 1999 13:10:55 -0000
sprintf( sprintf(
"%s, %02d %s %04d %02d:%02d:%02d -%04d", "%s, %02d %s %04d %02d:%02d:%02d -%04d",
$dow[ $date[6] ], $dow[ $date[6] ],
@ -154,19 +155,31 @@ sub Rfc822_date {
} }
# The following methods create valid dates for use in IMAP search strings # The following methods create valid dates for use in IMAP search strings
# - provide Rfc2060* methods/functions for backwards compatibility
sub Rfc2060_date { sub Rfc2060_date {
my $class = shift; # 11-Jan-2000 $_[0] =~ /^\d+$/ ? Rfc3501_date(@_) : shift->Rfc3501_date(@_);
my $stamp = $class =~ /^\d+$/ ? $class : shift; # method or function }
my @date = gmtime $stamp;
sub Rfc3501_date {
my $class = shift;
my $stamp = $class =~ /^\d+$/ ? $class : shift;
my @date = gmtime($stamp);
# 11-Jan-2000
sprintf( "%02d-%s-%04d", $date[3], $mnt[ $date[4] ], $date[5] + 1900 ); sprintf( "%02d-%s-%04d", $date[3], $mnt[ $date[4] ], $date[5] + 1900 );
} }
sub Rfc2060_datetime($;$) { sub Rfc2060_datetime($;$) {
my ( $class, $stamp, $zone ) = @_; # 11-Jan-2000 04:04:04 +0000 $_[0] =~ /^\d+$/ ? Rfc3501_datetime(@_) : shift->Rfc3501_datetime(@_);
$zone ||= '+0000'; }
my @date = gmtime $stamp;
sub Rfc3501_datetime($;$) {
my $class = shift;
my $stamp = $class =~ /^\d+$/ ? $class : shift;
my $zone = shift || '+0000';
my @date = gmtime($stamp);
# 11-Jan-2000 04:04:04 +0000
sprintf( sprintf(
"%02d-%s-%04d %02d:%02d:%02d %s", "%02d-%s-%04d %02d:%02d:%02d %s",
$date[3], $date[3],
@ -367,7 +380,8 @@ sub login {
return undef unless ( defined($passwd) and defined($id) ); return undef unless ( defined($passwd) and defined($id) );
if ( $passwd =~ m/\W/ ) { # need to quote # BUG: should use Quote() with $passwd and $id
if ( $passwd eq "" or $passwd =~ m/\W/ ) {
$passwd =~ s/(["\\])/\\$1/g; $passwd =~ s/(["\\])/\\$1/g;
$passwd = qq("$passwd"); $passwd = qq("$passwd");
} }
@ -465,6 +479,12 @@ sub _list_or_lsub {
sub list { shift->_list_or_lsub( "LIST", @_ ) } sub list { shift->_list_or_lsub( "LIST", @_ ) }
sub lsub { shift->_list_or_lsub( "LSUB", @_ ) } sub lsub { shift->_list_or_lsub( "LSUB", @_ ) }
sub xlist {
my ($self) = @_;
return undef unless $self->has_capability("XLIST");
shift->_list_or_lsub( "XLIST", @_ );
}
sub _folders_or_subscribed { sub _folders_or_subscribed {
my ( $self, $method, $what ) = @_; my ( $self, $method, $what ) = @_;
my @folders; my @folders;
@ -519,6 +539,25 @@ sub folders {
return wantarray ? @folders : \@folders; return wantarray ? @folders : \@folders;
} }
sub xlist_folders {
my ($self) = @_;
my $xlist = $self->xlist;
return undef unless defined $xlist;
my %xlist;
my $xlist_re = qr/\A\\(Inbox|AllMail|Trash|Drafts|Sent|Spam|Starred)\Z/;
for my $resp (@$xlist) {
my $rec = $self->_list_or_lsub_response_parse($resp);
next unless defined $rec->{name};
for my $attr ( @{ $rec->{attrs} } ) {
$xlist{$1} = $rec->{name} if ( $attr =~ $xlist_re );
}
}
return wantarray ? %xlist : \%xlist;
}
sub subscribed { sub subscribed {
my ( $self, $what ) = @_; my ( $self, $what ) = @_;
my @folders = $self->_folders_or_subscribed( "lsub", $what ); my @folders = $self->_folders_or_subscribed( "lsub", $what );
@ -1325,7 +1364,7 @@ sub _get_response {
if ($code) { if ($code) {
$code = uc($code) unless ( $good and $code eq $good ); $code = uc($code) unless ( $good and $code eq $good );
# on a successful LOGOUT $code is OK not BYE # on successful LOGOUT $code is OK (not BYE!) see RFC 3501 sect 7.1.5
if ( $code eq 'BYE' ) { if ( $code eq 'BYE' ) {
$self->State(Unconnected); $self->State(Unconnected);
$self->LastError($byemsg) if $byemsg; $self->LastError($byemsg) if $byemsg;
@ -1759,7 +1798,7 @@ sub _disconnect {
$self; $self;
} }
# LIST or LSUB Response # LIST/XLIST/LSUB Response
# Contents: name attributes, hierarchy delimiter, name # Contents: name attributes, hierarchy delimiter, name
# Example: * LIST (\Noselect) "/" ~/Mail/foo # Example: * LIST (\Noselect) "/" ~/Mail/foo
# NOTE: in _list_response_preprocess we append literal data so we need # NOTE: in _list_response_preprocess we append literal data so we need
@ -1772,10 +1811,10 @@ sub _list_or_lsub_response_parse {
$resp =~ s/\015?\012$//; $resp =~ s/\015?\012$//;
if ( if (
$resp =~ / ^\* \s+ (?:LIST|LSUB) \s+ # * LIST or LSUB $resp =~ / ^\* \s+ (?:LIST|XLIST|LSUB) \s+ # * LIST|XLIST|LSUB
\( ([^\)]*) \) \s+ # (attrs) \( ([^\)]*) \) \s+ # (attrs)
(?: \" ([^"]*) \" | NIL ) \s # "delimiter" or NIL (?: \" ([^"]*) \" | NIL ) \s # "delimiter" or NIL
(?:\s*\" (.*) \" | (.*) ) # "name" or name (?:\s*\" (.*) \" | (.*) ) # "name" or name
/ix /ix
) )
{ {
@ -2003,55 +2042,84 @@ sub fetch_hash {
s/([\( ])FAST([\) ])/${1}FLAGS INTERNALDATE RFC822\.SIZE$2/i; s/([\( ])FAST([\) ])/${1}FLAGS INTERNALDATE RFC822\.SIZE$2/i;
s/([\( ])FULL([\) ])/${1}FLAGS INTERNALDATE RFC822\.SIZE ENVELOPE BODY$2/i; s/([\( ])FULL([\) ])/${1}FLAGS INTERNALDATE RFC822\.SIZE ENVELOPE BODY$2/i;
} }
my %words = map { uc($_) => 1 } @words;
my $output = $self->fetch( $msgs, "($what)" ) or return undef; my $output = $self->fetch( $msgs, "($what)" ) or return undef;
for ( my $x = 0 ; $x <= $#$output ; $x++ ) { while ( my $l = shift @$output ) {
my $entry = {}; next if $l !~ m/^\*\s(\d+)\sFETCH\s\(/g;
my $l = $output->[$x]; my ( $mid, $entry ) = ( $1, {} );
my ( $key, $value );
ATTR:
while ( $l !~ m/\G\s*\)\s*$/gc ) {
if ( $l =~ m/\G\s*([\w\d\.]+(?:\[[^\]]*\])?)\s*/gc ) {
$key = uc($1);
}
elsif ( !defined $key ) {
# some kind of malformed response
$self->LastError("Invalid item name in FETCH response: $l");
return undef;
}
if ( $l =~ m/\G\s*$/gc ) {
$value = shift @$output;
$entry->{$key} = $value;
$l = shift @$output;
next ATTR;
}
elsif ( $l =~ m/\G(?:"([^"]+)"|([^()\s]+))\s*/gc ) {
$value = defined $1 ? $1 : $2;
$entry->{$key} = $value;
next ATTR;
}
elsif ( $l =~ m/\G\(/gc ) {
my $depth = 1;
$value = "";
while ( $l =~ m/\G(\(|\)|[^()]+)/gc ) {
my $stuff = $1;
if ( $stuff eq "(" ) {
$depth++;
$value .= "(";
}
elsif ( $stuff eq ")" ) {
$depth--;
if ( $depth == 0 ) {
$entry->{$key} = $value;
next ATTR;
}
$value .= ")";
}
else {
$value .= $stuff;
}
}
m/\G\s*/gc;
}
else {
$self->LastError("Invalid item value in FETCH response: $l");
return undef;
}
}
if ( $self->Uid ) { if ( $self->Uid ) {
my $uid = $l =~ /\bUID\s+(\d+)/i ? $1 : undef; $uids->{ $entry->{UID} } = $entry;
$uid or next;
if ( $uids->{$uid} ) { $entry = $uids->{$uid} }
else { $uids->{$uid} ||= $entry }
} }
else { else {
my $mid = $l =~ /^\* (\d+) FETCH/i ? $1 : undef; $uids->{$mid} = $entry;
$mid or next;
if ( $uids->{$mid} ) { $entry = $uids->{$mid} }
else { $uids->{$mid} ||= $entry }
} }
foreach my $w (@words) { for my $word ( keys %$entry ) {
if ( $l =~ /\Q$w\E\s*$/i ) { next if exists $words{$word};
$entry->{$w} = $output->[ $x + 1 ];
$entry->{$w} =~ s/(?:$CR?$LF)+$//og; if ( my ($stuff) = $word =~ m/^BODY(\[.*)$/ ) {
chomp $entry->{$w}; next if exists $words{ "BODY.PEEK" . $stuff };
}
elsif (
$l =~ /\( # open paren followed by ...
(?:.*\s)? # ...optional stuff and a space
\Q$w\E\s # escaped fetch field<sp>
(?:" # then: a dbl-quote
(\\.| # then bslashed anychar(s) or ...
[^"]+) # ... nonquote char(s)
"| # then closing quote; or ...
\( # ...an open paren
([^\)]*) # ... non-close-paren char(s)
\)| # then closing paren; or ...
(\S+)) # unquoted string
(?:\s.*)? # possibly followed by space-stuff
\) # close paren
/xi
)
{
$entry->{$w} = defined $1 ? $1 : defined $2 ? $2 : $3;
} }
delete $entry->{$word};
} }
} }
return wantarray ? %$uids : $uids; return wantarray ? %$uids : $uids;
} }
@ -2099,16 +2167,20 @@ sub close {
sub expunge { sub expunge {
my ( $self, $folder ) = @_; my ( $self, $folder ) = @_;
my $old = $self->Folder || ''; return undef unless ( defined $folder or defined $self->Folder );
if ( defined $folder && $folder eq $old ) {
my $old = defined $self->Folder ? $self->Folder : '';
if ( !defined($folder) || $folder eq $old ) {
$self->_imap_command('EXPUNGE') $self->_imap_command('EXPUNGE')
or return undef; or return undef;
} }
else { else {
$self->select($folder) or return undef; $self->select($folder) or return undef;
my $succ = $self->_imap_command('EXPUNGE'); my $succ = $self->_imap_command('EXPUNGE');
$self->select($old) or return undef; # BUG? this should be fatal?
$succ or return undef; # if $old eq '' IMAP4 select should close $folder without EXPUNGE
return undef unless ( $self->select($old) and $succ );
} }
return wantarray ? $self->History : $self->Results; return wantarray ? $self->History : $self->Results;
@ -2117,6 +2189,8 @@ sub expunge {
sub uidexpunge { sub uidexpunge {
my ( $self, $msgspec ) = ( shift, shift ); my ( $self, $msgspec ) = ( shift, shift );
return undef unless $self->has_capability("UIDPLUS");
my $msg = my $msg =
UNIVERSAL::isa( $msgspec, 'Mail::IMAPClient::MessageSet' ) UNIVERSAL::isa( $msgspec, 'Mail::IMAPClient::MessageSet' )
? $msgspec ? $msgspec

View file

@ -754,16 +754,14 @@ Example:
$imap->close or die "Could not close: $@\n"; $imap->close or die "Could not close: $@\n";
The B<close> method is implemented via the default method and is used The B<close> method is used to close the currently selected folder via
to close the currently selected folder via the CLOSE IMAP client the CLOSE IMAP client command. According to RFC3501, the CLOSE
command. According to RFC3501, the CLOSE command performs an implicit command performs an implicit EXPUNGE, which means that any messages
EXPUNGE, which means that any messages that you've flagged as that are flagged as I<\Deleted> (i.e. with the L</delete_message>
I<\Deleted> (say, with the L</delete_message> method) will now be method) will now be deleted. If you haven't deleted any messages then
deleted. If you haven't deleted any messages then B<close> can be B<close> can be thought of as an "unselect".
thought of as an "unselect".
Note again that this closes the currently selected folder, not the Note: this closes the currently selected folder, not the IMAP session.
IMAP session.
See also L</delete_message>, L</expunge>, and RFC3501. See also L</delete_message>, L</expunge>, and RFC3501.
@ -1063,19 +1061,14 @@ Example:
The B<expunge> method accepts one optional argument, a folder name. The B<expunge> method accepts one optional argument, a folder name.
It expunges the folder specified as the argument, or the currently It expunges the folder specified as the argument, or the currently
selected folder if no argument is supplied. selected folder (if any) when no argument is supplied.
Although RFC3501 does not permit optional arguments (like a folder Although RFC3501 does not permit optional arguments (like a folder
name) to the EXPUNGE client command, the L</expunge> method does, name) to the EXPUNGE client command, the L</expunge> method does.
which is especially interesting given that the L</expunge> method Note: expunging a folder deletes the messages that have the \Deleted
doesn't technically exist. In case you're curious, expunging a folder flag set (i.e. messages flagged via L</delete_message>).
deletes the messages that you thought were already deleted via
L</delete_message> but really weren't, which means you have to use a
method that doesn't exist to delete messages that you thought didn't
exist. (Seriously, I'm not making any of this stuff up.)
Or you could use the L</close> method, which deselects as well as See also the L</close> method, which "deselects" as well as expunges.
expunges and which likewise doesn't technically exist.
=head2 fetch =head2 fetch
@ -1168,27 +1161,12 @@ This would result in L<Data::Dumper> output similar to the following:
} }
}; };
You can specify I<BODY[HEADER.FIELDS ($fieldlist)> as an argument, but By itself this method may be useful for, say, speeding up programs that
you should keep the following in mind if you do: want the size of every message in a folder. It issues one command and
receives one (possibly long!) response from the server. However, it's
B<1.> You can only specify one argument of this type per call. If you true power lies in the as-yet-unwritten methods that will rely on this
need multiple fields, then you'll have to call B<fetch_hashref> method to deliver even more powerful result hashes. Look for more new
multiple times, each time specifying a different FETCH attribute but function in later releases.
the same.
B<2.> Fetch operations that return RFC822 message headers return the
whole header line, including the field name and the colon. For
example, if you do a C<$imap-E<gt>fetch_hash("BODY[HEADER.FIELDS
(Subject)]")>, you will get back subject lines that start with
"Subject: ".
By itself this method may be useful for, say, speeding up programs
that want the size of every message in a folder. It issues one
command and receives one (possibly long!) response from the server.
However, it's true power lies in the as-yet-unwritten methods that
will rely on this method to deliver even more powerful result hashes
(and which may even remove the restrictions mentioned in B<1> and
B<2>, above). Look for more new function in later releases.
This method is new with version 2.2.3 and is thus still experimental. This method is new with version 2.2.3 and is thus still experimental.
If you decide to try this method and run into problems, please see the If you decide to try this method and run into problems, please see the
@ -1269,6 +1247,76 @@ Notice that if you just want to list a folder's subfolders (and not
the folder itself), then you need to include the hierarchy separator the folder itself), then you need to include the hierarchy separator
character (as returned by the L</separator> method). character (as returned by the L</separator> method).
=head2 xlist_folders
Example:
my $xlist = $imap->xlist_folders
or die "Could not get xlist folders.\n";
IMAP servers implementing the XLIST extension (such as Gmail)
designate particular folders to be used for particular functions.
This is useful in the case where you want to know which folder should
be used for Trash when the actual folder name can't be predicted
(e.g. in the case of Gmail, the folder names change depending on the
user's locale settings).
The B<xlist_folders> method returns a hash listing any "xlist" folder
names, with the values listing the actual folders that should be used
for those names. For example, using this method with a Gmail user
using the English (US) locale might give this output from
L<Data::Dumper>:
$VAR1 = {
'Inbox' => 'Inbox',
'AllMail' => '[Gmail]/All Mail',
'Trash' => '[Gmail]/Trash',
'Drafts' => '[Gmail]/Drafts',
'Sent' => '[Gmail]/Sent Mail',
'Spam' => '[Gmail]/Spam',
'Starred' => '[Gmail]/Starred'
};
The same list for a user using the French locale might look like this:
$VAR1 = {
'Inbox' => 'Bo&AO4-te de r&AOk-ception',
'AllMail' => '[Gmail]/Tous les messages',
'Trash' => '[Gmail]/Corbeille',
'Drafts' => '[Gmail]/Brouillons',
'Sent' => '[Gmail]/Messages envoy&AOk-s',
'Spam' => '[Gmail]/Spam',
'Starred' => '[Gmail]/Suivis'
};
Mail::IMAPClient recognizes the following "xlist" folder names:
=over 4
=item Inbox
=item AllMail
=item Trash
=item Drafts
=item Sent
=item Spam
=item Starred
=back
These are currently the only ones supported by Gmail. The XLIST
extension is not documented, and there are no other known
implementations other than Gmail, so this list is based on what Gmail
provides.
If the server does not support the XLIST extension, this method
returns undef.
=head2 has_capability =head2 has_capability
Example: Example:
@ -2523,6 +2571,9 @@ lines returned from the UID EXPUNGE command.
B<uidexpunge> returns undef on failure. B<uidexpunge> returns undef on failure.
If the server does not support the UIDPLUS extension, this method
returns undef.
=head2 uidnext =head2 uidnext
Example: Example:

View file

@ -157,7 +157,7 @@ sub _addresses($$$)
foreach ( @{$self->{$name}} ) foreach ( @{$self->{$name}} )
{ my $pn = $_->personalname; { my $pn = $_->personalname;
my $name = $pn && $pn ne 'NIL' ? "$pn " : ''; my $name = $pn && $pn ne 'NIL' ? "$pn " : '';
push @list, $pn. '<'.$_->mailboxname .'@'. $_->hostname.'>'; push @list, $name . '<' . $_->mailboxname . '@' . $_->hostname . '>';
} }
wantarray ? @list wantarray ? @list

View file

@ -0,0 +1,233 @@
#!/usr/bin/perl
#
#
# tests for fetch_hash()
#
# fetch_hash() calls fetch() internally. rather than refactor
# fetch_hash() just for testing, we instead subclass M::IC and use the
# overidden fetch() to feed it test data.
use strict;
use warnings;
use Test::More tests => 18;
BEGIN { use_ok('Mail::IMAPClient') or exit; }
my @tests = (
[
"unquoted value",
[ q{* 1 FETCH (UNQUOTED foobar)}, ],
[ [1], qw(UNQUOTED) ],
{ "1" => { "UNQUOTED" => q{foobar}, } },
],
[
"quoted value",
[ q{* 1 FETCH (QUOTED "foo bar baz")}, ],
[ [1], qw(QUOTED) ],
{ "1" => { "QUOTED" => q{foo bar baz}, }, },
],
[
"parenthesized value",
[ q{* 1 FETCH (PARENS (foo bar))}, ],
[ [1], qw(PARENS) ],
{ "1" => { "PARENS" => q{foo bar}, }, },
],
[
"parenthesized value with quotes",
[ q{* 1 FETCH (PARENS (foo "bar" baz))}, ],
[ [1], qw(PARENS) ],
{ "1" => { "PARENS" => q{foo "bar" baz}, }, },
],
[
"parenthesized value with parens at start",
[ q{* 1 FETCH (PARENS ((foo) bar baz))}, ],
[ [1], qw(PARENS) ],
{ "1" => { "PARENS" => q{(foo) bar baz}, }, },
],
[
"parenthesized value with parens in middle",
[ q{* 1 FETCH (PARENS (foo (bar) baz))}, ],
[ [1], qw(PARENS) ],
{ "1" => { "PARENS" => q{foo (bar) baz}, }, },
],
[
"parenthesized value with parens at end",
[ q{* 1 FETCH (PARENS (foo bar (baz)))}, ],
[ [1], qw(PARENS) ],
{ "1" => { "PARENS" => q{foo bar (baz)}, }, },
],
[
"complex parens",
[ q{* 1 FETCH (PARENS ((((foo) "bar") baz (quux))))}, ],
[ [1], qw(PARENS) ],
{ "1" => { "PARENS" => q{(((foo) "bar") baz (quux))}, }, },
],
[
"basic literal value",
[ q{* 1 FETCH (LITERAL}, q{foo}, q{)}, ],
[ [1], qw(LITERAL) ],
{ "1" => { "LITERAL" => q{foo}, }, },
],
[
"multiline literal value",
[ q{* 1 FETCH (LITERAL}, q{foo\r\nbar\r\nbaz\r\n}, q{)}, ],
[ [1], qw(LITERAL) ],
{ "1" => { "LITERAL" => q{foo\r\nbar\r\nbaz\r\n}, }, },
],
[
"multiple attributes",
[ q{* 1 FETCH (FOO foo BAR bar BAZ baz)}, ],
[ [1], qw(FOO BAR BAZ) ],
{
"1" => {
"FOO" => q{foo},
"BAR" => q{bar},
"BAZ" => q{baz},
},
},
],
[
"dotted attribute",
[ q{* 1 FETCH (FOO.BAR foobar)}, ],
[ [1], qw(FOO.BAR) ],
{ "1" => { "FOO.BAR" => q{foobar}, }, },
],
[
"complex attribute",
[ q{* 1 FETCH (FOO.BAR[BAZ (QUUX)] quuz)}, ],
[ [1], q{FOO.BAR[BAZ (QUUX)]} ],
{ "1" => { q{FOO.BAR[BAZ (QUUX)]} => q{quuz}, }, },
],
[
"BODY.PEEK[] requests match BODY[] responses",
[ q{* 1 FETCH (BODY[] foo)} ],
[ [1], qw(BODY.PEEK[]) ],
{ "1" => { "BODY[]" => q{foo}, }, },
],
[
"BODY.PEEK[] requests match BODY.PEEK[] responses also",
[ q{* 1 FETCH (BODY.PEEK[] foo)} ],
[ [1], qw(BODY.PEEK[]) ],
{ "1" => { "BODY.PEEK[]" => q{foo}, }, },
],
[
"real life example",
[
'* 1 FETCH (UID 541 FLAGS (\\Seen) INTERNALDATE "15-Sep-2009 20:05:45 +1000" RFC822.SIZE 771 BODY[HEADER.FIELDS (TO FROM DATE SUBJECT)]',
'Date: Tue, 15 Sep 2009 20:05:45 +1000
To: rob@pyro
From: rob@pyro
Subject: test Tue, 15 Sep 2009 20:05:45 +1000
',
' BODY[]',
'Return-Path: <rob@pyro>
X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on pyro.home
X-Spam-Level:
X-Spam-Status: No, score=-0.5 required=5.0 tests=ALL_TRUSTED,BAYES_00,
FH_FROMEML_NOTLD,TO_MALFORMED autolearn=no version=3.2.5
X-Original-To: rob@pyro
Delivered-To: rob@pyro
Received: from pyro (pyro [127.0.0.1])
by pyro.home (Postfix) with ESMTP id A5C8115A066
for <rob@pyro>; Tue, 15 Sep 2009 20:05:45 +1000 (EST)
Date: Tue, 15 Sep 2009 20:05:45 +1000
To: rob@pyro
From: rob@pyro
Subject: test Tue, 15 Sep 2009 20:05:45 +1000
X-Mailer: swaks v20061116.0 jetmore.org/john/code/#swaks
Message-Id: <20090915100545.A5C8115A066@pyro.home>
X-Bogosity: Spam, tests=bogofilter, spamicity=0.999693, version=1.2.1
Lines: 1
This is a test mailing
',
')
',
],
[
[1],
q{BODY.PEEK[HEADER.FIELDS (To From Date Subject)]},
qw(FLAGS INTERNALDATE RFC822.SIZE BODY[])
],
{
"1" => {
'BODY[]' => 'Return-Path: <rob@pyro>
X-Spam-Checker-Version: SpamAssassin 3.2.5 (2008-06-10) on pyro.home
X-Spam-Level:
X-Spam-Status: No, score=-0.5 required=5.0 tests=ALL_TRUSTED,BAYES_00,
FH_FROMEML_NOTLD,TO_MALFORMED autolearn=no version=3.2.5
X-Original-To: rob@pyro
Delivered-To: rob@pyro
Received: from pyro (pyro [127.0.0.1])
by pyro.home (Postfix) with ESMTP id A5C8115A066
for <rob@pyro>; Tue, 15 Sep 2009 20:05:45 +1000 (EST)
Date: Tue, 15 Sep 2009 20:05:45 +1000
To: rob@pyro
From: rob@pyro
Subject: test Tue, 15 Sep 2009 20:05:45 +1000
X-Mailer: swaks v20061116.0 jetmore.org/john/code/#swaks
Message-Id: <20090915100545.A5C8115A066@pyro.home>
X-Bogosity: Spam, tests=bogofilter, spamicity=0.999693, version=1.2.1
Lines: 1
This is a test mailing
',
'INTERNALDATE' => '15-Sep-2009 20:05:45 +1000',
'FLAGS' => '\\Seen',
'BODY[HEADER.FIELDS (TO FROM DATE SUBJECT)]' =>
'Date: Tue, 15 Sep 2009 20:05:45 +1000
To: rob@pyro
From: rob@pyro
Subject: test Tue, 15 Sep 2009 20:05:45 +1000
',
'RFC822.SIZE' => '771'
},
},
],
);
my @uid_tests = (
[
"uid enabled",
[ q{* 1 FETCH (UID 123 UNQUOTED foobar)}, ],
[ [123], qw(UNQUOTED) ],
{ "123" => { "UNQUOTED" => q{foobar}, } },
],
);
package Test::Mail::IMAPClient;
use vars qw(@ISA);
@ISA = qw(Mail::IMAPClient);
sub new {
my ( $class, %args ) = @_;
my %me = %args;
return bless \%me, $class;
}
sub fetch {
my ( $self, @args ) = @_;
return $self->{_next_fetch_response} || [];
}
package main;
sub run_tests {
my ( $imap, $tests ) = @_;
for my $test (@$tests) {
my ( $comment, $fetch, $request, $response ) = @$test;
$imap->{_next_fetch_response} = $fetch;
my $r = $imap->fetch_hash(@$request);
is_deeply( $r, $response, $comment );
}
}
my $imap = Test::Mail::IMAPClient->new( Uid => 0 );
run_tests( $imap, \@tests );
$imap->Uid(1);
run_tests( $imap, \@uid_tests );

View file

@ -0,0 +1,36 @@
#!/usr/bin/perl
use strict;
use warnings;
use Test::More tests => 13;
BEGIN { use_ok('Mail::IMAPClient') or exit; }
{
my $obj = Mail::IMAPClient->new();
my %t = ( 0 => "01-Jan-1970" );
foreach my $k ( sort keys %t ) {
my $v = $t{$k};
my $s = $v . ' 00:00:00 +0000';
is( Mail::IMAPClient::Rfc2060_date($k), $v, "Rfc2060_date($k)=$v" );
is( Mail::IMAPClient::Rfc3501_date($k), $v, "Rfc3501_date($k)=$v" );
is( Mail::IMAPClient::Rfc3501_datetime($k),
$s, "Rfc3501_datetime($k)=$s" );
is( Mail::IMAPClient::Rfc2060_datetime($k),
$s, "Rfc3501_datetime($k)=$s" );
is( $obj->Rfc3501_date($k), $v, "->Rfc3501_date($k)=$v" );
is( $obj->Rfc2060_date($k), $v, "->Rfc2060_date($k)=$v" );
is( $obj->Rfc3501_datetime($k), $s, "->Rfc3501_datetime($k)=$s" );
is( $obj->Rfc2060_datetime($k), $s, "->Rfc2060_datetime($k)=$s" );
foreach my $z (qw(+0000 -0500)) {
my $vz = $v . ' 00:00:00 ' . $z;
is( Mail::IMAPClient::Rfc2060_datetime( $k, $z ),
$vz, "Rfc2060_datetime($k)=$vz" );
is( Mail::IMAPClient::Rfc3501_datetime( $k, $z ),
$vz, "Rfc3501_datetime($k)=$vz" );
}
}
}

View file

@ -1,5 +1,5 @@
# $Id: Makefile,v 1.26 2010/01/15 00:01:40 gilles Exp gilles $ # $Id: Makefile,v 1.27 2010/01/19 15:26:12 gilles Exp gilles $
TARGET=imapsync TARGET=imapsync
@ -12,20 +12,20 @@ usage:
@echo "make install # as root" @echo "make install # as root"
@echo "make testf # run tests" @echo "make testf # run tests"
@echo "make testv # run tests verbosely" @echo "make testv # run tests verbosely"
@echo "make test3xx # run tests with Mail-IMAPClient-3.xy" @echo "make test3xx # run tests with (last) Mail-IMAPClient-3.xy"
@echo "make test229 # run tests with Mail-IMAPClient-2.2.9" @echo "make test229 # run tests with Mail-IMAPClient-2.2.9"
@echo "make all " @echo "make all "
all: ChangeLog README VERSION all: ChangeLog README VERSION
.PHONY: test testp testf test3xx .PHONY: test tests testp testf test3xx
.test: $(TARGET) tests.sh .test: $(TARGET) tests.sh
/usr/bin/time sh tests.sh 1>/dev/null /usr/bin/time sh tests.sh 1>/dev/null
touch .test touch .test
.test_3xx: $(TARGET) tests.sh .test_3xx: $(TARGET) tests.sh
CMD_PERL='perl -I./Mail-IMAPClient-3.19/lib' /usr/bin/time sh tests.sh 1>/dev/null CMD_PERL='perl -I./Mail-IMAPClient-3.21/lib' /usr/bin/time sh tests.sh 1>/dev/null
touch .test_3xx touch .test_3xx
test_quick : test_quick_229 test_quick_3xx test_quick : test_quick_229 test_quick_3xx
@ -34,13 +34,14 @@ test_quick_229: $(TARGET) tests.sh
CMD_PERL='perl -I./Mail-IMAPClient-2.2.9' /usr/bin/time sh tests.sh locallocal 1>/dev/null CMD_PERL='perl -I./Mail-IMAPClient-2.2.9' /usr/bin/time sh tests.sh locallocal 1>/dev/null
test_quick_3xx: $(TARGET) tests.sh test_quick_3xx: $(TARGET) tests.sh
CMD_PERL='perl -I./Mail-IMAPClient-3.19/lib' /usr/bin/time sh tests.sh locallocal 1>/dev/null CMD_PERL='perl -I./Mail-IMAPClient-3.21/lib' /usr/bin/time sh tests.sh locallocal 1>/dev/null
testv: testv:
nice -40 sh -x tests.sh nice -40 sh -x tests.sh
test: .test_229 .test_3xx test: .test_229 .test_3xx
tests: test
test3xx: .test_3xx test3xx: .test_3xx

15
README
View file

@ -3,7 +3,7 @@ NAME
Synchronise mailboxes between two imap servers. Good at IMAP migration. Synchronise mailboxes between two imap servers. Good at IMAP migration.
More than 32 different IMAP server softwares supported with success. More than 32 different IMAP server softwares supported with success.
$Revision: 1.300 $ $Revision: 1.303 $
INSTALL INSTALL
imapsync works fine under any Unix OS with perl. imapsync works fine under any Unix OS with perl.
@ -24,8 +24,8 @@ INSTALL
Go into the directory imapsync-x.xx and read the INSTALL file. Go into the directory imapsync-x.xx and read the INSTALL file.
The INSTALL file is also at The INSTALL file is also at
http://www.linux-france.org/prj/imapsync/INSTALL (for windows users) http://www.linux-france.org/prj/imapsync/INSTALL (for windows users)
The freshmeat record is at http://freshmeat.net/projects/imapsync/ The freshmeat record is at http://freshmeat.net/projects/imapsync/
SYNOPSIS SYNOPSIS
imapsync [options] imapsync [options]
@ -42,6 +42,7 @@ SYNOPSIS
[--host2 server2] [--port2 <num>] [--host2 server2] [--port2 <num>]
[--user2 <string>] [--passfile2 <string>] [--user2 <string>] [--passfile2 <string>]
[--ssl1] [--ssl2] [--ssl1] [--ssl2]
[--tls1] [--tls2]
[--authmech1 <string>] [--authmech2 <string>] [--authmech1 <string>] [--authmech2 <string>]
[--noauthmd5] [--noauthmd5]
[--folder <string> --folder <string> ...] [--folder <string> --folder <string> ...]
@ -73,7 +74,7 @@ SYNOPSIS
[--split1] [--split2] [--split1] [--split2]
[--reconnectretry1 <int>] [--reconnectretry2 <int>] [--reconnectretry1 <int>] [--reconnectretry2 <int>]
[--version] [--help] [--version] [--help]
DESCRIPTION DESCRIPTION
The command imapsync is a tool allowing incremental and recursive imap The command imapsync is a tool allowing incremental and recursive imap
transfer from one mailbox to another. transfer from one mailbox to another.
@ -138,8 +139,8 @@ SECURITY
imasync is not totally protected against sniffers on the network since imasync is not totally protected against sniffers on the network since
passwords may be transferred in plain text if CRAM-MD5 is not supported passwords may be transferred in plain text if CRAM-MD5 is not supported
by your imap servers. Use --ssl1 and --ssl2 to enable encryption on by your imap servers. Use --ssl1 (or --tls1) and --ssl2 (or --tls2) to
host1 and host2. enable encryption on host1 and host2.
You may authenticate as one user (typically an admin user), but be You may authenticate as one user (typically an admin user), but be
authorized as someone else, which means you don't need to know every authorized as someone else, which means you don't need to know every
@ -362,5 +363,5 @@ SIMILAR SOFTWARES
Feedback (good or bad) will always be welcome. Feedback (good or bad) will always be welcome.
$Id: imapsync,v 1.300 2010/01/16 03:34:37 gilles Exp gilles $ $Id: imapsync,v 1.303 2010/01/20 04:12:52 gilles Exp gilles $

69
TODO
View file

@ -1,5 +1,5 @@
#!/bin/cat #!/bin/cat
# $Id: TODO,v 1.70 2010/01/16 03:38:47 gilles Exp gilles $ # $Id: TODO,v 1.72 2010/01/20 04:13:42 gilles Exp gilles $
TODO file for imapsync TODO file for imapsync
---------------------- ----------------------
@ -9,6 +9,36 @@ http://groups.google.fr/group/comp.mail.imap
Post on imapsync mailing-list when a new release comes. Post on imapsync mailing-list when a new release comes.
Start a wiki for imapsync.
Add a best practice migration tips document.
Add "output to reflect everything that imapsync was doing".
Not everything but flag synchronization will be nice"
Add and option to sync to & from files.
Add an option to make imapsync automatically
reconnect when the connection drops
Add an --aclregextrans2 flag.
"Today we discovered, that Cyrus and Dovecot use different characters for
their ACLs. Syncing ACLs vom Cyrus to Dovecot (at least 1.2) doesn't
work. Cyrus uses c and d, Dovecot uses k and x instead."
Peer Heinlein.
Add --subscribeall option.
Is it possible to have a option that subscribes all folders regardless of
subscription on the source server? Perhaps --subscribeall?
Add a --delete2folders option
"When syncing mailboxes with imapsync, is there a way to delete folders in the
target account? The --delete2 option only seems to delete individual
messages, not folders."
Add different levels of output to see clearly the
problem by default.
Add option --exclude_messages_with_flag Add option --exclude_messages_with_flag
Add more information about skipped messages. Add more information about skipped messages.
@ -20,11 +50,6 @@ mailboxes). May be the best is to take a part of the
body. Have to code this. body. Have to code this.
Add an option to store flags with "FLAGS.SILENT" instead of "+FLAGS.SILENT".
No, be "FLAGS.SILENT" the default and "+FLAGS.SILENT" an option.
Add TLS support with patches/imapsync-1.217_tls_support.patch
Add Rick Romero patch with Add Rick Romero patch with
--quiet No output at all --quiet No output at all
@ -45,8 +70,6 @@ Add kerberos authentification
Add NOOP commands to avoid timeouts. Add NOOP commands to avoid timeouts.
Start a wiki for imapsync.
Add a --pidfile option. Add a --pidfile option.
Write a clean_exit() replacing each die() or exit() call. Write a clean_exit() replacing each die() or exit() call.
@ -63,18 +86,6 @@ Fix this:
> # No NL Count on Windows my $length = ( -s $file ) + $bare_nl_count; > # No NL Count on Windows my $length = ( -s $file ) + $bare_nl_count;
> my $length = ( -s $file ); > my $length = ( -s $file );
Add a --delete2folders option
"When syncing mailboxes with imapsync, is there a way to delete folders in the
target account? The --delete2 option only seems to delete individual
messages, not folders."
Add different levels of output to see clearly the
problem by default.
Add --subscribeall option.
Is it possible to have a option that subscribes all folders regardless of
subscription on the source server? Perhaps --subscribeall?
Add stdin/stdout filter before transfer: Add stdin/stdout filter before transfer:
"Now i asked me, how to modify your perl program to work with "Now i asked me, how to modify your perl program to work with
@ -97,9 +108,6 @@ vs 250s avec la version 1.217". Marc Jauvin
Add LITERAL+ [RFC 2088] support to imapsync. Add LITERAL+ [RFC 2088] support to imapsync.
Add an option to make imapsync automatically
reconnect when the connection drops
Add an entry to Add an entry to
http://lsm.execpc.com/lsm/ http://lsm.execpc.com/lsm/
See template lsm.imapsync See template lsm.imapsync
@ -109,13 +117,6 @@ is changing and have it replace the domain in all user names that are
related to ACLs. related to ACLs.
See patches/imapsync-acls-users See patches/imapsync-acls-users
Add a best practice migration tips document.
Add "output to reflect everything that imapsync was doing".
Not everything but flag synchronization will be nice"
Add and option to sync to & from files.
Use examine() instead of select() in --dry mode. Use examine() instead of select() in --dry mode.
Add a method doing the switch automagicaly. Add a method doing the switch automagicaly.
@ -137,6 +138,14 @@ http://asg.web.cmu.edu/cyrus/download/imapd/altnamespace.html
Explain expunge behavior. Explain expunge behavior.
DONE. Be "FLAGS.SILENT" the normal behavior instead of "+FLAGS.SILENT".
DONE. Add TLS support with patches/imapsync-1.217_tls_support.patch
USELESS. Make --skipheader be multiple. Useless since we can USELESS. Make --skipheader be multiple. Useless since we can
use the or perl regex '^X-|^Date|^From' use the or perl regex '^X-|^Date|^From'

View file

@ -1 +1 @@
1.300 1.303

226
imapsync
View file

@ -9,7 +9,7 @@ tool. Synchronise mailboxes between two imap servers. Good
at IMAP migration. More than 32 different IMAP server softwares at IMAP migration. More than 32 different IMAP server softwares
supported with success. supported with success.
$Revision: 1.300 $ $Revision: 1.303 $
=head1 INSTALL =head1 INSTALL
@ -50,6 +50,7 @@ The option list:
[--host2 server2] [--port2 <num>] [--host2 server2] [--port2 <num>]
[--user2 <string>] [--passfile2 <string>] [--user2 <string>] [--passfile2 <string>]
[--ssl1] [--ssl2] [--ssl1] [--ssl2]
[--tls1] [--tls2]
[--authmech1 <string>] [--authmech2 <string>] [--authmech1 <string>] [--authmech2 <string>]
[--noauthmd5] [--noauthmd5]
[--folder <string> --folder <string> ...] [--folder <string> --folder <string> ...]
@ -162,7 +163,8 @@ the best solution.
imasync is not totally protected against sniffers on the imasync is not totally protected against sniffers on the
network since passwords may be transferred in plain text network since passwords may be transferred in plain text
if CRAM-MD5 is not supported by your imap servers. Use if CRAM-MD5 is not supported by your imap servers. Use
--ssl1 and --ssl2 to enable encryption on host1 and host2. --ssl1 (or --tls1) and --ssl2 (or --tls2) to enable
encryption on host1 and host2.
You may authenticate as one user (typically an admin user), You may authenticate as one user (typically an admin user),
but be authorized as someone else, which means you don't but be authorized as someone else, which means you don't
@ -418,7 +420,7 @@ Entries for imapsync:
Feedback (good or bad) will always be welcome. Feedback (good or bad) will always be welcome.
$Id: imapsync,v 1.300 2010/01/16 03:34:37 gilles Exp gilles $ $Id: imapsync,v 1.303 2010/01/20 04:12:52 gilles Exp gilles $
=cut =cut
@ -475,6 +477,7 @@ my(
$timestart, $timeend, $timediff, $timestart, $timeend, $timediff,
$timesize, $timebefore, $timesize, $timebefore,
$ssl1, $ssl2, $ssl1, $ssl2,
$tls1, $tls2,
$authuser1, $authuser2, $authuser1, $authuser2,
$authmech1, $authmech2, $authmech1, $authmech2,
$split1, $split2, $split1, $split2,
@ -487,7 +490,7 @@ my(
use vars qw ($opt_G); # missing code for this will be option. use vars qw ($opt_G); # missing code for this will be option.
$rcs = '$Id: imapsync,v 1.300 2010/01/16 03:34:37 gilles Exp gilles $ '; $rcs = '$Id: imapsync,v 1.303 2010/01/20 04:12:52 gilles Exp gilles $ ';
$rcs =~ m/,v (\d+\.\d+)/; $rcs =~ m/,v (\d+\.\d+)/;
$VERSION = ($1) ? $1: "UNKNOWN"; $VERSION = ($1) ? $1: "UNKNOWN";
@ -551,8 +554,8 @@ while (@argv_copy) {
my $banner = join("", my $banner = join("",
'$RCSfile: imapsync,v $ ', '$RCSfile: imapsync,v $ ',
'$Revision: 1.300 $ ', '$Revision: 1.303 $ ',
'$Date: 2010/01/16 03:34:37 $ ', '$Date: 2010/01/20 04:12:52 $ ',
"\n",localhost_info(), "\n",localhost_info(),
" and the module Mail::IMAPClient version used here is ", " and the module Mail::IMAPClient version used here is ",
$VERSION_IMAPClient,"\n", $VERSION_IMAPClient,"\n",
@ -607,20 +610,20 @@ $split1 ||= 1000;
$split2 ||= 1000; $split2 ||= 1000;
$host1 || missing_option("--host1") ; $host1 || missing_option("--host1") ;
$port1 ||= defined $ssl1 ? 993 : 143; $port1 ||= (defined $ssl1 and !defined $tls1) ? 993 : 143;
$host2 || missing_option("--host2") ; $host2 || missing_option("--host2") ;
$port2 ||= defined $ssl2 ? 993 : 143; $port2 ||= (defined $ssl2 && !defined $tls2) ? 993 : 143;
sub connect_imap { sub connect_imap {
my($host, $port, $debugimap, $ssl) = @_; my($host, $port, $debugimap, $ssl, $tls) = @_;
my $imap = Mail::IMAPClient->new(); my $imap = Mail::IMAPClient->new();
$imap->Server($host); $imap->Server($host);
$imap->Port($port); $imap->Port($port);
$imap->Debug($debugimap); $imap->Debug($debugimap);
$imap->Ssl($ssl) if ($ssl); $imap->Ssl($ssl) if ($ssl);
$imap->Tls($tls) if ($tls);
#$imap->connect() #$imap->connect()
myconnect($imap) myconnect($imap)
or die "Can not open imap connection on [$host]: $@\n"; or die "Can not open imap connection on [$host]: $@\n";
@ -646,10 +649,10 @@ if ($justconnect) {
my $imap1 = (); my $imap1 = ();
my $imap2 = (); my $imap2 = ();
$imap1 = connect_imap($host1, $port1, $debugimap, $ssl1); $imap1 = connect_imap($host1, $port1, $debugimap, $ssl1, $tls1);
print "Host1 software: ", server_banner($imap1); print "Host1 software: ", server_banner($imap1);
print "Host1 capability: ", join(" ", $imap1->capability()), "\n"; print "Host1 capability: ", join(" ", $imap1->capability()), "\n";
$imap2 = connect_imap($host2, $port2, $debugimap, $ssl2); $imap2 = connect_imap($host2, $port2, $debugimap, $ssl2, $tls2);
print "Host2 software: ", server_banner($imap2); print "Host2 software: ", server_banner($imap2);
print "Host2 capability: ", join(" ", $imap2->capability()), "\n"; print "Host2 capability: ", join(" ", $imap2->capability()), "\n";
$imap1->logout(); $imap1->logout();
@ -754,12 +757,12 @@ $timebefore = $timestart;
$debugimap and print "Host1 connection\n"; $debugimap and print "Host1 connection\n";
$imap1 = login_imap($host1, $port1, $user1, $password1, $imap1 = login_imap($host1, $port1, $user1, $password1,
$debugimap, $timeout, $fastio1, $ssl1, $debugimap, $timeout, $fastio1, $ssl1, $tls1,
$authmech1, $authuser1, $reconnectretry1); $authmech1, $authuser1, $reconnectretry1);
$debugimap and print "Host2 connection\n"; $debugimap and print "Host2 connection\n";
$imap2 = login_imap($host2, $port2, $user2, $password2, $imap2 = login_imap($host2, $port2, $user2, $password2,
$debugimap, $timeout, $fastio2, $ssl2, $debugimap, $timeout, $fastio2, $ssl2, $tls2,
$authmech2, $authuser2, $reconnectretry2); $authmech2, $authuser2, $reconnectretry2);
# history # history
@ -771,12 +774,13 @@ $debug and print "Host2 Buffer I/O: ", $imap2->Buffer(), "\n";
sub login_imap { sub login_imap {
my($host, $port, $user, $password, my($host, $port, $user, $password,
$debugimap, $timeout, $fastio, $debugimap, $timeout, $fastio,
$ssl, $authmech, $authuser, $reconnectretry) = @_; $ssl, $tls, $authmech, $authuser, $reconnectretry) = @_;
my ($imap); my ($imap);
$imap = Mail::IMAPClient->new(); $imap = Mail::IMAPClient->new();
$imap->Ssl($ssl) if ($ssl); $imap->Ssl($ssl) if ($ssl);
$imap->Tls($tls) if ($tls);
$imap->Clear(5); $imap->Clear(5);
$imap->Server($host); $imap->Server($host);
$imap->Port($port); $imap->Port($port);
@ -1065,6 +1069,8 @@ sub tests_compare_lists {
ok(+1 == compare_lists(["b"], ["a"]) , 'compare_lists, ["b"] > ["a"]') ; ok(+1 == compare_lists(["b"], ["a"]) , 'compare_lists, ["b"] > ["a"]') ;
ok(-1 == compare_lists(["a"], ["aa"]) , 'compare_lists, ["a"] < ["aa"]') ; ok(-1 == compare_lists(["a"], ["aa"]) , 'compare_lists, ["a"] < ["aa"]') ;
ok(-1 == compare_lists(["a"], ["a", "a"]), 'compare_lists, ["a"] < ["a", "a"]') ; ok(-1 == compare_lists(["a"], ["a", "a"]), 'compare_lists, ["a"] < ["a", "a"]') ;
ok( 0 == compare_lists([split(" ", "a b")], ["a", "b"]), 'compare_lists, split') ;
ok( 0 == compare_lists([sort split(" ", "b a")], ["a", "b"]), 'compare_lists, sort split') ;
} }
@ -1303,13 +1309,13 @@ sub tests_flags_regex {
} }
sub flags_regex { sub flags_regex {
my ($flags_f) = @_; my ($h1_flags) = @_;
foreach my $regexflag (@regexflag) { foreach my $regexflag (@regexflag) {
$debug and print "eval \$flags_f =~ $regexflag\n"; $debug and print "eval \$h1_flags =~ $regexflag\n";
eval("\$flags_f =~ $regexflag"); eval("\$h1_flags =~ $regexflag");
die("error: eval regexflag '$regexflag': $@\n") if $@; die("error: eval regexflag '$regexflag': $@\n") if $@;
} }
return($flags_f); return($h1_flags);
} }
sub acls_sync { sub acls_sync {
@ -1461,19 +1467,19 @@ FOLDER: foreach my $h1_fold (@h1_folders) {
$debug and print "LIST FROM: ", scalar(@h1_msgs), " messages [@h1_msgs]\n"; $debug and print "LIST Host1: ", scalar(@h1_msgs), " messages [@h1_msgs]\n";
# internal dates on "TO" are after the ones on "FROM" # internal dates on "TO" are after the ones on "FROM"
# normally... # normally...
my @h2_msgs = select_msgs($imap2); my @h2_msgs = select_msgs($imap2);
$debug and print "LIST TO : ", scalar(@h2_msgs), " messages [@h2_msgs]\n"; $debug and print "LIST Host2: ", scalar(@h2_msgs), " messages [@h2_msgs]\n";
my %h1_hash = (); my %h1_hash = ();
my %h2_hash = (); my %h2_hash = ();
#print "++++ Using cache ++++\n"; #print "++++ Using cache ++++\n";
print "++++ Host1 [$h1_fold] Parse 1 ++++\n"; print "++++ Host1 [$h1_fold] parsing headers ++++\n";
last FOLDER if $imap1->IsUnconnected(); last FOLDER if $imap1->IsUnconnected();
last FOLDER if $imap2->IsUnconnected(); last FOLDER if $imap2->IsUnconnected();
@ -1504,9 +1510,9 @@ FOLDER: foreach my $h1_fold (@h1_folders) {
$mess_skipped += 1; $mess_skipped += 1;
} }
} }
$debug and print "Time headers: ", timenext(), " s\n"; $debug and print "Time parsing headers on host1: ", timenext(), " s\n";
print "++++ Host2 [$h2_fold] Parse 1 ++++\n"; print "++++ Host2 [$h2_fold] parsing headers ++++\n";
my ($h2_heads, $h2_fir) = ({}, {}); my ($h2_heads, $h2_fir) = ({}, {});
$h2_heads = $imap2->parse_headers([@h2_msgs], @useheader) if (@h2_msgs); $h2_heads = $imap2->parse_headers([@h2_msgs], @useheader) if (@h2_msgs);
@ -1527,10 +1533,10 @@ FOLDER: foreach my $h1_fold (@h1_folders) {
#$mess_skipped += 1; #$mess_skipped += 1;
} }
} }
$debug and print "Time headers: ", timenext(), " s\n"; $debug and print "Time parsing headers on host2: ", timenext(), " s\n";
print "++++ Verifying [$h1_fold] -> [$h2_fold] ++++\n"; print "++++ Verifying [$h1_fold] -> [$h2_fold] ++++\n";
# messages in "from" that are not good in "to" # messages in host1 that are not good in host2
my @h1_hash_keys_sorted_by_uid my @h1_hash_keys_sorted_by_uid
= sort {$h1_hash{$a}{'m'} <=> $h1_hash{$b}{'m'}} keys(%h1_hash); = sort {$h1_hash{$a}{'m'} <=> $h1_hash{$b}{'m'}} keys(%h1_hash);
@ -1547,8 +1553,8 @@ FOLDER: foreach my $h1_fold (@h1_folders) {
#print "$m_id "; #print "$m_id ";
unless (exists($h1_hash{$m_id})) { unless (exists($h1_hash{$m_id})) {
my $h2_msg = $h2_hash{$m_id}{'m'}; my $h2_msg = $h2_hash{$m_id}{'m'};
my $flags = $h2_hash{$m_id}{'F'} || ""; my $h2_flags = $h2_hash{$m_id}{'F'} || "";
my $isdel = $flags =~ /\B\\Deleted\b/ ? 1 : 0; my $isdel = $h2_flags =~ /\B\\Deleted\b/ ? 1 : 0;
print "deleting message $m_id $h2_msg\n" print "deleting message $m_id $h2_msg\n"
if ! $isdel; if ! $isdel;
push(@expunge,$h2_msg) if $uidexpunge2; push(@expunge,$h2_msg) if $uidexpunge2;
@ -1597,7 +1603,7 @@ FOLDER: foreach my $h1_fold (@h1_folders) {
$mess_size_total_error += $h1_size; $mess_size_total_error += $h1_size;
next MESS; next MESS;
} }
#print "AAAmessage_string[$string]ZZZ\n";
#my $message_file = "tmp_imapsync_$$"; #my $message_file = "tmp_imapsync_$$";
#$imap1->select($h1_fold); #$imap1->select($h1_fold);
#unlink($message_file); #unlink($message_file);
@ -1670,27 +1676,27 @@ FOLDER: foreach my $h1_fold (@h1_folders) {
return($d); return($d);
} }
my $flags_f = $h1_hash{$m_id}{'F'} || ""; my $h1_flags = $h1_hash{$m_id}{'F'} || "";
# RFC 2060: This flag can not be altered by any client # RFC 2060: This flag can not be altered by any client
$flags_f =~ s@\\Recent\s?@@gi; $h1_flags =~ s@\\Recent\s?@@gi;
$flags_f = flags_regex($flags_f) if @regexflag; $h1_flags = flags_regex($h1_flags) if @regexflag;
$flags_f = flags_filter($flags_f, $permanentflags2) if ($permanentflags2); $h1_flags = flags_filter($h1_flags, $permanentflags2) if ($permanentflags2);
my $new_id; my $new_id;
print "flags from: [$flags_f][$d]\n"; print "flags from: [$h1_flags][$d]\n";
last FOLDER if $imap1->IsUnconnected(); last FOLDER if $imap1->IsUnconnected();
last FOLDER if $imap2->IsUnconnected(); last FOLDER if $imap2->IsUnconnected();
unless ($dry) { unless ($dry) {
if ($OSNAME eq "MSWin32") { if ($OSNAME eq "MSWin32") {
$new_id = $imap2->append_string($h2_fold,$string, $flags_f, $d); $new_id = $imap2->append_string($h2_fold,$string, $h1_flags, $d);
} }
else { else {
# just back to append_string since append_file 3.05 does not work. # just back to append_string since append_file 3.05 does not work.
#$new_id = $imap2->append_file($h2_fold, $message_file, "", $flags_f, $d); #$new_id = $imap2->append_file($h2_fold, $message_file, "", $h1_flags, $d);
# append_string 3.05 does not work too some times with $d unset. # append_string 3.05 does not work too some times with $d unset.
$new_id = $imap2->append_string($h2_fold,$string, $flags_f, $d); $new_id = $imap2->append_string($h2_fold,$string, $h1_flags, $d);
} }
unless($new_id){ unless($new_id){
no warnings 'uninitialized'; no warnings 'uninitialized';
@ -1737,46 +1743,52 @@ FOLDER: foreach my $h1_fold (@h1_folders) {
my $h2_msg = $h2_hash{$m_id}{'m'}; my $h2_msg = $h2_hash{$m_id}{'m'};
# used cached flag values for efficiency # used cached flag values for efficiency
my $flags_f = $h1_hash{$m_id}{'F'} || ""; my $h1_flags = $h1_hash{$m_id}{'F'} || "";
my $flags_t = $h2_hash{$m_id}{'F'} || ""; my $h2_flags = $h2_hash{$m_id}{'F'} || "";
# RFC 2060: This flag can not be altered by any client # RFC 2060: This flag can not be altered by any client
$flags_f =~ s@\\Recent\s?@@gi; $h1_flags =~ s@\\Recent\s?@@gi;
$flags_f = flags_regex($flags_f) if @regexflag; $h1_flags = flags_regex($h1_flags) if @regexflag;
$flags_f = flags_filter($flags_f, $permanentflags2) if ($permanentflags2); $h1_flags = flags_filter($h1_flags, $permanentflags2) if ($permanentflags2);
# compare flags - add missing flags
my @ff = split(' ', $flags_f );
my %ft = map { $_ => 1 } split(' ', $flags_t ); # compare flags - set flags if there a diffrence
my @flags_a = map { exists $ft{$_} ? () : $_ } @ff; my @h1_flags = sort split(' ', $h1_flags );
my @h2_flags = sort split(' ', $h2_flags );
my $diff = compare_lists(\@h1_flags, \@h2_flags);
$debug and print "Setting flags(@flags_a) ffrom($flags_f) fto($flags_t) on msg #$h2_msg in $h2_fold\n"; $diff and $debug and print "Replacing h2 flags($h2_flags) with h1 flags($h1_flags) on msg #$h2_msg in $h2_fold\n";
# This adds or changes flags but no flag are removed with this # This sets flags so flags can be removed with this
if (!$dry and @flags_a and !$imap2->store($h2_msg, "+FLAGS.SILENT (@flags_a)") ) { # When you remove a \Seen flag on host1 you want to it
warn "Could not add flags '@flags_a' flagf '$flags_f'", # to be removed on host2. Just add flags is not what
" flagt '$flags_t' on msg #$h2_msg in $h2_fold: ", # we need most of the time.
if (!$dry and $diff and !$imap2->store($h2_msg, "FLAGS.SILENT (@h1_flags)") ) {
warn "Could not add flags @h1_flags",
" on msg #$h2_msg in $h2_fold: ",
$imap2->LastError, "\n"; $imap2->LastError, "\n";
#$error++; #$error++;
} }
last FOLDER if $imap2->IsUnconnected(); last FOLDER if $imap2->IsUnconnected();
$debug and do { $debug and do {
my @flags_t = @{ $imap2->flags($h2_msg) || [] }; my @h2_flags = @{ $imap2->flags($h2_msg) || [] };
last FOLDER if $imap2->IsUnconnected(); last FOLDER if $imap2->IsUnconnected();
print "flags from: $flags_f\n", print "host1 flags: $h1_flags\n",
"flags to : @flags_t\n"; "host2 flags: @h2_flags\n";
print "Looking dates\n"; print "Looking dates\n";
#my $d_f = $imap1->internaldate($h1_msg); #my $h1_idate = $imap1->internaldate($h1_msg);
#my $d_t = $imap2->internaldate($h2_msg); #my $h2_idate = $imap2->internaldate($h2_msg);
my $d_f = $h1_hash{$m_id}{'D'}; my $h1_idate = $h1_hash{$m_id}{'D'};
my $d_t = $h2_hash{$m_id}{'D'}; my $h2_idate = $h2_hash{$m_id}{'D'};
print print
"idate from: $d_f\n", "host1 internal date: $h1_idate\n",
"idate to : $d_t\n"; "host2 internal date: $h2_idate\n";
#unless ($d_f eq $d_t) { #unless ($h1_idate eq $h2_idate) {
# print "!!! Dates differ !!!\n"; # print "!!! Dates differ !!!\n";
#} #}
}; };
@ -1784,7 +1796,7 @@ FOLDER: foreach my $h1_fold (@h1_folders) {
# Bad size # Bad size
print print
"Message $m_id SZ_BAD f:$h1_msg:$h1_size t:$h2_msg:$h2_size\n"; "Message $m_id SZ_BAD f:$h1_msg:$h1_size t:$h2_msg:$h2_size\n";
# delete in to and recopy ? # delete in host2 and recopy ?
# NO recopy CODE HERE. to be written if needed. # NO recopy CODE HERE. to be written if needed.
$error++; $error++;
if ($opt_G){ if ($opt_G){
@ -1917,10 +1929,10 @@ sub stats {
sub thank_author { sub thank_author {
return(join("", "Happy with this free, open and gratis WTFPL software?\n", return(join("", "Happy with this free, open and gratis DWTFPL software?\n",
"Encourage the author (Gilles LAMIRAL) by giving him a book:\n", "Encourage the author (Gilles LAMIRAL) by giving him a book:\n",
"http://www.amazon.com/gp/registry/wishlist/1C9UNDIH3P7R7/\n", "http://www.amazon.com/gp/registry/wishlist/1C9UNDIH3P7R7/\n",
"or via paypal:\n", "or just money via paypal:\n",
"http://www.linux-france.org/prj/imapsync/paypal.html\n")); "http://www.linux-france.org/prj/imapsync/paypal.html\n"));
} }
@ -1993,6 +2005,8 @@ sub get_options {
"fastio2!" => \$fastio2, "fastio2!" => \$fastio2,
"ssl1!" => \$ssl1, "ssl1!" => \$ssl1,
"ssl2!" => \$ssl2, "ssl2!" => \$ssl2,
"tls1!" => \$tls1,
"tls2!" => \$tls2,
"authmech1=s" => \$authmech1, "authmech1=s" => \$authmech1,
"authmech2=s" => \$authmech2, "authmech2=s" => \$authmech2,
"authuser1=s" => \$authuser1, "authuser1=s" => \$authuser1,
@ -2032,7 +2046,7 @@ sub get_options {
sub load_modules { sub load_modules {
require IO::Socket::SSL if ($ssl1 or $ssl2); require IO::Socket::SSL if ($ssl1 or $ssl2 or $tls1 or $tls2);
require Date::Manip if ($syncinternaldates || $idatefromheader) ; require Date::Manip if ($syncinternaldates || $idatefromheader) ;
require Term::ReadKey if ( require Term::ReadKey if (
@ -2171,6 +2185,8 @@ Several options are mandatory.
--authmech2 <string> : auth mechanism to use with host2. See --authmech1 --authmech2 <string> : auth mechanism to use with host2. See --authmech1
--ssl1 : use an SSL connection on host1. --ssl1 : use an SSL connection on host1.
--ssl2 : use an SSL connection on host2. --ssl2 : use an SSL connection on host2.
--tls1 : use an TLS connection on host1.
--tls2 : use an TLS connection on host2.
--folder <string> : sync this folder. --folder <string> : sync this folder.
--folder <string> : and this one, etc. --folder <string> : and this one, etc.
--folderrec <string> : sync this folder recursively. --folderrec <string> : sync this folder recursively.
@ -2221,13 +2237,14 @@ Several options are mandatory.
source server only. newly transferred messages source server only. newly transferred messages
are expunged if option --expunge is given. are expunged if option --expunge is given.
no expunge is done on destination account but no expunge is done on destination account but
it will change in future releases. it may change in future releases.
--expunge1 : expunge messages on source account. --expunge1 : expunge messages on source account.
--expunge2 : expunge messages on target account. --expunge2 : expunge messages on target account.
--uidexpunge2 : uidexpunge messages on the destination imap server --uidexpunge2 : uidexpunge messages on the destination imap server
that are not on the source server, requires --delete2 that are not on the source server, requires --delete2
--syncinternaldates : sets the internal dates on host2 same as host1. --syncinternaldates : sets the internal dates on host2 same as host1.
Turned on by default. Turned on by default. internal date is the date
a message arrived on a host (mtime).
--idatefromheader : sets the internal dates on host2 same as the --idatefromheader : sets the internal dates on host2 same as the
"Date:" headers. "Date:" headers.
--buffersize <int> : sets the size of a block of I/O. --buffersize <int> : sets the size of a block of I/O.
@ -2890,15 +2907,13 @@ no warnings 'once';
sub myconnect { sub myconnect {
my $self = shift; my $self = shift;
$self->Port(143) $debug and print "Entering myconnect\n";
if defined ($IO::Socket::INET::VERSION)
and $IO::Socket::INET::VERSION eq '1.25'
and !$self->Port;
%$self = (%$self, @_); %$self = (%$self, @_);
my $sock = ($self->Ssl ? IO::Socket::SSL->new : IO::Socket::INET->new); my $sock = (($self->Ssl) ? IO::Socket::SSL->new : IO::Socket::INET->new);
my $dp = ($self->Ssl ? 'imaps(993)' : 'imap(143)'); my $dp = ($self->Ssl ? 'imaps(993)' : 'imap(143)');
$debug and print "Calling configure\n";
my $ret = $sock->configure({ my $ret = $sock->configure({
PeerAddr => $self->Server , PeerAddr => $self->Server ,
PeerPort => $self->Port||$dp , PeerPort => $self->Port||$dp ,
@ -2913,24 +2928,45 @@ sub myconnect {
unless defined wantarray; unless defined wantarray;
return undef; return undef;
} }
$self->Socket($sock);
if ($self->Tls) {
$debug and print "Calling starttls\n";
$sock->autoflush(1);
my $banner = starttls($sock);
$debug and print "End starttls: $banner\n";
$self->State(Mail::IMAPClient::Connected);
}
$debug and print "Calling Socket\n";
if ($Mail::IMAPClient::VERSION =~ /^3/ and $self->Tls) {
$self->RawSocket($sock);
}else{
$self->Socket($sock);
}
if ( $Mail::IMAPClient::VERSION =~ /^2/ ) { if ( $Mail::IMAPClient::VERSION =~ /^2/ ) {
$debug and print "Calling myconnect_v2\n";
return undef unless myconnect_v2($self); return undef unless myconnect_v2($self);
$debug and print "End myconnect_v2\n";
} }
else { else {
$self->Ignoresizeerrors($allowsizemismatch); $self->Ignoresizeerrors($allowsizemismatch);
} }
if ($self->User and $self->Password) { if ($self->User and $self->Password) {
$debug and print "Calling login\n";
return $self->login ; return $self->login ;
} }
else { else {
return $self; return $self;
} }
} }
sub myconnect_v2 { sub myconnect_v2 {
my $self = shift; my $self = shift;
return $self if $self->Tls;
$self->State(Connected); $self->State(Connected);
$self->Socket->autoflush(1); $self->Socket->autoflush(1);
my ($code, $output); my ($code, $output);
@ -2955,6 +2991,40 @@ sub myconnect_v2 {
} }
sub starttls {
my $socket = shift;
$debug and print "Entering starttls\n";
my $banner = $socket->getline();
unless ($banner =~ /^\* OK \[CAPABILITY.*STARTTLS.*\]/) {
die "No STARTTLS capability: $banner";
}
print $socket "STARTTLS\015\012";
my $txt = $socket->getline();
$debug and print "$txt";
unless($txt =~ /^STARTTLS OK/){
die "Invalid response for STARTTLS: $txt\n";
}
$debug and print "Calling start_SSL\n";
unless(IO::Socket::SSL->start_SSL($socket,
{
SSL_version => "TLSV1",
SSL_startHandshake => 1,
SSL_verify_depth => 1,
}))
{
die "Couldn't start TLS: ".IO::Socket::SSL::errstr()."\n";
}
if (ref($socket) ne "IO::Socket::SSL") {
die "Socket has NOT been converted to SSL";
}else{
$debug and print "Socket successfuly converted to SSL\n";
}
$banner;
}
package Mail::IMAPClient; package Mail::IMAPClient;
@ -2964,3 +3034,11 @@ sub Split {
if (@_) { $self->{SPLIT} = shift } if (@_) { $self->{SPLIT} = shift }
return $self->{SPLIT}; return $self->{SPLIT};
} }
sub Tls {
my $self = shift;
if (@_) { $self->{TLS} = shift }
return $self->{TLS};
}

81
learn/imapclient_tls Executable file
View file

@ -0,0 +1,81 @@
#!/usr/bin/perl -w
use Mail::IMAPClient;
use IO::Socket::SSL qw(debug1 debug2 debug3) ;
$ARGV[3] or die "usage: $0 host user password folder\n";
$host = $ARGV[0];
$user = $ARGV[1];
$password = $ARGV[2];
$folder = $ARGV[3];
my $imap = Mail::IMAPClient->new();
my $socket = IO::Socket::INET->new(
Proto => 'tcp',
PeerAddr => $host,
PeerPort => 143,
);
$socket->autoflush(1);
my $banner = $socket->getline();
unless ($banner =~ /^\* OK \[CAPABILITY.*STARTTLS.*\]/) {
die "No STARTTLS capability: $banner";
}
print $socket "STARTTLS\015\012";
my $txt = $socket->getline();
unless($txt =~ /^STARTTLS OK/){
die "Invalid response for STARTTLS: $txt\n";
}
my $result = IO::Socket::SSL->start_SSL($socket,
{
SSL_startHandshake => 1,
SSL_version => "TLSv1",
SSL_verify_depth => 1,
}
);
print "start_SSL return $result\n";
unless ($result){
die "Couldn't start TLS: ".IO::Socket::SSL::errstr()."\n";
}
if (ref($socket) ne "IO::Socket::SSL") {
die "Socket has not been converted to SSL";
}else{
print "Socket has been converted to SSL\n";
}
$imap->State(Mail::IMAPClient::Connected);
$imap->Debug(1);
print "Socket\n";
$imap->RawSocket($socket);
#$imap->Socket($socket);
print $socket "a02 CAPABILITY\n";
print "getline\n";
$txt = $socket->getline();
print "getline : $txt \n";
#$imap->connect() or die;
print "User\n";
$imap->User($user);
$imap->Password($password);
print "login\n";
$imap->login() or die;
$imap->Uid(1);
$imap->Peek(1);
$imap->select($folder) or die;
$imap->close();

View file

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
# $Id: tests.sh,v 1.96 2010/01/14 23:40:54 gilles Exp gilles $ # $Id: tests.sh,v 1.98 2010/01/20 04:13:59 gilles Exp gilles $
# Example: # Example:
# CMD_PERL='perl -I./Mail-IMAPClient-3.14/lib' sh -x tests.sh # CMD_PERL='perl -I./Mail-IMAPClient-3.14/lib' sh -x tests.sh
@ -634,8 +634,8 @@ ll_flags()
--host2 $HOST2 --user2 titi \ --host2 $HOST2 --user2 titi \
--passfile2 ../../var/pass/secret.titi \ --passfile2 ../../var/pass/secret.titi \
--folder INBOX.yop.yap \ --folder INBOX.yop.yap \
--dry --debug \ --debug
--allow3xx
echo 'rm /home/vmail/titi/.yop.yap/cur/*' echo 'rm /home/vmail/titi/.yop.yap/cur/*'
} }
@ -647,7 +647,7 @@ ll_regex_flag()
--host2 $HOST2 --user2 titi \ --host2 $HOST2 --user2 titi \
--passfile2 ../../var/pass/secret.titi \ --passfile2 ../../var/pass/secret.titi \
--folder INBOX.yop.yap \ --folder INBOX.yop.yap \
--debug --regexflag 's/\\Answered/\\Flagged/g' --debug --regexflag 's/\\Answered/\\Seen/g'
echo 'rm -f /home/vmail/titi/.yop.yap/cur/*' echo 'rm -f /home/vmail/titi/.yop.yap/cur/*'
} }
@ -680,18 +680,63 @@ ll_regex_flag3()
} }
ll_tls_justconnect() {
$CMD_PERL ./imapsync \
--host1 l \
--host2 l \
--tls1 --tls2 \
--justconnect --debug
}
ll_tls_justlogin() {
$CMD_PERL ./imapsync \
--host1 $HOST1 --user1 tata \
--passfile1 ../../var/pass/secret.tata \
--host2 $HOST2 --user2 titi \
--passfile2 ../../var/pass/secret.titi \
--tls1 --tls2 \
--justlogin --debug
}
ssl_justconnect() {
ll_tls_devel() {
CMD_PERL='perl -I./Mail-IMAPClient-2.2.9' ll_justlogin ll_ssl_justlogin \
&& CMD_PERL='perl -I./Mail-IMAPClient-3.19/lib' ll_justlogin ll_ssl_justlogin \
&& CMD_PERL='perl -I./Mail-IMAPClient-2.2.9' ll_tls_justconnect ll_tls_justlogin \
&& CMD_PERL='perl -I./Mail-IMAPClient-3.19/lib' ll_tls_justconnect ll_tls_justlogin
}
ll_tls() {
$CMD_PERL ./imapsync \
--host1 $HOST1 --user1 tata \
--passfile1 ../../var/pass/secret.tata \
--host2 $HOST2 --user2 titi \
--passfile2 ../../var/pass/secret.titi \
--tls1 --tls2
}
ll_ssl_justconnect() {
$CMD_PERL ./imapsync \ $CMD_PERL ./imapsync \
--host1 $HOST1 \ --host1 $HOST1 \
--host2 $HOST2 \ --host2 $HOST2 \
--ssl1 --ssl2 \ --ssl1 --ssl2 \
--justconnect \ --justconnect
--allow3xx
} }
ll_ssl_justlogin() {
$CMD_PERL ./imapsync \
--host1 $HOST1 --user1 tata \
--passfile1 ../../var/pass/secret.tata \
--host2 $HOST2 --user2 titi \
--passfile2 ../../var/pass/secret.titi \
--ssl1 --ssl2 \
--justlogin
}
ll_ssl() { ll_ssl() {
if can_send; then if can_send; then
#echo3 Here is plume #echo3 Here is plume
@ -892,7 +937,7 @@ archiveopteryx_1() {
--allow3xx --allow3xx
} }
justlogin() { ll_justlogin() {
# Look in the file ../../var/pass/secret.tptp to see # Look in the file ../../var/pass/secret.tptp to see
# strange \ character behavior # strange \ character behavior
$CMD_PERL ./imapsync \ $CMD_PERL ./imapsync \
@ -903,7 +948,7 @@ justlogin() {
--allow3xx --justlogin --noauthmd5 --allow3xx --justlogin --noauthmd5
} }
justlogin_backslash_char() { ll_justlogin_backslash_char() {
# Look in the file ../../var/pass/secret.tptp to see # Look in the file ../../var/pass/secret.tptp to see
# strange \ character behavior # strange \ character behavior
$CMD_PERL ./imapsync \ $CMD_PERL ./imapsync \
@ -1169,7 +1214,6 @@ test $# -eq 0 && run_tests \
ll_folder_rev \ ll_folder_rev \
ll_subscribed \ ll_subscribed \
ll_subscribe \ ll_subscribe \
ll_justconnect \
ll_justfoldersizes \ ll_justfoldersizes \
ll_authmd5 \ ll_authmd5 \
ll_noauthmd5 \ ll_noauthmd5 \
@ -1190,7 +1234,14 @@ test $# -eq 0 && run_tests \
ll_regexmess_scwchu \ ll_regexmess_scwchu \
ll_flags \ ll_flags \
ll_regex_flag \ ll_regex_flag \
ll_justconnect \
ll_justlogin \
ll_ssl \ ll_ssl \
ll_ssl_justconnect \
ll_ssl_justlogin \
ll_tls_justconnect \
ll_tls_justlogin \
ll_tls \
ll_authmech_PLAIN \ ll_authmech_PLAIN \
ll_authmech_LOGIN \ ll_authmech_LOGIN \
ll_authmech_CRAMMD5 \ ll_authmech_CRAMMD5 \
@ -1202,13 +1253,11 @@ test $# -eq 0 && run_tests \
gmail_gmail \ gmail_gmail \
gmail_gmail2 \ gmail_gmail2 \
archiveopteryx_1 \ archiveopteryx_1 \
ssl_justconnect \
allow3xx \ allow3xx \
noallow3xx \ noallow3xx \
justlogin \
# msw # msw
# justlogin_backslash_char # ll_justlogin_backslash_char
# selective tests # selective tests