NAME
    Mail::Bulkmail - Platform independent mailing list module

AUTHOR
    Jim Thomason jim3@psynet.net

SYNOPSIS
    open (LIST, "./list.txt") || die "Can't open list!";

     $bulk = Mail::Bulkmail->new(
            From    => 'jimt@playboy.com',
            Subject => 'This is a test message!',
            Message => "Here is the text of my message!",
            'LIST'  => *LIST,
     );

    $bulk->bulkmail;

    close LIST;

    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. (Not that that's necessarily bad)

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.

REQUIRES
    Perl5.004, Socket

OBJECT METHODS
  CREATION

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

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

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

     $object = Mail::Bulkmail->new(
                            From    =>      'jim3@psynet.net',
                            Smtp    =>      'some.smtp.com'
                    );

  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 will not be set if $object->No_errors is 0 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 (jim3@psynet.net), or
               a name + e-mail address ("Jim
               Thomason"<jim3@psynet.net>). This is validated unless
               you turned off validation by setting No_errors. See
               above.

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

    Message    This is the actual text that will appear in the message
               body. You can include control fields that can be
               mapped into specific values. See MAPPING, below

    Map        This specifies a character map for the message text. See
               MAPPING, below.

    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?

    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.

    Precedence This sets the precedence of the e-mail message. This is
               validated unless you turn off validation by setting
               No_errors.

    Domain     You're going to be saying HELO to an SMTP server, you'd
               be 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.

    LIST       This is a glob to a filehandle. This is your actual list
               of e-mail addresses. You can either have one e-mail
               address per line, or have an multiple "::" seperated
               fields in addition to the e-mail address. If you
               choose to use "::" fields, read the section on
               MAPPING, below. This file should be openned with read
               access. Required if you're going to be bulkmailing.

    BAD        This is a glob to a filehandle. Bulkmail will happily
               print out all failed addresses, exactly as they
               appeared in LIST to this file. This file should be
               openned with write or append access. Totally
               optional.

    GOOD       This is a glob to a filehandle. Bulkmail will happily
               print out all successful addresses, exactly as they
               appeared in LIST to this file. This file should be
               openned with write or append access. Totally
               optional. GOOD is much more useful than BAD since
               GOOD is a duplicate of your original list, with all
               invalid addresses weeded out.

    ERROR      This is a glob to a filehandle. Any errors that occur in
               Bulkmail will be printed out here. Totally optional,
               but highly recommended. This file should be openned
               with write or append access.

    BANNED     This is a glob to a filehandle. BANNED allows you to weed
               your mailing list and not mail to any e-mail
               addresses that you may have in your list that you
               don't want to. For example, you may not want to be
               able to send any e-mail to
               "president@whitehouse.gov", putting that into your
               banned file will automatically skip that address
               while mailing. Additionally, you can specify domains
               so that no mail will go to "whitehouse.gov", for
               example. A word of caution: Subdomains are banned
               recursively. This means that "whitehouse.gov" will
               ban "staff.whitehouse.gov" but that
               "staff.whitehouse.gov" will allow e-mail to go
               through to "whitehouse.gov". This file should be
               openned with read access.

    Tz         This allows you to set the timezone. See above. You
               probably don't want to touch this.

    Date       This allows you to set the date. See above. You probably
               don't want to touch this.

    Duplicates Duplicates is off by default. Setting 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 ERROR, if you specified ERROR and you didn't turn
               Duplicates on.

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

    No_errors  No_errors() lets you decide to turn of error checking. By
               default, Mail::Bulkmail will only allow you to use
               valid e-mail addresses (well, kinda see the
               _valid_email function for comments), valid dates,
               valid timezones, and valid precedences. No_errors 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.

  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: headset(). Using it is
    a piece of cake:

    $bulk->headset('Reply-to', 'jim3@psynet.net');

    Will set a "Reply-to" header to the value of "jim3@psynet.net".
    Want to access it?

    $bulk->headset('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 headset.

    $bulk->headset('From', 'jim3@psynet.net');

    Is the same as:

    $bulk->From('jim3@psynet.net');

    Note that you can only set other _headers_ this way. The headers
    that have their own methods are From, Subject, and Precedence.
    Calling headset 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 headset
    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            =>      'jim3@psynet.net',
                    Subject         =>      'Some mass message',
                    'Reply-to'      =>      'jimt@playboy.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.

  VALIDATED ACCESSORS

    The properties that have validation checks are "From",
    "Precedence", "Date", and "Tz" 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 non-commented e-mail
               address. There is one bug in the routine that will
               allow "Jim<jim3@psynet.net" to pass as valid, but
               it's a nuisance to fix so I'm not going to. :-)

    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.

    Date       This checks that the date set is a valid RFC 822 date.
               You probably don't ever want to set the date, since
               it will be automagically inserted to each e-mail
               message as it is sent. Nonetheless, if you just have
               to use some other random date, set it here. But
               follow the spec, please.

    Tz         This checks that the timezone set is a valid RFC 822 Time
               zone. The only time I can think of where you'd want
               to set the time zone is if your machine is off and
               you want to correct it. For example, several of our
               servers seem to think that they're on Pacific Time
               instead of Central Time, which is annoying. Fix the
               time zone here if you need to.

    If you don't want to do any validation checks, then set
    No_errors equal to 1 (see METHODS, 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  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.

    mail      Okay, maybe mail is really where the magic is. This method
              sends out a message to a single address. You can use
              this method if you want to send out a message to only
              one person, though arguably there are better ways to
              send e-mail to a single individual than using
              Mail::Bulkmail. The first argument to mail is the e-
              mail address of the recipient. The second argument is
              an optional local map. See MAPPING below. Further
              arguments are optional headers. Calling mail directly
              is really only useful if you need to do preprocessing
              of your list before sending your message or if your
              list of addresses is stored in an array or some other
              non-filehandle location.

              Returns 1 on success, 0 on failure.

    connect   This method connects to your SMTP server. It is called by
              mail (and in turn, bulkmail). You should never need to
              directly call this unless you want to merely test SMTP
              connectivity.

              Returns 1 on success, 0 on failure.

    disconnect
              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     error is where the last error message is kept. Can be used
              as follows:

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

              All error messages will be logged if you specifed an
              ERROR file.

MAPPING
    Finally, the mysterious mapping section so often alluded to.

    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 mapping comes in handy. You are able to
    define a map to replace certain characters (control strings) in
    an e-mail message with certain other characters (values).

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

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

    At constrution:

            $bulk = Mail::Bulkmail->new(
                                    From    =>      jim3@psynet.net,
                                    Map             => {
                                                                    'DATE' => 'today',
                                                                    'company' => 'Playboy Enterprises'
                                                            }
                            );

    Or using the accessor:

            $bulk->Map({'DATE'=>yesterday});
            
    Global maps are not terribly useful beyond setting generic values, such as today's date within a message
    template.  Local maps are much more helpful since they allow values to be set individually in each
    message.  Local maps can be declared either in a call to the mail method or by using the BULK_FILEMAP
    key.  Local maps are declared with the same keyword (Map) as global maps.

    As a call to mail:

            $bulk->mail(
                            'jim3@psynet.net',
                            Map => {
                                    'ID'   => '36373',
                                    'NAME' => 'Jim Thomason',
                            }
                    );
     
    Using BULK_FILEMAP

            $bulk->Map({'BULK_FILEMAP'=>'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.

    BULK_FILEMAP will be explained more below.

  BULK_FILEMAP

    Earlier we learned that LIST files may be in two formats, either
    a single e-mail address per line, or a "::" delimited list of
    values, one of which must be an e-mail address.

    "::" delimited lists _must_ be used in conjunction with a
    BULK_FILEMAP parameter to Map. BULK_FILEMAP 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 map for every
    message. BULK_FILEMAP may only be set in a global map, its
    presence is ignored in local maps.

     If your list file is this:
       jim3@psynet.net::36373::Jim Thomason
       
    You can have a corresponding map as follows:

     $bulk->Map({
                    'BULK_FILEMAP'=>'BULK_EMAIL::ID::NAME'
                    });

    This BULK_FILEMAP will operate the same way that the local map
    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 maps,
    BULK_FILEMAPs and local maps simultaneously.

  Map precedence

    BULK_FILEMAP values will override global map values. local map
    values will override anything else. Evaluation of map control
    strings is

     local value -> BULK_FILEMAP value -> global value

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

CLASS VARIABLES
     $def_From              = 'Postmaster';
     $def_Smtp              = 'your.smtp.com';
     $def_Port              = '25';
     $def_Tries             = '5';
     $def_Subject           = "(no subject)";
     $def_Precedence        = "list";
     $def_No_errors         = 0;
     $def_Duplicates        = 0;

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

    def_From  Who will this message be from if no return address is
              specified or if it's invalid?

    def_Smtp  What's the default SMTP server to connect to? You really
              should set this variable! If you don't, you'll have to
              specify an SMTP server in every bulkmail object you
              set up. "your.smtp.com" doesn't work, it's example
              only.

    def_Port  What port on that machine should we try to connect to?

    def_Tries How many times should we try to reconnect if we fail?

    def_Subject
              What should the subject of the message be if we don't
              have one?

    def_Precedence
              What should the precedence for these messages be?

    def_No_errors
              Should we allow error checking? if No_errors is true,
              then we won't check for valid dates, time zones, email
              addresses, and precedences

    def_Duplicates
              If someone is on a list more than once, should they
              receive multiple copies of the message? =back

DIAGNOSTICS
              Bulkmail doesn't directly generate any errors. If
              something fails, it will return 0 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 type of your functions, if it's 0,
              check ->error to find out what happened.

HISTORY

    1.01	  Bug fixed.  _everyone_ should get this release.

    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
                  "headset" method. This is because methods cannot
                  have non-word characters in them. From, Subject,
                  and Precedence headers may also be accessed via
                  headset, if you so choose. AUTOLOAD now complains
                  loudly (setting ->error and printing to STDERR) if
                  it's called.

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

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

               open (LIST,   "./list.txt")            || die "Can't open list!";
               open (GOOD,   ">./good_list.txt") || die "Can't open good list!";
               open (BAD,    ">./baddata.txt")        || die "Can't open bad list!";
               open (ERROR,  ">./error.txt")          || die "Can't open error file!";
               open (BANNED, "./banned.txt")          || die "Can't open banned list!";

               $bulk = Mail::Bulkmail->new(
                      From    => $from,
                      Subject => $subject,
                      Message => $message,
                      X-Header=> "Rockin' e-mail!",
                      Map             => {
                                              '<DATE>'                => $today,
                                              BULK_FILEMAP    =>      "email::<ID>::<NAME>::<ADDRESS>"
                                              },
                      'LIST'  => *LIST,
                      'GOOD'  => *GOOD,
                      'BAD'   => *BAD,
                      'ERROR' => *ERROR,
                      'BANNED'=> *BANNED,
               );

              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 map to turn "<DATE>"
              control strings into the $today string, a BULK_FILEMAP
              to map in the name, id number, and address of the
              user. It defined the LIST as the LIST file openned
              earlier, 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();

              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.

  Single mailing

               use Mail::Bulkmail;
               
               $bulk = Mail::Bulkmail->new(
                      From    =>      $from,
                      Subject =>      $Subject,
                      Message =>      $message,
                      X-Header=>      "Rockin' e-mail!"
               );
               
               $bulk->mail(
                              'jim3@psynet.net',
                              Map             => {
                                                      '<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
              jim3@psynet.net

MISCELLANEA
              Mail::Bulkmail will automatically set three headers
              for you.

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

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

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

              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 James A Thomason III
              (jim3@psynet.net). 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 (jim3@psynet.net) 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. :-)