File Coverage

blib/lib/IMAP/Admin.pm
Criterion Covered Total %
statement 31 434 7.1
branch 2 158 1.2
condition 0 12 0.0
subroutine 8 28 28.5
pod 0 18 0.0
total 41 650 6.3


line stmt bran cond sub pod time code
1             # IMAP::Admin - perl module for helping ease the administration of imap servers
2              
3             package IMAP::Admin;
4              
5 1     1   484 use strict;
  1         1  
  1         22  
6 1     1   2 use Carp;
  1         2  
  1         63  
7 1     1   458 use IO::Select;
  1         1166  
  1         36  
8 1     1   404 use IO::Socket;
  1         15470  
  1         5  
9             #use IO::Socket::INET;
10 1     1   336 use Cwd;
  1         2  
  1         75  
11              
12 1     1   15 use vars qw($VERSION);
  1         2  
  1         3422  
13              
14             $VERSION = '1.6.8';
15              
16             sub new {
17 1     1 0 3648 my $class = shift;
18 1         8 my $self = {};
19 1         8 my @defaults = (
20             'Port' => 143,
21             'Separator' => '.',
22             'CRAM' => 0,
23             );
24              
25 1         5 bless $self, $class;
26 1 50       11 if ((scalar(@_) % 2) != 0) {
27 0         0 croak "$class called with incorrect number of arguments";
28             }
29 1         7 unshift @_, @defaults;
30 1         4 %{$self} = @_; # set up parameters;
  1         29  
31 1         2 $self->{'CLASS'} = $class;
32 1         7 $self->_initialize;
33 0         0 return $self;
34             }
35              
36             sub _initialize {
37 1     1   3 my $self = shift;
38              
39 1 50       5 if (!defined($self->{'Server'})) {
40 1         217 croak "$self->{'CLASS'} not initialized properly : Server parameter missing";
41             }
42 0 0         if (!defined($self->{'Login'})) {
43 0           croak "$self->{'CLASS'} not initialized properly : Login parameter missing";
44             }
45 0 0         if (!defined($self->{'Password'})) {
46 0           croak "$self->{'CLASS'} not initialized properly : Password parameter missing";
47             }
48 0 0         if ($self->{'CRAM'} != 0) {
49 0           my $cram_try = "use Digest::HMAC; use Digest::MD5; use MIME::Base64;";
50 0           eval $cram_try;
51             }
52 0 0         if (defined($self->{'SSL'})) { # attempt SSL connection instead
53             # construct array of ssl options
54 0           my $cwd = cwd;
55 0           my %ssl_defaults = (
56             'SSL_use_cert' => 0,
57             'SSL_verify_mode' => 0x00,
58             'SSL_key_file' => $cwd."/certs/client-key.pem",
59             'SSL_cert_file' => $cwd."/certs/client-cert.pem",
60             'SSL_ca_path' => $cwd."/certs",
61             'SSL_ca_file' => $cwd."/certs/ca-cert.pem",
62             );
63 0           my @ssl_options;
64             my $ssl_key;
65 0           my $key;
66 0           foreach $ssl_key (keys(%ssl_defaults)) {
67 0 0         if (!defined($self->{$ssl_key})) {
68 0           $self->{$ssl_key} = $ssl_defaults{$ssl_key};
69             }
70             }
71 0           foreach $ssl_key (keys(%{$self})) {
  0            
72 0 0         if ($ssl_key =~ /^SSL_/) {
73 0           push @ssl_options, $ssl_key, $self->{$ssl_key};
74             }
75             }
76 0           my $SSL_try = "use IO::Socket::SSL";
77              
78 0           eval $SSL_try;
79             # $IO::Socket::SSL::DEBUG = 1;
80 0 0         if (!eval {
81             $self->{'Socket'} =
82             IO::Socket::SSL->new(PeerAddr => $self->{'Server'},
83 0           PeerPort => $self->{'Port'},
84             Proto => 'tcp',
85             Reuse => 1,
86             Timeout => 5,
87             @ssl_options); }) {
88             $self->_error("initialize", "couldn't establish SSL connection to",
89 0           $self->{'Server'}, "[$!]");
90 0           delete $self->{'Socket'};
91 0           return;
92             }
93             } else {
94 0 0         if ($self->{'Server'} =~ /^\//) {
95 0 0         if (!eval {
96             $self->{'Socket'} =
97 0           IO::Socket::UNIX->new(Peer => $self->{'Server'}); })
98             {
99 0           delete $self->{'Socket'};
100             $self->_error("initialize", "couldn't establish connection to",
101 0           $self->{'Server'});
102 0           return;
103             }
104             } else {
105 0 0         if (!eval {
106             $self->{'Socket'} =
107             IO::Socket::INET->new(PeerAddr => $self->{'Server'},
108 0           PeerPort => $self->{'Port'},
109             Proto => 'tcp',
110             Reuse => 1,
111             Timeout => 5); })
112             {
113 0           delete $self->{'Socket'};
114             $self->_error("initialize", "couldn't establish connection to",
115 0           $self->{'Server'});
116 0           return;
117             }
118             }
119             }
120 0           my $fh = $self->{'Socket'};
121 0           my $try = $self->_read; # get Banner
122 0 0         if ($try !~ /\* OK/) {
123 0           $self->close;
124 0           $self->_error("initialize", "bad response from", $self->{'Server'},
125             "[", $try, "]");
126 0           return;
127             }
128             # this section was changed to accomodate motd's
129 0           print $fh "try CAPABILITY\n";
130 0           $try = $self->_read;
131 0           while ($try !~ /^\* CAPABILITY/) { # we have a potential lockup, should alarm this
132 0           $try = $self->_read;
133             }
134 0           $self->{'Capability'} = $try;
135 0           $try = $self->_read;
136 0 0         if ($try !~ /^try OK/) {
137 0           $self->close;
138 0           $self->_error("initialize", "Couldn't do a capabilites check [",
139             $try, "]");
140 0           return;
141             }
142 0 0         if ($self->{'CRAM'} > 0) {
143 0 0         if ($self->{'Capability'} =~ /CRAM-MD5/) {
144 0           _do_cram_login($self);
145             } else {
146 0 0         if ($self->{'CRAM'} > 1) {
147 0           print $fh qq{try LOGIN "$self->{'Login'}" "$self->{'Password'}"\n};
148             } else {
149 0           $self->close;
150 0           $self->_error("initialize","CRAM not reported in Capability check and fallback to PLAIN not selected", $self->{'Server'}, "[", $self->{'Capability'}, "]");
151 0           return;
152             }
153             }
154             } else {
155 0           print $fh qq{try LOGIN "$self->{'Login'}" "$self->{'Password'}"\n};
156             }
157 0           $try = $self->_read;
158 0 0         if ($try !~ /^try OK/) { # should tr this response
159 0           $self->close;
160 0           $self->_error("initialize", $try);
161 0           return;
162             } else {
163 0           $self->{'Error'} = "No Errors";
164 0           return;
165             }
166             # fall thru, can it be hit ?
167 0           $self->{'Error'} = "No Errors";
168 0           return;
169             }
170              
171             # this routine uses evals to prevent errors regarding missing modules
172             sub _do_cram_login {
173 0     0     my $self = shift;
174 0           my $fh = $self->{'Socket'};
175 0           my $ans;
176              
177 0           print $fh "try AUTHENTICATE CRAM-MD5\n";
178 0           my $try = $self->_read; # gets back the postal string
179 0           ($ans) = (split(' ', $try, 2))[1];
180 0           my $cram_eval = "
181             my \$hmac = Digest::HMAC->new(\$self->{'Password'}, 'Digest::MD5');
182             \$hmac->add(decode_base64(\$ans));
183             \$ans = encode_base64(\$self->{'Login'}.' '.\$hmac->hexdigest, '');
184             ";
185 0           eval $cram_eval;
186 0           print $fh "$ans\n";
187 0           return;
188             }
189              
190             sub _error {
191 0     0     my $self = shift;
192 0           my $func = shift;
193 0           my @error = @_;
194              
195 0           $self->{'Error'} = join(" ",$self->{'CLASS'}, "[", $func, "]:", @error);
196 0           return;
197             }
198              
199             sub error {
200 0     0 0   my $self = shift;
201 0           return $self->{'Error'};
202             }
203              
204             sub _read {
205 0     0     my $self = shift;
206 0           my $buffer = "";
207 0           my $char = "";
208 0           my $bytes = 1;
209 0           while ($bytes == 1) {
210 0           $bytes = sysread $self->{'Socket'}, $char, 1;
211 0 0         if ($bytes == 0) {
212 0 0         if (length ($buffer) != 0) {
213 0           return $buffer;
214             } else {
215 0           return;
216             }
217             } else {
218 0 0 0       if (($char eq "\n") or ($char eq "\r")) {
219 0 0         if (length($buffer) == 0) {
220             # cr or nl left over, just eat it
221             } else {
222 0           return $buffer;
223             }
224             } else {
225             # print "got char [$char]\n";
226 0           $buffer .= $char;
227             }
228             }
229             }
230             }
231              
232             sub close {
233 0     0 0   my $self = shift;
234              
235 0 0         if (!defined($self->{'Socket'})) {
236 0           return 0;
237             }
238 0           my $fh = $self->{'Socket'};
239 0           print $fh "try logout\n";
240 0           my $try = $self->_read;
241 0           close($self->{'Socket'});
242 0           delete $self->{'Socket'};
243 0           return 0;
244             }
245              
246             sub create {
247 0     0 0   my $self = shift;
248              
249 0 0         if (!defined($self->{'Socket'})) {
250 0           return 1;
251             }
252 0 0 0       if ((scalar(@_) != 1) && (scalar(@_) != 2)) {
253 0           $self->_error("create", "incorrect number of arguments");
254 0           return 1;
255             }
256 0           my $mailbox = shift;
257 0           my $fh = $self->{'Socket'};
258 0 0         if (scalar(@_) == 1) { # a partition exists
259 0           print $fh qq{try CREATE "$mailbox" $_[0]\n};
260             } else {
261 0           print $fh qq{try CREATE "$mailbox"\n};
262             }
263 0           my $try = $self->_read;
264 0 0         if ($try =~ /^try OK/) {
265 0           $self->{'Error'} = 'No Errors';
266 0           return 0;
267             } else {
268 0           $self->_error("create", "couldn't create", $mailbox, ":", $try);
269 0           return 1;
270             }
271             }
272              
273             sub rename {
274 0     0 0   my $self = shift;
275              
276 0 0         if (!defined($self->{'Socket'})) {
277 0           return 1;
278             }
279 0 0 0       if ((scalar(@_) != 2) && (scalar(@_) != 3)) {
280 0           $self->_error("rename", "incorrect number of arguments");
281 0           return 1;
282             }
283 0           my $old_name = shift;
284 0           my $new_name = shift;
285 0           my $partition = shift;
286              
287 0           my $fh = $self->{'Socket'};
288 0 0         if (defined $partition) {
289 0           print $fh qq{try RENAME "$old_name" "$new_name" $partition\n};
290             } else {
291 0           print $fh qq{try RENAME "$old_name" "$new_name"\n};
292             }
293 0           my $try = $self->_read;
294 0 0 0       if (($try =~ /^try OK/) || ($try =~ /^\* OK/)) {
295 0           $self->{'Error'} = 'No Errors';
296 0           return 0;
297             } else {
298 0           $self->_error("rename", "couldn't rename", $old_name, "to", $new_name,
299             ":", $try);
300 0           return 1;
301             }
302             }
303              
304             sub delete {
305 0     0 0   my $self = shift;
306              
307 0 0         if (!defined($self->{'Socket'})) {
308 0           return 1;
309             }
310 0 0         if (scalar(@_) != 1) {
311 0           $self->_error("delete", "incorrect number of arguments");
312 0           return 1;
313             }
314 0           my $mailbox = shift;
315 0           my $fh = $self->{'Socket'};
316 0           print $fh qq{try DELETE "$mailbox"\n};
317 0           my $try = $self->_read;
318 0 0         if ($try =~ /^try OK/) {
319 0           $self->{'Error'} = 'No Errors';
320 0           return 0;
321             } else {
322 0           $self->_error("delete", "couldn't delete", $mailbox, ":", $try);
323 0           return 1;
324             }
325             }
326              
327             sub h_delete {
328 0     0 0   my $self = shift;
329              
330 0 0         if (!defined($self->{'Socket'})) {
331 0           return 1;
332             }
333 0 0         if (scalar(@_) != 1) {
334 0           $self->_error("h_delete", "incorrect number of arguments");
335 0           return 1;
336             }
337 0           my $mailbox = shift;
338 0           my $fh = $self->{'Socket'};
339             # first get a list of all sub boxes then nuke them, accumulate errors
340             # then do something intelligent with them (hmmmmm)
341 0           my $box = join($self->{'Separator'}, $mailbox, "*");
342 0           my @sub_boxes = $self->list($box);
343 0           push @sub_boxes, $mailbox;
344             # uncomment following line if you are sanity checking h_delete
345             # print "h_delete: got this list of sub boxes [@sub_boxes]\n";
346 0           foreach $box (@sub_boxes) {
347 0           print $fh qq{try DELETE "$box"\n};
348 0           my $try = $self->_read;
349 0 0         if ($try =~ /^try OK/) {
350 0           $self->{'Error'} = 'No Errors';
351             } else {
352 0           $self->_error("h_delete", "couldn't delete",
353             $mailbox, ":", $try);
354 0           return 1; # or just return on the first encountered error ?
355             }
356             }
357 0           return 0;
358             }
359              
360             sub get_quotaroot { # returns an array or undef
361 0     0 0   my $self = shift;
362 0           my (@quota, @info);
363              
364 0 0         if (!defined($self->{'Socket'})) {
365 0           return 1;
366             }
367 0 0         if (!($self->{'Capability'} =~ /QUOTA/)) {
368 0           $self->_error("get_quotaroot", "QUOTA not listed in server's capabilities");
369 0           return 1;
370             }
371 0 0         if (scalar(@_) != 1) {
372 0           $self->_error("get_quotaroot", "incorrect number of arguments");
373 0           return 1;
374             }
375 0           my $mailbox = shift;
376 0           my $fh = $self->{'Socket'};
377 0           print $fh qq{try GETQUOTAROOT "$mailbox"\n};
378 0           my $try = $self->_read;
379 0           while ($try =~ /^\* QUOTA/) {
380 0 0         if ($try !~ /QUOTAROOT/) { # some imap servers give this extra line
381 0           @info = ($try =~ /QUOTA\s(.*?)\s\(STORAGE\s(\d+)\s(\d+)/);
382 0           push @quota, @info;
383             }
384 0           $try = $self->_read;
385             }
386 0 0         if ($try =~ /^try OK/) {
387 0           return @quota;
388             } else {
389 0           $self->_error("get_quotaroot", "couldn't get quota for", $mailbox, ":", $try);
390 0           return;
391             }
392             }
393              
394             sub get_quota { # returns an array or undef
395 0     0 0   my $self = shift;
396 0           my (@quota, @info);
397              
398 0 0         if (!defined($self->{'Socket'})) {
399 0           return;
400             }
401 0 0         if (!($self->{'Capability'} =~ /QUOTA/)) {
402 0           $self->_error("get_quota",
403             "QUOTA not listed in server's capabilities");
404 0           return;
405             }
406 0 0         if (scalar(@_) != 1) {
407 0           $self->_error("get_quota", "incorrect number of arguments");
408 0           return;
409             }
410 0           my $mailbox = shift;
411 0           my $fh = $self->{'Socket'};
412 0           print $fh qq{try GETQUOTA "$mailbox"\n};
413 0           my $try = $self->_read;
414 0           while ($try =~ /^\* QUOTA/) {
415 0           @info = ($try =~ /QUOTA\s(.*?)\s\(STORAGE\s(\d+)\s(\d+)/);
416 0           push @quota, @info;
417 0           $try = $self->_read;
418             }
419 0 0         if ($try =~ /^try OK/) {
420 0           return @quota;
421             } else {
422 0           $self->_error("get_quota", "couldn't get quota for", $mailbox, ":", $try);
423 0           return;
424             }
425             }
426              
427             sub set_quota {
428 0     0 0   my $self = shift;
429              
430 0 0         if (!defined($self->{'Socket'})) {
431 0           return 1;
432             }
433 0 0         if (!($self->{'Capability'} =~ /QUOTA/)) {
434 0           $self->_error("set_quota", "QUOTA not listed in server's capabilities");
435 0           return 1;
436             }
437 0 0         if (scalar(@_) != 2) {
438 0           $self->_error("set_quota", "incorrect number of arguments");
439 0           return 1;
440             }
441 0           my $mailbox = shift;
442 0           my $quota = shift;
443 0           my $fh = $self->{'Socket'};
444 0 0         if ($quota eq "none") {
445 0           print $fh qq{try SETQUOTA "$mailbox" ()\n};
446             } else {
447 0           print $fh qq{try SETQUOTA "$mailbox" (STORAGE $quota)\n};
448             }
449 0           my $try = $self->_read;
450 0 0         if ($try =~ /^try OK/) {
451 0           $self->{'Error'} = "No Errors";
452 0           return 0;
453             } else {
454 0           $self->_error("set_quota", "couldn't set quota for", $mailbox, ":", $try);
455 0           return 1;
456             }
457             }
458              
459             sub subscribe {
460 0     0 0   my $self = shift;
461              
462 0 0         if (!defined($self->{'Socket'})) {
463 0           return 1;
464             }
465 0 0         if (scalar(@_) != 1) {
466 0           $self->_error("subscribe", "incorrect number of arguments");
467 0           return 1;
468             }
469 0           my $mailbox = shift;
470 0           my $fh = $self->{'Socket'};
471 0           print $fh qq{try SUBSCRIBE "$mailbox"\n};
472 0           my $try = $self->_read;
473 0 0         if ($try !~ /^try OK/) {
474 0           $self->_error("subscribe", "couldn't suscribe ", $mailbox, ":",
475             $try);
476 0           return 1;
477             }
478 0           $self->{'Error'} = 'No Errors';
479 0           return 0;
480             }
481              
482             sub unsubscribe {
483 0     0 0   my $self = shift;
484              
485 0 0         if (!defined($self->{'Socket'})) {
486 0           return 1;
487             }
488 0 0         if (scalar(@_) != 1) {
489 0           $self->_error("unsubscribe", "incorrect number of arguments");
490 0           return 1;
491             }
492 0           my $mailbox = shift;
493 0           my $fh = $self->{'Socket'};
494 0           print $fh qq{try UNSUBSCRIBE "$mailbox"\n};
495 0           my $try = $self->_read;
496 0 0         if ($try !~ /^try OK/) {
497 0           $self->_error("unsubscribe", "couldn't unsuscribe ", $mailbox, ":",
498             $try);
499 0           return 1;
500             }
501 0           $self->{'Error'} = 'No Errors';
502 0           return 0;
503             }
504              
505             sub select { # returns an array or undef
506 0     0 0   my $self = shift;
507 0           my @info;
508              
509 0 0         if (!defined($self->{'Socket'})) {
510 0           return 1;
511             }
512 0 0         if (scalar(@_) != 1) {
513 0           $self->_error("select", "incorrect number of arguments");
514 0           return;
515             }
516              
517 0           my $mailbox = shift;
518 0           my $fh = $self->{'Socket'};
519 0           print $fh qq{try SELECT "$mailbox"\n};
520 0           my $try = $self->_read;
521 0           while ($try =~ /^\* (.*)/) { # danger danger (could lock up needs timeout)
522 0           push @info, $1;
523 0           $try = $self->_read;
524             }
525 0 0         if ($try =~ /^try OK/) {
526 0           return @info;
527             } else {
528 0           $self->_error("select", "couldn't select", $mailbox, ":", $try);
529 0           return;
530             }
531             }
532              
533             sub expunge { # returns an array or undef
534 0     0 0   my $self = shift;
535 0           my @info;
536              
537 0 0         if (!defined($self->{'Socket'})) {
538 0           return 1;
539             }
540 0 0         if (scalar(@_) != 0) {
541 0           $self->_error("expunge", "incorrect number of arguments");
542 0           return;
543             }
544              
545 0           my $mailbox = shift;
546 0           my $fh = $self->{'Socket'};
547 0           print $fh qq{try EXPUNGE\n};
548 0           my $try = $self->_read;
549 0           while ($try =~ /^\* (.*)/) { # danger danger (could lock up needs timeout)
550 0           push @info, $1;
551 0           $try = $self->_read;
552             }
553 0 0         if ($try =~ /^try OK/) {
554 0           return @info;
555             } else {
556 0           $self->_error("expunge", "couldn't expunge", $mailbox, ":", $try);
557 0           return;
558             }
559             }
560              
561             sub get_acl { # returns an array or undef
562 0     0 0   my $self = shift;
563              
564 0 0         if (!defined($self->{'Socket'})) {
565 0           return;
566             }
567 0 0         if (!($self->{'Capability'} =~ /ACL/)) {
568 0           $self->_error("get_acl", "ACL not listed in server's capabilities");
569 0           return;
570             }
571 0 0         if (scalar(@_) != 1) {
572 0           $self->_error("get_acl", "incorrect number of arguments");
573 0           return;
574             }
575 0           my $mailbox = shift;
576 0           my $fh = $self->{'Socket'};
577 0           print $fh qq{try GETACL "$mailbox"\n};
578 0           delete $self->{'acl'};
579 0           my $try = $self->_read;
580 0           while ($try =~ /^\*\s+ACL\s+/) {
581 0           my $acls = ($try =~ /^\* ACL\s+(?:\".*?\"|\S*)\s+(.*)/)[0]; # separate out the acls
582 0           my @acls = ($acls =~ /(\".*?\"|\S+)\s*/g); # split up over ws, unless quoted
583 0           push @{$self->{'acl'}}, @acls;
  0            
584 0           $try = $self->_read;
585             }
586 0 0         if ($try =~ /^try OK/) {
587 0           return @{$self->{'acl'}};
  0            
588             } else {
589 0           $self->_error("get_acl", "couldn't get acl for", $mailbox, ":", $try);
590 0           return;
591             }
592             }
593              
594             sub set_acl {
595 0     0 0   my $self = shift;
596 0           my ($id, $acl);
597              
598 0 0         if (!defined($self->{'Socket'})) {
599 0           return 1;
600             }
601 0 0         if (!($self->{'Capability'} =~ /ACL/)) {
602 0           $self->_error("set_acl", "ACL not listed in server's capabilities");
603 0           return 1;
604             }
605 0 0         if (scalar(@_) < 2) {
606 0           $self->_error("set_acl", "too few arguments");
607 0           return 1;
608             }
609 0 0         if ((scalar(@_) % 2) == 0) {
610 0           $self->_error("set_acl", "incorrect number of arguments");
611 0           return 1;
612             }
613 0           my $mailbox = shift;
614 0           my $fh = $self->{'Socket'};
615 0           while(@_) {
616 0           $id = shift;
617 0           $acl = shift;
618 0           print $fh qq{try SETACL "$mailbox" "$id" "$acl"\n};
619 0           my $try = $self->_read;
620 0 0         if ($try !~ /^try OK/) {
621 0           $self->_error("set_acl", "couldn't set acl for", $mailbox, $id,
622             $acl, ":", $try);
623 0           return 1;
624             }
625             }
626 0           $self->{'Error'} = 'No Errors';
627 0           return 0;
628             }
629              
630             sub delete_acl {
631 0     0 0   my $self = shift;
632 0           my ($id, $acl);
633              
634 0 0         if (!defined($self->{'Socket'})) {
635 0           return 1;
636             }
637 0 0         if (!($self->{'Capability'} =~ /ACL/)) {
638 0           $self->_error("delete_acl", "ACL not listed in server's capabilities");
639 0           return 1;
640             }
641 0 0         if (scalar(@_) < 1) {
642 0           $self->_error("delete_acl", "incorrect number of arguments");
643 0           return 1;
644             }
645 0           my $mailbox = shift;
646 0           my $fh = $self->{'Socket'};
647 0           while(@_) {
648 0           $id = shift;
649 0           print $fh qq{try DELETEACL "$mailbox" "$id"\n};
650 0           my $try = $self->_read;
651 0 0         if ($try !~ /^try OK/) {
652 0           $self->_error("delete_acl", "couldn't delete acl for", $mailbox,
653             $id, $acl, ":", $try);
654 0           return 1;
655             }
656             }
657 0           return 0;
658             }
659              
660             sub list { # wild cards are allowed, returns array or undef
661 0     0 0   my $self = shift;
662 0           my (@info, @mail);
663              
664 0 0         if (!defined($self->{'Socket'})) {
665 0           return;
666             }
667 0 0         if (scalar(@_) != 1) {
668 0           $self->_error("list", "incorrect number of arguments");
669 0           return;
670             }
671 0           my $list = shift;
672 0           my $fh = $self->{'Socket'};
673 0           print $fh qq{try LIST "" "$list"\n};
674 0           my $try = $self->_read;
675 0           while ($try =~ /^\* LIST.*?\) \".\" \"*(.*?)\"*$/) { # danger danger (could lock up needs timeout) " <- this quote makes emacs happy
676 0           push @mail, $1;
677 0           $try = $self->_read;
678             }
679 0 0         if ($try =~ /^try OK/) {
680 0           return @mail;
681             } else {
682 0           $self->_error("list", "couldn't get list for", $list, ":", $try);
683 0           return;
684             }
685             }
686              
687              
688             # Autoload methods go after =cut, and are processed by the autosplit program.
689              
690             1;
691             __END__