diff --git a/BUG_IMAPClient_3.xx b/BUG_IMAPClient_3.xx new file mode 100644 index 0000000..5826e11 --- /dev/null +++ b/BUG_IMAPClient_3.xx @@ -0,0 +1,9 @@ + + +BUGS found with Mail-IMAPClient-3.05/ + +1) --ssl* bugs. + + 30 timeout. + +2) --expunge2 does not expunge anything. diff --git a/CREDITS b/CREDITS index ff2a232..767e47e 100644 --- a/CREDITS +++ b/CREDITS @@ -14,6 +14,23 @@ b) If you can read french, please use the following wishlist : c) its paypal account gilles.lamiral@laposte.net +Blake Heinemann +Contributed by giving the book +"Perl Testing: A Developer's Notebook" + +Nathan Mills +Contributed by giving the book +"Mapping Hacks" + +Patrick Dayton (Medicus Insurance Compagny) +Contributed by giving the book +"Perl Hacks" + + +Daniel Kohn +Contributed by giving the book +"Combinatorial Optimization: Algorithms and Complexity" + Cvitkovich Andres Gave a patch to implement diff --git a/ChangeLog b/ChangeLog index 825358a..4ec1613 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,17 +1,50 @@ RCS file: RCS/imapsync,v Working file: imapsync -head: 1.241 +head: 1.249 branch: locks: strict - gilles: 1.241 access list: symbolic names: keyword substitution: kv -total revisions: 241; selected revisions: 241 +total revisions: 249; selected revisions: 249 description: ---------------------------- -revision 1.241 locked by: gilles; +revision 1.249 +date: 2008/03/19 02:14:24; author: gilles; state: Exp; lines: +7 -7 +warn about BUG_IMAPClient_3.xx +---------------------------- +revision 1.248 +date: 2008/03/19 02:05:46; author: gilles; state: Exp; lines: +14 -19 +Cleaned check_lib_version() +---------------------------- +revision 1.247 +date: 2008/03/19 01:41:49; author: gilles; state: Exp; lines: +1 -1 +Added id in output warn when no header found. +---------------------------- +revision 1.246 +date: 2008/03/19 01:07:26; author: gilles; state: Exp; lines: +19 -16 +Removed $^W use. +---------------------------- +revision 1.245 +date: 2008/03/10 23:49:42; author: gilles; state: Exp; lines: +53 -23 +Back to append_string() +Turn on --syncinternaldates by default +Date_Init("TZ=GMT") if no timezone (windows) set. +---------------------------- +revision 1.244 +date: 2008/02/29 22:43:22; author: gilles; state: Exp; lines: +5 -545 +Removed old *_2() functions (unused) +---------------------------- +revision 1.243 +date: 2008/02/29 16:47:58; author: gilles; state: Exp; lines: +632 -53 +Moved functins *_2() into override_imapclient() +---------------------------- +revision 1.242 +date: 2008/02/29 00:28:15; author: gilles; state: Exp; lines: +24 -13 +Ignore message when it has no header. +---------------------------- +revision 1.241 date: 2007/12/31 13:39:02; author: gilles; state: Exp; lines: +6 -6 Bug fix. --exclude and remove_from_requested_folders() ---------------------------- diff --git a/FAQ b/FAQ index 5dd2581..37a0524 100644 --- a/FAQ +++ b/FAQ @@ -3,6 +3,21 @@ | FAQ for imapsync | +------------------+ +======================================================================= +Q. How to install impasync ? + +R. http://www.linux-france.org/prj/imapsync/INSTALL + +======================================================================= +Q. How to configure impasync ? + +R. http://www.linux-france.org/prj/imapsync/README + +======================================================================= +Q. Can you give some configuration examples ? + +R. http://www.linux-france.org/prj/imapsync/FAQ + ======================================================================= Q. Where I can read IMAP RFCs ? @@ -26,6 +41,20 @@ Q. Where I can find old imapsync releases ? R. ftp://www.linux-france.org/pub/prj/imapsync/ +======================================================================= +Q. imapsync does not work with Mail::IMAPClient 3.0.x + How can I downgrade to 2.2.9 release? + +R. - Download Mail::IMAPClient 2.2.9 at + http://search.cpan.org/~djkernen/Mail-IMAPClient-2.2.9/ + http://search.cpan.org/CPAN/authors/id/D/DJ/DJKERNEN/Mail-IMAPClient-2.2.9.tar.gz + - untar it anywhere: + tar xzvf Mail-IMAPClient-2.2.9.tar.gz + - run imapsync with perl and -I option tailing to use Mail-IMAPClient-2.2.9: + perl -I./Mail-IMAPClient-2.2.9 imapsync [...] + or if imapsync is in directory /path/ + perl -I./Mail-IMAPClient-2.2.9 /path/imapsync [...] + ======================================================================= Q. We have found that the sent time and date have been changed to the time at which the file was synchronised. @@ -122,6 +151,17 @@ The default hard datasize limit on FreeBSD is 512MB. To raise it, put this kern.maxdsiz="1024M" +======================================================================= +Q. With huge account (many messages) when it comes to reading the +destination server it comes out this error: +"To Folder [INBOX.foobar] Not connected" +What can I do ? + +R. May be spending too much time on the source server, the connection +timed out on the destination server. +Try options : + --nofoldersizes --useheader Message-ID --fast + ======================================================================= Q. imapsync failed with a "word too long" error from the imap server, What can I do ? diff --git a/INSTALL b/INSTALL index 578bb36..9832c18 100644 --- a/INSTALL +++ b/INSTALL @@ -1,4 +1,4 @@ -# $Id: INSTALL,v 1.12 2007/10/30 00:49:03 gilles Exp gilles $ +# $Id: INSTALL,v 1.13 2008/03/19 00:28:36 gilles Exp gilles $ # # INSTALL file for imapsync # imapsync : IMAP sync or copy tool. @@ -42,12 +42,14 @@ Here is some individual module help: perl -mMail::IMAPClient -e 'print $Mail::IMAPClient::VERSION, "\n"' + New Mail-IMAPClient-3.xx doesn't work with imapsync for the moment. + - Perl Digest::MD5 module. http://search.cpan.org/ - http://search.cpan.org/~gaas/Digest-MD5-2.33/ + http://search.cpan.org/~gaas/Digest-MD5-2.36/ To know the version you have on your system try : perl -mDigest::MD5 -e 'print $Digest::MD5::VERSION, "\n"' - I use 2.20 (debian package) + I use 2.36 (debian etch package) - Term::ReadKey perl -mTerm::ReadKey -e '' @@ -94,7 +96,7 @@ titi@est.belle with a password located in the file /var/tmp/secret2 Of course, you can change the file tests.sh and run the tests with : -sh tests.sh +sh -x tests.sh The tests.sh script break on first failure ("set -e" directive). diff --git a/Mail-IMAPClient-3.00/lib/Mail/IMAPClient/BodyStructure/Parse.grammar b/Mail-IMAPClient-3.00/lib/Mail/IMAPClient/BodyStructure/Parse.grammar deleted file mode 100755 index 100d3f8..0000000 --- a/Mail-IMAPClient-3.00/lib/Mail/IMAPClient/BodyStructure/Parse.grammar +++ /dev/null @@ -1,282 +0,0 @@ -# Directives -# ( none) -# Start-up Actions - -{ - my $subpartCount = 0; - my $partCount = 0; -} - -# -# Atoms -TEXT: /^"TEXT"|^TEXT/i { $return = "TEXT" } -PLAIN: /^"PLAIN"|^PLAIN/i { $return = "PLAIN" } -HTML: /"HTML"|HTML/i { $return = "HTML" } -MESSAGE: /^"MESSAGE"|^MESSAGE/i { $return = "MESSAGE" } -RFC822: /^"RFC822"|^RFC822/i { $return = "RFC822" } -NIL: /^NIL/i { $return = "NIL" } -NUMBER: /^(\d+)/ { $return = $item[1]; $return||defined($return);} - -# Strings: - -SINGLE_QUOTED_STRING: "'" /(?:\\'|[^'])*/ "'" { - - $return = $item{__PATTERN1__} ; - $return||defined($return); -} - -DOUBLE_QUOTED_STRING: '"' /(?:\\"|[^"])*/ '"' { - - $return = $item{__PATTERN1__} ; - $return||defined($return); -} - -QUOTED_STRING: DOUBLE_QUOTED_STRING | SINGLE_QUOTED_STRING { - - $return = $item{DOUBLE_QUOTED_STRING}||$item{SINGLE_QUOTED_STRING} ; - $return||defined($return); -} - -BARESTRING: ...!/^[)('"]/ /^(?!\(|\))(?:\\ |\S)+/ { - $return = $item{__PATTERN1__} ; $return||defined($return); -} - -STRING: QUOTED_STRING | BARESTRING { - $return = $item{QUOTED_STRING}||$item{BARESTRING} ; - $return||defined($return); -} - -OLDSTRING: /^"((?:[^"\\]|\\.)*)"/ | /^([^ \(\)]+)/ - { $item{__PATTERN1__} =~ s/^"(.*)"$/$1/; - $return = $item{__PATTERN1__} || $item{__PATTERN2__} ; - $return||defined($return); - } - -#BARESTRING: /^[^(]+\s+(?=\()/ -# { $return = $item[1] ; $return||defined($return);} - -textlines: NIL | NUMBER { $return = $item[1] || $item[2]; $return||defined($return); } -rfc822message: MESSAGE RFC822 { $return = "MESSAGE RFC822" } -key: STRING { $return = $item{STRING} ; $return||defined($return);} -value: NIL | '(' kvpair(s) ')'| NUMBER | STRING - { $return = $item{NIL} || - $item{NUMBER} || - $item{STRING} || - { map { (%$_) } @{$item{'kvpair(s)'}} } ; - $return||defined($return); - } -kvpair: ...!")" key value - { $return = { $item{key} => $item{value} }; $return||defined($return);} -bodytype: STRING - { $return = $item{STRING} ; $return||defined($return);} -bodysubtype: PLAIN | HTML | NIL | STRING - { $return = $item{PLAIN}||$item{HTML}||$item{NIL}||$item{STRING} ; - $return||defined($return); - } -bodyparms: NIL | '(' kvpair(s) ')' - { - $return = $item{NIL} || - { map { (%$_) } @{$item{'kvpair(s)'}} }; - $return || defined($return); - } -bodydisp: NIL | '(' kvpair(s) ')' - { - $return = $item{NIL} || - { map { (%$_) } @{$item{'kvpair(s)'}} }; - $return || defined($return); - } -bodyid: ...!/[()]/ NIL | STRING - { $return = $item{NIL} || $item{STRING} ; $return||defined($return);} - -bodydesc: ...!/[()]/ NIL | STRING - { $return = $item{NIL} || $item{STRING}; $return||defined($return);} - -bodyenc: NIL | STRING | '(' kvpair(s) ')' - { - $return = $item{NIL} || - $item{STRING} || - { map { (%$_) } @{$item{'kvpair(s)'}} }; - $return||defined($return); - - } -bodysize: ...!/[()]/ NIL | NUMBER - { $return = $item{NIL} || $item{NUMBER}; $return||defined($return);} - -bodyMD5: NIL | STRING - { $return = $item{NIL} || $item{STRING}; $return||defined($return);} - -bodylang: NIL | STRING | "(" STRING(s) ")" - { $return = $item{NIL} || $item{'STRING(s)'}; $return||defined($return);} - -bodyextra: NIL | STRING | "(" STRING(s) ")" - { 0 } - -personalname: NIL | STRING - { $return = $item{NIL} || $item{STRING}; $return||defined($return);} - -sourceroute: NIL | STRING - { $return = $item{NIL} || $item{STRING}; $return||defined($return);} - -mailboxname: NIL | STRING - { $return = $item{NIL} || $item{STRING}; $return||defined($return);} - -hostname: NIL | STRING - { $return = $item{NIL} || $item{STRING}; $return||defined($return);} - -addressstruct: "(" personalname sourceroute mailboxname hostname ")" - { bless { - personalname => $item{personalname} , - sourceroute => $item{sourceroute} , - mailboxname => $item{mailboxname} , - hostname => $item{hostname} , - }, 'Mail::IMAPClient::BodyStructure::Address'; - } - -subject: NIL | STRING - { - $return = $item{NIL} || $item{STRING} ; - $return||defined($return); - } -inreplyto: NIL | STRING - { $return = $item{NIL} || $item{STRING} ;$return||defined($return);} - -messageid: NIL | STRING - { $return = $item{NIL} || $item{STRING} ;$return||defined($return);} - -date: NIL | STRING - { $return = $item{NIL} || $item{STRING} ;$return||defined($return);} - -cc: NIL | "(" addressstruct(s) ")" - { $return = $item{NIL} || $item{'addressstruct(s)'} } - -bcc: NIL | "(" addressstruct(s) ")" - { $return = $item{NIL} || $item{'addressstruct(s)'} } - -from: NIL | "(" addressstruct(s) ")" - { $return = $item{NIL} || $item{'addressstruct(s)'} } - -replyto: NIL | "(" addressstruct(s) ")" - { $return = $item{NIL} || $item{'addressstruct(s)'} } - -sender: NIL | "(" addressstruct(s) ")" - { $return = $item{NIL} || $item{'addressstruct(s)'} } - -to: NIL | "(" addressstruct(s) ")" - { $return = $item{NIL} || $item{'addressstruct(s)'} } - -envelopestruct: "(" date subject from sender replyto to cc bcc inreplyto messageid ")" - { $return = bless {}, "Mail::IMAPClient::BodyStructure::Envelope"; - $return->{$_} = $item{$_} - for qw/date subject from sender replyto to cc/ - , qw/bcc inreplyto messageid/ ; - $return; - } - -basicfields: bodysubtype bodyparms bodyid(?) - bodydesc(?) bodyenc(?) - bodysize(?) { - - - $return = - { bodysubtype => $item{bodysubtype} - , bodyparms => $item{bodyparms} - }; - $return->{$_} = ref $item{"$_(?}"} ? $item{"$_(?}"}[0] :$item{"$_(?}"} - for qw/bodyid bodydesc bodyenc bodysize/; - $return; - } - -textmessage: TEXT basicfields textlines(?) bodyMD5(?) - bodydisp(?) bodylang(?) bodyextra(?) - { - $return = $item{basicfields} || {}; - $return->{bodytype} = 'TEXT'; - foreach my $what (qw/textlines(?) bodyMD5(?) bodydisp(?) bodylang(?)/) - { my $k = $what; $k =~ s/\(\?\)$//; - $return->{$k} = $item{$what}[0] if ref $item{$what}; - } - - $return; - } - -othertypemessage: bodytype basicfields bodyparms(?) bodydisp(?) - bodylang(?) bodyextra(?) - { $return = {}; - foreach my $what ( qw/bodytype bodyparms(?) bodydisp(?)/ - , qw/bodylang(?) bodyextra(?)/ ) - { my $k = $what; $k =~ s/\(\?\)$//; - $return->{$k} = ref($item{$what})? $item{$what}[0] : $item{$what} ; - } - while( my($k,$v) = each %{$item{basicfields}} ) { $return->{$k} = $v } - $return; - } - -messagerfc822message: - rfc822message bodyparms bodyid bodydesc bodyenc bodysize - envelopestruct bodystructure textlines - bodyMD5(?) bodydisp(?) bodylang(?) bodyextra(?) - { - $return = {}; - foreach my $what (qw/ bodyparms bodyid bodydesc bodyenc bodysize - envelopestruct bodystructure textlines - bodyMD5(?) bodydisp(?) bodylang(?) bodyextra(?) - / - ) { - my $k = $what; $k =~ s/\(\?\)$//; - $return->{$k} = ref $item{$what} =~ 'ARRAY'? - $item{$what}[0] : $item{$what}; - } - while(my($k,$v) = each %{$item{bodystructure}[0]}) { $return->{$k} = $v} - while(my($k,$v) = each %{$item{basicfields}}) { $return->{$k} = $v} - $return->{bodytype} = "MESSAGE" ; - $return->{bodysubtype} = "RFC822" ; - $return; - } - -subpart: "(" part ")" - { $return = $item{part} ; - $return||defined($return); - } - - -part: subpart(s) basicfields - bodyparms(?) bodydisp(?) bodylang(?) bodyextra(?) - - { $return = bless $item{basicfields},"Mail::IMAPClient::BodyStructure"; - $return->{bodytype} = "MULTIPART"; - $return->{bodystructure} = $item{'subpart(s)'}; - foreach my $b (qw/bodyparms(?) bodydisp(?) bodylang(?) bodyextra(?)/) - { my $k = $b; $k =~ s/\(\?\)$//; - $return->{$k} = ref($item{$b}) ? $item{$b}[0] : $item{$b}; - } - $return; - } - | textmessage - { $return = bless $item{textmessage}, "Mail::IMAPClient::BodyStructure"; - $return||defined($return); - } - | messagerfc822message - { $return = bless $item{messagerfc822message}, "Mail::IMAPClient::BodyStructure"; - $return||defined($return); - } - | othertypemessage - { $return = bless $item{othertypemessage}, "Mail::IMAPClient::BodyStructure"; - $return||defined($return); - } - -bodystructure: "(" part(s) ")" - { $return = $item{'part(s)'} ; - $return||defined($return); - } - -start: /.*\(.*BODYSTRUCTURE \(/i part(1) /\).*\)\r?\n?/ - { - #print STDERR "item = ",Data::Dumper->Dump([\%item],['$item']); - $return = $item{'part(1)'}[0]; - $return || defined $return; - } - -envelope: /.*\(.*ENVELOPE/ envelopestruct /.*\)/ - { $return = $item{envelopestruct}; - $return || defined $return; - } diff --git a/Mail-IMAPClient-3.00/COPYRIGHT b/Mail-IMAPClient-3.05/COPYRIGHT similarity index 100% rename from Mail-IMAPClient-3.00/COPYRIGHT rename to Mail-IMAPClient-3.05/COPYRIGHT diff --git a/Mail-IMAPClient-3.00/Changes b/Mail-IMAPClient-3.05/Changes similarity index 96% rename from Mail-IMAPClient-3.00/Changes rename to Mail-IMAPClient-3.05/Changes index 1d69433..5a89171 100644 --- a/Mail-IMAPClient-3.00/Changes +++ b/Mail-IMAPClient-3.05/Changes @@ -3,6 +3,107 @@ All changes from 2.99_01 upward are made by Mark Overmeer. The changes before that are applied by David Kernen +version 3.05: Wed Feb 20 08:59:37 CET 2008 + + Fixes: + + - match ENVELOPE and BODYSTRUCTURE more strict in the + grammar, to avoid confusion. [Zach Levow] + + - get_envelope and get_bodystructure failed for servers which + did not return the whole answer in one piece. [Zach Levow] + + - do not produce parser errors when get_envelope does not + return an envelope. [Zach Levow] + + - PLAIN login response possibly solely a '+' [Zach] and [Nick] + +version 3.04: Fri Jan 25 09:25:51 CET 2008 + + Fixes: + + - read_header fix for UID on Windows Server 2003. + rt.cpan.org#32398 [Michiel Stelman] + + Improvements: + + - doc update on authentication, by [Thomas Jarosch] + +version 3.03: Wed Jan 9 22:11:36 CET 2008 + + Fixes: + + - LIST (f.i. used by folders()) did not return anything when the + passed argument had a trailing separator. [Gunther Heintze] + + - Rfc2060_datetime() must include a zone. + rt.cpan.org#31971 [David Golden] + + - folders() uses LIST, and then calls a STATUS on each of the + names found. This is superfluous, and will cause problems when + the STATUS fails... for instance because of ACL limitations + on the sub-folder. + rt.cpan.org#31962 [Thomas Jarosch] + + - fixed a zillion of problems in the BodyStructure parser. The + original author did not understand parsing, nor Perl. + + - part numbering wrong when nested messages contained multiparts + + Improvements: + + - implementation of DIGEST-MD5 authentication [Thomas Jarosch] + + - removed call for status() in Massage(), which hopefully speeds-up + things without destroying anything. It removed a possible deep + recursion, which no-one reported (so should be ok to remove it) + + - simplified folders() algorithm. + + - merged folder commands, like subscribe into one. + + - added unsubscribe() + rt.cpan.org#31268 [G Miller] + + - lazy-load Digest::HMAC_MD5 + +version 3.02: Wed Dec 5 21:33:17 CET 2007 + + Fixes: + + - Another attempt to get get FETCH UID right. Patch by [David Golden] + +version 3.01: Wed Dec 5 09:55:43 CET 2007 + + Changes: + + - removed version number from ::BodyStructure + + Fixes: + + - quote password at login. + rt.cpan.org#31035 [Andy Harriston] + + - empty return of flags command should be empty list, not undef. + rt.cpan.org#31195 [David Golden] + + - UID command does not work with folder management commands + rt.cpan.org#31182 [Robbert Norris] + + - _read_line simplifications avoids timeouts. + rt.cpan.org#31221 [Robbert Norris] + + - FETCH did not detect the UID of a message anymore. + [David Golden] + + Improvements: + + - proxyauth for SUN/iPlanet/NetScape IMAP servers. + patch by rt.cpan.org#31152 [Robbert Norris] + + - use grep in stead of map in one occasion in MessageSet.pm + [Yves Orton] + version 3.00: Wed Nov 28 09:56:54 CET 2007 Fixes: diff --git a/Mail-IMAPClient-3.00/INSTALL b/Mail-IMAPClient-3.05/INSTALL similarity index 100% rename from Mail-IMAPClient-3.00/INSTALL rename to Mail-IMAPClient-3.05/INSTALL diff --git a/Mail-IMAPClient-3.00/MANIFEST b/Mail-IMAPClient-3.05/MANIFEST similarity index 100% rename from Mail-IMAPClient-3.00/MANIFEST rename to Mail-IMAPClient-3.05/MANIFEST diff --git a/Mail-IMAPClient-3.00/META.yml b/Mail-IMAPClient-3.05/META.yml similarity index 79% rename from Mail-IMAPClient-3.00/META.yml rename to Mail-IMAPClient-3.05/META.yml index 742c8b8..06337d7 100644 --- a/Mail-IMAPClient-3.00/META.yml +++ b/Mail-IMAPClient-3.05/META.yml @@ -1,9 +1,10 @@ --- #YAML:1.0 name: Mail-IMAPClient -version: 3.00 +version: 3.05 abstract: IMAP4 client library license: ~ -generated_by: ExtUtils::MakeMaker version 6.36_01 +author: ~ +generated_by: ExtUtils::MakeMaker version 6.42 distribution_type: module requires: Carp: 0 @@ -21,5 +22,5 @@ requires: Test::More: 0 Test::Pod: 0 meta-spec: - url: http://module-build.sourceforge.net/META-spec-v1.2.html - version: 1.2 + url: http://module-build.sourceforge.net/META-spec-v1.3.html + version: 1.3 diff --git a/Mail-IMAPClient-3.00/Makefile.PL b/Mail-IMAPClient-3.05/Makefile.PL similarity index 94% rename from Mail-IMAPClient-3.00/Makefile.PL rename to Mail-IMAPClient-3.05/Makefile.PL index dd09d08..58fc669 100644 --- a/Mail-IMAPClient-3.00/Makefile.PL +++ b/Mail-IMAPClient-3.05/Makefile.PL @@ -2,6 +2,8 @@ use ExtUtils::MakeMaker; use warnings; use strict; +sub set_test_data(); + WriteMakefile ( NAME => 'Mail::IMAPClient', , ABSTRACT => 'IMAP4 client library' @@ -28,8 +30,14 @@ WriteMakefile set_test_data(); -sub set_test_data { - unless(-f "lib/Mail/IMAPClient.pm") +exit 0; + +### +### HELPERS +### + +sub set_test_data() +{ unless(-f "lib/Mail/IMAPClient.pm") { warn "ERROR: not in installation directory\n"; return; } @@ -46,7 +54,7 @@ __INTRO my $yes = prompt "Do you want to run the extended tests? (n/y)"; return if $yes !~ /^[Yy](?:[Ee]:[Ss]?)?$/ ; - unless(open TST,">./test.txt") + unless(open TST, '>', "./test.txt") { warn "ERROR: couldn't open ./test.txt: $!\n"; return; } diff --git a/Mail-IMAPClient-3.00/README b/Mail-IMAPClient-3.05/README similarity index 100% rename from Mail-IMAPClient-3.00/README rename to Mail-IMAPClient-3.05/README diff --git a/Mail-IMAPClient-3.00/Todo b/Mail-IMAPClient-3.05/Todo similarity index 100% rename from Mail-IMAPClient-3.00/Todo rename to Mail-IMAPClient-3.05/Todo diff --git a/Mail-IMAPClient-3.00/examples/build_dist.pl b/Mail-IMAPClient-3.05/examples/build_dist.pl similarity index 100% rename from Mail-IMAPClient-3.00/examples/build_dist.pl rename to Mail-IMAPClient-3.05/examples/build_dist.pl diff --git a/Mail-IMAPClient-3.00/examples/build_ldif.pl b/Mail-IMAPClient-3.05/examples/build_ldif.pl similarity index 100% rename from Mail-IMAPClient-3.00/examples/build_ldif.pl rename to Mail-IMAPClient-3.05/examples/build_ldif.pl diff --git a/Mail-IMAPClient-3.00/examples/cleanTest.pl b/Mail-IMAPClient-3.05/examples/cleanTest.pl similarity index 100% rename from Mail-IMAPClient-3.00/examples/cleanTest.pl rename to Mail-IMAPClient-3.05/examples/cleanTest.pl diff --git a/Mail-IMAPClient-3.00/examples/copy_folder.pl b/Mail-IMAPClient-3.05/examples/copy_folder.pl similarity index 100% rename from Mail-IMAPClient-3.00/examples/copy_folder.pl rename to Mail-IMAPClient-3.05/examples/copy_folder.pl diff --git a/Mail-IMAPClient-3.00/examples/cyrus_expire.pl b/Mail-IMAPClient-3.05/examples/cyrus_expire.pl similarity index 100% rename from Mail-IMAPClient-3.00/examples/cyrus_expire.pl rename to Mail-IMAPClient-3.05/examples/cyrus_expire.pl diff --git a/Mail-IMAPClient-3.00/examples/cyrus_expunge.pl b/Mail-IMAPClient-3.05/examples/cyrus_expunge.pl similarity index 100% rename from Mail-IMAPClient-3.00/examples/cyrus_expunge.pl rename to Mail-IMAPClient-3.05/examples/cyrus_expunge.pl diff --git a/Mail-IMAPClient-3.00/examples/find_dup_msgs.pl b/Mail-IMAPClient-3.05/examples/find_dup_msgs.pl similarity index 100% rename from Mail-IMAPClient-3.00/examples/find_dup_msgs.pl rename to Mail-IMAPClient-3.05/examples/find_dup_msgs.pl diff --git a/Mail-IMAPClient-3.00/examples/imap_to_mbox.pl b/Mail-IMAPClient-3.05/examples/imap_to_mbox.pl similarity index 100% rename from Mail-IMAPClient-3.00/examples/imap_to_mbox.pl rename to Mail-IMAPClient-3.05/examples/imap_to_mbox.pl diff --git a/Mail-IMAPClient-3.00/examples/imtestExample.pl b/Mail-IMAPClient-3.05/examples/imtestExample.pl similarity index 100% rename from Mail-IMAPClient-3.00/examples/imtestExample.pl rename to Mail-IMAPClient-3.05/examples/imtestExample.pl diff --git a/Mail-IMAPClient-3.00/examples/migrate_mail2.pl b/Mail-IMAPClient-3.05/examples/migrate_mail2.pl similarity index 100% rename from Mail-IMAPClient-3.00/examples/migrate_mail2.pl rename to Mail-IMAPClient-3.05/examples/migrate_mail2.pl diff --git a/Mail-IMAPClient-3.00/examples/migrate_mbox.pl b/Mail-IMAPClient-3.05/examples/migrate_mbox.pl similarity index 100% rename from Mail-IMAPClient-3.00/examples/migrate_mbox.pl rename to Mail-IMAPClient-3.05/examples/migrate_mbox.pl diff --git a/Mail-IMAPClient-3.00/examples/populate_mailbox.pl b/Mail-IMAPClient-3.05/examples/populate_mailbox.pl similarity index 100% rename from Mail-IMAPClient-3.00/examples/populate_mailbox.pl rename to Mail-IMAPClient-3.05/examples/populate_mailbox.pl diff --git a/Mail-IMAPClient-3.00/examples/sharedFolder.pl b/Mail-IMAPClient-3.05/examples/sharedFolder.pl similarity index 100% rename from Mail-IMAPClient-3.00/examples/sharedFolder.pl rename to Mail-IMAPClient-3.05/examples/sharedFolder.pl diff --git a/Mail-IMAPClient-3.00/lib/Mail/IMAPClient.pm b/Mail-IMAPClient-3.05/lib/Mail/IMAPClient.pm similarity index 87% rename from Mail-IMAPClient-3.00/lib/Mail/IMAPClient.pm rename to Mail-IMAPClient-3.05/lib/Mail/IMAPClient.pm index 1a0d509..1a1d66c 100644 --- a/Mail-IMAPClient-3.00/lib/Mail/IMAPClient.pm +++ b/Mail-IMAPClient-3.05/lib/Mail/IMAPClient.pm @@ -2,7 +2,7 @@ use warnings; use strict; package Mail::IMAPClient; -our $VERSION = '3.00'; +our $VERSION = '3.05'; use Mail::IMAPClient::MessageSet; @@ -18,7 +18,6 @@ use Carp qw(carp); use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK); use Errno qw/EAGAIN/; use List::Util qw/first min max sum/; -use Digest::HMAC_MD5 qw/hmac_md5_hex/; use MIME::Base64; use constant Unconnected => 0; @@ -57,7 +56,7 @@ BEGIN { # set-up accessors foreach my $datum ( qw(State Port Server Folder Peek User Password Timeout Buffer - Debug Count Uid Debug_fh Maxtemperrors Authmechanism Authcallback + Debug Count Uid Debug_fh Maxtemperrors Authuser Authmechanism Authcallback Ranges Readmethod Showcredentials Prewritemethod Ignoresizeerrors Supportedflags Proxy)) { no strict 'refs'; @@ -136,12 +135,13 @@ sub Rfc2060_date sprintf "%02d-%s-%04d", $date[3], $mnt[$date[4]], $date[5]+1900; } -sub Rfc2060_datetime -{ my ($class, $stamp) = @_; # 11-Jan-2000 04:04:04 +sub Rfc2060_datetime($;$) +{ my ($class, $stamp, $zone) = @_; # 11-Jan-2000 04:04:04 +0000 + $zone ||= '+0000'; my @date = gmtime $stamp; - sprintf "%02d-%s-%04d %02d:%02d:%02d", $date[3], $mnt[$date[4]] - , $date[5]+1900, $date[2], $date[1], $date[0]; + sprintf "%02d-%s-%04d %02d:%02d:%02d %s", $date[3], $mnt[$date[4]] + , $date[5]+1900, $date[2], $date[1], $date[0], $zone; } # Change CRLF into \n @@ -177,9 +177,12 @@ sub Clear $oldclear; } -# read-only access to the transaction number: +# read-only access to the transaction number sub Transaction { shift->Count }; +# remove doubles from list +sub _remove_doubles(@) { my %seen; grep { ! $seen{$_}++ } @_ } + # the constructor: sub new { my $class = shift; @@ -311,6 +314,11 @@ sub login if $auth ne 'LOGIN'; my $passwd = $self->Password; + if($passwd =~ m/\W/) # need to quote + { $passwd =~ s/(["\\])/\\$1/g; + $passwd = qq{"$passwd"}; + } + my $id = $self->User; $id = qq{"$id"} if $id !~ /^".*"$/; @@ -321,6 +329,11 @@ sub login $self; } +sub proxyauth +{ my ($self, $user) = @_; + $self->_imap_command("PROXYAUTH $user") ? $self->Results : undef; +} + sub separator { my ($self, $target) = @_; unless(defined $target) @@ -362,8 +375,8 @@ sub sort sub list { my ($self, $reference, $target) = @_; defined $reference or $reference = ""; - defined $target or $target = '*'; - length $target or $target = '""'; + defined $target or $target = '*'; + length $target or $target = '""'; $target eq '*' || $target eq '""' or $target = $self->Massage($target); @@ -414,10 +427,7 @@ sub subscribed /ix; } - # for my $f (@folders) { $f =~ s/^\\FOLDER LITERAL:://;} - # remove doubles - my @clean; my %memory; - foreach (@folders) { push @clean, $_ unless $memory{$_}++ } + my @clean = _remove_doubles @folders; wantarray ? @clean : \@clean; } @@ -597,8 +607,9 @@ sub message_to_file my $string = "$trans ${uid}FETCH $msgs $cmd"; $self->_record($trans, [0, "INPUT", $string] ); - + print "string [$string]\n"; my $feedback = $self->_send_line($string); + print "feedback [$feedback]\n"; unless($feedback) { $self->LastError("Error sending '$string' to IMAP: $!"); return undef; @@ -610,9 +621,11 @@ sub message_to_file until($code) { my $output = $self->_read_line($handle) or return undef; - foreach my $o (@$output) - { $self->_record($trans,$o); + { + print "oD[", $o->[DATA], "]\n"; + print "oT[", $o->[TYPE], "]\n"; + $self->_record($trans,$o); next unless $self->_is_output($o); $code = $o->[DATA] =~ /^$trans\s+(OK|BAD|NO)/mi ? $1 : undef; @@ -1099,8 +1112,7 @@ sub _imap_command } sub _imap_uid_command -{ my $self = shift; - my $cmd = shift; +{ my ($self, $cmd) = (shift, shift); my $args = @_ ? join(" ", '', @_) : ''; my $uid = $self->Uid ? 'UID ' : ''; $self->_imap_command("$uid$cmd$args"); @@ -1256,7 +1268,7 @@ sub _send_line # It is also re-implemented in: message_to_file # -# syntax: $output = $self->_readline($literal_callback, $output_callback) +# $output = $self->_read_line($literal_callback, $output_callback) # Both input argument are optional, but if supplied must either # be a filehandle, coderef, or undef. # @@ -1284,8 +1296,8 @@ sub _read_line my $fast_io = $self->Fast_io; until(@$oBuffer # there's stuff in output buffer: - && $oBuffer->[-1][DATA] =~ /\r\n$/ # the last thing there has cr-lf: - && $oBuffer->[-1][TYPE] eq "OUTPUT" # that thing is an output line: + && $oBuffer->[-1][TYPE] eq 'OUTPUT' # that thing is an output line: + && $oBuffer->[-1][DATA] =~ /\r?\n$/ # the last thing there has cr-lf: && !length $iBuffer # and the input buffer has been MT'ed: ) { my $transno = $self->Transaction; @@ -1328,19 +1340,17 @@ sub _read_line while($iBuffer =~ s/^(.*?\r?\n)//) # consume line { my $current_line = $1; - - # This part handles IMAP "Literals", - # which according to rfc2060 look something like this: - # [tag]|* BLAH BLAH {nnn}\r\n - # [nnn bytes of literally transmitted stuff] - # [part of line that follows literal data]\r\n - - if($current_line !~ s/\s*\{(\d+)\}\r\n$//) - { push @$oBuffer, [$index++, "OUTPUT" , $current_line]; + if($current_line !~ s/\s*\{(\d+)\}\r?\n$//) + { push @$oBuffer, [$index++, 'OUTPUT' , $current_line]; next; } + push @$oBuffer, [$index++, 'OUTPUT', $current_line]; + ## handle LITERAL + # BLAH BLAH {nnn}\r\n + # [nnn bytes of literally transmitted stuff] + # [part of line that follows literal data]\r\n my $expected_size = $1; @@ -1349,54 +1359,57 @@ sub _read_line "retrieve from the " . length($iBuffer) . " bytes in: $iBuffer"); - my $litstring = $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 = ''; - while($expected_size > length $litstring) - { if($timeout) - { # wait for data from the the IMAP socket. - my $rvec = 0; - vec($rvec, fileno($self->Socket), 1) = 1; - unless(CORE::select($rvec, undef, $rvec, $timeout)) - { $self->LastError("Tag $transno: Timeout waiting for " - . "literal data from server"); - return undef; - } - } - else # 1 ms before retry - { CORE::select(undef, undef, undef, 0.001); - } - - fcntl($socket, F_SETFL, $self->{_fcntl}) - if $fast_io && defined $self->{_fcntl}; - - my $ret = $self->_sysread($socket, \$litstring - , $expected_size - length $litstring, length $litstring); - - $self->_debug("Received ret=$ret and buffer = " . - "\n$litstring\nwhile processing LITERAL"); - - if($timeout && !defined $ret) - { $self->_record($transno, - [ $self->_next_index($transno), "ERROR", - "$transno * NO Error reading data from server: $!"]); - return undef; - } - - if($ret == 0 && $socket->eof) - { $self->_record($transno, - [ $self->_next_index($transno), "ERROR", - "$transno * BYE Server unexpectedly closed connection: $!"]); - $self->State(Unconnected); - return undef; + while($expected_size > length $litstring) + { if($timeout) + { # wait for data from the the IMAP socket. + my $rvec = 0; + vec($rvec, fileno($self->Socket), 1) = 1; + unless(CORE::select($rvec, undef, $rvec, $timeout)) + { $self->LastError("Tag $transno: Timeout waiting for " + . "literal data from server"); + return undef; + } + } + else # 1 ms before retry + { CORE::select(undef, undef, undef, 0.001); + } + + fcntl($socket, F_SETFL, $self->{_fcntl}) #???why + if $fast_io && defined $self->{_fcntl}; + + my $ret = $self->_sysread($socket, \$litstring + , $expected_size - length $litstring, length $litstring); + + $self->_debug("Received ret=$ret and buffer = " . + "\n$litstring\nwhile processing LITERAL"); + + if($timeout && !defined $ret) + { $self->_record($transno, + [ $self->_next_index($transno), "ERROR", + "$transno * NO Error reading data from server: $!"]); + return undef; + } + + if($ret==0 && $socket->eof) + { $self->_record($transno, + [ $self->_next_index($transno), "ERROR", + "$transno * BYE Server unexpectedly closed connection: $!"]); + $self->State(Unconnected); + return undef; + } } } - if(length $litstring > $expected_size) - { # copy the extra struff into the iBuffer: - $iBuffer = substr $litstring, $expected_size, length($litstring)-$expected_size,''; - } - else { $iBuffer = '' }; - if(!$literal_callback) { ; } elsif(UNIVERSAL::isa($literal_callback, 'GLOB')) { print $literal_callback $litstring; @@ -1411,31 +1424,8 @@ sub _read_line . "invalid callback; must be a filehandle or CODE"); } - $self->Fast_io($fast_io) if $fast_io; - - # Now let's make sure there are no IMAP server output lines - # (i.e. [tag|*] BAD|NO|OK Text) embedded in the literal string - my $trailer; - if($iBuffer =~ s/\r?\n((?:\*|\d+)\s(?:BAD|NO|OK)[^\n]*\r?\n\z)//i) - { $trailer = $1; - $self->_debug("Got output in literal: $trailer"); - } - - $self->_debug("literal includes ')' of FETCH") - if length $iBuffer - && $current_line =~ m/\bFETCH\b/i - && $iBuffer =~ s/\)$//; - - if(length $iBuffer) - { $self->_debug("literal: too much >>$iBuffer<<"); - $litstring .= $iBuffer; - $iBuffer = ''; - } - - push @$oBuffer, [$index++, "OUTPUT", $current_line]; - push @$oBuffer, [$index++, "LITERAL", $litstring]; - push @$oBuffer, [$index++, "OUTPUT", $trailer] - if $trailer; + $self->Fast_io($fast_io) if $fast_io; # ??? + push @$oBuffer, [$index++, 'LITERAL', $litstring]; } } @@ -1518,47 +1508,46 @@ sub logout $self; } -sub folders +sub folders($) { my ($self, $what) = @_; return wantarray ? @{$self->{Folders}} : $self->{Folders} - if ref $self->{Folders} && !$what; + if !$what && $self->{Folders}; + + my @list; + if($what) + { my $sep = $self->separator($what); + my $whatsub = $what =~ m/\Q${sep}\E$/ ? "$what*" : "$what$sep*"; + push @list, $self->list(undef, $whatsub); + push @list, $self->list(undef, $what) if $self->exists($what); + } + else + { push @list, $self->list(undef, undef); + } my @folders; - my @list = $self->list(undef,($what ? $what.$self->separator($what)."*" : undef ) ); - push @list, $self->list(undef, $what) - if $what && $self->exists($what); - - for(my $m = 0; $m < scalar(@list); $m++ ) + for(my $m = 0; $m < @list; $m++ ) { if($list[$m] && $list[$m] !~ /\r\n$/ ) { $self->_debug("folders: concatenating $list[$m] and $list[$m+1]"); $list[$m] .= $list[$m+1]; - $list[$m+1] = ""; - $list[$m] .= "\r\n" unless $list[$m] =~ /\r\n$/; + splice @list, $m+1, 1; } - $list[$m] =~ / ^\*\s+LIST # * LIST - \s+\([^\)]*\)\s+ # (Flags) - (?:"[^"]*"|NIL)\s+ # "delimiter" or NIL - (?:"([^"]*)"|(.*))\r\n$ # Name or "Folder name" + $list[$m] =~ / ^\* \s+ LIST \s+ \([^\)]*\) \s+ # * LIST (Flags) + (?:\" [^"]* \" | NIL ) \s+ # "delimiter" or NIL + (?:\"([^"]*)\" | (\S+)) \s*$ # "name" or name /ix or next; - my $folder = $1 || $2; - $folder = qq("$folder") - if $1 && !$self->exists($folder); - - push @folders, $folder + push @folders, $1 || $2; } - my (@clean, %memory); - foreach my $f (@folders) { push @clean, $f unless $memory{$f}++ } + my @clean = _remove_doubles @folders; $self->{Folders} = \@clean unless $what; wantarray ? @clean : \@clean; } - sub exists { my ($self, $folder) = @_; $self->status($folder) ? $self : undef; @@ -1580,11 +1569,12 @@ sub get_bodystructure } else { $self->_debug("get_bodystructure: reassembling original response"); - my $start = 0; - foreach my $o ($self->Results) + my $started = 0; + my $output = ''; + foreach my $o ($self->_transaction) { next unless $self->_is_output_or_literal($o); - next unless $start or - $o->[DATA] =~ /BODYSTRUCTURE \(/i and ++$start; # Hi, vi! ;-) + $started++ if $o->[DATA] =~ /BODYSTRUCTURE \(/i; ; # Hi, vi! ;-) + $started or next; if(length $output && $self->_is_literal($o) ) { my $data = $o->[DATA]; @@ -1612,24 +1602,30 @@ sub get_envelope return undef; } - my @out = $self->fetch($msg,"ENVELOPE"); + my @out = $self->fetch($msg, 'ENVELOPE'); my $bs = ""; - my $output = first { /ENVELOPE \(/i } @out; # Wee! ;-) + my $output = first { /ENVELOPE \(/i } @out; # vi ;-) + + unless($output) + { $self->LastError("Unable to use get_envelope: $@"); + return undef; + } + if($output =~ /\r\n$/ ) { eval { $bs = Mail::IMAPClient::BodyStructure::Envelope->new($output) }; } else { $self->_debug("get_envelope: reassembling original response"); - my $start = 0; - foreach my $o ($self->Results) + my $started = 0; + $output = ''; + foreach my $o ($self->_transaction) { next unless $self->_is_output_or_literal($o); $self->_debug("o->[DATA] is $o->[DATA]"); - next unless $start or - $o->[DATA] =~ /ENVELOPE \(/i and ++$start; - # Hi, vi! ;-) + $started++ if $o->[DATA] =~ /ENVELOPE \(/i; # Hi, vi! ;-) + $started or next; - if ( length($output) and $self->_is_literal($o) ) { + if(length($output) && $self->_is_literal($o) ) { my $data = $o->[DATA]; $data =~ s/"/\\"/g; $data =~ s/\(/\\\(/g; @@ -1658,7 +1654,7 @@ sub fetch : $what; $self->_imap_uid_command(FETCH => $take, @_) - or return (); + or return; wantarray ? $self->History : $self->Results; } @@ -1735,46 +1731,29 @@ sub store wantarray ? $self->History : $self->Results; } -sub subscribe -{ my ($self, @a) = @_; +sub _imap_folder_command($$) +{ my ($self, $command) = (shift, shift); delete $self->{Folders}; - $a[-1] = $self->Massage($a[-1]) if @a; - $self->_imap_uid_command(SUBSCRIBE => @a) - or return undef; - wantarray ? $self->History : $self->Results; -} + my $folder = $self->Massage(shift); -sub delete -{ my ($self, @a) = @_; - delete $self->{Folders}; - $a[-1] = $self->Massage($a[-1]) if @a; - $self->_imap_uid_command(DELETE => @a) - or return undef; - wantarray ? $self->History : $self->Results; -} + $self->_imap_command("$command $folder") + or return; -sub myrights -{ my ($self, @a) = @_; - delete $self->{Folders}; - $a[-1] = $self->Massage($a[-1]) if @a; - $self->_imap_uid_command(MYRIGHTS => @a) - or return undef; wantarray ? $self->History : $self->Results; } + +sub subscribe($) { $_[0]->_imap_folder_command(SUBSCRIBE => $_[1]) } +sub unsubscribe($) { $_[0]->_imap_folder_command(UNSUBSCRIBE => $_[1]) } +sub delete($) { $_[0]->_imap_folder_command(DELETE => $_[1]) } +sub create($) { $_[0]->_imap_folder_command(CREATE => $_[1]) } -sub create -{ my ($self, @a) = @_; - delete $self->{Folders}; - $a[0] = $self->Massage($a[0]) if @a; - $self->_imap_uid_command(CREATE => @a) - or return undef; - wantarray ? $self->History : $self->Results; -} +# rfc2086 +sub myrights($) { $_[0]->_imap_folder_command(MYRIGHTS => $_[1]) } sub close { my $self = shift; delete $self->{Folders}; - $self->_imap_uid_command('CLOSE') + $self->_imap_command('CLOSE') or return undef; wantarray ? $self->History : $self->Results; } @@ -1817,10 +1796,10 @@ sub rename sub status { my ($self, $folder) = (shift, shift); - my $which = @_ ? join(" ", @_) : 'MESSAGES'; - defined $folder or return; + my $which = @_ ? join(" ", @_) : 'MESSAGES'; + my $box = $self->Massage($folder); $self->_imap_command("STATUS $box ($which)") or return undef; @@ -1839,21 +1818,21 @@ sub flags # Send command $self->fetch($msg, "FLAGS") - or return undef; + or return; - my $u_f = $self->Uid; + my $u_f = $self->Uid; my $flagset = {}; # Parse results, setting entry in result hash for each line - foreach my $resultline ($self->Results) - { $self->_debug("flags: line = '$resultline'"); - if ( $resultline =~ - /\*\s+(\d+)\s+FETCH\s+ # * nnn FETCH - \( # open-paren - (?:\s?UID\s(\d+)\s?)? # optional: UID nnn - FLAGS\s?\((.*)\)\s? # FLAGS (\Flag1 \Flag2) - (?:\s?UID\s(\d+))? # optional: UID nnn - \) # close-paren + foreach my $line ($self->Results) + { $self->_debug("flags: line = '$line'"); + if ( $line =~ + /\* \s+ (\d+) \s+ FETCH \s+ # * nnn FETCH + \( + (?:\s* UID \s+ (\d+) \s* )? # optional: UID nnn + FLAGS \s* \( (.*?) \) \s* # FLAGS (\Flag1 \Flag2) + (?:\s* UID \s+ (\d+) \s* )? # optional: UID nnn + \) /x ) { my $mailid = $u_f ? ($2||$4) : $1; @@ -1883,16 +1862,6 @@ sub supported_flags(@) grep { $sup->{ /^\\(\S+)/ ? lc $1 : ()} } @_; } -# parse_headers modified to allow second param to also be a -# reference to a list of numbers. If this is a case, the headers -# are read from all the specified messages, and a reference to -# an hash of mail numbers to references to hashes, are returned. -# I found, with a mailbox of 300 messages, this was -# *significantly* faster against our mailserver (< 1 second -# vs. 20 seconds) -# -# 2000-03-22 Adrian Smith (adrian.smith@ucpag.com) - sub parse_headers { my ($self, $msgspec, @fields) = @_; my $fields = join ' ', @fields; @@ -1905,25 +1874,36 @@ sub parse_headers my @raw = $self->fetch($string) or return undef; - my %headers; # HASH from message ids to headers - my $h; # HASH of fields for current msgid - my $field; # previous field name + my %headers; # message ids to headers + my $h; # fields for current msgid + my $field; # previous field name, for unfolding my %fieldmap = map { ( lc($_) => $_ ) } @fields; + my $msgid; foreach my $header (map {split /\r?\n/} @raw) - { - if($header =~ s/^(?:\*|UID) \s+ (\d+) \s+ FETCH \s+ - \( .*? BODY\[HEADER (?:\.FIELDS)? .*? \]\s*//ix) + { # little problem: Windows2003 has UID as body, not in header + if($header =~ s/^\* \s+ (\d+) \s+ FETCH \s+ + \( (.*?) BODY\[HEADER (?:\.FIELDS)? .*? \]\s*//ix) { # start new message header - $h = $headers{$1} = {}; + ($msgid, my $msgattrs) = ($1, $2); + $h = {}; + if($self->Uid) # undef when win2003 + { $msgid = $msgattrs =~ m/\b UID \s+ (\d+)/x ? $1 : undef } + + $headers{$msgid} = $h if $msgid; } - $header =~ /\S/ or next; + $header =~ /\S/ or next; # skip empty lines. # ( for vi if($header =~ /^\)/) # end of this message { undef $h; # inbetween headers next; } + elsif(!$msgid && $header =~ /^\s*UID\s+(\d+)\s*\)/) + { $headers{$1} = $h; # finally found msgid, win2003 + undef $h; + next; + } unless(defined $h) { last if $header =~ / OK /i; @@ -2067,7 +2047,7 @@ sub search foreach ($self->History) { chomp; s/\r\n?/ /g; - s/^\*\s+SEARCH\s+(?=.*\d.*)// or next; + s/^\*\s+SEARCH\s+(?=.*?\d)// or next; push @hits, grep /^\d+$/, split; } @@ -2209,7 +2189,7 @@ sub namespace { return undef; } - my $got = $self->_imap_command("NAMESPACE") or return (); + my $got = $self->_imap_command("NAMESPACE") or return; my @namespaces = map { /^\* NAMESPACE (.*)/ ? $1 : () } $got->Results; @@ -2254,43 +2234,47 @@ sub is_parent for(my $m = 0; $m < @$list; $m++) { return undef - if $list->[$m] =~ /NoInferior/i; + if $list->[$m] =~ /\bNoInferior\b/i; if($list->[$m] =~ s/(\{\d+\})\r\n$// ) { $list->[$m] .= $list->[$m+1]; - $list->[$m+1] = ""; + splice @$list, $m+1, 1; } $line = $list->[$m] if $list->[$m] =~ - / ^\*\s+LIST # * LIST - \s+\([^\)]*\)\s+ # (Flags) - "[^"]*"\s+ # "delimiter" - (?:"([^"]*)"|(.*))\r\n$ # Name or "Folder name" - /x; + /^ \* \s+ LIST \s+ # * LIST + \([^\)]*\) \s+ # (Flags) + \"[^"]*\" \s+ # "delimiter" + (?:\"[^"]*\"|\S+) \s*$ # Name or "Folder name" + /x; } unless(length $line) - { $self->_debug("Warning: separator method found no correct o/p in:\n\t". + { $self->_debug("Warning: separator method found no correct o/p in:\n\t". join "\n\t", @$list); - } - my $f = defined $line && $line =~ /^\*\s+LIST\s+\(([^\)]*)\s*\)/ ? $1 : undef; - return 1 if $f =~ /HasChildren/i; - return 0 if $f =~ /HasNoChildren/i; - - unless($f =~ /\\/) # no flags at all unless there's a backslash - { my $sep = $self->separator($folder) || $self->separator(undef); - my $lead = $folder . $sep; - my $len = length $lead; - return scalar grep {$lead eq substr($_, 0, $len)} $self->folders; + return 0; } - 0; # ??? + $line =~ /^\*\s+LIST\s+ \( ([^\)]*) \s*\)/x + or return 0; + + my $flags = $1; + + return 1 if $flags =~ /HasChildren/i; + return 0 if $flags =~ /HasNoChildren/i; + return 0 if $flags =~ /\\/; # other flags found + + # flag not supported, try via folders() + my $sep = $self->separator($folder) || $self->separator(undef); + my $lead = $folder . $sep; + my $len = length $lead; + scalar grep {$lead eq substr($_, 0, $len)} $self->folders; } sub selectable { my ($self, $f) = @_; - not grep /NoSelect/i, $self->list("", $f); + not( grep /NoSelect/i, $self->list("", $f) ); } sub append @@ -2520,10 +2504,12 @@ sub authenticate until($code) { my $output = $self->_read_line or return undef; foreach my $o (@$output) - { $self->_record($count,$o); - $code = $o->[DATA] =~ /^\+\s+(.*)$/ ? $1 : undef; + { $self->_record($count, $o); + $code = $o->[DATA] =~ /^\+\s+(\S+)\s*$/ ? $1 + : $o->[DATA] =~ /^\+\s*$/ ? 'OK' + : undef; - if ($o->[DATA] =~ /^\*\s+BYE/) + if($o->[DATA] =~ /^\*\s+BYE/) { $self->State(Unconnected); return undef; } @@ -2536,9 +2522,36 @@ sub authenticate if($scheme eq 'CRAM-MD5') { $response ||= sub { my ($code, $client) = @_; - my $hmac = hmac_md5_hex(decode_base64($code), $client->Password); + use Digest::HMAC_MD5; + my $hmac = Digest::HMAC_MD5::hmac_md5_hex(decode_base64($code), $client->Password); encode_base64($client->User." ".$hmac); - } + }; + } + elsif($scheme eq 'DIGEST-MD5') + { $response ||= sub + { my ($code, $client) = @_; + require Authen::SASL; + require Digest::MD5; + + my $authname = $client->Authuser; + defined $authname or $authname = $client->User; + + my $sasl = Authen::SASL->new + ( mechanism => 'DIGEST-MD5' + , callback => + { user => $client->User + , pass => $client->Password + , authname => $authname + } + ); + + # client_new is an empty function for DIGEST-MD5 + my $conn = $sasl->client_new('imap', 'localhost', ''); + my $answer = $conn->client_step(decode_base64 $code); + + encode_base64($response, '') + if defined $answer; + }; } elsif($scheme eq 'PLAIN') # PLAIN SASL { $response ||= sub @@ -2562,12 +2575,12 @@ sub authenticate return undef; } - undef $code = $scheme eq 'PLAIN' ? 'OK' : undef; + undef $code; until($code) { my $output = $self->_read_line or return undef; foreach my $o (@$output) { $self->_record($count, $o); - $code = $o->[DATA] =~ /^\+\s+(.*)$/ ? $1 : undef; + $code = $o->[DATA] =~ /^\+\s+(.*?)\s*$/ ? $1 : undef; if($code) { unless($self->_send_line($response->($code, $self))) @@ -2719,23 +2732,15 @@ sub quota_usage ( map { /.*STORAGE\s+(\d+)\s+\d+.*\n$/ ? $1 : () } $self->Results)[0]; } -sub Quote { - my ($class, $arg) = @_; - return $class->Massage($arg, NonFolderArg); -} +sub Quote($) { $_[0]->Massage($_[1], NonFolderArg) } -sub Massage -{ my ($self, $arg, $notFolder) = @_; - $arg or return; - my $escaped_arg = $arg; - $escaped_arg =~ s/"/\\"/g; - $arg = substr($arg, 1, length($arg)-2) if $arg =~ /^".*"$/ - && ! ( $notFolder || $self->status(qq("$escaped_arg"), "MESSAGES")); +sub Massage($;$) +{ my ($self, $name, $notFolder) = @_; + $name =~ s/^\"(.*)\"$/$1/ unless $notFolder; - if($arg =~ /["\\]/) { $arg = "{".length($arg)."}\r\n$arg" } - elsif($arg =~ /[\s{}()]/) { $arg = qq("$arg") } - - $arg; + $name =~ /["\\]/ ? "{".length($name)."}\r\n$name" + : $name =~ /[\s{}()]/ ? qq["$name"] + : $name; } sub unseen_count diff --git a/Mail-IMAPClient-3.00/lib/Mail/IMAPClient.pod b/Mail-IMAPClient-3.05/lib/Mail/IMAPClient.pod similarity index 98% rename from Mail-IMAPClient-3.00/lib/Mail/IMAPClient.pod rename to Mail-IMAPClient-3.05/lib/Mail/IMAPClient.pod index f83f919..4b75c9d 100644 --- a/Mail-IMAPClient-3.00/lib/Mail/IMAPClient.pod +++ b/Mail-IMAPClient-3.05/lib/Mail/IMAPClient.pod @@ -41,7 +41,7 @@ object's status, see the section labeled L<"Status Methods">, below. RFC2060 defines two commands for authenticating to an IMAP server: LOGIN for plain text authentication and AUTHENTICATE for more secure authentication mechanisms. Currently Mail::IMAPClient supports -CRAM-MD5, LOGIN, PLAIN (SASL), and NTLM authentication. +DIGEST-MD5, CRAM-MD5, LOGIN, PLAIN (SASL), and NTLM authentication. There are also a number of methods and parameters that you can use to build your own authentication mechanism. Since this topic is a source of @@ -161,16 +161,24 @@ call L, who will call L. If B sees that you've set an I then it will call B, using your I and I parameters as arguments. +=item Authuser + +Normally you authenticate and log in with the username specified in +the User parameter. When you are using DIGEST-MD5 as I, +you can optionally specify a different username for the final log in. +This can be useful to mark messages as seen for the I +if you don't know the password of the user as the seen state +is often a per-user state. + =item Authcallback The I parameter, if set, should contain a pointer to a subroutine. The L method will use this as the callback argument to the B method if the I and I parameters are both set. If you set I -but not I then the default callback for your mechanism -will be used. CRAM-MD5, PLAIN (SASL), and NTLM authentication mechanisms -have a default callback; in every other case not supplying the callback -results in an error. +but not I then the default callback for your mechanism will +be used. All supported authentication mechanisms have a default callback; +in every other case not supplying the callback results in an error. Most advanced authentication mechanisms require a challenge-response exchange. After the L method sends " AUTHENTICATE @@ -518,6 +526,21 @@ seconds since the epoch date. It returns an RFC2060 compliant date string for that date (as required in date-related arguments to SEARCH, such as "since", "before", etc.). +=head2 Rfc2060_datetime + +Example: + + $date = $imap->Rfc2060_datetime($seconds); + # or: + $date = Mail::IMAPClient->Rfc2060_datetime($seconds); + +The B method accepts one or two arguments: a obligatory +timestamp and an optional zone. The zone shall be formatted as +C<< [+-]\d{4} >>, and defaults to C<< +0000 >>. The timestamp follows the +definition of the output of the platforms specific C