The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Mail::Box::Message - Manage one message within a mail-folder

SYNOPSIS

   use Mail::Box::Message;
   my $folder = new Mail::Box::Message 'text';

DESCRIPTION

Read Mail::Box::Manager first. This page also describes Mail::Box::Message::Runtime, Mail::Box:Message::Dummy, Mail::Box::Message::NotParsed, and Mail::Box::Message::NotReadHead.

Extending MIME::Entity

Because reading MIME::Entity objects from file is quite time-consuming and memory flooding, this module actively tries to keep most messages in the folder-files where they are stored in un-parsed form. When a folder is opened, only some of the header-lines of the messages are taken and stored in objects. Not before the message is seriously used, the message is translated into a real MIME::Entity.

Delayed loading of messages

When a folder is read, the lazy_extract option to Mail::Box::new determines if the content of the message is to be parsed immediately into a Mail::Box::Message or should be left unparsed in the folder-file. In the latter case, a Mail::Box::Message::NotParsed is created.

Not-parsing messages improves the speed of folder reading, and reduce the memory footprint of your program considerably. However, it keeps the folder-file locked, so should not be used on the incoming messages mail-box.

The parsing of a message (reading the non-parsed messages from the folder file and translating it into Mail::Box::Message type objects) is automatically triggered when you access head-lines which were not specified in take_headers when the folder was read, and when you call methods which are not defined on a non-parsed message.

Classes defined in the module

This page handles three types of messages:

  • Mail::Box::Message

    A read message. This is a full decendent of MIME::Entity, with all information on the message and its parts stored in easily and fast accessible memory-structures.

  • Mail::Box::Message::Dummy

    A message which is a place-holder in a thread description. Because folders are an (organized) mess of messages, the order in which thread-elements are discovered may vary.

    When a thread is discovered (a message contains In-Reply-To or References fields) this object-type is used to fill holes in the linked list of thread messages. See Mail::Box::Threads.

  • Mail::Box::Message::NotParsed

    This is a nearly ready-to-go message. When scanning a folder, the lazy_extract parameter of Mail::Box::new determines which messages are directly parsed into a Mail::Box::Message, and which are not yet parsed.

    Messages which are not directly parsed into a real message object are stored as an Mail::Box::Message::NotParsed object. This object only contains a few important header-lines, and triggers to load the message for real on the moment other information on this message is needed.

    Which header-lines are taken is determined by the take_headers parameter of Mail::Box::new. Taking too few headers for your task will result in bad performance: all message will be read. When you take too many headers, this is bad for the memory usage of your program.

Two more classes are defined, to help the message-types to function properly:

  • Mail::Box::Message::Runtime

    Data and functionality which is not stored in the folder, but is shared by various message-types (a real-message, a dummy, and a not-parsed)

  • Mail::Box::Message::NotReadHead

    A header which is part of a Mail::Box::Message::NotParsed. It contains some header-lines, but far from all. It can trigger the not-parsed to load itself from the folder.

State transition

Messages are detected when reading a folder, or composed by the user when it is sent by a used. The relationships shown below are only about reading existing folders.

                  unknown references
   Mail::Box      and reply-to's        Mail::Box
   finds message ---------------------> ::Message::Dummy
         |                                    |
         v messages                           |
         |                                    |
         |                                    |
        / \was dummy                          |
        |  '--------------> merge <-----------'
        |                     |
        |                     |
        \new                  |
         `-->-----,  ,-----<--'
                  lazy
                extract?
             yes/       \no
               /         `--->-------,
              v                       |
        Mail::Box                     |
        ::Message::NotParsed          |
              |                       v
              |serious use         Mail::Box
               `---------------->-- ::Message

Class structure for messages

As example, the next scheme uses Mbox, however there are more parallel extentions of Mail::Box::Message (in the future).

     ::Message::Mbox             ::Message::Mbox
          |     \                  / ::NotParsed
          |      \                /         |
          |       \              /          |
          |        ::Message::Mbox          |
          |              ::Runtime          |
          |                                 |
          v                                 v
     ::Message         ::Dummy          ::Message
          |   \           |           ::NotParsed
          |    \          |           /
          |     \         v          /
          |       ::Message::Runtime
          v
     MIME::Entity
          |
          |
          v
     Mail::Internet

The rest of this manual-page describes the functionalities of each single class.

Mail::Box::Message::Runtime

This class is a base for all kinds of messages. It defines the simularities between messages in various stages: dummy, not-parsed, or parsed.

new ARGS

Initialize the runtime variables of a message. The following options are supported:

  • folder => FOLDER

    (obligatory) The folder where this message appeared in. The argument is an instance of (a sub-class of) a Mail::Box.

  • size => INTEGER

    The size of the message inclusive headers and accompanying lines (such as the `From' line in mboxes) in bytes.

  • messageID => STRING

    The id on which this message can be recognized. If none specified, there will be one assigned to the message to be able to pass unique message-ids between objects.

  • modified => BOOL

    Whether there are some modifications to the message from the start-on. For instance, new message will be flagged modified immediately.

  • deleted => BOOL

    Is the file deleted from the start?

  • labels => [ STRING => VALUE, ... ]

    Set the specified labels to their accompanying value. In most cases, this value will only be used as boolean, but it might be more complex.

messageID

Retreive the message's id. Every message has a unique message-id. This id is used mainly for recognizing discussion threads.

folder [FOLDER]

In with folder did we detect this message/dummy? This is a reference to the folder-object.

size

Returns the size of the message, including headers.

Example: print $folder->message(8)->size;

isParsed

isParsed checks whether the message is parsed (read from file). Returns true or false. Only a Mail::Box::Message will return true.

isDummy

isDummy Checks whether the message is only found in a thread, but not (yet) in the folder. Only a Mail::Box::Message::Dummy will return true.

modified [BOOL]

Check (or set, when an argument is supplied) that the message-contents has been changed by the program. This is used later, when the messages are written back to file.

Examples: if($message->modified) {...} $message->modified(1);

delete

Flag the message to be deleted. The real deletion only takes place on a synchronization of the folder.

iExamples: $message->delete; delete $message;

deleted [BOOL]

Check or set the deleted flag for this message. This method returns undef (not deleted, false) or the time of deletion (true). With a BOOL argument, the status is changed first.

Examples: if($message->deleted) {...} $message->deleted(0); # undelete

Label management

Labels are used to store knowledge about handling of the message within the folder. Flags about whether a message was read, replied to, or (in some cases) scheduled for deletion.

setLabel LIST
label STRING [ ,STRING ,...]

Get the value related to the label(s). This returns a list of values, which may be empty, undefined, or a value which evaluates to TRUE.

Example: if($message->label('seen')) {...} my ($seen, $current) = $msg->label('seen', 'current');

labels

Returns all known labels. In SCALAR context, it returns the knowledge as reference to a hash. This is a reference to the original data, but you shall *not* change that data directly: call setLabel() for changes!

In LIST context, you get a list of names which are defined. Be warned that they will not all evaluate to true, although most of them will.

Thread management

references
in-reply-to

Retreive what the message says is the discussion thread it is in. This is an ordered list of message-ids.

Examples: my @ref_ids = $message->references; my $nesting = $message->references; my @replies = $message->in_reply_to;

shortString

Convert the message header to a short string, representing the most important facts (for debugging purposes only).

Mail::Box::Message

This object extends a MIME::Entity. This is the only object type defined in this set of packages which represents a real message (which parsed into memory, with access to all headers, and writeable)

PUBLIC INTERFACE

new LINES [, OPTIONS]

Create a new message. LINES is a reference to an array of lines which describe the message, or a file-handle. The options is a list of labeled values. Extentions (sub-classes) of an Mail::Box::Message may define more options, but these are general:

body

Call body to get the whole message-body in storable message format (multiparts are folded back into one message). This may be useful to print the whole message. If you want to access the message content, you should used "bodyhandle" or "parts" which are described below.

bodyhandle

Some messages are multi-parts, other are plain. Check which type of message you have (with is_multipart as described in MIME::Entity) before accessing its content.

If the parsing of the folder was delayed (controlled by the `lazy_extract' option at folder-creation) this implies that the message-body is read from folder and parsed. This is performed automatically.

If you do not have a multipart message, you call bodyhandle, which returns a handle to a handle-type object which can be opened to read the body of the message.

Example: sub print_body($) { my ($self,$message) = @_; if($message->is_multipart) { $self->print_body($_) foreach $message->parts } else { $message->bodyhandle->print } }

parts [ARRAY]

Some messages are multi-parts, other are plain. Check which type of message you have (with is_multipart as described in MIME::Entity) before accessing its content.

If the parsing of the folder was delayed (controlled by the `lazy_extract' option at folder-creation) this implies that the message-body is read from folder and parsed. This is performed automatically.

When you do have a multipart message, you have to call parts to get a list of Mail::Box::Message objects, each representing one part of the message. Be warned that even such part can be split in nested parts. You can pass an array-reference with messages to set a new list of parts.

See "body" and "bodyhandle" above.

removePart PART|INDEX

Remove one part of the list of parts from this message. Specify a PART (Mail::Box::Message instance) or an index (sequence-number is list of parts)

isPart

A part of a message is of the same type as the message itself. This call can distinguish between the two.

Mail::Box::Message::Dummy

A dummy message is a placeholder, for instance for messages in threads which are found in a reference-list, but not (yet) in the folder.

new MESSAGE-ID

(Class method) Create a new dummy message.

Examples: my $message = Mail::Box::Message::Dummy->new($id); if($message->isDummy) {...}

Mail::Box::Message::NotParsed

A message where most of the data still resides in the folder-file, is a 'message which is not read' (yet). This status is signalled by the type of the object.

PUBLIC INTERFACE

new ARGS

Create a not parsed message. The message can have a not-read-head, which means that only a few of the header-lines are kept, or a real MIME::Head to start with.

AUTOLOAD

When any method is called which can not be performed without parsing the whole folder message, Perl calls the autoloader. A check is performed to see if not anyone has accidentally an old message-handle: we may otherwise read the message twice.

This function returns a Mail::Box::Message::NotReadHead object on which you may perform any actions as expected from MIME::Header object. Performing actions on a header which is part of a non-parsed message is complicated by the fact that we have some header-lines, but not all.

See "Mail::Box::Message::NotReadHead" below.

Mail::Box::Message::NotReadHead

This object contains a few header-lines which were captured during the initial reading of the folder. It will also automagically load the messages on any other call to head.

PUBLIC INTERFACE

See MIME::Header for all methods you can perform on this object. The following methods need extra consideration.

new ARGS

(Class method) Called by the Mail::Box::Message::NotParsed method new to store some header-lines which were capurtured during the initial reading through the folder file. See the take_headers option on how to add header-lines to this structure.

The following parameters can be passed to new:

  • expect FIELDNAMES

    (obligatory) Which fields will be taken from the folder-file. Even when we do not actually find all these fields, we still have to know when the message lacks the field, to avoid that the message is read from the folder to find-out that the line isn't there either.

get TAG [, INDEX]
get_all TAG
count TAG

Overruled methods, which first check if we know about the header. In case we do, the values can be returned immediately. Otherwise we trigger the message to be read from the folder-file and then retry on the real Mail::Box::Message object.

setField NAME, CONTENT

Store a content into a field. Do only specify fields which are defined with expect when this object was instantiated.

You may add the same name more than once. In that case, the get method in list context (and the get_all method) will return all definition in the order that they were added. For the Received: field in messages, that means that the most recently added line to the message is put first in the list (they are in reverse order in the mime-header.

message [MESSAGE]

Get (or check) the message to which this header belongs.

AUTOLOAD

Load the message, then capture the real header, and finally calls the method on the what should have been called in the first place.

Example: $message->head->get('some-field')

where $message is a Mail::Box::Message::NotParsed, will return a Mail::Box::Message::NotReadHead from its head call. A get of a previously captured header returns the right value, however in case not, the following steps are taken:

  • get calls AUTOLOAD to get a new header.

  • NotReadHead::AUTOLOAD will call NotParsed::AUTOLOAD to read the message.

  • NotParsed::AUTOLOAD reads the message from the folder and returns it to the header autoloader.

  • The header autoloader calls the real get method of the Mail::Box::Message, as autoloaders should do.

AUTHOR

Mark Overmeer (Mark@Overmeer.net). All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

VERSION

This code is alpha, version 0.3

3 POD Errors

The following errors were encountered while parsing the POD:

Around line 817:

'=item' outside of any '=over'

Around line 907:

You forgot a '=back' before '=head1'

Around line 1094:

You forgot a '=back' before '=head1'