Skip to page content or skip to Accesskey List.
Search evolt.org
evolt.org login: or register

Work

Main Page Content

Simple CGI Email Subroutine

Rated 3.91 (Ratings: 1) (Add your rating)

Log in to add a comment
(8 comments so far)

Want more?

 
Picture of MartinB

Martin Burns

Member info | Full bio

User since: April 26, 1999

Last login: October 04, 2009

Articles written: 128

When I'm writing CGI programs, I find that many - if not most - need to send email at some point. Rather than rewrite a mailing routine every time, I produced a simple reuseable Perl subroutine. What you're about to see is a simplified version of it, for you to build on yourself if you like.

Here's the subroutine. You'll need to save it locally, rename it with a .pl file extension, and upload it to your webserver.

System requirements

You'll need 3 things:

  1. A Unix server. It won't work on NT, but it might on MacOSX Server.
  2. Access to Sendmail or an equivalent. Exim and qmail usually work just fine, but check with your sysadmin.
  3. Perl5. Perl4 *should* work, but I can't promise anything.

How to use it.

All you have to do is require the file, tell it where your Sendmail is, and call the subroutine with appropriate variables. Here's the most trivial example:

!#/usr/bin/perl
require '/path/to/sendmail_evolt_org.pl';
$SENDMAIL = '/usr/bin/sendmail';
&simple;_sendmail('Hello!', 'scripts@easyweb.co.uk', 'martin@easyweb.co.uk', 'Hello World');
Easy!

The general format of the subroutine variables is:

subject, from, to, body, cc, bcc
The body, cc and bcc variables are completely optional, and you can either have each variable literally used as above (make sure you use single quotes, or your @ signs will cause trouble), or derived from variables:
&simple;_sendmail($subject, $from, $to, $body, $cc, $bcc);

Limitations

  • This subroutine doesn't check the validity of email address formats. If you're deriving any address from user input, it's wise to add some validation.
  • This subroutine doesn't attach files or support HTML email.
  • You can only use one recipient in each of the from, cc and bcc fields.
None of these limitations is particularly taxing to rectify.

The routine

Let's step through it.

&nbsp;<font color='#0000cc'>67:</font> sub simple_sendmail {<br>
<font color='#0000cc'>102:</font> }<br>
<font color='#0000cc'>103:</font> 1;
This is the bare outline of any subroutine. Because we're requiring the file, it works as if the file is part of the main script, so you don't need a #!usr/bin/perl line. All you have to do is ensure that it's in correct syntax, and the last line evaluates to true.

<font color='#0000cc'>69:</font> local($subject, $from, $to, $body, $cc, $bcc) = @_;
This grabs the incoming data (stored in the Perl special variable @_), and puts it into sensible variable names. Because we're using local variables, there won't be any problems if you've used $to (etc) in your main script.

<font color='#0000cc'>73:</font> open(MAIL, &quot;&#124;$SENDMAIL -t&quot;);<br>
    <font color='#0000cc'>75:</font> $&#124; = 1;
This is the main engine of the script. Unix works in such a way that everything - programs included - can be considered as a file. So what we do here is open a connection to the 'file' that is the Sendmail program, and attach it to a filehandle, MAIL; just as if we were outputting to a real file. Line 75 ensures that each line will be dispatched as it's output, which will give a more reliable result.

<font color='#0000cc'>80:</font> print MAIL &quot;To: $to\n&quot;;<br>
    <font color='#0000cc'>81:</font> print MAIL &quot;From: $from\n&quot;;<br>
    <font color='#0000cc'>82:</font> print MAIL &quot;CC: $cc\n&quot; if $cc;<br>
    <font color='#0000cc'>83:</font> print MAIL &quot;BCC: $bcc\n&quot; if $bcc;<br>
    <font color='#0000cc'>84:</font> print MAIL &quot;Subject: $subject\n&quot;;
These are the functional headers, which tell the email where to go, who it's from, and what its subject is.

<font color='#0000cc'>85:</font> print MAIL &quot;x-mailer: EasyWeb Design - email scripts@easyweb.co.uk\n&quot;;<br>
    <font color='#0000cc'>86:</font> print MAIL &quot;x-copyright: simple_sendmail 1.0, Copyright (C) 1999 Martin Burns\n&quot;;<br>
    <font color='#0000cc'>87:</font> print MAIL &quot;x-license: GNU General Public License version 2\n&quot;;<br>
    <font color='#0000cc'>88:</font> print MAIL &quot;x-license_url: http://evolt.org/index.cfm?menu=8&cid=137&catid=17\n\n&quot;;
If you've looked at our mail headers tutorial, you'll remember that any header which begins with x- is ours to play with. So I've used it to add an unobtrusive credit to myself for the routine. Most people won't notice it, but someone who's in a position to give you work just might. I've also put on the licensing info for the routine, so that anyone literate enough to be interested in how it's done can find out what restrictions I've put on its distribution.

I also use the x-mailer header - typically used by email clients - to distinguish incoming email from forms. As I receive a bcc from most forms I install for monitoring and diagnostic purposes, I can filter them into my deleted mail folder, where they sit for 24 hours before they're auto-deleted. My mail client (Claris Emailer) also colour codes them for me, so it's easy to keep an eye what's going on.

The other important thing to notice is that, while most of the other headers end with a line-break to separate them, the last one ends with two. This is how mail clients know where the headers end, and the message body begins - there's a blank line between the two.

Finally, we get to print the body of the message, if there is one:

<font color='#0000cc'>93:</font> if ($body) {<br>
    <font color='#0000cc'>94:</font> &nbsp;&nbsp;print MAIL $body;<br>
    <font color='#0000cc'>95:</font> }<br>
    <font color='#0000cc'>96:</font> print MAIL &quot;\n&quot;;
And there's another blank line at the end.

The very last thing we need to do is close the file handle:

<font color='#0000cc'>101:</font> close(MAIL);

So there you have it - a simple, all-purpose mail subroutine.

Martin Burns has been doing this stuff since Netscape 1.0 days. Starting with the communication ends that online media support, he moved back through design, HTML and server-side code. Then he got into running the whole show. These days he's working for these people as a Project Manager, and still thinks (nearly 6 years on) it's a hell of a lot better than working for a dot-com. In his Copious Free Time™, he helps out running a Cloth Nappies online store.

Amongst his favourite things is ZopeDrupal, which he uses to run his personal site. He's starting to (re)gain a sneaking regard for ECMAscript since the arrival of unobtrusive scripting.

He's been a member of evolt.org since the very early days, a board member, a president, a writer and even contributed a modest amount of template code for the current site. Above all, he likes evolt.org to do things because it knowingly chooses to do so, rather than randomly stumbling into them. He's also one of the boys and girls who beervolts in the UK, although the arrival of small children in his life have knocked the frequency for 6.

Most likely to ask: Why would a client pay you to do that?

Least likely to ask: Why isn't that navigation frame in Flash?

Submitted by gampid on August 31, 1999 - 13:41.

This seems like the wrong way to do this. I just wrote a quick little module that takes both CGI.pm like arguments and getters and setters for the mail headers and body. It then can handle dealing with sendmail without me having to mess up my code with any mention of the beast. -Gampid

login or register to post comments

Submitted by artboy on August 31, 1999 - 23:02.

You can also use Mail::Mailer and just create a "mail" object and feed it a hash containing your header info.

login or register to post comments

Submitted by MartinB on September 1, 1999 - 03:36.

As the Perl line goes - "There's more than one way to do it". I prefer not to have the layers of abstraction, but YMMV. If either of you would like to write a tutorial on your preferred method, that would be very welcome indeed.

login or register to post comments

Submitted by kellan on September 1, 1999 - 13:46.

I'm loath to admit that Tom Christiansen is every right, but as Tom once said on comp.lang.perl.misc, "Just because there is more then one way to do things, doesn't make it right!" (or something like that) And off the top of my head I can't think of time when abstraction isn't a good thing. Some problems with using sendmail: 1) If you don't have sendmail installed(i.e. a non-Unix system, or a security conscious system) 2) If you don't want to pay the price of forking sendmail which is a pretty hefty program. 3. Even if you are on Unix, and you've got sendmail, and you are planning to stay there into the forseeable future the code looks kind of clunky :) 4. You want to do something fancy like MIME attachments. Some other possible solutions: 1) Mail::Mailer (available at CPAN) use Mail::Mailer qw(smtp); my $mailer = new Mail::Mailer 'smtp'; $mailer->open(To => ['me@foo.com'], Cc => ['you@bar.com'], From => 'us@quxx.com' Subject => 'Using Mailer 101' ); print $mailer "And thats how you use Mail::Mailer\n"; $mailer->close; 2) Net::SMTP if you have some desire to speak raw SMTP And I don't mean to pounce. I just had an allergic reaction to the abstraction comment :) (Hmmm. I wonder what this is going to look like. This is my first post here and this itsy-bitsy text box leaves a little to the imagination.)

login or register to post comments

Submitted by MartinB on September 2, 1999 - 11:58.

If you've got the module(s), that's fine. My way works fine and reliably if you've got a sendmail-type app (most commercial hosts will, even if it's Exim, which isn't as clunky not so expensive). If you think abstraction is a bad thing, what do you think the module's doing? And Apache? And browsers which don't hook themselves into your OS? As I said - a full tutorial and explanation of the modules would be cool.

login or register to post comments

Submitted by gampid on September 2, 1999 - 12:13.

I think what he was trying to say is abstraction is a really good thing. Also, if you're having problems with low end web hosters not giving you access to install you're own modules, or access MySQL check out Pair.com. They're pretty cheap and reliable.

login or register to post comments

Submitted by MartinB on September 2, 1999 - 15:43.

Yes, you're totally right; I misread it. I guess having written comments arguing both sides, you can go either way. All I can say is
  1. The above subroutine works very well
  2. It teaches people a bit about building libs
  3. It teaches people a bit about Unix
  4. It gives people a basis to build on (like attachments, PGP and so on).
Not bad for 40 lines of code...

login or register to post comments

Submitted by MartinB on January 28, 2000 - 14:16.

Just spotted a typo in the article.
&simple;_sendmail($subject, $from, $to, $body, $cc, $bcc);
should have been
&simple_sendmail($subject, $from, $to, $body, $cc, $bcc);
The additional semicolon at the start of the line will tell Perl that the line ends there, so Perl will try to parse the remaining code as a line on it's own, which will give a syntax error.

login or register to post comments

The access keys for this page are: ALT (Control on a Mac) plus:

evolt.orgEvolt.org is an all-volunteer resource for web developers made up of a discussion list, a browser archive, and member-submitted articles. This article is the property of its author, please do not redistribute or use elsewhere without checking with the author.