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"). 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 "Jimconnect || 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 => { '' => $today, BULK_FILEMAP => "email::::::
" }, '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 "" 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 => { '' => $today, '' => 36373, '' => 'Jim Thomason', '
' => '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. :-)