Perlfect Solutions
 

Sending email using perl and sendmail.

A very common task for a cgi script is to be able to inform a set of users with data generated by itself or other programs, cgi's or not. For example, you might be one of the web designers who have joined one of the myriad of free counter programs on the internet that email you with nice statistics and reports about your web pages' traffic. Systems like that are responsible for informing such a large number subscribers that sending the reports manually would require a full-time employee devoted to this task only. Obviously this wouldn't be a sensible option even for a relatively large organization.

The way to automate this task is to let a perl program do those tedious bits of work for you. In this article we will build a perl script which does exactly that. We are going to go step by step giving explanations and analyzing the tricky parts.

Perl, being perl, provides the programmer with more than one ways to do same thing, sending email included. In this script we are going to use sendmail. Sendmail, is an open source program used on most unix computers and some nt workstations as well. Sendmail as its name implies has the ability to send email! We are going to use perl's ability to open pipes to programs to run sendmail and feed it with input. If you are not familiar with sendmail it doesn't really matter though; you should just understand that sendmail is able to send an email, with its headers and content, to your mail gateway which will in turn forward it to its recipient(s).

Here is a very simple program that emails a confirmation to a user that his/her request to subscribe to a newsletter has been accepted:

#!/usr/bin/perl -w use strict; use CGI; use Email::Valid; my $query = new CGI; # it is important to check the validity of the email address # supplied by the user both to catch genuine (mis-)typing errors # but also to avoid exploitation by malicious users who could # pass arbitrary strings to sendmail through the "send_to" # CGI parameter - including whole email messages unless (Email::Valid->address($query->param('send_to'))) { print $query->header; print "You supplied an invalid email address."; exit; } my $sendmail = "/usr/sbin/sendmail -t"; my $reply_to = "Reply-to: foo\@bar.org\n"; my $subject = "Subject: Confirmation of your submission\n"; my $content = "Thanks for your submission."; my $to = $query->param('send_to')."\n"; my $file = "subscribers.txt"; unless ($to) { print $query->header; print "Please fill in your email and try again"; } open (FILE, ">>$file") or die "Cannot open $file: $!"; print $to,"\n"; close(FILE); my $send_to = "To: ".$query->param('send_to'); open(SENDMAIL, "|$sendmail") or die "Cannot open $sendmail: $!"; print SENDMAIL $reply_to; print SENDMAIL $subject; print SENDMAIL $send_to; print SENDMAIL "Content-type: text/plain\n\n"; print SENDMAIL $content; close(SENDMAIL); print $query->header; print "Confirmation of your submission will be emailed to you.";

A note about security

Before attempting to explain how the script works here is an important security note: always validate user supplied input. In the case of our CGI mailer the "send_to" parameter comes from a user submitted form and hence could be exploited by a malicious party to pass arbitrary arguments to the sendmail program. To avoid this hazard we utilize the Email::Address module from CPAN to check the conformance of the supplied email address. If the address is invalid - because of a genuine typing error or an exploitation attempt - we return an error message. Otherwise, we proceed with emailing the confirmation using the technique described in the rest of this article.

How the script works

At first glance you can notice that this a relatively small program which if it wasn't that verbose would be even smaller. Looking through it you will also see that it is very simple to understand even for the Perl beginner; however it more than fullfils the task of sending email.

Let's have a look at it line by line... The cgi script takes its input from a web form. This hypothetical form consists one text input field:

<FORM method="POST" action="http://perlfect.com/cgi-perlfect/cgimail.pl"> <INPUT type="text" name="send_to"> <INPUT type="submit"> </FORM>

The script uses the CGI.pm module to parse the form data. If you are not familiar with that module I suggest that you read and learn about it as it will make you life as a scripter a lot happier. The param() function provided by CGI.pm returns the value of a form field given its name as an argument and that's all you need to know for now; hence we use it in our script to find out what the user has entered in the text box. If the user has not entered anything the script returns an error message prompting the user to try again after filling in the appropriate text field.

If the user has entered an email address this is appended to a text file for later use by another program and then the script procedes to return a confirmation email to the user.

An email message consists of some headers and the content. There are many standard headers but the ones you will most commonly encounter and the one we use here are:

To: A comma separated list of recipient addresses.
From: The email address of the sender.
Reply-to: The email address to whic replies should be sent.
Subject: The subject of the message.
Content-type: The MIME type of the content.

The headers precede the content of the message. The content type header is written just before the content and is followed by two newline characters.

Sendmail has the ability, as most unix programs, to read from standard input hence all we need to do is a open a pipe to it and provide it with the input we want it to process. You will notice that we have given the -t option to sendmail. This merely tells sendmail to scan the message for a To:, Cc: or Bcc: header and extract the list of recipients from there. Having opened the pipe succesfully we print the message to it. First the headers, each one followed by a newline character, the a newline by itself and finally the content of the message. Finally we close the pipe. The email has been succesfully sent!

Here is a list of useful things you can do by using sendmail and perl:

  1. Inform visitors of your site that have asked, that your site has been updated. The script used as an example here would be a good way to collect the addresses of the people you want to email.
  2. Inform yourself of the way your scripts are running. For example you can write a few lines of code that email you when something goes wrong in a script that you 've written.
  3. Create an online mailing list.

These are only some of the things you can do, but there is one thing you shouldn't do, except if you are really nasty. That is, do not spam people. Never email people that have not asked for the information you are providing as it will probably make them angry and in the future they will ignore any that corespondence from you. Have fun and be polite!

Online Documentation/Tutorials

  • Your sendmail program's man pages will provide more detailed info about sending mail.
  • Computing Securely, a collection of security tips from Randal L. Schwatz.
  • You might want to have a look at the documentation of the Mail:: modules available at CPAN. There are also many other modules for sending and processing mail there.

Digg! Save This Page

Comments

Anonymous   

Posted at 2:29pm on Thursday, March 15th, 2007

awesome site

srikumar   

Posted at 11:47pm on Wednesday, March 21st, 2007

This information was really useful. As i am a beginner of perl script, i was able to get the full information in a more easy way.

Luthra   

Posted at 6:32am on Thursday, March 22nd, 2007

Thanks for the help!

Anandhakumar   

Posted at 6:56am on Thursday, March 22nd, 2007

I m new to perl even i can understand this article.Thanks a lot....

Anonymous   

Posted at 6:30am on Friday, March 30th, 2007

hi there,
i want to find a way to eliminate the bad urls which is in the form of binary format from a set of URL's ... can anyone suggest a way to do that using perl script?

zino   

Posted at 5:45am on Saturday, March 31st, 2007

thank you for your short tutorial.
can you please recommend books for me. picuroil@yahoo.ie
Best Regards,
Zino.

amit   

Posted at 4:51am on Thursday, April 5th, 2007

this is good

IgorManchofsky   

Posted at 10:47pm on Thursday, April 5th, 2007

How secure is it to use this?

Ajay   

Posted at 11:52pm on Thursday, April 12th, 2007

Nice script.It reduce my head ache :) now i can use this script as template for all projects. Thanks

Lokanath Reddy   

Posted at 2:09am on Friday, April 13th, 2007

Nice Site....

cg-i   

Posted at 11:05am on Saturday, April 14th, 2007

very nice cgi script

Norvin Whitney   

Posted at 10:11am on Thursday, April 19th, 2007

How to attach a zip file to this
Cheers

vishnu   

Posted at 12:45am on Sunday, May 6th, 2007

Please check out this site.

http://www.youtube.com/watch?v=5P6UU6m3cqk

when you this site and

reply your name,telphone,email.

Your article is very very good.

Keep Smileing .........

Prashanth   

Posted at 11:50am on Monday, May 14th, 2007

I am beginner in perl and found it very useful. Thanks

monkey   

Posted at 11:01pm on Tuesday, May 29th, 2007

Look at the 3rd print to SENDMAIL - it won't have the "To:".
And how about foo@bar.org - @bar will try to be resolved.
Put a "use strict;" as the 2nd line of code...
Add "-w" on the perl binary path, or "use warnings;"

Frank Wiles   

Posted at 9:39am on Thursday, May 31st, 2007

I've been using the MIME::Lite:TT and MIME::Lite::TT::HTML perl modules recently for sending E-mail. I love them, less coding, ability to use templates for your messages.

I even wrote a small howto article on the subject, which you can view at:
http://www.revsys.com/writings/perl/sending-email-with-perl.html

MK   

Posted at 2:40pm on Wednesday, June 6th, 2007

Seems like it is definitely for beginners who like to use file handles for i/o or server communication. Would be more interesting if appropriate CPAN modules would be listed. This is beginner-level.

Rajeev NR   

Posted at 3:32am on Friday, June 15th, 2007

wait

Digitalpbk   

Posted at 7:17pm on Sunday, June 17th, 2007

thanx
http://digitalpbk.blogspot.com

Roshan K Joy,SATP,SERC,IISc Bangalore   

Posted at 9:49pm on Sunday, June 17th, 2007

thanx for a cute code like this whicj would give me a smile from my Boss

Randal L. Schwartz   

Posted at 8:06pm on Thursday, June 28th, 2007

This code, if installed, allows arbitrary spam to be sent from your domain, and YOU will get the blame and YOU will get the blacklist entry.

DO NOT USE THIS CODE. Get some clues. {sigh}

Please read my http://www.stonehenge.com/merlyn/UnixReview/col48.html

Perlfect   

Posted at 7:38pm on Friday, June 29th, 2007

Good point Randal, the code and the article have been updated.

Ashok   

Posted at 6:11am on Friday, July 13th, 2007

This is very Good!.. Keep going..

Jeff   

Posted at 12:26pm on Sunday, July 15th, 2007

Yet more rubbish code which gives Perl a bad name. Thanks for proving any fool who can read a script to distribute spam. No use strict; use warnings; ? Stupidity

Dirsh-   

Posted at 5:46pm on Monday, July 16th, 2007

Can someone help me with a project. I guess first I am just curious if its even possible... Here is the run down.

The scope of this project is to create a script that would automate an email response using Outlook 2007 / 2003. This script will produce three automated emails, the senders information is pulled from an excel list that is manually maintained with various information; 3 of which will be constant ... [email address], [first and last name], and [date of the inquisition]. I believe these are the only constants needed for this ... but I could be very wrong.

When a prospect inquires about XYZ their [email address], [first and last name], and [date of the inquisition] is acquired and stored in an excel spreadsheet. I would like to develop a script that would run as a scheduled task and perform these things:

1) Send a pre-drafted email 2 days after the inquisition date on the spread sheet.

2) Send a different pre-drafted email 2 weeks after the inquisition date.

3) Send a third pre-drafted email 4 weeks from the inquisition date.

*The emails would be merged with the information from spreadsheet to attain the first name making it personalized*

*Being that the script would run as a scheduled task everyday. The script would base all calculation of dates on the day the script was run.*

Bob Roske   

Posted at 11:33pm on Sunday, August 5th, 2007

Use the year plus the Julian date and store two days out, 14 days out and 28 days out in 3 fields. If the computed days are > 365 subtract 365 and increment the year. Read the fields and send the appropriate email based on which field matches.

yuvraj   

Posted at 5:07am on Wednesday, August 8th, 2007

wen i m checking the status of send mail it shows running but when i send mail through the mail function of php then it gives a error. and shows unrecognise host name.

srk   

Posted at 2:38am on Friday, August 31st, 2007

Hi,

I am using Mail::Sendmail in Perl to send mail. But I am getting an error message "RCPT TO: error (550 5.7.1 unable to relay for xxx@xxx.xx)"... My SMTP is running. I guess it is an SMTP error. Can any one solve this problem??

Thanx...

bagel   

Posted at 1:17pm on Wednesday, October 3rd, 2007

I have been told that the perl script needs to send HTTP headers before the email message. How do you do this?

asaverus   

Posted at 5:21pm on Thursday, November 8th, 2007

Thanx, elegant and clear script. It's great when something runs first time eh.

torkel   

Posted at 4:12pm on Wednesday, December 12th, 2007

You should check the return value of close(SENDMAIL). I think it contains the exit status of sendmail.

orina   

Posted at 2:01am on Wednesday, January 9th, 2008

i amb using Mail::CheckUser instead of Email::Valid. Is this the same?

praveen   

Posted at 8:58am on Monday, February 4th, 2008

this is good

Puneet   

Posted at 10:19am on Wednesday, February 27th, 2008

This is well explained

Charlie   

Posted at 8:10pm on Friday, March 7th, 2008

Hey, whiners, what did you think you were getting when you googled this address? Be nice to the people that get you started. Yeah, yeah, use strict. use politeness; use common::decency.

Steve Ki   

Posted at 6:29pm on Wednesday, March 12th, 2008

How can I attach a zip file to above example

Anonymous   

Posted at 6:48am on Wednesday, March 19th, 2008

In the script, the "print" statement that prints
$to to FILE appears to be missing the FILE arg, i.e.

print FILE $to,"n";

right or wrong?

Borys Marcelo   

Posted at 12:37pm on Friday, May 2nd, 2008

Hi, here is a simple version of the script above for those like me that only need to send email without using CGI:

my $EMAIL_BODY="MY EMAIL BODY";
my $EMAIL_TO="foo@bar.org";
my $sendmail = "/usr/sbin/sendmail -t -v";
my $reply_to = "Reply-to: borysmbn";
my $from = "From: borysmbn";
my $SUBJECT_AUX="Subject: Your Subjectn";
my $send_to = "To: ".$EMAIL_TO."n";
open(SENDMAIL, "|$sendmail") or die "Cannot open $sendmail: $!";
print SENDMAIL $send_to;
print SENDMAIL $from;
print SENDMAIL $reply_to;
print SENDMAIL $SUBJECT_AUX;
print SENDMAIL "Content-type: text/plainnn";
print SENDMAIL $EMAIL_BODY;
close(SENDMAIL);

awesome tutorial, BTW.

A tip is not to forget about the parameters at the beginning of each parameter to send, like in the var $send_to="To: ".$EMAIL_TO."n" we cannot forget about the "To:" at the beginning and about the "n" at the end as you are issuing commands, or you will end up with an error message.

Regards

Borys Marcelo   

Posted at 12:40pm on Friday, May 2nd, 2008

in the above comment, when you read $send_to="To: ".$EMAIL_TO."n" we cannot forget about the "To:" at the beginning and about the "n" means "n" (new line) instead of "n". I can't edit, I'm posting again,

Regards

Rimic   

Posted at 1:02am on Monday, May 5th, 2008

I am not a developer and i am just a student. I need some help on how attach a word document file and image file on form to be sent into an email. CGI or Perl script with html code will help me a lot.

Sankar M,SERC,IISc,bangalore   

Posted at 9:11am on Tuesday, June 3rd, 2008

Really This is very easy way to send mail. nice code.It works. Cheers,
San

tiny teens   

Posted at 8:38am on Saturday, June 7th, 2008

It's a pleasant surprise to find a sanctury from all that modern inane garbage they call music.

Armin Garcia   

Posted at 10:08am on Wednesday, June 11th, 2008

Hi !!!
Does anybody knows how i attach a bzip file using perl????

I use
Mail::Internet
Email:Folder

modules, i hope anybody help me.... thnaks :)

jay   

Posted at 10:07am on Monday, June 23rd, 2008

nice

Comments to date: 44.

Your name:
Your comments:

Security check *

 

Like it? Share it!

  Post to del.icio.us
Post to
del.icio.us
   

Hosted Perlfect Search(beta)

New
Don't have the time or the expertise to install and maintain Perlfect Search? Then our freehosted Pelrfect Search service is for you!

Suggested Reading

Perl Cookbook The Perl Cookbook is full of quick solutions to everyday programming problems in perl with explanations and tips easy to understand even for beginners, but also frequently useful even to more experienced programmers. The code is clear and straightforward and the topics covered as well-thought and correspond to real world examples, so frequently you can literally copy code snippets from the book and fit them in your program. It is a nice complement for the Camel Book on your bookshelf.