File Coverage

blib/lib/Mail/Box/IMAP4.pm
Criterion Covered Total %
statement 33 184 17.9
branch 0 80 0.0
condition 0 40 0.0
subroutine 11 27 40.7
pod 14 16 87.5
total 58 347 16.7


line stmt bran cond sub pod time code
1             # Copyrights 2001-2023 by [Mark Overmeer].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.03.
5             # This code is part of distribution Mail-Box-IMAP4. Meta-POD processed with
6             # OODoc into POD and HTML manual-pages. See README.md
7             # Copyright Mark Overmeer. Licensed under the same terms as Perl itself.
8              
9             package Mail::Box::IMAP4;
10 2     2   253048 use vars '$VERSION';
  2         13  
  2         107  
11             $VERSION = '3.008';
12              
13 2     2   13 use base 'Mail::Box::Net';
  2         4  
  2         1037  
14              
15 2     2   270674 use strict;
  2         5  
  2         37  
16 2     2   11 use warnings;
  2         5  
  2         45  
17              
18 2     2   951 use Mail::Box::IMAP4::Message;
  2         6  
  2         82  
19 2     2   890 use Mail::Box::IMAP4::Head;
  2         5  
  2         59  
20 2     2   924 use Mail::Transport::IMAP4;
  2         7  
  2         89  
21              
22 2     2   14 use Mail::Box::Parser::Perl;
  2         5  
  2         62  
23 2     2   12 use Mail::Message::Head::Complete;
  2         13  
  2         72  
24 2     2   10 use Mail::Message::Head::Delayed;
  2         4  
  2         47  
25              
26 2     2   17 use Scalar::Util 'weaken';
  2         4  
  2         4235  
27              
28              
29             sub init($)
30 0     0 0   { my ($self, $args) = @_;
31              
32 0           my $folder = $args->{folder};
33              
34             # MailBox names top folder directory '=', but IMAP needs '/'
35 0 0 0       $folder = '/'
36             if ! defined $folder || $folder eq '=';
37              
38             # There's a disconnect between the URL parser and this code.
39             # The URL parser always produces a full path (beginning with /)
40             # while this code expects to NOT get a full path. So, we'll
41             # trim the / from the front of the path.
42             # Also, this code can't handle a trailing slash and there's
43             # no reason to ever offer one. Strip that too.
44 0 0         if($folder ne '/')
45 0           { $folder =~ s,^/+,,g;
46 0           $folder =~ s,/+$,,g;
47             }
48              
49 0           $args->{folder} = $folder;
50              
51 0   0       my $access = $args->{access} ||= 'r';
52 0           my $writeable = $access =~ m/w|a/;
53 0   0       my $ch = $self->{MBI_c_head} = $args->{cache_head} || ($writeable ? 'NO' : 'DELAY');
54              
55 0 0 0       $args->{head_type} ||= 'Mail::Box::IMAP4::Head'
      0        
56             if $ch eq 'NO' || $ch eq 'PARTIAL';
57              
58 0   0       $args->{body_type} ||= 'Mail::Message::Body::Lines';
59 0   0       $args->{message_type} ||= 'Mail::Box::IMAP4::Message';
60              
61 0 0         if(my $client = $args->{imap_client}) {
62 0           $args->{server_name} = $client->PeerAddr;
63 0           $args->{server_port} = $client->PeerPort;
64 0           $args->{username} = $client->User;
65             }
66              
67 0           $self->SUPER::init($args);
68              
69 0           $self->{MBI_domain} = $args->{domain};
70 0   0       $self->{MBI_c_labels} = $args->{cache_labels} || ($writeable ? 'NO' : 'DELAY');
71 0   0       $self->{MBI_c_body} = $args->{cache_body} || ($writeable ? 'NO' : 'DELAY');
72              
73              
74 0   0       my $transport = $args->{transporter} || 'Mail::Transport::IMAP4';
75 0 0         $transport = $self->createTransporter($transport, %$args)
76             unless ref $transport;
77              
78 0           $self->transporter($transport);
79              
80 0 0         defined $transport
81             or return;
82              
83             $args->{create}
84 0 0         ? $self->create($transport, $args)
85             : $self;
86             }
87              
88             sub create($@)
89 0     0 1   { my($self, $name, $args) = @_;
90              
91 0 0         if($args->{access} !~ /w|a/)
92 0           { $self->log(ERROR =>
93             "You must have write access to create folder $name.");
94 0           return undef;
95             }
96              
97 0           $self->transporter->createFolder($name);
98             }
99              
100             sub foundIn(@)
101 0     0 1   { my $self = shift;
102 0 0         unshift @_, 'folder' if @_ % 2;
103 0           my %options = @_;
104              
105             (exists $options{type} && $options{type} =~ m/^imap/i)
106 0 0 0       || (exists $options{folder} && $options{folder} =~ m/^imap/);
      0        
107             }
108              
109             sub type() {'imap4'}
110              
111              
112              
113             sub close(@)
114 0     0 1   { my $self = shift;
115 0 0         $self->SUPER::close(@_) or return ();
116 0           $self->transporter(undef);
117 0           $self;
118             }
119              
120             sub listSubFolders(@)
121 0     0 1   { my ($thing, %args) = @_;
122 0           my $self = $thing;
123              
124 0 0 0       $self = $thing->new(%args) or return () # list toplevel
125             unless ref $thing;
126              
127 0           my $imap = $self->transporter;
128 0 0         defined $imap ? $imap->folders($self) : ();
129             }
130              
131 0     0 0   sub nameOfSubfolder($;$) { $_[1] }
132              
133             #-------------------------------------------
134              
135             sub readMessages(@)
136 0     0 1   { my ($self, %args) = @_;
137              
138 0           my $name = $self->name;
139 0 0         return $self if $name eq '/';
140              
141 0           my $imap = $self->transporter;
142 0 0         defined $imap or return ();
143              
144 0           my @log = $self->logSettings;
145 0           my $seqnr = 0;
146              
147 0           my $cl = $self->{MBI_c_labels} ne 'NO';
148 0           my $wl = $self->{MBI_c_labels} ne 'DELAY';
149              
150 0           my $ch = $self->{MBI_c_head};
151 0 0         my $ht = $ch eq 'DELAY' ? $args{head_delayed_type} : $args{head_type};
152 0 0         my @ho = $ch eq 'PARTIAL' ? (cache_fields => 1) : ();
153              
154             $self->{MBI_selectable}
155 0 0         or return $self;
156              
157 0           foreach my $id ($imap->ids)
158 0           { my $head = $ht->new(@log, @ho);
159             my $message = $args{message_type}->new
160 0           ( head => $head
161             , unique => $id
162             , folder => $self
163             , seqnr => $seqnr++
164              
165             , cache_labels => $cl
166             , write_labels => $wl
167             , cache_head => ($ch eq 'DELAY')
168             , cache_body => ($ch ne 'NO')
169             );
170              
171             my $body = $args{body_delayed_type}
172 0           ->new(@log, message => $message);
173              
174 0           $message->storeBody($body);
175              
176 0           $self->storeMessage($message);
177             }
178              
179 0           $self;
180             }
181            
182              
183              
184             sub getHead($)
185 0     0 1   { my ($self, $message) = @_;
186 0 0         my $imap = $self->transporter or return;
187              
188 0           my $uidl = $message->unique;
189 0           my @fields = $imap->getFields($uidl, 'ALL');
190              
191 0 0         unless(@fields)
192 0           { $self->log(WARNING => "Message $uidl disappeared from $self.");
193 0           return;
194             }
195              
196 0           my $head = $self->{MB_head_type}->new;
197 0           $head->addNoRealize($_) for @fields;
198              
199 0           $self->log(PROGRESS => "Loaded head of $uidl.");
200 0           $head;
201             }
202              
203              
204              
205             sub getHeadAndBody($)
206 0     0 1   { my ($self, $message) = @_;
207 0 0         my $imap = $self->transporter or return;
208 0           my $uid = $message->unique;
209 0           my $lines = $imap->getMessageAsString($uid);
210              
211 0 0         unless(defined $lines)
212 0           { $self->log(WARNING => "Message $uid disappeared from $self.");
213 0           return ();
214             }
215              
216 0           my $parser = Mail::Box::Parser::Perl->new # not parseable by C parser
217             ( filename => "$imap"
218             , file => Mail::Box::FastScalar->new(\$lines)
219             );
220              
221 0           my $head = $message->readHead($parser);
222 0 0         unless(defined $head)
223 0           { $self->log(WARNING => "Cannot find head back for $uid in $self.");
224 0           $parser->stop;
225 0           return ();
226             }
227              
228 0           my $body = $message->readBody($parser, $head);
229 0 0         unless(defined $body)
230 0           { $self->log(WARNING => "Cannot read body for $uid in $self.");
231 0           $parser->stop;
232 0           return ();
233             }
234              
235 0           $parser->stop;
236              
237 0           $self->log(PROGRESS => "Loaded message $uid.");
238 0           ($head, $body->contentInfoFrom($head));
239             }
240              
241              
242              
243             sub body(;$)
244 0     0 1   { my $self = shift;
245 0 0         unless(@_)
246 0 0         { my $body = $self->{MBI_cache_body} ? $self->SUPER::body : undef;
247             }
248              
249 0           $self->unique();
250 0           $self->SUPER::body(@_);
251             }
252              
253              
254              
255             sub write(@)
256 0     0 1   { my ($self, %args) = @_;
257 0 0         my $imap = $self->transporter or return;
258              
259 0 0         $self->SUPER::write(%args, transporter => $imap) or return;
260              
261 0 0         if($args{save_deleted})
262 0           { $self->log(NOTICE => "Impossible to keep deleted messages in IMAP");
263             }
264 0           else { $imap->destroyDeleted($self->name) }
265              
266 0           $self;
267             }
268              
269             sub delete(@)
270 0     0 1   { my $self = shift;
271 0           my $transp = $self->transporter;
272 0           $self->SUPER::delete(@_); # subfolders
273 0           $transp->deleteFolder($self->name);
274             }
275              
276              
277              
278             sub writeMessages($@)
279 0     0 1   { my ($self, $args) = @_;
280              
281 0           my $imap = $args->{transporter};
282 0           my $fn = $self->name;
283              
284 0           $_->writeDelayed($fn, $imap) for @{$args->{messages}};
  0            
285              
286 0           $self;
287             }
288              
289              
290              
291             my %transporters;
292             sub createTransporter($@)
293 0     0 1   { my ($self, $class, %args) = @_;
294              
295 0   0       my $hostname = $self->{MBN_hostname} || 'localhost';
296 0   0       my $port = $self->{MBN_port} || '143';
297 0   0       my $username = $self->{MBN_username} || $ENV{USER};
298              
299 0 0         my $join = exists $args{join_connection} ? $args{join_connection} : 1;
300              
301 0           my $linkid;
302 0 0         if($join)
303 0           { $linkid = "$hostname:$port:$username";
304 0 0         return $transporters{$linkid} if defined $transporters{$linkid};
305             }
306              
307             my $transporter = $class->new
308             ( %args,
309             , hostname => $hostname, port => $port
310             , username => $username, password => $self->{MBN_password}
311             , domain => $self->{MBI_domain}
312 0 0         ) or return undef;
313              
314 0 0         if(defined $linkid)
315 0           { $transporters{$linkid} = $transporter;
316 0           weaken($transporters{$linkid});
317             }
318              
319 0           $transporter;
320             }
321              
322              
323              
324             sub transporter(;$)
325 0     0 1   { my $self = shift;
326              
327 0           my $imap;
328 0 0         if(@_)
329 0           { $imap = $self->{MBI_transport} = shift;
330 0 0         defined $imap or return;
331             }
332             else
333 0           { $imap = $self->{MBI_transport};
334             }
335              
336 0 0         unless(defined $imap)
337 0           { $self->log(ERROR => "No IMAP4 transporter configured");
338 0           return undef;
339             }
340              
341 0           my $name = $self->name;
342              
343 0           $self->{MBI_selectable} = $imap->currentFolder($name);
344             return $imap
345 0 0         if defined $self->{MBI_selectable};
346              
347 0           $self->log(ERROR => "Couldn't select IMAP4 folder $name");
348 0           undef;
349             }
350              
351              
352              
353             sub fetch($@)
354 0     0 1   { my ($self, $what, @info) = @_;
355 0 0         my $imap = $self->transporter or return [];
356 0 0         $what = $self->messages($what) unless ref $what eq 'ARRAY';
357 0           $imap->fetch($what, @info);
358             }
359              
360              
361             #-------------------------------------------
362              
363             1;