Season's Greetings Mass Mailer
In my last article for Linux Journal, (http://www.linuxjournal.com/content/best-wishes-new-year) I shared some of my New Year's Resolutions. One of those resolutions was to communicate more regularly with my friends and family. In this article, I'm going to describe one of the first steps I took toward making this happen.
A few months ago, I left a job that I had been at for 14 years, so I had accumulated quite a few people at work that I cared about. As I was saying good-bye to many of my soon to be ex-coworkers, I promised to get an email out to them as soon as I could to let them know how I was doing. That was, like I said, a few months ago and the letter never went out; life just seemed to get in the way and I never got around to it.
I knew I didn't have time to write an individual letter to each of my friends. Doing the old copy-paste routine is actually pretty time consuming and more error prone than you might imagine; it's real easy to forget to add a subject, or to not get all of the message into the clipboard, etc. What I needed was an efficient, error proof means of sending out an email to all of my friends and family. I'm a programmer by trade, so the solution was obvious. I understand that there are unscrupulous people out there who have built a business model out of this very same solution, but that's not my intention. I just don't want to have to hand-address 100 emails, or worse yet, that many envelopes!
As I have written before, (http://www.linuxjournal.com/content/managing-your-life-egroupware) I use E-Groupware to store just about every piece of information that is important to me. So, I had a ready-made list of friends and their email addresses. I guess I could have used EGW's email distribution mechanism, but I don't use EGW's email client and wanted something a bit more manageable. What I ended up doing was creating a new contact category. Then I assigned this category to the people I wanted to receive my newsletter. As time goes on, I'll be able to manage my “Christmas Card List” with EGW. Nice.
Writing a program that speaks SMTP is one thing. My program needed to format the message properly for a variety of mail clients. What's worse is that I wanted to send a brief text message and attach my newsletter as a .pdf file. I also wanted to attach a family picture as a .jpg file. My initial thoughts were that this was going to be difficult, even in perl. Man, was I wrong! It turned that by using the MIME::Lite module, it was almost trivial. Let's look at some code.
1 #!/usr/bin/perl
2
3 use DBI;
4 use MIME::Lite;
5
6 $dbh = DBI->connect("dbi:Pg:dbname=db_name;host=example.com”, "db_user", "passwd");
7
8 $sth = $dbh->prepare("select cat_id from egw_categories where cat_name='Christmas'");
9 $sth->execute();
10 ($id) = $sth->fetchrow_array();
11
12 open FILE, "./2008.txt";
13 @data = <FILE>;
14 $data = join("", @data);
15
16 $clause = "(cat_id = \'$id\' or cat_id like \'$id,%\' or cat_id like \'%,$id,%\'
or cat_id like \'%,$id\')";
17
18 $sth = $dbh->prepare(" select contact_email from egw_addressbook where $clause
19 and contact_email is not null
20 union
21 select contact_email_home from egw_addressbook where $clause
22 and contact_email_home is not null");
23 $sth->execute();
24
25 while ((@a) = $sth->fetchrow_array()) {
26 print "X: $a[0]\n";
27
28 $a[0] = "mdiehl\@diehlnet.com";
29
30 $message = MIME::Lite->new(
31 From => "mdiehl\@diehlnet.com",
32 To => $a[0],
33 Subject => "Greetings!",
34 Data => $data,
35 );
36
37
38 $message->attach(
39 Type => "application/pdf",
40 Encoding => "base64",
41 Path => "./2008.pdf",
42 Filename => "2008.pdf"
43 );
44
45 $message->attach(
46 Type => "image/jpeg",
47 Encoding => "base64",
48 Path => "./2008.jpg",
49 Filename => "2008.jpg"
50 );
51
52 MIME::Lite->send("sendmail", "smtp.example.com", timeout=>20);
53
54 $message->send;
55 }
Lines 1-7 of the program are mere boilerplate. In lines 8-10, I look up the index number for the contact category named “Christmas.” We'll use that index number later on, so we keep in a variable.
Lines 12-14 are where I read in an external file for use as the brief message I mentioned earlier. This method allowed me to create the file with my favorite editor and even spell-check it before I sent it. I also didn't have to hard-code my message in my program, which is a double plus.
I'll come back to line 16 in a moment, but first let's talk about the SQL in lines 18-23. Some of my friends have business email addresses and some have personal email addresses. Some have both. EGW allows me to store these addresses separately. I wanted to write a single SQL query that would fetch all of these addresses in a single column so that my perl script would be more intuitive. What I did is wrote a query for the home address and a query for the business address and took the union of the two queries. Most of the selection logic is tied up in that $clause variable that I defined on line 16.
I hate the way EGW associates a category with a contact. EGW uses the cat_id field to store a comma-delimited list of categories that a contact belongs to, and the list isn't even sorted. So, in line 16, I build a clause that looks for our category id in the cat_id field, wherever it may appear in the field. The value may be the only one on the list, in which case, there will be no commas. It may also be the first, middle, or last entry on the list. I assigned this clause to a variable because both halves of the SQL union needed the same logic and simply copying and pasting it in would obscure the logic. It's ugly, but it works. Now that you know what it's supposed to do, it's not too hard to understand.
On line 25, we start to loop over each record so we can send them an email. Line 26 is just a print statement so I can be sure the program is running properly. Line 28 is a bit more interesting. Here I overwrite the data fetched from the database with my own email address. This is a debugging trick. I didn't want to debug this program and risk sending a bunch of garbage emails to people I claim to care about. Just before I sent my letter out, I removed this line and the program worked as expected... and tested.
In lines 30-35, we create a new email message with the MIME::Lite module. We also assign values to the From:, To:, and Subject: headers. The text message that we read in earlier is put into the email at this point, using the Data field.
Then we create 2 file attachments in lines 38-50. The first one is the .pdf file while the second one is the .jpg file. There are a few things that are noteworthy about this part of the code. First, I had to assign a mime time to each attachment, as well as an encoding scheme. This much is pretty intuitive from looking at the code.
Another thing that is worth mentioning about these attachments is that I was able to specify the name of an external file that I wanted attached, instead of having to read the file in myself. Also, I was able to specify a “suggested” filename for the attachment in the event that one of my friends actually wants to save the file to their hard drive.
Lines 52-55 are where I send the message out the door and close the main loop. I had initially tried to use my own mail server as the gateway, but ran into a problem. My server, for some reason, was refusing to relay the messages. Certainly, I want to be a good Net Citizen and not be an open relay, but I should have been able to use my server to send regular emails. I never resolved this issue and simply used my ISP's relay.
When I was contemplating this program, I was a bit concerned that my friends might find it a bit tacky that I essentially wrote a mass-mailer in order to send them an otherwise impersonal email greeting. So far, no body seems either to have noticed, or cared that the message wasn't written directly to them. I've received replies indicating that they were happy to receive my message and enjoyed the family picture. Overall, I think the process was a success. There's also the added benefit of having the program and contact list ready for use the next time I want to say hi to my friends or customers.
Mike Diehl is a freelance Computer Nerd specializing in Linux administration, programing, and VoIP. Mike lives in Albuquerque, NM. with his wife and 3 sons. He can be reached at mdiehl@diehlnet.com
Today’s modular x86 servers are compute-centric, designed as a least common denominator to support a wide range of IT workloads. Those generic, virtualized IT workloads have much different resource optimization requirements than hyperscale and cloud applications. They have resulted in a “one size fits all” enterprise IT architecture that is not optimized for a specific set of IT workloads, and especially not emerging hyperscale workloads, such as web applications, big data, and object storage. In this report, you will learn how shifting the focus from traditional compute-centric IT architectures to an innovative disaggregated fabric-based architecture can optimize and scale your data center.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
| Non-Linux FOSS: Seashore | May 10, 2013 |
| Trying to Tame the Tablet | May 08, 2013 |
| Dart: a New Web Programming Experience | May 07, 2013 |
- RSS Feeds
- New Products
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Drupal Is a Framework: Why Everyone Needs to Understand This
- A Topic for Discussion - Open Source Feature-Richness?
- Home, My Backup Data Center
- Dart: a New Web Programming Experience
- Developer Poll
- May 2013 Issue of Linux Journal: Raspberry Pi
- Trying to Tame the Tablet
- great post
4 min 22 sec ago - Google Docs
26 min 53 sec ago - Reply to comment | Linux Journal
5 hours 15 min ago - Reply to comment | Linux Journal
6 hours 2 min ago - Web Hosting IQ
7 hours 35 min ago - Thanks for taking the time to
9 hours 12 min ago - Linux is good
11 hours 10 min ago - Reply to comment | Linux Journal
11 hours 27 min ago - Web Hosting IQ
11 hours 57 min ago - Web Hosting IQ
11 hours 58 min ago
Enter to Win an Adafruit Prototyping Pi Plate Kit for Raspberry Pi

It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Prototyping Pi Plate Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- Next winner announced on 5-21-13!
Free Webinar: Linux Backup and Recovery
Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.
In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.



Comments
Great guide
Hi there,
Thanks for posting your guide, I was unsure whether MIME::Lite was suitable for use in a mass-emailer, good to have your example and experience proving that it is.
Jeez!
Lines 30-54 are super simple. I love it!