File Coverage

blib/lib/Mail/Dir/Message.pm
Criterion Covered Total %
statement 9 65 13.8
branch 0 42 0.0
condition n/a
subroutine 3 16 18.7
pod 11 13 84.6
total 23 136 16.9


line stmt bran cond sub pod time code
1             # Copyright (c) 2016 cPanel, Inc.
2             # All rights reserved.
3             # http://cpanel.net/
4             #
5             # Distributed under the terms of the MIT license. See the LICENSE file for
6             # further details.
7              
8             package Mail::Dir::Message;
9              
10 1     1   5 use strict;
  1         1  
  1         24  
11 1     1   5 use warnings;
  1         2  
  1         26  
12              
13 1     1   5 use File::Basename ();
  1         1  
  1         1108  
14              
15             =head1 NAME
16              
17             Mail::Dir::Message - A message in a Maildir queue
18              
19             =head1 SYNOPSIS
20              
21             #
22             # Mark message as Draft
23             #
24             $message->mark('D');
25              
26             #
27             # Verify that message was marked as Draft
28             #
29             print "Message is a draft\n" if $message->draft;
30              
31             =head1 DESCRIPTION
32              
33             C objects represent messages delivered to a Maildir mailbox,
34             and are created queries to the mailbox as issued by the method
35             C-Emessages()>. C objects are not
36             presently meant to be instantiated directly.
37              
38             =cut
39              
40             sub from_file {
41 0     0 0   my ( $class, %args ) = @_;
42              
43 0 0         die('No Maildir object specified') unless defined $args{'maildir'};
44 0 0         die('Maildir object is of incorrect type') unless $args{'maildir'}->isa('Mail::Dir');
45 0 0         die('No mailbox specified') unless defined $args{'mailbox'};
46 0 0         die('No message filename specified') unless defined $args{'file'};
47 0 0         die('No message name specified') unless defined $args{'name'};
48 0 0         die('No stat() object provided for message') unless defined $args{'st'};
49 0 0         die('stat() object is not an ARRAY') unless ref( $args{'st'} ) eq 'ARRAY';
50              
51 0 0         if ( defined $args{'dir'} ) {
52 0 0         die('"dir" may only specify "tmp", "new" or "cur"') unless $args{'dir'} =~ /^(?:tmp|new|cur)$/;
53             }
54              
55 0           my $flags = '';
56              
57 0 0         if ( $args{'flags'} ) {
    0          
58 0           $flags = parse_flags( $args{'flags'} );
59             }
60             elsif ( $args{'name'} =~ /:(?:1,.*)2,(.*)$/ ) {
61 0           $flags = parse_flags($1);
62             }
63              
64             return bless {
65             'maildir' => $args{'maildir'},
66             'mailbox' => $args{'mailbox'},
67             'dir' => $args{'dir'},
68             'file' => $args{'file'},
69             'name' => $args{'name'},
70             'size' => $args{'st'}->[7],
71             'atime' => $args{'st'}->[8],
72             'mtime' => $args{'st'}->[9],
73 0           'ctime' => $args{'st'}->[10],
74             'flags' => $flags
75             }, $class;
76             }
77              
78             =head1 READING MESSAGES
79              
80             =over
81              
82             =item C<$message-Eopen()>
83              
84             Open the current message, returning a file handle. Will die() if any errors
85             are encountered. It is the caller's responsibility to subsequently close the
86             file handle when it is no longer required.
87              
88             =cut
89              
90             sub open {
91 0     0 1   my ($self) = @_;
92              
93 0 0         CORE::open( my $fh, '<', $self->{'file'} ) or die("Unable to open message file $self->{'file'} for reading: $!");
94              
95 0           return $fh;
96             }
97              
98             =back
99              
100             =head1 MOVING MESSAGES
101              
102             =over
103              
104             =item C<$message-Emove(I<$mailbox>)>
105              
106             Move the current message to a different Maildir++ mailbox. This operation is
107             only supported when the originating mailbox is created with Maildir++
108             extensions.
109              
110             =back
111              
112             =cut
113              
114             sub move {
115 0     0 1   my ( $self, $mailbox ) = @_;
116              
117 0 0         die('Maildir++ extensions not supported') unless $self->{'maildir'}->{'maildir++'};
118 0 0         die('Specified mailbox is same as current mailbox') if $mailbox eq $self->{'maildir'}->{'mailbox'};
119              
120 0           my $mailbox_dir = $self->{'maildir'}->mailbox_dir($mailbox);
121 0           my $new_file = "$mailbox_dir/cur/$self->{'name'}:2,$self->{'flags'}";
122              
123 0 0         unless ( rename( $self->{'file'}, $new_file ) ) {
124 0           die("Unable to rename() $self->{'file'} to $new_file: $!");
125             }
126              
127 0           $self->{'file'} = $new_file;
128              
129 0           return $self;
130             }
131              
132             sub parse_flags {
133 0     0 0   my ($flags) = @_;
134 0           my $ret = '';
135              
136 0 0         die('Invalid flags') unless $flags =~ /^[PRSTDF]*$/;
137              
138 0           foreach my $flag (qw(D F P R S T)) {
139 0 0         $ret .= $flag if index( $flags, $flag ) >= 0;
140             }
141              
142 0           return $ret;
143             }
144              
145             =head1 REMOVING MESSAGES
146              
147             =over
148              
149             =item C<$message-Eremove()>
150              
151             Unlink the current message.
152             This method has the same return value as L.
153             B if removal succeeds, the object is no longer valid and should be disposed of.
154              
155             Do not use this to soft-delete messages. For that, set the C flag instead.
156              
157             =back
158              
159             =cut
160              
161             sub remove {
162 0     0 1   my ( $self ) = @_;
163 0           return unlink $self->{'file'};
164             }
165              
166             =head1 SETTING MESSAGE FLAGS
167              
168             =over
169              
170             =item C<$message-Emark(I<$flags>, I<$queue>)>
171              
172             Set any of the following message status flags on the current message. More
173             than one flag may be specified in a single call, in any order.
174              
175             =over
176              
177             =item * C

178              
179             Mark the message as "Passed".
180              
181             =item * C
182              
183             Mark the message as "Replied".
184              
185             =item * C
186              
187             Mark the message as "Seen".
188              
189             =item * C
190              
191             Mark the message as "Trashed".
192              
193             =item * C
194              
195             Mark the message as a "Draft".
196              
197             =item * C
198              
199             Mark the message as "Flagged".
200              
201             =back
202              
203             You can also specify the queue to which the message will be moved:
204              
205             =over
206              
207             =item * Missing or undefined
208              
209             Move the message to C.
210              
211             =item * C
212              
213             Move the message to C.
214              
215             =item * C
216              
217             Move the message to C.
218              
219             =item * C
220              
221             Move the message to C.
222              
223             =item * C
224              
225             Keep the message in the same queue, do not move it.
226              
227             =back
228              
229             =cut
230              
231             sub mark {
232 0     0 1   my ( $self, $flags, $dir ) = @_;
233 0           $flags = parse_flags($flags);
234              
235 0 0         if ( defined $dir ) {
236 0 0         die('Queue may only be "keepq", "tmp", "new" or "cur"') unless $dir =~ /^(?:keepq|tmp|new|cur)$/;
237 0 0         $dir = $self->{'dir'} if $dir eq 'keepq';
238             }
239             else {
240 0           $dir = 'cur';
241             }
242              
243 0           my $mailbox_dir = $self->{'maildir'}->mailbox_dir( $self->{'mailbox'} );
244 0           my $new_file = "$mailbox_dir/$dir/$self->{'name'}:2,$flags";
245              
246 0 0         unless ( rename( $self->{'file'}, $new_file ) ) {
247 0           die("Unable to rename() $self->{'file'} to $new_file: $!");
248             }
249              
250 0           $self->{'dir'} = $dir;
251 0           $self->{'file'} = $new_file;
252 0           $self->{'flags'} = $flags;
253              
254 0           return $self;
255             }
256              
257             =head1 CHECKING MESSAGE STATE
258              
259             The following methods can be used to quickly check for specific message state
260             flags.
261              
262             =over
263              
264             =item C<$message-Eflags()>
265              
266             Returns a string containing all the flags set for the current message.
267              
268             =cut
269              
270             sub flags {
271 0     0 1   shift->{'flags'};
272             }
273              
274             =item C<$message-Epassed()>
275              
276             Returns 1 if the message currently has the "Passed" flag set.
277              
278             =cut
279              
280             sub passed {
281 0     0 1   shift->{'flags'} =~ /P/;
282             }
283              
284             =item C<$message-Ereplied()>
285              
286             Returns 1 if the message has been replied to.
287              
288             =cut
289              
290             sub replied {
291 0     0 1   shift->{'flags'} =~ /R/;
292             }
293              
294             =item C<$message-Eseen()>
295              
296             Returns 1 if a client has read the current message.
297              
298             =cut
299              
300             sub seen {
301 0     0 1   shift->{'flags'} =~ /S/;
302             }
303              
304             =item C<$message-Etrashed()>
305              
306             Returns 1 if the message is currently trashed after one helluva wild night with
307             its best buds.
308              
309             =cut
310              
311             sub trashed {
312 0     0 1   shift->{'flags'} =~ /T/;
313             }
314              
315             =item C<$message-Edraft()>
316              
317             Returns 1 if the message is a draft.
318              
319             =cut
320              
321             sub draft {
322 0     0 1   shift->{'flags'} =~ /D/;
323             }
324              
325             =item C<$message-Eflagged()>
326              
327             Returns 1 if the message is flagged as important.
328              
329             =cut
330              
331             sub flagged {
332 0     0 1   shift->{'flags'} =~ /F/;
333             }
334              
335             =back
336              
337             =cut
338              
339             1;
340              
341             __END__