Howdy Again Perl Monks!

I'm tasked with sending out emails to all of the subscribers to our company's email newsletter. I'm using the MIME::Lite module to allow HTML tags and a PDF attachment. I have created a sample script that seems to work fine overall, but I have two questions.

1) What is the most efficient way to send out a group email like this? We have about 500 subscribers currently. Right now I'm grabbing all the email addresses from our database putting them into an array and then I'm going through each item in the array and sending the email message out to each one.

sub sendemail { $j = 0; foreach $i(@names){ ## Grab user info from the table $name = $names[$j]; # Get their name $email = $emails[$j]; # Get their email $subkey = $subkeys[$j]; #Get their subscriber key $remotehost = $ENV{'HTTP_HOST'}; $remotehost = "http://" . $remotehost . "/"; $locunsubscribe = 'perl/nl_unsubscribe.pl' . '?' . $subkey; $locunsubscribe = $remotehost . $locunsubscribe; mailit(); $j++; } } sub mailit{ print "<p>...Attempting to send message to user:"; print "<H4>Record for $user</H4>"; print "<UL>"; print "<LI>User: $user"; print "<LI>Name: $name"; print "<LI>Email: $email"; print "</UL>"; print "<HR>"; my $wai = 'Wilcox Associates Inc.<admin@wilcoxassoc.com>'; my $when = localtime(); my $locwai = '../Contactinfo/feedback.htm'; my $mail_host = 'mail.wilcoxassoc.com'; # Replace w/ browse button on form to get these. my $fullpath = 'd:\temp\test.pdf'; my $filename = 'test.pdf'; ### Create the multipart container $msg = MIME::Lite->new ( From => $wai, To => $email, Subject => $subject, Type =>'multipart/mixed' ) or die "Error creating multipart container: $!\n"; ### Add the text message part $msg->attach ( Type => 'TEXT/HTML', Data => "<p>Dear $name,</p><p>$body</p><p></p><p></p>To unsubscribe +from the WAI Newsletter, follow this link: $locunsubscribe<hr>THIS IS AN AUTOMATED MESSAGE! PLEASE DON'T REPLY TO + THIS EMAIL." ) or die "Error adding the text message part: $!\n"; ### Add the specified PDF file $msg->attach ( Type => 'application/pdf', Path => $fullpath, Filename => $filename, Disposition => 'attachment' ) or die "Error adding $fullpath: $!\n"; ### Send the Message MIME::Lite->send('smtp', $mail_host, Timeout=>60); $msg->send or push @notsent,$email; }
I just don't know if this is the most efficient way. Is there a way to group all the emails together into one single message? For example, in Outlook you can create a group of email addresses and it sends out a single email to each address. Or does my route essentially do the same thing?

Also, when I run my script on my local server, I get an message from my Perl editor (OptiPerl) that shows an email and has this message:

SMTP RCPT command failed: <email@here.com>: Relay access denied

and it points to this code: $msg->send or push @notsent,$email;

But my script never finishes. It apparently dies after sending out all the emails but before it goes into my results subroutine here:

# Show the results of the send. This sub checks to # see if the array contains any addresses that it coulnd't send to and + displays them: sub showresults{ print "<h3>Showing Results</h3>"; if (@notsent) { print "<font color = 'red'><p>Out of the following emails:"; print "<br>@to"; print "<p>The following were not sent:"; print "<br>@notsent</font>"; } else { print "<font color = 'blue'><p>Emails successfully sent to the +se addresses:"; print "<br>@to</font>"; } print "<p><a href=$accountmanagement>Return to the Account Management +Area</a></p>"; print "<p><a href=$locscriptdownload>Visit the Script Repository</a></ +p>"; }


In this situation, I need it to be able to simply collect the email address that it couldn't send to the subscriber and continue with the script.

2) What code should I use to capture an email address that it couldn't send to and force it to continue the script? I'm using this line but I don't think that does what I'm hoping it would:
$msg->send or push @notsent,$email;


Below contains my entire script:
#!/usr/local/bin/perl # This script sends out an email to a specified group of individuals f +rom # the database use CGI qw(:all); use DBI; use Net::SMTP; use MIME::Lite; print header; #################### # PROGRAM OVERVIEW # #################### # Is user admin? If not kill process. isadmin(); # Display an email form. # - Subject # - Content # - Send # Clicking the send button will # check if all email fields are filled out. # And if they are, will process the data, and send the # email to the selected users. # If not, it will display the form again. if (param("subject") and param("body")) { # Get the array / hash of NL subscribers getsubscribers(); sendemail(); # Show the results of the routine. showresults (); } else { displayform(); } ######################## # SUBROUTINES ########## ######################## sub isadmin { $servername = $ENV{'SERVER_NAME'}; ## Sends Environement Variables if ($servername eq "localhost" or $servername eq "JaredWork"){ $remoteuser = 'admin'; } elsif ($servername eq "www.wilcoxassoc.com"){ $remoteuser = $ENV{'REMOTE_USER'}; } else{ foreach $key (sort(keys %ENV)){ print ("<p>$key = $ENV{$key}"); } &dienice("Couldn't connect to unknown servername of: $serv +ername"); } $remoteuser = lc($remoteuser); $datasource = 'dbi:mysql:userinfo'; $dbusername = 'perlscript'; $dbpassword = 'scriptpass'; ## Connect to database $dbh = DBI->connect($datasource,$dbusername,$dbpassword) or dienic +e ("Can't connect: $! ." . $DBI::errstr ); ## Grab user info from the table $sth = $dbh->prepare("SELECT * FROM registered WHERE username = ?") or dienice ("Couldn't prepare select state +ment: $!" . $dbh->errstr); $sth->execute ($remoteuser) or dienice ("Couldn't execute prepared + statement $!" . $dbh->errstr); @row = $sth->fetchrow_array(); $user = $row[0]; # Get their username $name = $row[1]; # Get their name $email = $row[2]; # Get their email $isadmin = $row[4]; # Get the admin column if (length($name)<=0) { dienice("There is no username that matches $remoteuser in the database. Use a different username."); } if ($isadmin ne "Y" and $isadmin ne "S") { dienice("$name, You do not have priveleges to send out a mailing to the users."); } } sub getsubscribers { $sth = $dbh->prepare("SELECT * FROM subscriber") or dienice ("C +ouldn't prepare select statement to get list of NL subscribers: $!" . + $dbh->errstr); $sth->execute() or dienice ("Couldn't execute prepared statement $ +!" . $dbh->errstr); while (@row = $sth->fetchrow_array) { $name = $row[2]; # Get their names $email = $row[1]; # Get their emails $subkey = $row[7]; # Get their subkeys # print "<p>Testing... $name, $email, $subkey"; push @names,$name; push @emails,$email; push @subkeys,$subkey; } } sub displayform { print "<h2>PC-DMIS Newsletter Subscribers - Email Form</h2>"; print "<p>Use this form to send out emails to newsletter subscribers. Simply type the subject of the email and the body into the form below. You don't need to include a 'Dear (name)' line. This is handled automatically:</p>"; # print "<p>Testing...list of emails to send to: @emails</p>"; $locscriptdownload='../scriptdownload.pl'; $accountmanagement='accountmanagement.pl'; print <<FORM; <form method="post" action=""> <p><b>Subject: </b> <input type="text" name="subject" size="50"> </p> <table width="100" border="1" height="403"> <tr> <td width="66%" bgcolor="#CCCCFF" height="357"> <p><b>Body:</b></p> <p align="center"> <textarea name="body" cols="50" rows="20"></textarea> </p> </td> </tr> </table> <p> <input type="submit" name="Submit" value="Send Now"> <input type="reset" name="Reset" value="Reset"> </p> </form> <p><a href="$accountmanagement">Return to the Account Management Are +a</a></p> FORM } sub sendemail { print "<h3>Entering Send Mail Routine</h3>"; print "<p>This email message will be sent:"; $body = param("body"); $subject = param("subject"); print "<P><b>Subject:</b> $subject"; print "<P><b>Body:</b> $body<hr>"; $j = 0; foreach $i(@names){ ## Grab user info from the table $name = $names[$j]; # Get their name $email = $emails[$j]; # Get their email $subkey = $subkeys[$j]; #Get their subscriber key $remotehost = $ENV{'HTTP_HOST'}; $remotehost = "http://" . $remotehost . "/"; $locunsubscribe = 'perl/nl_unsubscribe.pl' . '?' . $subkey; $locunsubscribe = $remotehost . $locunsubscribe; mailit(); $j++; } } sub mailit{ print "<p>...Attempting to send message to user:"; print "<H4>Record for $user</H4>"; print "<UL>"; print "<LI>User: $user"; print "<LI>Name: $name"; print "<LI>Email: $email"; print "</UL>"; print "<HR>"; my $wai = 'Wilcox Associates Inc.<admin@wilcoxassoc.com>'; my $when = localtime(); my $locwai = '../Contactinfo/feedback.htm'; my $mail_host = 'mail.wilcoxassoc.com'; # Replace w/ browse button on form to get these. my $fullpath = 'd:\temp\test.pdf'; my $filename = 'test.pdf'; ### Create the multipart container $msg = MIME::Lite->new ( From => $wai, To => $email, Subject => $subject, Type =>'multipart/mixed' ) or die "Error creating multipart container: $!\n"; ### Add the text message part $msg->attach ( Type => 'TEXT/HTML', Data => "<p>Dear $name,</p><p>$body</p><p></p><p></p>To unsubscribe +from the WAI Newsletter, follow this link: $locunsubscribe<hr>THIS IS AN AUTOMATED MESSAGE! PLEASE DON'T REPLY TO + THIS EMAIL." ) or die "Error adding the text message part: $!\n"; ### Add the specified PDF file $msg->attach ( Type => 'application/pdf', Path => $fullpath, Filename => $filename, Disposition => 'attachment' ) or die "Error adding $fullpath: $!\n"; ### Send the Message MIME::Lite->send('smtp', $mail_host, Timeout=>60); $msg->send or push @notsent,$email; } # Show the results of the send. This sub checks to # see if the array contains any addresses that it coulnd't send to and + displays them: sub showresults{ print "<h3>Showing Results</h3>"; if (@notsent) { print "<font color = 'red'><p>COULD NOT SEND TO the following +emails:"; print "<br>@notsent</font>"; } else { print "<font color = 'blue'><p>All emails were sent."; } print "<p><a href=$accountmanagement>Return to the Account Management +Area</a></p>"; print "<p><a href=$locscriptdownload>Visit the Script Repository</a></ +p>"; } #################### ### DYING NICELY ### sub dienice { my($errmsg) = @_; print <<DIENICE; <HTML><HEAD><TITLE>ERROR!</TITLE></HEAD> <BODY bgcolor="#CDDAE0" text="red" link="#000066" vlink="#6666 +66" alink="#CCCCCC"> <H1>ERROR...</H1> <P>$errmsg</P> </BODY> </HTML> DIENICE exit; }

In reply to Most efficient way to send mass email? by JaredHess

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.