NAME
    Mail::Bulkmail - Platform independent mailing list module

AUTHOR
    Jim Thomason jim3@psynet.net

SYNOPSIS
     $bulk = Mail::Bulkmail->new(
            "LIST"  => "/home/jim3",
            From    => 'jim3@psynet.net',
            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 on the fly.

    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    =>      'jim3@psynet.net',
                            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 will not be set if $bulk->Trusting has a true 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 (jim3@psynet.net), or
               a name + e-mail address ("Jim
               Thomason"<jim3@psynet.net>). 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: jim3@psynet.net,
               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 BMUST 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.

    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 the 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 = ('jim3@psynet.net', 'someguy@somewhere.com', 'invalid_@address');
                $bulk->(\@list);
                
                #or, with an anonymous array
                $bulk->(['jim3@psynet.net', '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 list 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 = ('jim3@psynet.net', '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 MAILING");
                $sth->execute;
                
                $bulk->LIST(\&{$sth->fech_row_array});

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

               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
               push-ed 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 ".jim3@ psynet.net" 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 to (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.

               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

    banned     banned will allows you to provie 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.

                jim3@psynet.net
                yahoo.com

               would ban email from jim3@psynet.net, 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

                JIM3@psynet.net
                jim3@psynet.net
                Jim3@psynet.net
                
               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 "JIM3@psynet.net" and then try to
               unsubscribe using "jim3@psynet.net" 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 "JIM3@PSYNET.NET" will also ban
               "jim3@psynet.net", and "JiM3@PsyNet.net". 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*: header

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

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

               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 of 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', 'jim3@psynet.net');

    Will set a "Reply-to" header to the value of "jim3@psynet.net".
    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', '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 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            =>      '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.

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

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

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

    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*

    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, 0 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, 0 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 error messages will be logged if you
                         specifed an ERRFILE file.

MERGING
               Finally, the mysterious merging section so often
               alluded to.

               Mail merging is exactly the same as "file merging"
               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 merging 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 map 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    =>      jim3@psynet.net,
                                               merge           => {
                                                                               'DATE' => 'today',
                                                                               'company' => 'Playboy Enterprises'
                                                                       }
                                       );

               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(
                                       'jim3@psynet.net',
                                       {
                                               '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.

               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.

               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:
                  jim3@psynet.net::36373::Jim Thomason
                  or
                  ["jim3@psynet.net", "36373", "Jim Thomason"]
                  or
                  {
                       "BULK_EMAIL" => "jim3@psynet.net,
                       "ID"             => "36373",
                       "NAME"           => "Jim Thomason"
                  }
                  
               You can have a corresponding merge as follows:

                $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 2.00, 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)
                       jim3@psynet.net-+-ID #1-+-Jim Thomason
                       jimt@playboy.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

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

                local value -> BULK_MAILMERGE 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=yes,Iguanas=important

               To specify that I want info on bears and rabbits, but
               not 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!
                I see that you like rabbits.  Rabbits are cool.
                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 value
               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
                my $def_From           = 'Postmaster';
                my $def_To                     = 'postmaster@your.smtp.com';
                my $def_Smtp           = 'your.smtp.com';              #<--Set this variable.  Important!
                my $def_Domain         = "playboy.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_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 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
    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 (filemerge 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 "headset" 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();

               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(
                               'jim3@psynet.net',
                               {
                                       '<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

  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" => "jimt\@playboy.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\@playboy.com",
                                       "test" => "test\@playboy.com"
                               },
                               "Marvelous" => {
                                       "Max" => "Max is marvelous!",
                                       "Charlie" => "Charlie is marvelous!"
                               }

                       }
                ) or die "Cannot create object!";

               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.

               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 send
               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 25 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, 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.

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

               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.

    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:....) *only if using the
                   envelope*

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