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

   # Usually these message objects are created indirectly
   use Mail::Box::Message;
   my $folder = new Mail::Box::Mbox folder => ...;
   my $msg    = $folder->message(1);
   $msg->delete;
   $msg->size;   # and much more

DESCRIPTION

These pages do only describe methods which relate to folders. If you access the knowledge of a message, then read MIME::Entity.

Read Mail::Box::Manager first. This page also describes Mail::Box::Message::Parsed, Mail::Box::Message::NotParsed, Mail::Box::Message::NotReadHead, and Mail::Box:Message::Dummy. These objects are used as base-classes for messages which are not totally read, are fully read, or to be written to some kind of folder.

During its life, a message will pass through certain stages. These stages were introduced to reduce the access-time to the folder. Changing from stage, the message changes from object-class (try to do this in any other language than Perl!).

Classes described in this manual-page

This page handles three types of messages:

  • Mail::Box::Message::Parsed

    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::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.

  • Mail::Box::Message::Dummy

    A message which is a place-holder in a thread description. This object-type is used to fill holes in the linked list of thread messages. See Mail::Box::Threads.

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

  • Mail::Box::Message

    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.

The bottom of this page provides more details about the implementation, but first the use.

CLASS Mail::Box::Message

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

Retrieve 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::Parsed 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.

headIsRead

Checks if the head of the message is read. This is true for fully parsed messages and messages where the header was accessed once.

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.

Examples:

   $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
seqnr [INTEGER]

Get (add set) the number of this message is the current folder.

timestamp

Returns an indication on the moment that the message was originally sent. The exact value which is returned is operating-system dependent, but on UNIX systems will be in seconds since 1 January 1970.

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

The LIST is a set of scalars forming key,value pairs.

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.

shortString

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

diskDelete

Remove a message from disk. This is not from the folder, but everything else, like parts of the message which are stored externally from the folder.

CLASS Mail::Box::Message::Parsed

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)

METHODS

new [OPTIONS]

Create a new message. The options available are taken from Mail::Box::Message::new(), as described above.

If you want to add a message to a folder, which is derived from some strange source, then you do:

     use MIME::Parser;
     my $parser = MIME::Parser->new;
     my $entity = $parser->parse(@lines);
     $folder->addMessage($entity);

The addMessage() accepts anything what is based on Mail::Internet.

coerce FOLDER, MESSAGE [,OPTIONS]

(Class method) Coerce a MESSAGE into a Mail::Box::Message::Parsed. This method is automatically called if you add a strange message-type to a FOLDER. You usually do not need to call this yourself.

The coerced message is returned on success, else undef.

Example:

   my $folder = Mail::Box::Mbox->new;
   my $entity = MIME::Entity->new(...);
   Mail::Box::MBox::Message::Parsed->coerce($inbox, $entity);
   # now $entity is a Mail::Box::Mbox::Message::Parsed

It better to use

   $folder->coerce($entity);

which does exacty the same, by calling coerce in the right package.

copyTo FOLDER

Move the message to the indicated opened FOLDER. The message is removed from the folder where it resides in (flagged to be deleted). The content of the message is copied.

Example:

   my $draft = $mgr->open(folder => 'Draft');
   $message->copyTo($draft);
forceLoad

This method is called to signal that the message must be loaded, although the method only returns the folder.

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::Parsed 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::Parsed 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::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.

METHODS

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.

CLASS 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

(Class method) Create a new dummy message.

Examples:

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

CLASS 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.

METHODS

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::Parsed 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::Parsed, as autoloaders should do.

IMPLEMENTATION

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::Parsed 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::Parsed 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.

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::Parsed

Class structure for messages

As example, the next scheme uses the fake folder-type XYZ, which may be for instance mbox or MH.

       ::XYZ::Parsed              ::XYZ::NotParsed
          |      \                /         |
          |       \              /          |
          ^        ::XYZ::Message           ^
          |                                 |
          |                                 |
     ::Message    ::Message::Dummy    ::Message
      ::Parsed            |          ::NotParsed
          |    ^          ^           ^
          ^     \         |          /
          |      `--- ::Message ----'
          |
     MIME::Entity
          |
          ^
          |
     Mail::Internet

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 beta, version 1.318