Mail::Box(3pm)
π Mail::Box(3pm) β the perl doc you're reading is literally User Contributed Perl Documentation πΈ
okay bestie, sit down, hydrate, because we're doing Mail::Box β manage a mailbox, a folder with messages. it's giving folder wrangler, it's giving main character energy for your email. π
β¨ NAME β¨
Mail::Box β manage a mailbox, a folder with messages π¬
π INHERITANCE π
Mail::Box
is a Mail::Reporter
Mail::Box is extended by
Mail::Box::Dir
Mail::Box::File
Mail::Box::Net
so like, Mail::Box is a Mail::Reporter, and then Mail::Box::Dir, Mail::Box::File and Mail::Box::Net all extend it. family tree but make it folders. π§
β SYNOPSIS β
use Mail::Mbox::Mbox ();
my $emails = Mail::Mbox::Mbox->new(folder => $ENV{MAIL});
print $folder->name;
print $folder->message(0); # get the first message.
$folder->message(3)->delete; # delete the third message
say "Folder size = ", $folder->nrMessages;
# Iterate over the messages.
foreach my $msg ($folder->messages) {...} # all messages
foreach my $msg (@$folder) {...} # all messages
$folder->addMessage(Mail::Message->new(...));
Managing multiple mailboxes:
use Mail::Box::Manager;
my $mgr = Mail::Box::Manager->new;
my $folder = $mgr->open(folder => $ENV{MAIL}, ...);
Tied-interface:
tie my(@inbox), 'Mail::Box::Tie::ARRAY', $folder;
tie my(%inbox), 'Mail::Box::Tie::HASH', $inbox;
# Four times the same:
$inbox[3]->print; # tied
$folder->[3]->print; # overloaded folder
$folder->message(3)->print; # usual
print $folder->[3]; # overloaded message
# Twice times the same
$inbox{$msgid}->print; # tied
$folder->messageId($msgid)->print;# usual
no cap, look at all those ways to grab the same message. four different paths, one destiny. iconic. π
π DESCRIPTION π
A Mail::Box::Manager creates Mail::Box objects. But you already knew that, because you started with the Mail::Box-Overview manual page. That page is obligatory reading, sorry bestie π it's not optional, it's the vibe check you have to pass first.
β οΈ Be aware: This module versions 4.0 and up is not fully compatible with older releases: mainly the exception handling has changed. When you need to upgrade, please read the wiki <https://github.com/markov2/perl5-Mail-Box/wiki/>. Version 3 is still maintained and may see new releases as well.
Mail::Box is the base class for accessing various types of mailboxes (folders) in a uniform manner. The various folder types vary on how they store their messages, but when some effort those differences could be hidden behind a general API. For example, some folders store many messages in one single file, where other store each message in a separate file within the same directory. lowkey that's the whole flex of this module β one API, many storages, zero chaos (okay some chaos). π«
No object in your program will be of type Mail::Box: it is only used as base class for the real folder types. Mail::Box is extended by file, directory, and remote folder types. See "Different kinds of folders" ποΈ
Extends "DESCRIPTION" in Mail::Reporter.
π¦ OVERLOADED π¦
overload: '""' stringification
The folder objects stringify to their name. This simplifies especially print statements and sorting a lot. fr fr, folders as strings is a whole quality-of-life moment. β¨
Β» example: use overloaded folder as string
# Three lines with overloading: resp. cmp, @{}, and ""
foreach my $folder (sort @folders)
{ my $msgcount = @$folder;
print "$folder contains $msgcount messages\n";
}
overload: '@{}' use as ARRAY
When the folder is used as if it is a reference to an array, it will show the messages, like messages() and message() would do.
Β» example: use overloaded folder as array
my $msg = $folder->[3];
my $msg = $folder->message(3); # same
foreach my $msg (@$folder) ...
foreach my $msg ($folder->messages) ... # same
overload: cmp string comparison
Folders are compared based on their name. The sort rules are those of the build-in cmp. so yes, you can sort your folders and it just works. periodt. π
π₯ METHODS π₯
Extends "METHODS" in Mail::Reporter.
π Constructors π
Extends "Constructors" in Mail::Reporter.
$class->new(%options)
Open a new folder. A list of labeled %options for the mailbox can be supplied. Some options pertain to Mail::Box, and others are added by sub-classes.
To control delay-loading of messages, as well the headers as the bodies, a set of *_type options are available. extract determines whether we want delay-loading. Improves base, see "Constructors" in Mail::Reporter
here's the whole default table bestie, every single one, no skipping π«Ά
-Option --Default
access 'r'
body_delayed_type Mail::Message::Body::Delayed
body_type <folder specific>
coerce_options +[]
create false
extract 10240
field_type undef
fix_headers false
folder $ENV{MAIL}
folderdir undef
head_delayed_type Mail::Message::Head::Delayed
head_type Mail::Message::Head::Complete
keep_dups false
lock_file undef
lock_timeout 1 hour
lock_type Mail::Box::Locker::DotLock
lock_wait 10 seconds
locker undef
manager undef
message_type <folder-class>::Message
multipart_type Mail::Message::Body::Multipart
remove_when_empty true
save_on_exit true
trusted <depends on folder location>
same thing but as a lil checklist of what the defaults literally are:
accessβ'r'body_delayed_typeβMail::Message::Body::Delayedbody_typeβ<folder specific>coerce_optionsβ+[]createβfalseextractβ10240field_typeβundeffix_headersβfalsefolderβ$ENV{MAIL}folderdirβundefhead_delayed_typeβMail::Message::Head::Delayedhead_typeβMail::Message::Head::Completekeep_dupsβfalselock_fileβundeflock_timeoutβ1 hourlock_typeβMail::Box::Locker::DotLocklock_waitβ10 secondslockerβundefmanagerβundefmessage_typeβ<folder-class>::Messagemultipart_typeβMail::Message::Body::Multipartremove_when_emptyβtruesave_on_exitβtruetrustedβ<depends on folder location>
and now the full receipts on each one, because we do not gatekeep: π
access => $modeβ Access-rights to the folder. Folders are opened for read-only (which means write-protected) by default! The$modecan be'r'(read-only, default),'a'(append only),'rw'(read-write), and'd'(delete only).
This $mode describes the abstract folder access: how the underlaying files are really accessed depends on the action which needs to take place.
π¨ Be warned: writing a MBOX type folder may create a new file to replace the old folder. The permissions and owner of the file may get changed by this. like, it will not ask you first. it just does it. π
body_delayed_type => CLASSβ The bodies which are delayed: which will be read from file when it is needed, but not before. lazy girl behavior, but make it intentional. πbody_type => CLASS|CODEβ When messages are read from a folder-file, the headers will be stored in ahead_typeobject. For the body, however, there is a range of choices about type, which are all described in Mail::Message::Body.
Specify a CODE-reference which produces the body-type to be created, or a CLASS of the body which is used when the body is not a multipart or nested. In case of a code reference, the header structure is passed as first argument to the routine.
Do not return a delayed body-type (like ::Delayed), because that is determined by the extract option while the folder is opened. Even delayed message will require some real body type when they get parsed eventually. Multiparts and nested messages are also outside your control.
For instance:
$mgr->open('InBox', body_type => \&which_body);
sub which_body($) {
my $head = shift;
my $size = $head->guessBodySize || 0;
my $type = $size > 100000 ? 'File' : 'Lines';
"Mail::Message::Body::$type";
}
The default depends on the mail-folder type, although the general default is Mail::Message::Body::Lines. Please check the applicable manual pages. π
coerce_options => ARRAYβ Keep configuration information for messages which are coerced into the specified folder type, starting with a different folder type (or even no folder at all). Messages which are coerced are always fully read, so this kind of information does not need to be kept here.create => BOOLEANβ Automatically create the folder when it does not exist yet. This will only work when access is granted for writing or appending to the folder.
π¨ Be careful: you may create a different folder type than you expect unless you explicitly specify Mail::Box::Manager::open(type). bestie, read the type. please. π
extract => INTEGER|CODE|METHOD|'LAZY'|'ALWAYS'β Defines when to parse (process) the content of the message. When the header of a message is read, you may want to postpone the reading of the body: header information is more often needed than the body data, so why parse it always together? The cost of delaying is not too high, and with some luck you may never need parsing the body. literally the procrastination option and it's good. β¨
If you supply an INTEGER to this option, bodies of those messages with a total size less than that number will be extracted from the folder only when necessary. Messages where the size (in the Content-Length field) is not included in the header, like often the case for multiparts and nested messages, will not be extracted by default.
If you supply a CODE reference, that subroutine is called every time that the extraction mechanism wants to determine whether to parse the body or not. The subroutine is called with the following arguments:
CODE->(FOLDER, HEAD)
where FOLDER is a reference to the folder we are reading. HEAD refers to the Mail::Message::Head::Complete head of the message at hand. The routine must return a "true" value (extract now) or a "false" value (be lazy, do not parse yet). Think about using the Mail::Message::Head::guessBodySize() and Mail::Message::guessTimestamp() on the header to determine your choice.
The third possibility is to specify the NAME of a method. In that case, for each message is called:
FOLDER->NAME(HEAD)
Where each component has the same meaning as described above.
The fourth way to use this option involves constants: with "LAZY" all messages will be delayed. With "ALWAYS" you enforce unconditional parsing, no delaying will take place. The latter is usuful when you are sure you always need all the messages in the folder.
$folder->new(extract => 'LAZY'); # Very lazy
$folder->new(extract => 10000); # Less than 10kB
# same, but implemented yourself
$folder->new(extract => &large);
sub large($) {
my ($f, $head) = @_;
my $size = $head->guessBodySize;
defined $size ? $size < 10000 : 1
};
# method call by name, useful for Mail::Box
# extensions. The example selects all messages
# sent by you to be loaded without delay.
# Other messages will be delayed.
$folder->new(extract => 'sent_by_me');
sub Mail::Box::send_by_me($) {
my ($self, $header) = @_;
$header->get('from') =~ m/\bmy\@example.com\b/i;
}
(yes the example really does contain usuful, we keep everything exactly as it is around here, typos and all π«Ά)
field_type => $classβ The type of the fields to be used in a header. Must extend Mail::Message::Field.fix_headers => BOOLEANβ so normally, when MIME headers are broken, the parser just gives up π and every line it couldn't parse gets dumped into the body of the message. Set this flag and instead the messed-up line gets tacked onto the previous header field and parsing keeps going. SeeMail::Box::Parser::Perl::new(fix_header_errors).folder => $nameβ which folder are we opening, bestie (for reading or for writing)? When it's for reading (that's theaccessoption set to"r"or"a") the mailbox has to already exist and be readable, periodt. The file or directory doesn't actually need to exist if you're opening for reading and writing ("rw"). And yes, write-permission gets checked when you open an existing mailbox.
The folder name can start with a "=" β that means "hey, this is named relative to whatever directory is in new(folderdir)". No equals sign? Then it's just taken as a relative or absolute path. Slay.
folderdir => $directoryβ where do folders live by default? π If a folder-name is preceded by an equals-sign ("=", a"mutt"convention) it's explicitly saying "this folder sits below the default directory". Like: withfolderdir => '/tmp'andfolder => '=abc', the folder-file's name is/tmp/abc. Each folder type already has some default set. iykyk.head_delayed_type => $classβ the headers that get delayed: read from the file only when they're actually needed, not a second before. Chill vibes.head_type => $classβ the type of header that holds all the header info. Must extendMail::Message::Head::Complete.keep_dups => BOOLEANβ do we keep duplicate messages inside the folder or not? A message counts as a duplicate if its message-id is the same as a message already parsed within that same folder. If this option isfalse(which is the default, ngl) those dupes get automatically deleted, because storing the same message twice is just useless π« .lock_file => $fileβ the name of the$fileused to lock. You must specify this whenever locking is going to be used.lock_timeout => $secondsβ if the lock file is older than the given number of$seconds, it's treated as a mistake: the original lock gets released and accepted for this folder. Bye ghost lock π .lock_type => $class|$name|\@namesβ the type of the locker object. Could be a$classthat extendsMail::Box::Locker, or one of the known locker types by$name:"DotLock","Flock","FcntlLock","Mutt","NFS","POSIX", or"NONE". Pass an ARRAY and a Multi locker gets built using the specified list of locker@names.lock_wait => $secondsβ how many$secondsdo we wait before giving up on opening this folder.locker => $lockerβ an object that extendsMail::Box::Locker, which will handle the folder locking and replace the default lock behavior. main character energy πmanager => $managerβ a reference to the object managing this folder β typically aMail::Box::Managerinstance.message_type => $classβ what kind of message objects live in this type of folder? The default is built from the folder class followed by"::Message". So for instance, the message type forMail::Box::POP3isMail::Box::POP3::Message.multipart_type => CLASSβ the default type of objects created for multipart message bodies.remove_when_empty => BOOLEANβ should the folder file or directory get removed automatically when writing would result in a folder with no messages and no sub-folders? β¨save_on_exit => BOOLEANβ sets the policy for saving the folder when it's closed. You can close a folder manually (seeclose()) or in a bunch of implicit ways, including the moment the program terminates.trusted => BOOLEANβ flags whether to trust the data in the folder or not. Folders sitting in yourfolderdirare trusted by default (even when their names weren't specified starting with a"="). Folders outside the folderdir, or read from STDIN (Mail::Message::Construct::read()), are not trusted by default and require some extra checking.
If you do not check the encodings of received messages, you may end up printing binary data to the screen, which is a security risk. Not cute. Not iconic. Be careful. π
π Attributes π
Extends Attributes in Mail::Reporter. Okay so here's what we got:
$obj->fixHeaders()β the accessor for that fixy behavior.$obj->folderdir( [$directory] )β get it or set it: the$directoryused to store mail-folders by default.
π« example:
print $folder->folderdir;
$folder->folderdir("$ENV{HOME}/nsmail");
$obj->isTrusted()β is this folder trusted or nah.$obj->name()β returns the name of the folder. What that name actually represents depends on the real type of mailbox being used.
π« example:
print $folder->name;
print "$folder"; # overloaded stringification
$obj->removeEmpty()β the accessor for the remove-when-empty setting.$obj->type()β returns a name for the type of mail box. So it can be"mbox","mh","maildir", or"pop3"π¦.
πΈ The folder πΈ
$obj->addMessage($message, %options)β adds a message to the folder. Usually that message is aMail::Box::Messageobject or a sub-class of it. Heads up: the message shall not be in another folder when you use this method. If it is, useMail::Box::Manager::moveMessage()orMail::Box::Manager::copyMessage()through the manager instead, bestie.
Messages whose ids already exist in this folder are not added. Sorry to that message.
BE WARNED that message labels may get lost when a message is moved from one folder type to another. An attempt is made to translate labels, but there are so many differences in interpretation by applications. It's giving chaos, fr fr.
-Option--Default
share <not used>
share => BOOLEANβ try to share the physical resource of the current message with the indicated message. Sometimes it's possible to share messages between different folder types. When sharing isn't possible, this option is simply ignored.
Sharing the resource is quite dangerous, and only available for a limited number of folder types β at the moment only some Mail::Box::Dir folders; these file-based messages can be hardlinked (on platforms that support it). The link may get broken when one message is modified in one of the folders.... but maybe not, depending on the folder types involved. Risky business π.
π« example:
$folder->addMessage($msg);
$folder->addMessages($msg1, $msg2, ...);
$obj->addMessages(@messages)β adds a set of message objects to the open folder all at once. For some folder types this may be faster than adding them one at a time. Efficiency slay β¨.
π« example:
$folder->addMessages($msg1, $msg2, ...);
$class->appendMessages(%options)β append one or more messages to a folder that hasn't been opened. Usually this method gets called byMail::Box::Manager::appendMessage(), in which case the correctness of the folder type is checked for you π«Ά.
For some folder types you're required to open the folder before it can be used for appending. This can be fast, but it can also be very slow (depends on the implementation, no cap). All %options passed will also be used to open the folder, if that's needed.
-Option --Default
folder <required>
message undef
messages undef
share false
folder => $nameβ the$nameof the folder the messages are to be appended to. The folder implementation will avoid opening the folder when possible, because that's resource consuming.message => $messageβ one single message.messages => $message|\@messagesβ one reference to one$messageor an ARRAY of@messages, which may be of any type. The messages will first be coerced into the correct message type to fit in the folder, and then added to it.share => BOOLEANβ try to share physical storage of the message. Only available for a limited number of folder types, otherwise it's a no-op.
π« example:
my $message = Mail::Message->new(...);
Mail::Box::Mbox->appendMessages(
folder => '=xyz',
message => $message,
folderdir => $ENV{FOLDERS},
);
better:
my Mail::Box::Manager $mgr;
$mgr->appendMessages($message, folder => '=xyz');
$obj->close(%options)β closes the folder, which usually means writing the changes. It returns"false"when writing is required but fails. Please actually check that result, bestie, please π.
WARNING: When moving messages from one folder to another, be sure to write the destination folder before writing and closing the source folder. Otherwise you may lose data if the system crashes or if there are software problems. Do not play with this one.
-Option --Default
force false
save_deleted false
write 'MODIFIED'
force => BOOLEANβ override thenew(access)setting that was specified when the folder was opened. This option only has an effect if its value is TRUE. NOTE: writing to the folder may not be permitted by the operating system, in which case even"force"will not help. Sorry π¬.save_deleted => BOOLEANβ also write messages that were flagged to be deleted to their folder. The flag for deletion is conserved (when possible), which means that re-opening the folder may remove the messages for real. Seewrite(save_deleted).write => 'ALWAYS'|'NEVER'|'MODIFIED'β specifies whether the folder should be written. As you'd expect,"ALWAYS"means always (even if there are no changes),"NEVER"means changes to the folder will be lost π, and"MODIFIED"only saves the folder if there are any changes.
π« example:
my $f = $mgr->open('spam', access => 'rw')
or die "Cannot open spam: $!\n";
$f->message(0)->delete
if $f->messages;
$f->close
or die "Couldn't write $f: $!\n";
$obj->copyTo($folder, %options)β copies the folder's messages to a new folder. The new folder may be of a different type. Glow-up for your mail π¦.
-Option --Default
delete_copied false
select 'ACTIVE'
share <not used>
subfolders <folder type dependent>
delete_copied => BOOLEANβ marks the messages from the source folder for deletion, right after they got copied over. Heads up bestie: the deletion only actually happens once the originating folder gets closed. πselect => 'ACTIVE'|'DELETED'|'ALL'|LABEL|!LABEL|FILTERβ which messages are we copying, exactly? Go read the description ofmessages()if you wanna know how this whole selection thing works. β¨share => BOOLEANβ try to share the message between both folders. SomeMail::Box::Dirfolder types are down for this and do it by creating a hardlink (on UNIX/Linux). π«Άsubfolders => BOOLEAN|'FLATTEN'|'RECURSE'β how do we treat sub-folders, hmm? When it's "false" (0or"undef"), sub-folders just get ignored, no thoughts. With"FLATTEN", messages from the sub-folders get pulled up into the main copy."RECURSE"recursively copies the sub-folders too. By default, if the destination folder supports sub-folders then"RECURSE"is used, otherwise"FLATTEN". And a value of"true"just picks the default for you. π
Β» example:
my $mgr = Mail::Box::Manager->new;
my $imap = $mgr->open(type => 'imap', host => ...);
my $mh = $mgr->open(type => 'mh', folder => '/tmp/mh',
create => 1, access => 'w');
$imap->copyTo($mh, delete_copied => 1);
$mh->close; $imap->close;
ποΈ $obj->delete(%options)
Yeets the specified folder file or folder directory (whichever it is, depends on the folder type) off of disk. And okay bestie, let's be so real: THIS IS DANGEROUS β you "may" lose data. It returns a "true" value when it worked. π
WARNING: when you're moving messages from one folder to another, please please PLEASE write the destination folder before deleting the source folder. Otherwise you may lose data if the system crashes or if there are software problems. Not a vibe, don't do it. π
Here's the option rundown (defaults included):
recursive => BOOLEANβ default:true.
Β» example: removing an open folder
my $folder = Mail::Box::Mbox->new(folder => 'InBox', access => 'rw');
... some other code ...
$folder->delete;
Β» example: removing an closed folder
my $folder = Mail::Box::Mbox->new(folder => 'INBOX', access => 'd');
$folder->delete;
π $obj->organization()
Returns how the folder is organized: as one "FILE" with many messages, a "DIRECTORY" with one message per file, or by a "REMOTE" server. Three whole personality types, iconic. π
π $obj->size()
Returns the size of the folder in bytes, and no, it does not count the deleted messages. Fair warning though: the error in the result it hands you can be as large as 10%, because the in-memory representation of messages isn't always the same as the size when they're written. So like... approximately, bestie. π
π $obj->update(%options)
Reads new messages from the folder β the ones that were received after it got opened. Ngl this is quite dangerous and technically shouldn't even be possible, since folders that are open are locked. But some applications don't use locks, or use the wrong kind of locks. π So this method reads the changes (not always failsafe, watch out) and folds them into the open folder's administration.
The %options are extra values that get passed along to the updateMessages() method, which is the one doing the actual work here. πͺ
π $obj->url()
Represents the folder as a URL (Universal Resource Locator) string. You can hand a URL like this straight over as a folder name to Mail::Box::Manager::open(). So convenient honestly. β¨
Β» example:
print $folder->url;
# may result in
# mbox:/var/mail/markov or
# pop3://user:password@pop.aol.com:101
π© Folder flags
$obj->access()
Returns the access mode of the folder, as set by new(access). π
$obj->isModified()
Checks if the folder β as it lives in memory β is modified. You get a "true" value when any of the messages is marked for deletion, has changed, or when messages were added after the folder was read from file. π«
WARNING: this flag is not related to an external change to the folder structure on disk. For that whole situation, go look at update() instead. Don't mix them up bestie. π€
$obj->modified( [BOOLEAN] )
Sets whether the folder is modified or not. You're in charge here. π
$obj->writable()
Checks whether the current folder is writable. Simple as that.
Β» example:
$folder->addMessage($msg) if $folder->writable;
π The messages
$obj->current( [$number|$message|$message_id] )
Some mail-readers keep track of the current message β the last one that got used. This method returns [after setting] the current message. You can pass a $number to say "that message number is now current", or a $message/$message_id instead (as long as you're sure the header is already loaded, otherwise they won't be recognized, no cap). π―
Β» example:
$folder->current(0);
$folder->current($message);
$obj->find($message_id)
Like messageId(), this method searches for a message with the $message_id and returns the matching message object. But here's the difference: "find" will cause unparsed messages in the folder to actually get parsed until the message-id turns up. And it scans the folder back to front. π
$obj->findFirstLabeled( $label, [BOOLEAN, [\@msgs]] )
Finds the first message that has this $label with the correct setting. The "BOOLEAN" says whether you want any "true" value or any "false" value found in the ARRAY of @msgs. By default it goes looking for a "true" value. If a message doesn't have the requested label at all, it's treated as "false". π€·
Β» example: looking for a labeled message
my $current = $folder->findFirstLabeled('current');
my $first = $folder->findFirstLabeled(seen => 0);
my $last = $folder->findFirstLabeled(seen => 0,
[ reverse $self->messages('ACTIVE') ] )
$obj->message( $index, [$message] )
Gets or sets a message at a certain index. Messages that are flagged for deletion still get counted, jsyk. And negative indexes start from the end of the folder. π’
Β» example:
my $msg = $folder->message(3);
$folder->message(3)->delete; # status changes to `deleted'
$folder->message(3, $msg);
print $folder->message(-1); # last message.
$obj->messageId( $message_id, [$message] )
With just one argument, it returns the message in the folder with the specified $message_id. If you pass a reference to a message object as that optional second argument, the message gets stored in the folder first, replacing any existing message whose message ID is $message_id. (Fun fact: the message ID of $message doesn't even have to match $message_id.) π
!!WARNING!!: when the message headers are delay-parsed, the message might be sitting in the folder but not yet parsed into memory. In that case, usefind()instead ofmessageId()if you genuinely need a thorough search. This is especially the case for directory organized folders without special indexi, likeMail::Box::MH. π
The $message_id may still be wrapped in angles, which will get stripped. When that happens, blanks (which come from header line folding) are removed too. And any other info hanging around the angles gets removed as well. π§Ή
Β» example:
my $msg = $folder->messageId('<complex-message.id>');
$folder->messageId("<complex-message\n.id>", $msg);
my $msg = $folder->messageId('complex-message.id');
my $msg = $folder->messageId('garbage <complex-message.id> trash');
$obj->messageIds()
Returns a list of all message-ids in the folder, including the ones belonging to messages that are marked for deletion. Everyone's invited. π
For some folder-types (like MH), this method may cause all the message-files to be read. Go check their respective manual pages if you care.
Β» example:
foreach my $id ($folder->messageIds) {
$folder->messageId($id)->print;
}
$obj->messages( <'ALL'|$range|'ACTIVE'|'DELETED'|$label| !$label|$filter> )
Returns multiple messages from the folder. The default is "ALL", which returns all the messages (as you probably expected, bestie). The "ACTIVE" flag returns the messages that are not flagged for deletion. That's the exact opposite of "DELETED", which returns all the messages that are gonna be deleted when the folder is closed. βοΈ
You can also pass a $range: two numbers specifying the begin and end index in the array of messages. Negative indexes count from the end of the folder. And if an index is out-of-range, the returned list just comes back shorter β no complaints, no drama. π
Anything that isn't one of the predefined names is seen as a label, and the messages with that label set get returned. When the sequence starts with an exclamation mark (!), the search result gets flipped. π
For more complex searches you can specify a $filter, which is simply a code reference. The message is passed in as the only argument.
Β» example:
foreach my $message ($folder->messages) {...}
foreach my $message (@$folder) {...}
# twice the same
my @messages = $folder->messages;
my @messages = $folder->messages('ALL');
# Selection based on a range (begin, end)
my $subset = $folder->messages(10,-8);
# twice the same:
my @not_deleted= grep {not $_->isDeleted}
$folder->messages;
my @not_deleted= $folder->messages('ACTIVE');
# scalar context the number of messages
my $nr_of_msgs = $folder->messages;
# third message, via overloading
$folder->[2];
# Selection based on labels
$mgr->moveMessages($spam, $inbox->message('spam'));
$mgr->moveMessages($archive, $inbox->message('seen'));
$obj->nrMessages(%options)
Just calls messages() in scalar context, so you get a count back instead of the actual messages themselves. Some people seem to understand it better this way, and honestly, respect. π«‘
Note that nrMessages() defaults to giving you a count of "ALL" messages in the folder β including both "ACTIVE" and "DELETED" ones. So like, everybody.
The %options get passed to (and explained in) messages(). π¬
$obj->scanForMessages($message, $message_ids, $timespan, $window)
So you start with a $message, and you're hunting for a set of messages that are related to it. Think messages that show up in the 'In-Reply-To' and 'Reference' header fields of that message, for example. You know these messages by their $message_ids, and you want to find them inside the folder. π§΅
When all the message-ids are already known, looking up messages is simple: they're found in a plain hash using messageId(). But Mail::Box is lazy wherever it can be, so lots of messages may not have been read from file yet β and honestly? That's the preferred situation, because it saves time and memory. Stay lazy, stay efficient. π€β¨
...and it's way smarter to search backward, bestie, no cap π
β related messages are almost always chilling right next to each other, so this method starts scanning the folder from the $message you hand it and goes back toward the front of the folder.
The $timespan is your little cutoff switch based on the time wrapped up inside the message π₯. When you pass the constant string "EVER", then the search is never limited by that. When you hand it an integer, it gets used as absolute time in time-ticks β the kind your platform-dependent "time" function gives you. And in every other case, it gets passed to timespan2seconds() to work out the threshold as time relative to the message's time.
Then there's $window, which limits the search by number of messages to be scanned β an integer, or the constant string "ALL" if you're feeling feral β¨.
What comes back to you? The message-ids that were not found during the scan. But β οΈ be warned: a message-id could already be known and therefore not found, so check that first, okay bestie?
π‘ example: scanning through a folder for a message
my $refs = $msg->get('References') or return;
my @msgids = $ref =~ m/\<([^>]+\>/g;
my @failed = $folder->scanForMessages($msg, \@msgids, '3 days', 50);
π¦ Sub-folders π¦
$any->listSubFolders(%options)
Lists out the names of all the sub-folders of this folder β but not recursively descending, okay, we're staying on one level π. Use these names as the argument to openSubFolder() to actually get access to that folder.
For MBOX folders, the sub-folders are simulated, jsyk.
Here's the gossip on the options π :
check=>BOOLEANβ defaultfalse. Should every returned foldername be checked to make sure it's actually the right type? Each sub-folder may legitimately need to be opened to check this, which comes with a folder-type-dependent penalty β and in some cases it's very expensive π.folder=>$nameβ default is<from calling object>. The folder whose sub-folders you want listed.folderdir=>$dirβ default is<from folder>.skip_empty=>BOOLβ defaultfalse. Shall empty folders (folders that currently don't contain any messages) be included? Empty folders aren't useful to open, but they may be useful to save to!
π‘ example:
my $folder = $mgr->open('=in/new');
my @subs = $folder->listSubFolders;
my @subs = Mail::Box::Mbox->listSubFolders(folder => '=in/new');
my @subs = Mail::Box::Mbox->listSubFolders; # toplevel folders.
$any->nameOfSubFolder( $subname, [$parentname] )
Returns the constructed name of the folder with NAME, which is a sub-folder of this current one π«Ά. You've either gotta call this method as an instance method, or specify a $parentname.
π‘ example: how to get the name of a subfolder
my $sub = Mail::Box::Mbox->nameOfSubfolder('xyz', 'abc');
print $sub; # abc/xyz
my $f = Mail::Box::Mbox->new(folder => 'abc');
print $f->nameOfSubfolder('xyz'); # abc/xyz
my $sub = Mail::Box::Mbox->nameOfSubfolder('xyz', undef);
print $sub; # xyz
$obj->openRelatedFolder(%options)
Opens a folder (usually a sub-folder) using the same options as this one πΈ. If there's a folder manager in play, it'll be told about this new folder. The %options overrule the options that were used for the folder this method got called on.
$obj->openSubFolder($subname, %options)
Opens (or creates, if it doesn't exist yet!) a new subfolder inside an existing folder β¨.
π‘ example:
my $folder = Mail::Box::Mbox->new(folder => '=Inbox');
my $sub = $folder->openSubFolder('read');
$any->topFolderWithMessages()
Some folder types can have messages in the top-level folder, and some other ones literally can't. That's the whole vibe π.
π§ Internals π§
$obj->coerce($message, %options)
Coerces the $message to be the correct type so it can be placed in the folder πͺ. You can specify Mail::Internet and MIME::Entity objects here β they'll be translated into Mail::Message messages first.
$any->create($foldername, %options)
Creates a folder π. If the folder already exists, it gets left unchanged β no drama. The folder is created, but not opened! If you want to open a file that may need to be created, then use Mail::Box::Manager::open() with the create flag, or Mail::Box::new(create).
folderdir=>$directoryβ defaultundef. When the foldername is preceded by aβ=β, the$directorywill be searched for the named folder.
$obj->determineBodyType($message, $head)
Determines which kind of body will be created for this message when the folder is read initially π.
$class->foundIn( [$foldername], %options )
Figures out if the specified folder is of the type handled by the folder class. This method gets extended by each folder sub-type.
The $foldername specifies the name of the folder, as specified by the application. You need to specify the "folder" option when you skip this first argument.
The %options is a list of extra info for the request. Read the docs for each type of folder for type-specific options, but every folder class will at least support the "folderdir" option:
folderdir=>$directoryβ defaultundef. The location where the folders of this class are stored by default. If the user specifies a name that starts with aβ=β, that means the folder is to be found in this default$directory.
π‘ example:
Mail::Box::Mbox->foundIn('=markov', folderdir => "$ENV{HOME}/Mail");
Mail::Box::MH->foundIn(folder => '=markov');
$obj->lineSeparator( [<STRING|'CR'|'LF'|'CRLF'>] )
Returns the character (or characters!) used to separate lines in the folder file β optionally after setting it to STRING, or one of the constants π¦. The first line of the folder sets the default.
UNIX uses a "LF" character, Mac a "CR", and Windows uses both a "CR" and a "LF". Each separator gets represented by a "\n" inside your program π. But when you're processing platform-foreign folders, complications pop up β think about the "Size" field in the header, bestie.
When the separator is changed, the whole folder may need to be rewritten. Althoughβ¦ that may not actually be required π .
$obj->locker()
Returns the locking object, periodt.
$obj->read(%options)
Reads messages from the folder into memory π§ . The %options are folder specific. Do not call read() yourself β it will be called for you when you open the folder via the manager, or when you instantiate a folder object directly.
π NOTE: if you're copying messages from one folder to another, useaddMessages()instead ofread().
π‘ example:
my $mgr = Mail::Box::Manager->new;
my $folder = $mgr->open('InBox'); # implies read
my $folder = Mail::Box::Mbox->new(folder => 'Inbox'); # same
$obj->readMessages(%options)
Called by read() to actually read the messages from one specific folder type π. The read() handles organizing all the general activities.
The %options are "trusted", "head_type", "field_type", "message_type", "body_delayed_type", and "head_delayed_type" as defined by the folder at hand. The defaults are the constructor defaults (see new()).
$obj->storeMessage($message)
Stores the message in the folder without the checks that addMessage() performs π.
$obj->toBeThreaded($messages)
The specified message is ready to be removed from a thread π§΅. This gets passed on to the mail-manager, which keeps an overview of which thread-detection objects are floating around.
$obj->toBeUnthreaded($messages)
The specified message is ready to be included in a thread π§΅β¨. This gets passed on to the mail-manager, which keeps an overview of which thread-detection objects are floating around.
$obj->updateMessages(%options)
Called by update() to read the messages that arrived in the folder after it was opened π¬. Sometimes external applications dump messages into a folder without locking (or using a different lock than your application does).
Although this is quite dangerous, it only fails when a folder is updated (reordered or a message removed) at exactly the same time as new messages arrive. These collisions are sparse, so like⦠lowkey fine.
The options are the same as for readMessages().
$obj->write(%options)
Writes the data to disk πΎ. The folder (a "true" value) is returned if it was successful. Deleted messages are transformed into destroyed messages β their memory gets freed.
β οΈ WARNING: When moving messages from one folder to another, be sure to write (or close()) the destination folder before writing (or closing) the source folder β otherwise you may lose data if the system crashes or if there are software problems. This is the one that bites people, bestie, pls don't skip it π
To write a folder to a different file, you must first create a new folder, then move all the messages, and then write or close() that new folder.
force=>BOOLEANβ defaultfalse. Overrides write-protection withnew(access)while opening the folder (whenever possible β it may still be blocked by the operating system).save_deleted=>BOOLEANβ defaultfalse. Do also write messages that where flagged to be deleted to their folder. The flag for deletion is conserved (when possible), which means that a re-open of the folder may remove the messages for real. Seeclose(save_deleted).
$obj->writeMessages(%options)
Called by write() to actually write the messages from one specific folder type π
. The "write" handles organizing the general activities. All options to write() are passed to "writeMessages" as well. Besides that, a few extras get added by "write" itself.
messages=>ARRAYβ default<required>. The messages to be written, which is a sub-set of all the messages in the current folder.
π Other methods π
$any->timespan2seconds($time)
The parameter $time is a string that starts with a float, and then one of the words 'hour', 'hours', 'day', 'days', 'week', or 'weeks' β³. For instance: '1 hour' or '4 weeks'.
π Error handling π
Extends "Error handling" in Mail::Reporter.
$obj->AUTOLOAD()
Inherited, see "Error handling" in Mail::Reporter
$obj->notImplemented()
Inherited, see "Error handling" in Mail::Reporter
π§Ό Cleanup π§Ό
Extends "Cleanup" in Mail::Reporter.
$obj->DESTROY()β Perl calls this one automatically when a folder object isn't reachable by the rest of your program anymore, like it's being ghosted by the runtime π. It improves on the base version β peep "Cleanup" in Mail::Reporter for the deets.
π DETAILS π
ποΈ Different kinds of folders
Okay so bestie, folders come in three general flavors: the ones that cram messages into one file, the ones that spread them out into a directory, and the ones that don't even let you touch the message data directly. Each flavor has its own base class, iykyk.
- File based folders β
Mail::Box::File
These keep the whole folder (aka the whole set of messages) inside one single file. The slay of it? Your folder is literally one file, so when you need to access everything all at once, it's speedy πββοΈπ¨.
The main downside compared to directory based folders is that you gotta build some whole system to keep the messages apart. Like MBOX drops a message separator line between messages, and that line can get confused with the actual message contents π.
So where file based folders are faster for grabbing everything at once, grabbing one message is (wayyy) slower, because the whole folder has to be read first. Though tbh, with directory based folders you have to figure out which message you actually need, and that's its own little hassle.
Examples of file based folders: MBOX, DBX, and NetScape β¨.
- Directory based folders β
Mail::Box::Dir
Instead of shoving multiple messages into one file, you can put each message in its own separate file and collect all those files in a directory to represent the folder π.
Main disadvantages: you get an enormous amount of tiny little files in your file-system π, and searching through the whole folder is extremely slow because sooo many files have to be opened to do it.
But the best feature of this whole setup is that each message is kept exactly as it was received, and can be processed with external scripts too β you don't even need a mail user agent (MUA) π .
Examples of directory organized folders: MH, Maildir, EMH and XMH.
- Network (external) folders β
Mail::Box::Net
See, both types above give you direct access to the message data. These ones? They hold the message data for you: you gotta request messages, or parts of them. These folders don't have a filename, file-system privileges or system locking to stress about, but typically they need a hostname, folder and message IDs, and authorization π.
Examples: the popular POP and IMAP, plus database oriented message storage.
π Available folder types
Mail::Box::Dbx(read only)
Dbx files are made by Outlook Express. Using the external (optional) Mail::Transport::Dbx module, you can read these folders, even when you're running MailBox on a UNIX/Linux platform π§.
Writing and deleting messages just isn't supported by the library, and therefore not by MailBox either. Read access is enough to do folder conversions, for instance.
Mail::Box::IMAP4(partially)
The IMAP protocol is very complex, bestie, like it's giving final boss energy π€. Some parts are implemented to create (sub-optimal but usable) IMAP clients. Besides that, there are also some parts for IMAP servers present. The most important lacking feature is support for encrypted connections π.
Mail::Box::Maildir
Maildir folders have a directory for each folder. A folder directory contains "tmp", "new", and "cur" sub-directories, each containing messages with a different purpose. Files with new messages are created in "tmp", then moved to "new" (ready to be accepted). Later, they're moved to the "cur" directory (accepted) β¨. Each message is one file with a name starting with a timestamp. The name also contains flags about the status of the message.
Maildir folders can not be used on Windows by reason of file-name limitations on that platform π«.
Mail::Box::Mbox
A folder type in which all related messages are stored in one file. This is a very common folder type for UNIX π.
Mail::Box::MH
This folder creates a directory for each folder, and a message is one file inside that directory. The message files are numbered sequentially in order of arrival. A special .mh_sequences file maintains flags about the messages π·οΈ.
Mail::Box::POP3(read/delete only)
POP3 is a protocol you can use to retrieve messages from a remote system. After the connection to a POP server is made, the messages can be looked at and removed as if they're right there on the local system π.
Mail::Box::Netzwert
The Netzwert folder type is optimized for mailbox handling on a cluster of systems with a shared NFS storage. The code is not released under GPL (yet) π.
Other folder types are on the (long) wishlist to get implemented. Please, help implementing more of them π«Ά.
π§ Folder class implementation
The class structure of folders is very close to that of messages. For instance, a Mail::Box::File::Message relates to a Mail::Box::File folder. The folder types are:
Mail::Box::Netzwert
Mail::Box::Mbox | Mail::Box::Maildir Mail::Box::POP3
| Mail::Box::Dbx | | Mail::Box::MH | Mail::Box::IMAP4
| | | | | | |
| | | | | | |
Mail::Box::File Mail::Box::Dir Mail::Box::Net
| | |
`--------------. | .---------------'
| | |
Mail::Box
|
|
Mail::Reporter (general base class)
By far most folder features are implemented in Mail::Box, so they're available to all folder types π
. Sometimes, features which appear in only some of the folder types get simulated for folders that miss them β like sub-folder support for MBOX β¨.
π¨ DIAGNOSTICS π¨
- Error: Folder $name is opened read-only
You can't write to this folder unless you've opened the folder to write or append with new(access), or the "force" option is set "true". Cast by write().
- Warning: changes not written to read-only folder $name.
You opened the folder read-only β which is the default set by new(access) β made modifications, and now want to close it π. Set close(force) if you want to overrule the access mode, or close the folder with close(write) set to "NEVER". Cast by close().
- Error: class $package does not implement method $method.
Fatal error: that specific $package (or one of its superclasses) doesn't implement this method where it should. This message means some other related classes do implement this method, however the class at hand does not π. Probably you should investigate this and probably inform the author of the package. Cast by notImplemented().
- Error: copying failed for one message.
For some reason β like disk full, removed by external process, or read-protection β it's impossible to copy one of the messages π. Copying will proceed for the other messages. Cast by copyTo().
- Error: destination folder $name is not writable.
The folder where the messages are copied to is not opened with write access (see new(access)). This has no relation with write permission to the folder which is controlled by your operating system. Cast by copyTo().
- Warning: different messages with id $msgid.
The message id is discovered more than once within the same folder, but the content of the message seems to be different π€¨. This should not be possible: each message must be unique. Cast by messageId().
- Warning: folder $name not deleted: not writable.
The folder must be opened with write access via new(access), otherwise removing it will be refused. So, you may have write-access according to the operating system, but that will not automatically mean that this "delete" method permits you to. The reverse remark is valid as well. Cast by delete().
- Error: invalid timespan '$span'.
The string does not follow the strict rules of the time span syntax which is permitted as parameter. Cast by timespan2seconds().
- Warning: message-id '$msgid' does not contain a domain.
According to the RFCs, message-ids need to contain a unique random part, then an "@", and then a domain name. This is made to avoid the creation of two messages with the same id. The warning emerges when the "@" is missing from the string. Cast by messageId().
- Error: no folder name specified.
You didn't specify the name of a folder to be opened π« . Use the new(folder) option or set the "MAIL" environment variable. Cast by new().
- Error: unknown value to folder->close(write => $when).
Cast by close().
- Error: you cannot add a message which is already part of a folder to a new one. Please use moveTo or copyTo.
Cast by addMessage().
π SEE ALSO π
This module is part of Mail-Box version 4.02, built on April 10, 2026. Website: http://perl.overmeer.net/CPAN/ πΈ
π LICENSE π
For contributors see file ChangeLog.
This software is copyright (c) 2001-2026 by Mark Overmeer.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself β¨.
π POD ERRORS π
Hey! The above document had some coding errors, which are explained below:
- Around line
316:
Unterminated C< ... > sequence π
and that's the whole page bestie, we ate and left no crumbs ππ
β this was perl v5.40.1, dated 2026-05-09, and it's the Mail::Box(3pm) manual, periodt ππ¦
π¦ where this page comes from
- ubuntu stonking: libmail-box-perl 4.02-1 Β· Artistic-1.0-Perl OR GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms