This is Info file pm.info, produced by Makeinfo version 1.68 from the
input file bigpm.texi.


File: pm.info,  Node: Mail/Box/Message,  Next: Mail/Box/Thread,  Prev: Mail/Box/Mbox/Message,  Up: Module List

Manage one message within a mail-folder
***************************************

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 *Note Mail/Box/Manager: 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 *Note Mail/Box/Threads: 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|' in this node, 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
     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::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
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.

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 `' in this node or `' in this node 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 `' in this node and `' in this node 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.

head
     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 `' in this node 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' in this node 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         ^
        |               |
        ^           ::Thread
        |
          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.100


File: pm.info,  Node: Mail/Box/Thread,  Next: Mail/Box/Threads,  Prev: Mail/Box/Message,  Up: Module List

one discussion thread in a folder
*********************************

NAME
====

   Mail::Box::Thread - one discussion thread in a folder

SYNOPSIS
========

     my $thread = $message->thread;
     $thread->printThread;

DESCRIPTION
===========

   Read *Note Mail/Box/Manager: Mail/Box/Manager, and *Note
Mail/Box/Threads: Mail/Box/Threads, first.

   A (message-)thread is a message, with the messages which followed in
reply on that message.  And the messages with replied the messages which
replied the original message.  And so on.  Some threads are only one
message (never replied to), some threads are very long.

Mail::Box::Thread
=================

new ARGS
     The instatiation of a thread is done by its subclasses.  You will not
     call this method by yourself (it is even not implemented).

     In the current implementation, there are no options added to the
     `Mail::Box::Message''s object creation.

thread
     Returns the first message in the thread where this message is part
     of.  This may be this message itself.  This also may return any other
     message in the folder.  Even a dummy message can be returned, when the
     first message in the thread was not stored in the folder.

     Example:

          my $start = $folder->message(42)->thread;

inThread
     Include the message in a thread.  If the message was not known to the
     thread-administration yet, it will be added to those structures.

repliedTo
     Returns the message where this one is a reply to.  In SCALAR context,
     this will return the MESSAGE which was replied to by this one.  This
     message object may be a dummy message. In case the message seems to
     be the first message of a thread, the value undef is returned.

     In LIST context, this method also returns how sure these are messages
     are related.  When extended thread discovery in enabled, then some
     magic is applied to relate messages.  In LIST context, the first
     returned argment is a MESSAGE, and the second a STRING constant.
     Values for the STRING may be:

        * 'REPLY'

          This relation was directly derived from an `in-reply-to' message
          header field.  The relation is very sure.

        * 'REFERENCE'

          This relation is based on information found in a `Reference'
          message header field.  One message may reference a list of
          messages which precede it in the thread.  Let's hope they are
          stored in the right order.

        * 'GUESS'

          The relation is a big guess, of undetermined type.

     More constants may be added later.

     Examples:

          my $question = $answer->repliedTo;
          my ($question, $quality) = $answer->repliedTo;
          if($question && $quality eq 'REPLY') { ... };

follows MESSAGE|MESSAGE-ID, STRING
     Register that the specified MESSAGE (or MESSAGE-ID) is a reply on this
     message, where the quality of the relation is specified by the
     constant STRING.

     The relation may be specified more than once, but there can be only
     one.  Once a reply (STRING equals REPLY) is detected, that value will
     be kept.

followedBy [MESSAGE-ID|MESSAGE, ...]
     Register that the MESSAGEs (or MESSAGE-IDs) are follow-ups to this
     message.  There may be more than one of these follow-ups which are
     not related to each-other in any other way than sharing the same
     parent.

     If the same relation is defined more than ones, this will not cause
     duplication of information.

followUps
     Returns the list of follow-ups to this message.  This list contains
     parsed, not-parsed, and dummy messages.  followUps returns
     MESSAGE-objects, while `followUpIDs' returns the IDs only.

threadFilled [BOOL]
     Returns (after setting) a flag whether the thread (where this message
     is the start of) is fully processed in finding holes.  If this is set
     on TRUE, than any dummies still in this thread could not be found
     within the limits of `thread_window' and `thread_timespan'.

Actions on whole threads
------------------------

   Some conveniance methods are added to threads, to simplify retreiving
knowledge from it.

recurseThread CODE-REF
     Execute a function for all sub-threads.  If the subroutine returns
     true, sub-threads are visited, too.  Otherwise, this branch is
     aborted.

totalSize
     Sum the size of all the messages in the thread.

nrMessages
     Number of messages in this thread.

ids
     Collect all the ids in this thread.

     Examples:

          $newfolder->addMessages($folder->ids($thread->ids));
          $folder->delete($thread->ids);

folded [BOOL]
     Returns whether this (part of the) folder has to be shown folded or
     not.  This is simply done by a label, which means that most
     folder-types can store this.

threadToString
     Translate a thread into a string.  The string will contain at least
     one line for each message which was found, but tries to fold dummies.
     This is useful for debugging, but most message-readers will prefer to
     implement their own thread printer.

     Example:

          print $message->threadToString;

     may result in

          Subject of this message
          |- Re: Subject of this message
          |-*- Re: Re: Subject of this message
          | |- Re(2) Subject of this message
          | |- [3] Re(2) Subject of this message
          | `- Re: Subject of this message (reply)
          `- Re: Subject of this message

     The `*' represents a lacking message.  The `[3]' presents a folded
     thread with three messages.

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


File: pm.info,  Node: Mail/Box/Threads,  Next: Mail/Box/Tie,  Prev: Mail/Box/Thread,  Up: Module List

maintain threads within a folder
********************************

NAME
====

   Mail::Box::Threads - maintain threads within a folder

SYNOPSIS
========

     my Mail::Box $folder = ...;
     foreach my $thread ($folder->threads)
     {   $thread->printThread;
     }

DESCRIPTION
===========

   Read *Note Mail/Box/Manager: Mail/Box/Manager, and *Note Mail/Box:
Mail/Box, first.  It might be helpfull to study *Note Mail/Box/Thread:
Mail/Box/Thread, too.

   A (message-)thread is a message, with the messages which followed in
reply on that message.  And the messages with replied the messages which
replied the original message.  And so on.  Some threads are only one
message (never replied to), some threads are very long.

METHODS
=======

new ARGS
     `Mail::Box::Threads' is sub-classed always by `Mail::Box'.  This
     object is not meant to be instantiated itself: do not call new on it
     (you'll see it even fails because there is no new()!).

     The construction of thread administration accepts the following
     options:

        * dummy_type => CLASS

          Of which class are dummy messages?  Usually, this is the basic
          dummy type, `Mail::Box::Message::Dummy' in this node, unless you
          intend to extend the object.

        * thread_window => INTEGER|'ALL'

          The thread-window describes how many messages should be checked
          at maximum to fill `holes' in threads for folder which use
          delay-loading of message headers.  The default value is 10.

          The constant 'ALL' will cause thread-detection not to stop trying
          to fill holes, but continue looking until the first message of
          the folder is reached.  Gives the best quality results, but may
          perform bad.

        * thread_timespan => TIME|'EVER'

          Specify how fast threads usually work: the amount of time
          between an answer and a reply.  This is used in combination with
          the `thread_window' option to determine when to give-up filling
          the holes in threads.

          TIME is a string, which starts with a float, and then one of the
          words 'hour', 'hours', 'day', 'days', 'week', or 'weeks'.  For
          instance:

               thread_timespan => '1 hour'
               thread_timespan => '4 weeks'

          The default is '3 days'.  TIME may also be the string 'EVER',
          which will effectively remove this limit.

        * thread_body => BOOL

          May thread-detection be based on the content of a message?  This
          has a serious performance implication when there are many
          messages without `In-Reply-To' and References headers in the
          folder, because it will cause many messages to be parsed.

          NOT USED YET.  Defaults to FALSE.

toBeThreaded MESSAGE [, ...]
toBeUnthreaded MESSAGE-ID [, ...]
     Register a message to be put in (withdrawn from) a thread when the
     user is asking for threads.  If no-one ever asks for threads, then no
     work is done on them.

createDummy MESSAGE-ID
     Create a dummy message for this folder.  The dummy is a place-holder
     in a thread description to represent a message which is not found in
     the folder (yet).

processDelayedThreading
     Parse all messages which where detected in the folder, but were not
     processed into a thread yet.

thread MESSAGE
     Based on a message, and facts from previously detected threads, try
     to build solid knowledge about the thread where this message is in.

inThread MESSAGE
     Collect the thread-information of one message.  The `In-Reply-To' and
     `Reference' header-fields are processed.  If this method is called on
     a message whose header was not read yet (as usual for MH-folders, for
     instance) the reading of that header will be triggered here.

     Examples:

          $folder->inThread($message);
          $message->inThread;    #same

outThread MESSAGE-ID
     Remove the message, which is represented by its message-id, from the
     thread-infrastructure.  A message is replaced by a dummy, if it has
     follow-ups.

registerThread MESSAGE|MESSAGE-ID
     Register the message as start of a thread.

threads
     Returns all messages which start a thread.  The list may contain dummy
     messages, and messages which are scheduled for deletion.

     To be able to return all threads, thread construction on each message
     is performed first, which may be slow for some folder-types because
     is will enforce parsing of message-bodies.

knownThreads
     Returns the list of all messages which are known to be the start of a
     thread.  Threads containing messages which where not read from their
     folder (like often happends MH-folder messages) are not yet known, and
     hence will not be returned.

     The list may contain dummy messages, and messages which are scheduled
     for deletion.  Threads are detected based on explicitly calling
     `inThread()' and `thread()' with a messages from the folder.

     Be warned that, each time a message's header is read from the folder,
     the return of the method can change.

IMPLEMENTATION
==============

   This module implements thread-detection on a folder.  Messages created
by the better mailers will include `In-Reply-To' and References lines,
which are used to figure out how messages are related.  If you prefer a
better thread detection, then you can ask for it, but there may be a
serious performance hit (depends on the type of folder used).

Delayed thread detection
------------------------

   With threads you get the start-messages of each thread of this folder.
When that message was not found in the folder (not saved or already
removed), you get a message of the dummy-type.  These thread descriptions
are in perfect state: all messages of the folder are included somewhere,
and each missing message of the threads (`holes') are filled by dummies.

   However, to be able to detect all threads it is required to have the
headers of all messages, which is very slow for some types of folders,
especially MH and IMAP folders.

   For interactive mail-readers, it is prefered to detect threads only on
messages which are in the viewport of the user.  This may be sloppy in
some situations, but everything is preferable over reading an MH mailbox
with 10k e-mails to read only the see most recent messages.

   In this object, we take special care not to cause unnecessary parsing
(loading) of messages.  Threads will only be detected on command, and by
default only the message headers are used.

   The user of the folder signals that a message has to be included in a
thread within the thread-list, by calling

     $folder->inThread($message);   #or
     $message->inThread;

   This only takes the information from this message, and stores this in a
thread-structure.  You can also directly ask for the thread where the
message is in:

     my $thread = $message->thread;

   When the message was not put in a thread, it is done now.  But, more
work is done to return the best thread.  Based on various parameters,
which where specified when the folder was created, the method walks
through the folder to fill the holes which are in this thread.

   Walking from back to front (latest messages are usually in the back of
the folder), message after message are triggered to be indexed in their
thread.  At a certain moment, the whole thread of the requested method is
found, a certain maximum number of messages was tried, but that didn't
help (search window bound reached), or the messages within the folder are
getting too old.  Then the search to complete the thread will end,
although more messages of the could be in the folder.

   Finally, for each message where the head is known, for instance for all
messages in mbox-folders, the correct thread is determined immediately.
Also, all messages where the head get loaded later, are automatically
included.

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


File: pm.info,  Node: Mail/Box/Tie,  Next: Mail/Bulkmail,  Prev: Mail/Box/Threads,  Up: Module List

Acces an existing message-folder as array
*****************************************

NAME
====

   Mail::Box::Tie - Acces an existing message-folder as array

SYNOPSIS
========

     tie my @inbox, 'Mail::Box::File', file => $ENV{MAIL};
     tie my @inbox, $folder;

     foreach (@inbox) {print $_->short}
     print $inbox[3];
     push @inbox, Mail::Box::Message->new(...);
     my $folder = tied @inbox;

DESCRIPTION
===========

   Read *Note Mail/Box/Manager: Mail/Box/Manager, first.  Folders
certainly look as arrays, so why not just access them as one?  Each folder
is a sub-class of this class.

METHODS
=======

   Not all operations on arrays are supported.  Actually, most functions
which would reduce the size of the array are modified to signal messages as
ready for removal.

   Examples of what you can do:

     tie my @inbox, 'Mail::Box::File', ...;
     my $message = new Mail::Box::Message(...);

     push @inbox, $message;
     delete $inbox[2];         # becomes undef
     $inbox[3]   = $message;
     print $inbox[0]->status;
     my $emails  = @inbox;
     untie @inbox;             # calls write()

     # Direct access to the Mail::Box object.
     my $folder = tied @inbox;
     $folder->synchonize;

   Examples what you *cannot* do:

     shift/unshift/pop/splice @inbox;

tie ARRAY, FOLDERTYPE, PARAMS
tie ARRAY, FOLDERTYPE, FOLDER
     There are to ways to construct a tie.  In the first case, you start
     with a tie, and may later ask for the tied folder structure.  In the
     second version, you have first created a folder, and then put a tie
     around it.

     The first version: tie an ARRAY to a folder of type FOLDERTYPE, where
     the constructor of the folder requires some parameters.  Possible
     PARAMS are the parameters of the new constructor of the specified
     folder-type.

     Example:

          tie my(@inbox), 'Mail::Box::File', folder => $ENV{MAIL};
          my $inbox = tied @inbox;

     The second version: tie an ARRAY interface around an existing FOLDER.
     The type as specified with FOLDERTYPE is only used to find the
     correct TIEARRAY method, usually the result of `ref FOLDER'.

     Example:

          my $inbox = Mail::Box::File->new(folder => $ENV{MAIL});
          tie my(@inbox), ref $inbox, $inbox;

IMPLEMENTED METHODS
===================

   This module implements TIEARRAY, FETCH, STORE, FETCHSIZE, DELETE, PUSH,
and DESTROY.

   This module does not implement all other methods as described in the
*Note Tie/Array: Tie/Array, manual-page.

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


File: pm.info,  Node: Mail/Bulkmail,  Next: Mail/Cap,  Prev: Mail/Box/Tie,  Up: Module List

Platform independent mailing list module
****************************************

NAME
====

   Mail::Bulkmail 2.05 - Platform independent mailing list module

AUTHOR
======

   Jim Thomason thomasoniii@yahoo.com

SYNOPSIS
========

     $bulk = Mail::Bulkmail->new(
               "LIST" => "/home/jim3.list",
               "From" => 'thomasoniii@yahoo.com',
            "Subject" => 'This is a test message!',
            "Message" => "Here is the text of my message!"
     );

     $bulk->bulkmail;

   Be sure to set your default variables in the module, or set them in
each bulk mail object.  Otherwise, you'll be using the defaults.

DESCRIPTION
===========

   Mail::Bulkmail gives a fairly complete set of tools for managing
mass-mailing lists.  I wrote it because our existing tools were just too
damn slow for mailing out to thousands of recipients.

   2.00 is a major major *major* upgrade to the previous version (1.11).
I literally threw out all of the code from 1.00 and started over.  Well,
almost all of it, I'm really content with the email validation, so I kept
that.  :)

   Everything else is brand spanking new.  All of the bugs from the 1.x
releases should be gone (ever try allowing duplicates?  Good.).  And, of
course, a bunch of new toys have been added in.

   The two major additions to v2 are the ability to send via envelope and
support for dynamic messaging.  Sending via the envelope allows you to
potentially transfer your email *much* faster (I've been estimating a 900%
speed increase vs. non-envelope sending in 1.11).  Dynamic messaging
allows you to actually construct the message that you're sending out on
the fly.  Specify which components of a message you want to include, and
Bulkmail will generate the message for you.  So every person on your list
could potentially receive a different message, if you wanted.

   Dynamic messaging is a few steps above a simple mail merge.  While you
could accomplish the same effect using a simple mail merge it wouldn't be
pretty.  You'd have to duplicate each component of the message for each
person on the list.

   Further changes are listed in the version history and FAQ sections
below, I just wanted to mention the big guns up front.

REQUIRES
========

   Perl5.004, Socket

OBJECT METHODS
==============

CREATION
--------

   New Mail::Bulkmail objects are created with the new() constructor.  For
a minimalist creation, do this:

   $bulk = Mail::Bulkmail->new();

   You can also initialize values at creation time, such as:

     $bulk = Mail::Bulkmail->new(
                From => 'thomasoniii@yahoo.com',
                Smtp => 'some.smtp.com'
     );

   When Bulkmail objects are destroyed, they automatically disconnect from
the server they're connected to if they're still connected.

add_attr
     Mail::Bulkmail is much easier to subclass now (I hope).  I like using
     arrays for my objects instead of hashes.  Perhaps one day I'll switch
     to pseudo-hashes, but not yet.

     Until that time, you need to allocate new space in the array for your
     new attributes if you want to subclass the thing.  But how do you do
     that nicely?  Push onto the blessed arrayref?  Too messy, and you
     can't do the nice trick of setting up a variable with the value of
     the place in the array.  Besides, if I do switch away from arrays
     this'll break.  So use add_attr to tack it onto the end of the object.

     package Mail::Bulkmail::My_version;

     @ISA = qw(Mail::Bulkmail);

          my $new_attribute = Mail::Bulkmail->add_attr();

          $my_bulk_object->[$new_attribute] = "my value";

BUILT IN ACCESSORS
------------------

   Okay, here's where the fun stuff beings.  Since these are objects, the
important stuff is how you access your data.

   Object methods work as you probably expect.

   $bulk->property

     Will return the value of "property" in $bulk

   $bulk->property("new value")

   Will set the value of "property" in $bulk to "new value" and return
"new value".  The property may not be set if $bulk->Trusting has a false
value and the property has a validation check on it.  See Validated
Accessors, below.

   All accessor methods are case sensitive.  Be careful!

   Here are all of the accessors that come built in to your Mail::Bulkmail
objects.

From
     The e-mail address this list is coming from.  This can be either a
     simple e-mail address (thomasoniii@yahoo.com), or a name + e-mail
     address ("Jim Thomason"<thomasoniii@yahoo.com>).  This is checked to
     make sure it's a valid email address unless you have Trusting turned
     on (see below).

     *v1.x equivalent*:  From

Subject
     The subject of the e-mail message.  If it's not set, you'll use the
     default.

     *v1.x equivalent*:  Subject

Message
     This is the actual text that will appear in the message body.  You
     can also specify control fields to allow your message to be
     dynamically individually built on the fly, as well as do a mail merge
     to personalize your email to each recipient

     *v1.x equivalent*:  Message

merge
     This is where you define a mail merge for your message.  See the
     section MERGING below.

     A merge is defined with a hashref as follows:

          $bulk->merge(
             "Date"    => "June 22nd",
             "Company" => "Foofram Industries"
          );

     *v1.x equivalent*:  Map

Smtp
     This sets the SMTP server that you're going to connect to.  You'll
     probably just want to use whatever you've set as your default SMTP
     server in the module.  You did set your default SMTP server when you
     double-checked all the other defaults, right?

     *v1.x equivalent*:  Smtp

Port
     This sets the port on which to connect to your SMTP server.  You'll
     probably just want to use 25 (the default).

     *v1.x equivalent*:  Port

Tries
     This sets the number of times that you will attempt to connect to a
     server.  You'll probably just want to use the default.

     *v1.x equivalent*:  Tries

Precedence
     This sets the precedence of the e-mail message.  This is validated
     unless you turn off validation by making your object Trusting.
     Default precedence is "list", although you can set a precedence of
     either "bulk" (bulk, usually unsolicited mail) or "junk" (totally
     worthless message)

     *v1.x equivalent*:  Precedence

Domain
     You're going to be saying HELO to an SMTP server, you'd better be
     willing to give it a domain as well.  You can explicitly set the
     Domain here, or choose not to.  If no Domain is set, the domain of
     the From e-mail address will be used instead.  It doesn't do you any
     good to set Domain after you've connected to a server.

     *v1.x equivalent*:  Domain

HTML
     People can be dopes.  It's very very easy to send out mass HTML email
     with Mail::Bulkmail, just set a content-type:

          $bulk->header("Content-type", "text/html");

     But most people don't seem to know that, so I've added the HTML
     accessor.  Give it true value to send out HTML mail, a false value to
     send out plaintext.  It's false by default.

use_envelope
     use_envelope is like lasing a stick of dynamite.  Mail::Bulkmail is
     fast.  Mail::Bulkmail with use_envelope is ungodly incredibly
     unbelievably fast.

     For the uninformed, an email message contains two parts, the message
     itself and the envelope.   Mail servers only care about the envelope
     (for the most part), since that's where they find out who the message
     is to and from, and they don't really need to know anything else.

     A nifty feature of the envelope is that you can submit multiple
     addresses within the envelope, and then your mail server will
     automagically send along the message to everyone contained within the
     envelope.  You end up sending a hell of a lot less data across your
     connection, your SMTP server has less work to do, and everything ends
     up working out wonderfully.

     There are two catches.  First of all, with envelope sending turned
     off, the recipient will have their own email address in the "To"
     field (To: thomasoniii@yahoo.com, fer instance).  With the envelope
     on, the recipient will only receive a generic email address ("To:
     list@myserver.com", fer instance)  Most people don't care since that's
     how most email lists work, but you should be aware of it.

     Secondly, you *MUST* and I mean *MUST* sort your list by domain.
     Envelopes can only be bundled up by domain, so that we send all email
     to a domain in one burst, all of the email to another domain in the
     next burst, and so on.  So you need to have all of your domains
     clustered together in your list.  If you don't, your list will still
     go out, but it will be a *lot* slower, since Mail::Bulkmail has a
     fair amount more processing to do when you send with then envelope.
     This is normally more than offset by the gains received from sending
     fewer messages.  But with an unsorted list, you never see the big
     gains and you see a major slow down.  Sort your lists.

envelope_limit
     It's entirely likely that with a very large list you'll have a very
     large number of people in the same domain.  For instance, there are
     an awful lot of people that have yahoo addresses.  So, for example,
     say that you have a list of 100,000 people and 20,000 of them are in
     the foobar.com domain and you're sending using the envelope.  That
     means that the server at foobar.com is going to receive one message
     with 20,000 people in the envelope!

     Now, this might be a bad thing.  We don't know if the foobar.com mail
     server will actually process a message with 20,000 envelope
     recipients.  It may or may not and the only way to find out is to try
     it.  If it does work, then great no worries, but if it doesn't, then
     you're stuck.  If you stop using envelope sending, you sacrifice its
     major speed gains, but if you keep using it you can't send to
     foobar.com.

     envelope_limit fixes that.

     envelope_limit is precisely what it sounds like, it allows you to
     specify a limit on the number of recipients that will be specified in
     your envelope.  That way, with our previous example, you can specify
     an envelope limit of 1000, for example.

          $bulk->envelope_limit(1000);

     This means that foobar.com will get 20 messages, each with 1000
     recipients in the envelope.  Of course, this still may not be small
     enough, so you can tweak it as much as necessary.

     Setting an envelope limit does trade off some of the gains from using
     the envelope, but it's still over all a vast speed boost over not
     using it.

     envelope_limit is set to 0 (zero) by default, meaning that there is
     no limit specified.

LIST
     IO is a lot smarter in v2.0.  In Bulkmail 1.x, the various IO methods
     (LIST, BAD, etc.) had to be globs to file handles, which was rather
     restrictive.

     In 2.0, you have four options for how you want to import your list, a
     string, or a reference to either an array, a glob, or a function.

     If you have a flat text file, you can use it by simply passing a
     string containing the path to the file:

          $bulk->LIST("/home/jim3/list.txt");

     And Bulkmail will open the file and manage it internally, so you
     don't need to worry about polluting your namespace with filehandles
     the way you did with 1.x.

     Of course, if you *want* to pollute your namespace, then feel free to.

          open (LIST, "/home/jim3/list.txt");
          $bulk->LIST(\*LIST);

     Just note that you now have to pass a reference to the glob, not the
     glob itself as you did in 1.x.

     Flat file lists will read in one entry per line, where a line is
     determined by whatever value you've set with lineterm().

     Alternatively, you can pass a ref to an array for your list.

     my @list = ('thomasoniii@yahoo.com', 'someguy@somewhere.com',
     'invalid_@address');

     $bulk->LIST(\@list);

     #or, with an anonymous array

     $bulk->LIST(['thomasoniii@yahoo.com', 'someguy@somewhere.com',
     'invalid_@address']);

     You probably don't want to use arrays for your lists unless you're
     doing small tests.  Otherwise, you'll read your whole list into
     memory in advance, which is probably not what you wanted to do.

     Arrays as lists will return the values in order from the front to the
     end of the array.

     Probably the most powerful method to build your list is to pass a ref
     to a function.

          {
           my @list = ('thomasoniii@yahoo.com', 'someguy@somewhere.com', 'invalid_@address');

          sub some_function {return shift @list};
           };

          $bulk->LIST(\&some_function);

     Of course, in this case it's wasteful to actually pass a function
     reference instead of just an array ref to @list, but it serves as a
     good example.

     By passing function refs around, you can extract your list directly
     from a database if you want.

          my $dbh = DBI->connect();
          my $sth = $dbh->prepare("SELECT name, email FROM MAIL_LIST");
          $sth->execute;

          sub mail_list {return $sth->fetch_row_array};

          $bulk->LIST(\&mail_list);

     No more having to export your list to a flat file first.

     You can't just pass a ref to $sth->fetch_row_array because it doesn't
     work that way.  \&{$sth->fetchrow_array} returns a coderef to a hash
     containing the return value, which ain't what you want.
     &$sth...doesn't work and so on.  If anyone *does* know a way to do it
     directly, please do let me know.  :)

     The values are returned in whatever order your function returns them
     in.  Be sure to return undef when you're done, otherwise Bulkmail
     won't know when you've finished.

     Each of these methods returns "lines" of entries in your mailing
     list.  So what the hell's a line?  An email address?  A delimited
     string?  A code ref?  Actually, it's anything you want.  :)  See the
     section on MERGING below.

     *v1.x equivalent*:  LIST

BAD
     This is an optional item which specifies a place to log bad addresses
     (invalid, banned, etc.).  Just like LIST above, in v1.x it had to be
     a glob to a file handle, but not so any more!

     You have the same four options that you did for list, a string, a ref
     to a glob, a ref to a function, and a ref to an array.

     The string will cause a file to be opened for appending (">>").  The
     ref to a glob is a file that you already have open for appending (or
     simply for writing).

     If you pass a ref to an array, any items will be pushed onto the
     array as they're encountered.

     If you pass a ref to a function, then the function will be called
     with a single argument of whatever it is that was going to be logged.

     For example, if ".thomasoniii@yahoo.com" is encountered (a bad
     address!), any of the following would end up happening, depending
     upon what "BAD" is:

          print BAD ".jim3@ psynet.net", $bulk->lineterm(); #BAD is a file
          push @BAD, ".jim3@ psynet.net";                   #BAD is an array
          &BAD(".jim3@ psynet.net");                        #BAD is a function

     *v1.x equivalent*:  BAD

GOOD
     This is an optional item which specifies a place to log good
     addresses (anything not invalid or banned).  That way, you'll have a
     list at the end of all of your good addresses with the bad ones
     weeded out.

     There is one issue with the GOOD list, when using the envelope.
     You're not guaranteed that everything in the good list actually went
     through, unlike when not using the envelope.  When not using the
     envelope, a message is logged as being good as soon as it's
     completely transmitted to the server.  When using the envelope,
     however, a message is logged as being good as soon as the attempt is
     made to transmit it to the server.  As long as the message is
     accepted and delivered, everything is fine.  But if the message isn't
     accepted (if you specified too many people in the envelope, for
     instance), you'll log everyone else in the envelope as being good
     even though they weren't actually sent to.

     This is a terribly irritating bug to me, but I haven't thought of a
     clever way to handle it perfectly without caching those recipients
     elsewhere, which I'd rather not do since it's messy.  Ho hum.  Let me
     know if you have a clever solution.

     You have the same options as with BAD, above.

     *v1.x equivalent*:  GOOD

ERRFILE
     This is an optional item which specifies a place to log any and all
     errors that occur while running.  It is recommended that you run with
     this option on, so it's easier to see if anything bad is happening.

     *v1.x equivalent*:  ERROR

log_full_line
     It occurred to me that log_it was only logging the email address of a
     person.  So if you encounter a bad address of, say
     'thomasoniii@yahoo', it will log 'thomasoniii@yahoo'.  No problems,
     right?  But what if you're using a mailmerge?  Then things can get
     tricky.  If your line is, for example,

          #BULK_MAILMERGE is BULK_EMAIL::NAME::TITLE
          thomasoniii@yahoo::Jim::Perl Bulkmail Guru

     you would only log (in the bad file):

          thomasoniii@yahoo

     This may or may not be what you want.  As of v2.04, you have the
     option of choosing to log the full "line" of information.  With
     log_full_line set to true, this would be in the BAD file:

          thomasoniii@yahoo.com::Jim::Perl Bulkmail Guru

     Which may come in handy for you, or it may not.  But you at least
     have the option of doing it.  Why did I add this feature?  I was
     running a list that was extracting information via a SQL query and
     pulling out several pieces of information.  After the message was
     sent, I neede to perform another query to update that information in
     the table.  Easily done by setting GOOD to a function reference, but
     that GOOD was only receiving the email address back from bulkmail,
     not the full line of info.  That meant that I had to cache the other
     data in a seperate hash table and then come back to it later.  Most
     inelegent. This is much better.  :)

     There are a couple of "gotchas" when it comes to log_full_line that I
     haven't quite ironed out to my satisfaction yet.  If you have ideas
     about better ways to handle them, please let me know.

     First of all, remember that when logging a full line, you get back
     exactly what you put in as your "line" (recalling that "lines" can be
     strings, hashes, arrays, codes, etc.)  So if your "line" of
     information is an array (ref), then you'll log that array ref.
     Mail::Bulkmail tries to guess about a smart way to log the item if
     it's logging to a text file.  Arrays will be de-referenced and
     delimited by whatever ->BMD is.  Hashes will be squashed into their
     values and delimited by ->BMD.  The keys won't be stored.  Any other
     reference will give you an error, and then happily log the reference
     which is probably useless.  Delimited strings are logged unchanged.

     But this guessing at de-referencing is only done for files.  If
     you're logging to a function or an array, you're expected to know how
     to de-reference it yourself.  It'll just be a minor code tweak, don't
     worry.  Just be sure to remember it.

     log_full_line is set to false by default, but I may set it to true by
     default in a long-in-the-future release (think v2.5 or higher).

     *No v1.x equivalent*

banned
     banned will allows you to provide a list of banned email addresses or
     domains.  These are people that you never *ever* want to send email
     to under any circumstances.  People that email you and say "Remove me
     from your mailing list and never email me again!" will go in this
     category.

     A banned list can be built the same way as GOOD, BAD, LIST, etc.,
     with an array, a filehandle, a function, or a string containing a
     filename.  Banned entries are one per line.

          thomasoniii@yahoo.com
          yahoo.com

     would ban email from thomasoniii@yahoo.com, and anyone within the
     yahoo.com domain.  Please note that domains will only be banned
     upwards, not downwards.  So with an entry like this:

          yahoo.com
          mail.msn.com

     your list will be blocked from going to yahoo.com, and mail.msn.com.
     It will also be blocked from mail.yahoo.com (contains yahoo.com), but
     not from webserver.msn.com (webserver.msn.com does not contain
     mail.msn.com).

     You can also construct a banned list using a hashref, though it must
     be precisely constructed or you'll shoot yourself in the foot
     bigtime.  Fortunately, the format is simple.

          $banned{lowercase email address} = email address.

     Mail::Bulkmail needs its banned information in this format to
     function correctly.  Consequently, if you give it a non-hashref value
     (array, glob, etc.) it will construct this hash internally.  So if
     you have a large number of banned addresses, you'll probably want to
     put them in a dbm file and hand in a ref to it, so as not to store
     everything in memory.

     Why the funky hash format?  One of the screwball, IMHO, things about
     the email specification is that the domain part of an email address
     is case insensitive, but the local part is case sensitive.  This
     means that

          thomasoniii@yahoo.com
          ThomasonIII@yahoo.com
          tHOMaSoNIii@yahoo.com

     all could be different addresses.  So, in theory, you could have
     those three addresses in your mailing list and they're three
     different people!  Consequently, we need to keep track of exactly how
     the email address was typed or we may lose some information.

     Yeah, I know it's arguably being silly to do this, since I've never
     (*ever*) encountered an email server that allowed multiple
     differently-cased email addresses like this, but dammit I want to
     have the option in here to deal with it!  :-)

     'course, people could very well subscribe to your list using
     "thomasonIII@yahoo.com" and then try to unsubscribe using
     "thomasoniii@yahoo.com" and mess things up royally.  That's why we
     have the safe_banned method.  *See safe_banned, below*

     *v1.x equivalent*:  BANNED

safe_banned
     safe_banned is set to true by default.  safe_banned makes your
     matches on addresses case insensitive.  So that a request to ban
     "thomasoniii@yahoo.com" will also ban "ThomasonIII@yahoo.com", and
     "thomASONIii@yahoo.com".  You almost definitely want to leave this
     on, for safety's sake, but you can turn it off if you'd like.

     *See banned above*

allow_duplicates
     allow_duplicates is off by default.  Setting allow_duplicates to 1
     will allow people with multiple entries in your mailing list to
     receive multiple copies of the message.  Otherwise, they will only
     receive one copy of the message.  Duplicate addresses are printed out
     to ERRFILE, if you specified ERRFILE and you didn't turn
     allow_duplicates on.

     allow_duplicates respects safe_banned.  So if safe_banned is false,
     it will do local-part case-insensitive matching for duplicates,
     otherwise it will do local-part case-sensitive matching.

Tz
     This returns the current timezone.

     *v1.x equivalent*:  _def_Tz

Date
     This returns the current date in RFC 1123 format.

     *v1.x equivalent*:  _def_Date

header
     header() is actually a method that pretends to be an accessor.  See
     ADDTIONAL ACCESSORS, below.

     *v1.x equivalent*:  headset

HFM
     HFM (Headers From Message) will extract any valid headers from the
     message body.  A valid header is of the form "Name:value", one per
     line with an empty line seperating the headers from the message.

     It is *much* better to explicitly set the headers using the header
     method because it's a tougher to make mistakes using header.
     Nonetheless, setting HFM to any true value will cause the module to
     look in the message for headers.  Any valid headers extracted from
     the message will override existing headers.  Dynamically generated
     headers will override extracted headers, however.  Headers extracted
     from the message will be removed from the message body.

     But be perfectly sure you know what you're doing.

          $bulk->HFM(1);

          $bulk->Message(
              "This is my message.  I'm going to try sending it out to everyone that I know.
              Messages are cool, e-mailing software is neat, and everyone will love me for it.
              Oh happy day, happy happy day.
              Love,

          Jim";

     Before v2.03, since HFM is set to true, the first four lines are
     extracted from the message and sent as headers.  The extent of the
     message that goes through is "Jim" (everything after the first blank
     line which separates headers from message body).

     After v2.03, this will generate an error since HFM now makes sure
     that the headers are formed properly.  It still doesn't verify its
     headers, though, so you still need to be careful.  Maybe in the next
     release...

     Prior to v2.03, HFM would unwrap wrapped headers.  Since 2.04, HFM
     passes any wrapped headers through unchanged.

     HFM is off by default.

     *v1.x equivalent*:  HFM

BMD
     BMD (bulkmail delimiter) tells the module what delimiter to use in
     the file when using BULK_MAILMERGEs (see below)

     *Important: BMD must be different than DMD and DHD*

     BMD is "::" by default.

     *v1.x equivalent*:  BMD

DMD
     DMD (dynamic mail delimiter) tells the module what delimiter to use
     in the file when using dynamic messages (see below)

     DMD is "," by default.

DMDE
     DMDE (Dynamic Mail delimeter for Equal) tells the module what
     delimiter to use in the file when using for equalities in dynamic
     messages (see below)

     DMDE is "=" by default.

DHD
     DHD (dynamic header delimiter) tells the module what delimiter to use
     in the file when using dynamic headers (see below)

     DHD is "," by default.

DHDE
     DHDE (Dynamic Header delimeter for Equal) tells the module what
     delimiter to use in the file when using for equalities in dynamic
     headers (see below)

     DHDE is "=" by default.

lineterm
     lineterm is nifty.  It allows you to set the ending line character in
     your files.  So if you have a file with email addresses that is
     inexplicably delimited with "<!X!>", then simply set lineterm to
     "<!X!>" and off you go.  No need to convert your files before hand.

     lineterm is "\n" by default.

Trusting
     Trusting() lets you decide to turn off error checking.  By default,
     Mail::Bulkmail will only allow you to use valid e-mail addresses
     (well, kinda see the valid_email method for comments), valid dates,
     valid timezones, and valid precedences.  Trusting is off by default.
     Turn it on by setting it to some non-zero value.  This will bypass
     all error checking.  You should probabaly just leave it off so you
     can check for valid e-mails, dates, etc.  But you have the option, at
     least.

     *v1.x equivalent*:  No_errors

ADDITIONAL ACCESSORS
--------------------

   You're perfectly welcome to access any additional data that you'd like.
We're gonna assume that you're accessing or setting a header other than
the standard ones that are provided.  You even get a special method to
access them: header().  Using it is a piece of cake:

   $bulk->header('Reply-to', 'thomasoniii@yahoo.com');

   Will set a "Reply-to" header to the value of "thomasoniii@yahoo.com".
Want to access it?

   $bulk->header('Reply-to');

   What's that you ask?  Why don't we set *all* headers this way?  Well,
truth be told you can set them using header.

   $bulk->header('From', 'thomasoniii@yahoo.com');

   Is the same as:

   $bulk->From('thomasoniii@yahoo.com');

   Note that you can only set other _headers_ this way.  The headers that
have their own methods are From, Subject, and Precedence.  Calling header
on something else, though (like "Smtp") will set a header with that value,
which is probably not what you want to do (a "Smtp: your.server.com"
header is reeeeeal useful).  I'd recommend just using the provided From,
Subject, and Precedence headers.  That's what they're there for.

   What's that?  Why the hell can't you just say $bulk->my_header('some
value')?  It's because you may want to have a header with a non-word
character in it (like "Reply-to"), and methods with non-word characters
are a Perl no-no.  So since it's not possible for me to check every damn
header to see if it has a non-word character in it (things get stripped
and messed up and the original value is lost), you'll just have to use
header to set or access additional headers.

   OR-You can just set your headers at object construction.
Realistically, you're going to be setting all of your headers at
construction time, so this is not a problem.  Just remember to quote those
things with non-word characters in them.

     $bulk->Mail::Bulkmail->new(
            From        => 'thomasoniii@yahoo.com',
            Subject     => 'Some mass message',
            'Reply-to'  => 'thomasoniii@yahoo.com'
        );

   If you don't quote headers with non-word characters, all sorts of nasty
errors may pop up.  And they're tough to track down.  So don't do it.
You've been warned.

   As of v2.03, ->header() without a specific header name will return a
hashref containing all additional headers that have been set.

   *Also see dynamic headers below*

VALIDATED ACCESSORS
-------------------

   The properties that have validation checks are "From", "To", "Domain",
and "Precedence" to try to keep you from making mistakes.  The only one
that should really ever concern you is perhaps "From"

From
     This checks the return e-mail address against RFC 822 standards.  The
     validation routine is not perfect as it's really really hard to be
     perfect, but it should accept any valid non-group e-mail address.
     There is one bug in the routine that will allow
     "Jim<thomasoniii@yahoo.com" to pass as valid, but it's a nuisance to
     fix so I'm not going to.  :-)

     *v1.x equivalent*:  From

To
     This checks the to e-mail address against RFC 822 standards.  The
     validation routine is not perfect as it's really really hard to be
     perfect, but it should accept any valid non-group e-mail address.
     There is one bug in the routine that will allow
     "Jim<thomasoniii@yahoo.com" to pass as valid, but it's a nuisance to
     fix so I'm not going to.  :-)

     The ->To address is used when you are sending to a list using the
     envelope.  *See use_envelope, above*

Domain
     Domain sets which domain you'll use to say HELO to your SMTP server.
     If no domain is specified, you'll just use the domain part of your
     From address.  You probably won't need to set this ever.

Precedence
     We are doing bulkmail here, so the precedence should always be
     "list", "bulk", or "junk" and nothing else.  We might as well be
     polite and not make our servers think that we're sending out 60,000
     first-class or special-delivery messages.  You probably don't want to
     fiddle with this.

     *v1.x equivalent*:  Precedence

     If you don't want to do any validation checks, then set Trusting
     equal to 1 (see Trusting, below).  That will bypass all validation
     checks and allow you to insert "Garbonzo" as your date if you desire.
     It's recommended that you leave error checking on.  It's pretty good.
     And you have more important things to worry about.

Methods
-------

   There are several methods you are allowed to invoke upon your bulkmail
object.

bulkmail (?local merge?)
     This method is where the magic is.  This method starts up your
     mailing, sending your message to every person specified in LIST.
     bulkmail returns nothing.  bulkmail merely loops through everything
     in your LIST file and calls mail on each entry.

     bulkmail is a hell of a lot more complex then it used to be.  It used
     to just pass each address off to the mail method, so it was
     essentially just a big for loop.

     Now it's gotta do condition checking, verifications, and 4 or 5
     method calls instead of one.  Obviously, those 4-5 method calls are
     going to slow down your list processing, so that's bad.  How much
     it'll slow down I'm not really sure.  I shouldn't be much...10% I'm
     guessing.  Maybe.

     So why the hell did I complicate this up and make it slower, you ask?
     It needs the extra tricks to enable envelope sending.  Envelope
     sending will typically provide you with a performance increase of
     somewhere around 400%, I'm estimating.  The little slowdown from the
     method calls seemed unimportant.

     bulkmail can be handed a local merge hash.  *See merging, below*

     Returns 1 on success, undef on failure.

mail (line ?local merge?)
     mail is much much dumber than it used to be.  Give it a line (as in
     whatever a line would look like if extracted from your list) and an
     optional local merge, and it will email that one person.  You can now
     very easily accomplish the exact same thing by setting LIST to an
     array with one item and using bulkmail, but I figured I'd keep mail
     around for the heck of it so everyone easily knows that you can email
     just one person.

     There may be better modules for emailing to just one person, though.

     Returns 1 on success, undef on failure.

connect (no arguments)
     This method connects to your SMTP server.  It is called by the
     internal build_envelope method.  You can explicitly call it yourself,
     if you'd like.  That way you can verify that you can connect to your
     server in advance, and do something if you can't, I suppose.

     Returns 1 on success, undef on failure.

disconnect (no arguments)
     This method disconnects from your SMTP server.  It is called at
     object destruction, or explicitly if you wish to disconnect earlier.
     You should never need to call this method.  Returns nothing.

error (no arguments)
     error is where the last error message is kept.  Can be used as
     follows:

     $bulk->connect || die $bulk->error;

     All object error messages will be logged if you specifed an ERRFILE
     file.  Class errors will not be logged internally, you'll have to do
     that yourself.

     error is also usable as a class method:

     Mail::Bulkmail->error();

     will return whatever the last global class-wide error is, such as an
     object construction failure.  In fact, currently that's the only
     error it catches.  But you can now easily do:

          my $bulk = Mail::Bulkmail->new(
             "From" => 'thomasoniiI@yaho'     #invalid address!
          ) or die Mail::Bulkmail->error();

     to find out why construction failed.

MERGING
=======

   Finally, the mysterious merging section so often alluded to.

   Mail merging is exactly the same as "file mapping" was in v1.x.  I just
didn't realize until long after I released it that "file map" was stupid
and that "mail merge" is the correct term.  I'm finally correcting that
error.  If you understood mapping in v1.x, you'll understand merging now.
:-)

   You are sending out bulk e-mail to any number of people, but perhaps
you would like to personalize the message to some degree.  That's where
merging comes in handy.  You are able to define a merge to replace certain
characters (control strings) in an e-mail message with certain other
characters (values).

   Now in v2.0 you can go one step further and use dynamic messages, which
actually allows you to construct your message on the fly, instead of just
inserting values.  *See dynamic messages, below*

   Merges can be global so that all control strings in all messages will
be replaced with the same value or local so that control strings are
replaced with different values depending upon the recipient.

   Merges are declared at object constrution or by using the merge
accessor.  merge values are either anonymous hashes or references to
hashes.  For example:

   At constrution:

     $bulk = Mail::Bulkmail->new(
                 "From"    => "thomasoniii@yahoo.com",
                 "merge"   => {
                                 'DATE'    => 'today',
                                 'company' => 'Thomason Industries'
                              }
             );

   Or using the accessor:

     $bulk->merge({'DATE' => 'yesterday'});

   Global merges are not terribly useful beyond setting generic values,
such as today's date within a message template or the name of your
company.  Local merges are much more helpful since they allow values to be
set individually in each message.  Local merges can be declared either in
a call to the mail method or by using the BULK_MAILMERGE key.  Local
merges are declared with the same keyword (merge) as global merges.

   As a call to mail:

     $bulk->mail(
             'thomasoniii@yahoo.com',
             {
               'ID'   => '36373',
               'NAME' => 'Jim Thomason',
             }
     );

   Using BULK_MAILMERGE

     $bulk->merge({'BULK_MAILMERGE'=>'BULK_EMAIL::ID::NAME'});

   Be careful with your control strings to make sure that you don't
accidentally replace text in the message that you didn't mean to.  Control
strings are case sensitive, so that "name" in a message from the above
example would not be replaced by "Jim Thomason" but "NAME" would be.

   NOTE: I would *highly* recommend against having "BULK_" or "DYNAMIC_"
in any of your keys (except BULK_EMAIL, of course).  BULK_* keys are used
internally by Mail::Bulkmail for keeping track of things that it needs to
keep track of.  BULK_MAILMERGE, BULK_EMAIL, DYNAMIC_MESSAGE, and
DYNAMIC_HEADERS are examples of internal keys.  BULK_LINE is also hanging
around inside, but you never see it, now do you?  But you never know what
keys I may need to add internally at a later date.  I will always prepend
those keys with 'BULK_' or 'DYNAMIC_', so you be sure to never prepend
your keys with 'BULK_' or 'DYNAMIC_' and we'll all get along just fine.

   BULK_MAILMERGE will be explained more below.

BULK_MAILMERGE
--------------

   First of all, BULK_MAILMERGE is not compatible with use_envelope.  Use
one or the other, but not both.  It'll yell at you if you do.

   Earlier we learned that LIST files may be in two main formats, either a
single e-mail address per line, or an email address and several values per
"line", either delimited in a line of a file, or stored in an array or a
hash or a function or whatever.

   Delimited lists _must_ be used in conjunction with a BULK_MAILMERGE
parameter to merge.  BULK_MAILMERGE allows you to specify that each e-mail
message will have unique values inserted for control strings without
having to loop through the address list yourself and specify a new local
merge for every message.  BULK_MAILMERGE may only be set in a global map,
its presence is ignored in local merges.

     If your list file is this:
       thomasoniii@yahoo.com::36373::Jim Thomason
       or
       ["thomasoniii@yahoo.com", "36373", "Jim Thomason"]
       or
       {
           "BULK_EMAIL" => "thomasoniii@yahoo.com,
           "ID"         => "36373",
           "NAME"         => "Jim Thomason"
       }

   You can have a corresponding merge as any one of the following:

     $bulk->merge({
             'BULK_MAILMERGE'=>'BULK_EMAIL::ID::NAME'
             });

     $bulk->merge({
             'BULK_MAILMERGE'=>["BULK_EMAIL", "ID", "NAME"]
             });

     $bulk->merge({
             'BULK_MAILMERGE'=>
                 {"BULK_EMAIL" => undef,
                  "ID" => undef,
                  "NAME" => undef
                 }
             });

   This BULK_MAILMERGE will operate the same way that the local merge
above operated.  "BULK_EMAIL" is the only required item, it is case
sensitive.  This is where in your delimited line the e-mail address of the
recipient is.  "BULK_EMAIL" _is_ used as a control string in your message.
Be careful.  So if you want to include someone's e-mail address within
the text of your message, put the string "BULK_EMAIL" in your message body
wherever you'd like to insert it.

   Everything else may be anything you'd like, these are the control
strings that will be substituted for the values at that location in the
line in the file.  You may use global merges, BULK_MAILMERGEs and local
merges simultaneously.

   BULK_MAILMERGEs are declared as delimited by the BMD method (or "::" by
default), the data in the actual file is also delimited by the BMD method.
The default delimiter is "::", but as of version 1.10, you may use BMD to
choose any arbitrary delimiter in the file.

   For example:

     $bulk->BMD("-+-");

     $bulk->merge({'BULK_MAILMERGE'=>'BULK_EMAIL-+-ID-+-NAME'});

     (in your list file)
     thomasoniii@yahoo.com-+-ID #1-+-Jim Thomason
     thomasoniii@yahoo.com-+-ID #2-+-Jim Thomason

   If you have set LIST to a function, or array, you can have each line
return in an array or a hash.  Obviously, if LIST is a file, then every
line has to be a delimited string as listed above.

   But with arrays or functions, you don't have to return a delimited
string.  You can return your entry in an array or in a hash.  An array is
listed in the same order as the BULK_MAILMERGE, and operates the same way.
It's just a little cleaner and quicker since we skip the split step.

   The hash method is a little slower since it's a hash, and it also takes
up a little more memory since you're returning more values.

   You'll almost never want to use the hash method, since the array one is
preferrable.  I'm debating whether or not to expand that hash returning
approach to allow you to dynamically construct mail merges on the fly for
each individual item.  What do you think about that idea?

merge precedence
----------------

   local merge values will override global merge values.  BULK_MAILMERGE
merge values will override anything else.  Evaluation of merge control
strings is

     BULK_MAILMERGE value -> local value -> global value

   where the first value found is the one that is used.

DYNAMIC MESSAGES
================

   Dynamic messages rock.  :)

   We had a dotcom company come in one day to try to sell us on their
email solution for our mailing lists.  I calmly sat there, listened to
their presentation, and jotted down notes about anything they said that I
thought would be good to incorporate into Mail::Bulkmail.  The best thing
that they had was dynamic messages.

   Dynamic messages are mail merges taken to the next level. A mail merge
allows you to insert simple piece of information into your message, the
person's name or phone number or something for personalization purposes.
But it's not a good idea to do much beyond that because it gets messy to
try to maintain it across your list and keep consistency across everything.
A global mail merge is better, but not great.

   Enter dynamic messages.

   Dynamic messages allow you to actually construct your message on the
fly based upon preferences specified by the user.

   Say you've got a mailing list on animals, and you want to maintain one
list to send out to the people who like bears, rabbits, and iguanas.  One
list is easier to maintain than three, and conceptually they all like
animals, so it makes sense.  Besides, some people may want info on bears
and rabbits and wouldn't it be nice to send them one email instead of two?

   Dynamic messages must be used in conjunction with BULK_MAILMERGE, since
we're building them based upon the preferences of the individual
recipient.  Use the DYNAMIC_MESSAGE keyword in your BULK_MAILMERGE:

     "BULK_MAILMERGE" => "BULK_EMAIL::Name::ID::DYNAMIC_MESSAGE"

   and then your email entry would be:

     thomasoniii@yahoo.com::Jim Thomason::36373::Bears=yes,Rabbits=no,Iguanas=headlines

   To specify that I want info on bears, no info on rabbits, and just
headlines on iguanas.

   Then you use the ->dynamic method to declare your hash of hashes.

     $bulk->dynamic(
        "Bears" => {
            "yes" => "I see you like bears.  Bears are cuddly and we like them too!",
            "black" => "Here is your update on the black bear...",
            "polar" => "here is your update on the polar bear...",
            "no" => ""
        },
        "Rabbits" => {
            "yes" => "I see that you like rabbits.  Rabbits are cool."
            "cottontail" => "Here is information on the cotton tail rabbit..."
            "no" => ""
        },
        "Iguanas" => {
            "yes" =" Here is info on iguanas",
            "no" => ""
            "headlines" => "Here are important iguana stories"
        }
     );

   or at object creation:

     my $bulk = Mail::Bulkmail->new(
         "message" => "
         Bears
         Rabbits
         Iguanas",
         "dynamic" =>
         {
             "Bears" => {
                "yes" => "I see you like bears.  Bears are cuddly and we like them too!",
                "black" => "Here is your update on the black bear...",
                "polar" => "here is your update on the polar bear...",
                "no" => ""
            },
            "Rabbits" => {
                "yes" => "I see that you like rabbits.  Rabbits are cool."
                "cottontail" => "Here is information on the cotton tail rabbit..."
                "no" => ""
            },
            "Iguanas" => {
                "yes" =" Here is info on iguanas",
                "no" => ""
                "headlines" => "Here are important iguana stories"
            }
        }
     );

   Which will create this message:

     I see you like bears.  Bears are cuddly and we like them too!
     Here are important iguana stories

   It operates the same way as a mail merge, substituting the key word for
whatever keyword value is listed in the DYNAMIC_MESSAGE item.

   Dynamic messages execute before mail merges, so you can mail merge a
dynamic message as well!

   BULK_MAILMERGE = "BULK_EMAIL::NAME::DYNAMIC_MESSAGE";

     $bulk->dynamic(
        "Bears" => {
            "personal" => "I see you like bears, NAME",
            "impersonal" => "I see you like bears, whoever you are"
        }
     );

     thomasoniii@yahoo.com::Jim Thomason::Bears=personal

   would send:

   I see you like bears, Jim Thomason.

   So you can send truly dynamic, personalized messages.

DYNAMIC HEADERS
===============

   Well, I'm kinda spent after the huge lecture on dynamic messages above,
so I'll be briefer.

   Dynamic headers operate exactly the same way, except with headers
instead of message components.  So you can send individual people
individual subjects, for instance.

   use DYNAMIC_HEADERS in a BULK_MAILMERGE:

   BULK_MAILMERGE = "BULK_EMAIL::DYNAMIC_HEADERS";

   Use the dynamic_headers method:

     $bulk->dynamic_headers(
        "Subject" => {
            "Special offer" => "A special offer for valued customers",
            "First time" => "Thanks for your first order!",
            "No order" => "We miss your business!"
        }
     );

   or at object construction:

     my $bulk = Mail::Bulkmail->new(
        "dynamic_headers" =>{
            "Subject" => {
                "Special offer" => "A special offer for valued customers",
                "First time" => "Thanks for your first order!",
                "No order" => "We miss your business!"
            }
        }
     );

   So that

   thomasoniii@yahoo.com::Subject=Special offer

   Will send out your email message to thomasoniii@yahoo.com with "A
special offer for valued customers" as the subject.

   Again, you can use a mail merge into a dynamic header, if you'd like.
So you can insert a personalized header ID, for instance.

CLASS VARIABLES
===============

   (well, *technically* they aren't class variables, since they're
lexically scoped, but the gist is the same)

     my $def_From              = 'Postmaster';
     my $def_To                = 'postmaster@your.smtp.com';
     my $def_Smtp              = 'your.smtp.com';       #<--Set this variable.  Important!
     my $def_Domain            = "smtp.com";
     my $def_Port              = '25';
     my $def_Tries             = '5';
     my $def_Subject           = "(no subject)";
     my $def_Precedence        = "list";                #list, bulk, or junk
     my $def_Trusting          = 0;
     my $def_log_line          = 0;
     my $def_envelope_limit    = 0;
     my $def_allow_duplicates  = 0;

     my $def_BMD               = "::";
     my $def_DHD               = ",";
     my $def_DMD               = ",";
     my $def_DMDE              = "=";
     my $def_DHDE              = "=";

     my $def_lineterm          = "\n";

     my $def_HFM               = 0;

   The default values. for various items.  All of which may be overridden
in individual objects.

   These all should be obvious based upon what you've read so far.

DIAGNOSTICS
===========

   Bulkmail doesn't directly generate any errors.  If something fails, it
will return undef and set the ->error property of the bulkmail object.  If
you've provided an error log file, the error will be printed out to the
log file.

   Check the return of your functions, if it's false, check ->error to
find out what happened.

   isDuplicate and isBanned will return 0 if an address is not a duplicate
or banned, respectively, but this is (probably) not an error condition.

HISTORY
=======

- 2.05 10/3/00
     Added envelope_limit method.  See 'envelope_limit', above.

     Cleaned up the documentation a lot.

     Re-wrote the date generation methods.  They're now 5-10% faster and I
     fixed an *old* bug causing mail to sometimes appear to have been sent
     yesterday, or tomorrow.

     Altered logging when using the envelope, see item GOOD, above.

     Fixed a bug with undefined values in mailmerges

- 2.04 8/29/00
     Added log_full_line flag.  See 'log_full_line', above.

     Trusting is now more trusting.

     Domains can once again be banned.

     Error checking is done less often and in a slightly different order
     now

     ->bulkmail now returns 1 on success.  Doh.

     Fixed an annoyingly subtle bug with construction of dynamic messages

     Repaired a long-standing bug in the docs.

- 2.03 8/22/00
     Tweaked the constructor.

     Enhanced 'error'.  See 'error', above.

     Enhanced HFM.

     Various bug fixes.

     Enhanced the test suite.

- 2.01 8/16/00
     Fixed a *really* stupid error.  Merge hashes and dynamic hashes
     weren't properly initialized. Damn.

- 2.00 8/11/00
     Re-wrote everything.  Literally *everything*.  Total re-write.
     Should be a much better module now.  :)

- 1.11 11/09/99
     Banned addresses now checks entire address case insensitively instead
     of leaving the local part alone.  Better safe than sorry.

     $self->fmdl is now used to split BULK_FILEMAP

     Various fixes suggested by Chris Nandor to make -w shut up.

     Changed the way to provide local merges to mail and bulkmail so it's
     more intuitive.

- 1.10 09/08/99
     Several little fixes.

     The module will now re-connect if it receives a 221 (connection
     terminated) message from the server.

     Fixed a potential near-infinite loop in the _valid_email routine.

     _valid_email now merrily strips away comments (even nested ones).  :)

     hfm (headers from message) method added.

     fmdl (filemap delimiter) method added.

- 1.01 09/01/99
     E-mail validation and date generation bug fixes

- 1.00 08/18/99
     First public release onto CPAN

- 0.93 08/12/99
     Re-vamped the documentation substantially.

- 0.92 08/12/99
     Started adding a zero in front of the version name, just like I
     always should have

     Changed accessing of non-standard headers so that they have to be
     accessed and retrieved via the "header" method.  This is because
     methods cannot have non-word characters in them.

     From, Subject, and Precedence headers may also be accessed via
     header, if you so choose.

     AUTOLOAD now complains loudly (setting ->error and printing to
     STDERR) if it's called.

- 0.91 08/11/99
     Fixed bugs in setting values which require validation checks.  Fixed
     accessing of non-standard headers so that the returns are identical
     to every other accesor method.

- 0.90
     08/10/99 Initial "completed" release.  First release available to
     general public.

EXAMPLES
========

bulkmailing
-----------

   Here's how we use Bulkmail in one of our programs:

     use Mail::Bulkmail;

     $bulk = Mail::Bulkmail->new(
        'From'       => $from,
        'Subject'    => $subject,
        'Message'    => $message,
        'X-Header'   => "Rockin' e-mail!",
        'merge'      => {
                         '<DATE>'            => $today,
                         'BULK_MAILMERGE'    => "email::<ID>::<NAME>::<ADDRESS>"
                        },
        'LIST'       => './list.txt',
        'GOOD'       => './good_list.txt',
        'BAD'        => './baddata.txt',
        'ERROR'      => './error.txt',
        'BANNED'     => './banned.txt',
     );

   That example will set up a new bulkmail object, fill in who it's from,
the subject, and the message, as well as a "X-header" header which is set
to "Rockin' e-mail!".  It will also define a merge to turn "<DATE>"
control strings into the $today string, a BULK_MAILMERGE to merge in the
name, id number, and address of the user.  It opens a LIST file, and sets
up GOOD, BAD, and ERROR files for logging.  It also uses a BANNED list.

   This list is then mailed to by simply calling

   $bulk->bulkmail() or die $bulk->error();

   Easy as pie.  Especially considering that when we had to write all of
this code out in our original implementation, it took up well over 100
lines (and was 400x slower).

Single mailing
--------------

     use Mail::Bulkmail;

     $bulk = Mail::Bulkmail->new(
         'From'     => $from,
         'Subject'  => $Subject,
         'Message'  => $message,
         'X-Header' => "Rockin' e-mail!"
     );

     $bulk->mail(
          'thomasoniii@yahoo.com',
          {
             '<DATE>'    => $today,
             '<ID>'      => '36373',
             '<NAME>'    => 'Jim Thomason',
             '<ADDRESS>' => 'Chicago, IL'
           }
     );

   This will e-mail out a message identical to the one we bulkmailed up
above, but it'll only go to thomasoniii@yahoo.com

HUGE example with dynamic messaging
-----------------------------------

     {
        my @stuff = (
            \&solitary_address,
            ['some_address@somewhere.com', "HOOSIER", "BETDA", "GAMMA",
                "hoosier=alpha,pickle=something",
                "To=test,From=mike,Subject=special,Marvelous=Charlie"
            ],
            'some_other_address@somewhere.com::able::baker::charlie::::Subject=special',
            'some_address@somewhere_else.com::alpha::bravo::niner::::Subject=special'
        );

     sub email_list {
         return shift @stuff;
     };

     sub solitary_address {
         return ['another_address@some_server_somewhere.com', "hoosier", "betda", "gamma",
         "hoosier=alpha,pickle=something",
         "To=admin,From=herbert,Subject=yodel,Marvelous=Charlie"
         ]
     };

     };

     my %hash = ("this" => "That");
     my $bulk = Mail::Bulkmail->new(
        "From"             => "thomasoniii\@yahoo.com",
        "Subject"          => "Test with envelope",
        "Smtp"             => "email.emailserv.com",
        "LIST"             => \&email_list,
        "ERRFILE"          => \*STDERR,
        "use_envelope"     => 0,
        "Trusting"         => 0,
        "To"               => "My_list@my_server.com",
        "allow_duplicates" => 1,
        "Message"          => "azz--hello there who are you? (hoosier) (pickle) I see that you're at BULK_EMAIL",
        "merge" => {
            "this is a test" => "something",
            "who" => "what",
            "where" => "there",
            "ttt" => "things",
            "BULK_MAILMERGE" => "BULK_EMAIL::azz::bzz::czz::DYNAMIC_MESSAGE::DYNAMIC_HEADERS"
        },
        "dynamic" => {
            "hoosier" => {
                "alpha" => "This is an alpha email component",
                "beta" => "This is a beta email component",
                "agent" => "This is an agent email component"
            },
            "pickle" => {
                "something" => "You've requested the pickle agent!"
            }
        },
        "dynamic_headers" => {
            "Subject" => {
                "Hello!" => "Why HELLO there.",
                "yodel" => "I'm yodelling!",
                "special" => "Get this special offer!"
            },
            "From" => {
                "herbert" => 'herber@hoover.com',
                "mike" => 'mike@wallace.com'
            },
            "To" => {
                "admin" => "admin\@somewhere.com",
                "test" => "test\@elsewhere.com"
            },
            "Marvelous" => {
                "Max" => "Max is marvelous!",
                "Charlie" => "Charlie is marvelous!"
            }

     }
      ) or die Mail::Bulkmail->error();

   *Study this example*.  Change the email addresses.  Run it.  Understand
it.  Be happy.

FAQ
===

   *So just how fast is this thing, anyway?*

   I don't know any more, I don't have access to the same gigantic lists I
used to anymore.  :~(

   Anyway, I'm guesstimating that normal emailing will be about 5-10%
slower than before, at most.  But envelope mailing will be 400+ percent
faster.

   Well, there's a caveat to that.  I'm estimating that normal emailing
the same way you'd use v1.11 will be 5-10% slower than before.  "normal"
means using flat files as your lists.  If you start using functions or SQL
queries to build your list, then all bets are off.  For instance, one list
I'm using now sends to about 50 people in about 50 seconds (terribly
slow).  But it's repeatedly performing a SQL query 'til it gets the result
it likes, comparing that result against several conditions, deciding to
continue, and then completely building the message on the fly so every
single one is unique.  That's a lot of overhead which slows it down quite
a bit.  So YMMV, as usual.

   Here's the 1.x answer, with 2.00 comments

   Really fast.  Really stupendously incredibly fast.

   The largest list that I have data on has 91,140 people on it.  This
list runs through to *completion* in about an hour and 43 minutes, which
means that Mail::Bulkmail can process (at least) 884 messages per minute
or about 53,100 per hour. (*the guess is that with 2.00 and envelope
sending, you could email to these people in roughly 17 minutes*)

   *So? How big were the individual messages sent out?  Total data
transferred is what counts, not total recipients!*

   How right you are.  The last message sent out was 4,979 bytes.  4979 x
91,140 people is 453,786,060 bytes of data transferred, or about 453.786
megabytes in 1 hour and 43 minutes.  This is a sustained transfer rate of
about 4.4 megabytes per minute, or 264.34 megabytes per hour. (*This
hasn't changed in 2.00, we're just smart enough to send less data*)

   *Am I going to see transfer speeds that fast?*

   Maybe, maybe not.  It depends on how busy your SMTP server is.  If you
have a relatively unused SMTP server with a fair amount of horsepower, you
can easily get these speeds or beyond.  If you have a relatively busy
and/or low powered SMTP server, you're not going to reach speeds that fast.

   *How much faster will Mail::Bulkmail be than my current system?*

   This is a very tough question to answer, since it depends highly upon
what your current system is.  For the sake of argument, let's assume that
for your current system, you open an SMTP connection to your server, send
a message, and close the connection.  And then repeat.  Open, send, close,
etc.

   Mail::Bulkmail will always be faster than this approach since it opens
one SMTP connection and sends every single message across on that one
connection.  How much faster depends on how busy your server is as well as
the size of your list.

   Lets assume (for simplicity's sake) that you have a list of 100,000
people.  We'll also assume that you have a pretty busy SMTP server and it
takes (on average) 25 seconds for the server to respond to a connection
request.  We're making 100,000 connection requests (with your old system).
That means 100,000 x 25 seconds = almost 29 days waiting just to make
connections to the server!  Mail::Bulkmail makes one connection, takes 25
seconds for it, and ends up being 100,000x faster!

   But, now lets assume that you have a very unbusy SMTP server and it
responds to connection requests in .003 seconds.  We're making 100,000
connection requests.  That means 100,000 x .0003 seconds = about 5 minutes
waiting to make connections to the server.  Mail::Bulkmail makes on
connection, takes .0003 seconds for it, and ends up only being 1666x
faster.  But, even though being 1,666 times faster sounds impressive, the
world won't stop spinning on its axis if you use your old system and take
up an extra 5 minutes.

   And this doesn't even begin to take into account systems that don't
open and close SMTP connections for each message.

   *2.00 will probably be a little slower than 1.x without envelope
sending.  It'll be *much* faster with it*

   In short, there's no way to tell how much of a speed increase you'll
see.

   *Have you benchmarked it against anything else?*

   Not scientifically.  I've heard that Mail::Bulkmail is about 4-5x
faster than Listcaster from Mustang Software, but I don't have any hard
numbers.

   If you want to benchmark it against some other system and let me know
the results, it'll be much appreciated.  :-)

   *Wait a minute!  You said up there that Mail::Bulkmail opens one
connection and sends all the messages through.  What happens if the
connection is dropped midway through?*

   Well, either something good or something bad depending on what happens.
If it's something good, the server will send a 221 message (server
closing) which Mail::Bulkmail should pick up and some point, realize its
disconnected and then reconnect for the next message.  If it's something
bad, the server will just stop replying and Mail::Bulkmail will sit there
forever wondering why the server won't talk to it anymore.

   Realistically, if your server bellyflopped and is not responding at all
and won't even alert that it's disconnected, you probably have something
serious to worry about.

   A future release will probably have a time-out option so Mail::Bulkmail
will bow out and assume its disconnected after a certain period of time.

   *What about multipart messages? (MIME attachments)*

   *grumble grumble*  This is forthcoming, but it won't be in before
version 2.5.  Maybe 3.0...

   My current employer absolutely needs a mailing system that can handle
attachments, so I figure I might as well finally get around to building it
into the module.

   In the mean time, you can set your own headers, boundaries, etc. and
just do the MIME encoding yourself.  It will work, I just won't do it for
you.

   Note that if you just want to sent out a regular HTML message instead
of text that you can just use the ->HTML flag to tell the module that it's
HTML.

   *I'd like to send out a mass-mailing that has different From and To
fields in the message and the envelope.  Can I do this?*

   Oh all right, go ahead.   I've decided not to punish the legitimate
mass emailers because of the spammers.  So go to town.  I figure it
couldn't hurt once people start realizing that a Perl module is one of the
fastest freakin' mass mailers around.  Power to the cause!

   *Can I send spam with this thing?*

   No.  Don't be a jerk.

   *So what is it with these version numbers anyway?*

   I'm going to try to be consistent in how I number the releases.

   The *hundredths* digit will indicate bug fixes, minor behind-the-scenes
changes, etc.

   The *tenths* digit will indicate new and/or better functionality, as
well as some minor new features.

   The ones digit will indicate a major new feature or re-write.

   Basically, if you have x.ab and x.ac comes out, you want to get it
guaranteed.  Same for x.ad, x.ae, etc.

   If you have x.ac and x.ba comes out, you'll probably want to get it.
Invariably there will be bug fixes from the last "hundredths" release, but
it'll also have additional features.  These will be the releases to be
sure to read up on to make sure that nothing drastic has changes.

   If you have x.ac and y.ac comes out, it will be the same as x.ac->x.ba
but on a much larger scale.  Judging by the amount of revision and
improvement between 1.11 and 2.00, there's a very good chance you'll want
to look at this release.  But, also judging by 1.11->2.00, you'll want to
really pour over the docs, since it probably won't be backwards compatable
and you'll have to fiddle with your script to use it.

   *So what can I expect to see in the future?*

   Neat things.  Really *really* neat things.  I've got a few tricks up my
sleeve that will send the performance through the roof.  In theory.  If I
can get them to work.  Be patient.

   But good things are in the works.  I just have too much fun developing
this module.  :)

   *Wow, this module is really cool.  Have you contributed anything else
to CPAN?*

   Yes, Carp::Notify and Text::Flowchart

   *Was that a shameless plug?*

   Why, yes.  Yes it was.

   *Anything else you want to tell me?*

   Sure, anything you need to know.  Just drop me a message.

MISCELLANEA
===========

   Mail::Bulkmail will automatically set three headers for you (well,
maybe four).

  1. Who the message is from (From:....)

  2. The subject of the message (Subject:...)

  3. The precedence of the message (Precedence:...)

  4.      Who the message is to (To:....) I<only if using the envelope>
          (To: will actually always be set, but if not using the envelope it will
          be set to the individual receiving it)

        The defaults will be set unless you give them new values, but
regardless these headers *will* be set.  No way around it.  Additional
headers are set solely at the descretion of the user.

   Also, this module was originally written to make my life easier by
including in one place all the goodies that I used constantly.  That's not
to say that there aren't goodies that I haven't included that would be
beneficial to add.  If there's something that you feel would be worthwhile
to include, please let me know and I'll consider adding it.

   How do you know what's a worthwhile addition?  Basically, if you need
to do some sort of pre-processing to your e-mail addresses so that you
have to use your own loop and calls to mail() instead of using bulkmail(),
and you're using said loop and processing in several routines, it may be a
useful addition.  Definitely let me know about those.

   That's not to say that random suggestions wouldn't be good, those I'll
listen to as well.  But something big like that is probably a useful thing
to have so I'd be most interested in hearing about them.

COPYRIGHT (again)
=================

   Copyright (c) 1999, 2000 James A Thomason III (thomasoniii@yahoo.com).
All rights reserved.  This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.

CONTACT INFO
============

   So you don't have to scroll all the way back to the top, I'm Jim
Thomason (thomasoniii@yahoo.com) and feedback is appreciated.  Bug
reports/suggestions/questions/etc.  Hell, drop me a line to let me know
that you're using the module and that it's made your life easier.  :-)


