Print this Article (NS4)
Netscape Navigator
Internet Explorer
Opera
Neoplanet

Forums
HTML
General
Site Dev
Programming
Flash
Grafix (Art)

Laboratory
Smart HTML
Color Lab
Generators

Contents
Simple CGI
  1. Hello World
  2. Print function
  3. Quoting, EOF
  4. Metacharacters
  5. Special Characters
Perl Basics 1
  1. Variables
  2. Arrays
  3. Hashes
  4. Split function
  5. Subroutines
  6. Defaults
Form CGI
  1. Loops
  2. Conditions
  3. Boolean Statements
  4. Pattern Matching
Time CGI
  1. Local Time
  2. GM Time
  3. time function
Perl Basics 2
  1. Reading Files
  2. Writing Files
  3. Including Files
  4. chop function
  5. chomp function
Guestbook CGI

Redirect CGI

Poll CGI

  1. Giving Commands
  2. Voting
  3. Results Display
  4. Adding Your Vote
Password CGI
  1. Authentification
  2. Multiple Users
  3. Encryption
Mailing List CGI
  1. Sendmail
  2. Multiple Recipients
Unlimited Subdomains CGI

News Grabber CGI

  1. LWP::Simple
Message Board CGI (Part 1)

Back to the Top


Perl Case Study - Mailing List CGI
By Lisa Hui

You must have the sendmail program installed on the same server as your cgi-bin for this to work! If you have telnet access to your webspace account, then type where sendmail or whereis sendmail at the prompt. It will return the path to the sendmail program; write this down (or somehow, keep a record of it - we will be using this information in the case study)! If you don't have telnet access, read up on your webspace service provider's FAQ (the answer to "Where is Sendmail?" is probably there) - if it isn't there, you can always contact the webservice provider for the information. Sendmail is a very common program that should be installed on the server running your cgi-bin. I will be using /usr/bin/sendmail in the example. What will also help you get a handle of the "extras" to this script. Make sure you understand what is happening on each line!

#!/usr/local/bin/perl
######################
# File: mailinglist-basic.cgi
# This script will get input from a form and send the message to
# a list of subscribers through the sendmail mechanism.

# Define a few important variables
$mailprogram = "/usr/lib/sendmail -t"; # path to sendmail with a required modifier
@subscribers = ("first\@footest.com","second\@footest.com","redbaron\@simfoo.com");
$sender = "myEmail\@mydomain.com"; # notice that all email @ signs are backslashed
$sendername = "My Name";

# Get the subject of the message and the message itself from a
# web-based form; refer to Perl Case Study - Form CGI for more
# details on the code below
if ($ENV{"REQUEST_METHOD"} eq 'GET') { $buffer = $ENV{'QUERY_STRING'}; }
else { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); }

@pairs = split(/&/, $buffer);

foreach $pair (@pairs) {
   ($name, $value) = split(/=/, $pair);
   $value =~ tr/+/ /;
   $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
   $FORM{$name} = $value;
}

print "Content-type: text/html\n\n";

# count the number of messages set with this variable
# (not necessary in the actual program)
$count = 0;

# Open the mail program and then just print the message
# as if it were to a file; notice the format the email
# message is in (refer back to the top to match the
# variables to what you defined earlier

# Then send this message to every subscriber ("for each
# recipient in the subscribers list, send this message")

foreach $recipient(@subscribers) {

   open (MAIL, "|$mailprogram") || die "Can't open $mailprogram!\n";
   print MAIL "To: $recipient\n";
   print MAIL "From: $sendername < $sender >\n";
   print MAIL "Subject: $FORM{'subject'}\n";

   print MAIL "$FORM{'message'}";

   close(MAIL);

   $count++;
}

# Print out the number of messages that were successfully sent
print "$count message(s) sent!";


This is the script in nearly its most basic form. You call it as the ACTION of a form (be sure to specify the subject and message (<input name="subject" ...>, <textarea name="message" ...></textarea> in the form tags). You could of course, print as much data as you'll need in your message, just add more 'print MAIL "$moredata";' lines that correspond to the $data you processed from the form ... well you get the idea :)

But what about those neat little sign-up forms sites have on their websites to subscribe to their mailing lists?

Or basic mailing list script can't do that! But it's an easy addition. Since this is a prelude to our Poll CGI Case Study, make sure you pay attention to how we handle possible multiple task coding with simple if/else statements - the primitive form of a C++ switch statement :). A good knowledge of how form data is passed to the CGI script would be very helpful at this point (see our Form CGI Case Study if you haven't already) :)

1. We want the form to be able to "tell" the script what it is supposed to do with the data being passed to it. In our example, when we want to subscribe a new user, we want to tell the script to add the new email address submitted to the list of subscribers. When we want to send a message to the subscribers, we want to tell the script to do with the subject and message instead of adding it to the subscribers list. So:

if a person wants to subscribe, add the email address to the list.
else, if you want to send a message, get the list and send it to everyone.
else, if a person wants to unsubscribe from the list, delete the name from the list.
else, the script has no idea what you're asking it to do.

2. Since we currently are storing the list of subscribers in the script itself, this would require modifying the script every time a new user subscribes - not very good. A better alternative is to keep a separate file with a list of email addresses (there is even an operator to append new data to the end of a file/list!).

3. Let's add in a few more checking routines: make sure that the new subscriber's email address is valid - it must have a @ and a period in the domain name.

4. Welcome the new subscriber to the list! Add a function that sends a predefined message (by you) to the subscriber when they subscribe to the list. Also don't forget to mention that they can unsubscribe any time they would like to just by visiting your site again and using the unsubscribe option.

5. Add an option that allows the recipient to unsubscribe to your list by just entering in their email address and selecting the unsubscribe option. How would this work? You would open the file, scan the entries for the entered email address and then rewrite the file while omitting the selected email address. (If you didn't understand that, don't worry, you will when we dive into the code).

6. Add a "spam" check! Prevent a person from maliciously adding the same email address over and over to the mailing list (which, if successful, would result in multiple email mailings being sent to the same address - which would also result in the need to unsubscribe more than once).

Let's see how it works:

#!/usr/local/bin/perl
######################
# File: mailinglist-basic.cgi
# This script will get input from a form and send the message to
# a list of subscribers through the sendmail mechanism.

# Define a few important variables
$mailprogram = "/usr/lib/sendmail -t"; # path to sendmail with a modifier
$subscription = "subscribelist.txt"; # name of the file that stores the email list
$greetings = "welcomemessage.txt"; # welcome message to send to new subscribers

$sender = "myEmail\@mydomain.com"; # notice that all email @ signs are backslashed
$sendername = "Your Name";

# Get the subject of the message and the message itself from a
# web-based form; refer to Perl Case Study - Form CGI for more
# details on the code below
if ($ENV{"REQUEST_METHOD"} eq 'GET') { $buffer = $ENV{'QUERY_STRING'}; }
else { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); }

@pairs = split(/&/, $buffer);

foreach $pair (@pairs) {
   ($name, $value) = split(/=/, $pair);
   $value =~ tr/+/ /;
   $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
   $FORM{$name} = $value;
}

print "Content-type: text/html\n\n";

# Now that you have the data, find out what you're telling
# the script to do and let it call the appropriate function
# (subroutine) to perform the task.
if ($FORM{'doThis'} eq "subscribe") { &add_to_list; }
elsif ($FORM{'doThis'} eq "send") { &send_message; }
elsif ($FORM{'doThis'} eq "unsubscribe") { &remove_from_list; }
else { print "Undefined task!"; }

# **************************************************
# The Subroutines

sub add_to_list {

   #Check the validity of the email address added by matching
   #the email pattern syntax
   unless($FORM{'email'} =~ /.*\@.*\..*/) {
      print "Error - email address invalid!";
      exit;
   }

   #Instead of appending the email address at the end of the list
   #mindlessly, retrieve the current list
   open(OLDLIST, "$subscription");
      @subscribers = <OLDLIST>;
   close(OLDLIST);

   #Now write over the original subscription list entry by entry
   #Compare each entry with that of the new subscriber - if they are
   #one and the same, print out a message saying that the email
   #address is already on the list, otherwise, write the old entries
   #into the file.

   #At the end, don't forget to add the new entry to the end of the
   #file - AFTER the foreach loop.
   open(NEWLIST, ">$subscription");
      foreach $recipient(@subscribers) {
         chomp($recipient);
         if ($recipient eq $FORM{'email'}) {
             print "You are already subscribed!";
             $subscribed = true;
         }
         else { print NEWLIST "$recipient\n"; }
      }

      if(!$subscribed) { print NEWLIST "$FORM{'email'}\n"; }
   close(NEWLIST);

   #Get the welcome message from a file
   open(WELCOME, "$greetings");
      @welcomeMessage = <WELCOME>;
   close(WELCOME);

   #Now that the email address has been successfully added, send
   #a message to the new subscriber confirming the addition:

   open (MAIL, "|$mailprogram") || die "Can't open $mailprogram!\n";
      print MAIL "To: $FORM{'email'}\n";
      print MAIL "From: $sendername < $sender >\n";
      print MAIL "Subject: Subscription Confirmed!\n";

          foreach $line(@welcomeMessage) {
             print MAIL "$line";
          }

   close(MAIL);

}

sub send_message {

   # Open the mail program and then just print the message
   # as if it were to a file; notice the format the email
   # message is in (refer back to the top to match the
   # variables to what you defined earlier

   # Then send this message to every subscriber ("for each
   # recipient in the subscribers list, send this message")
   foreach $recipient(@subscribers) {

      open (MAIL, "|$mailprogram") || die "Can't open $mailprogram!\n";
         print MAIL "To: $recipient\n";
         print MAIL "From: $sendername < $sender >\n";
         print MAIL "Subject: $FORM{'subject'}\n";

         print MAIL "$FORM{'message'}";

      close(MAIL);

   }

   # Print out a "success" message to the browser
   print "Your message was sent to the list!";

}

sub remove_from_list {

   #You will receive the email address that is to be removed from
   #the form. So now you get the list from the file
   open(OLDLIST,"$subscription");
      @subscribers = <OLDLIST>;
   close(OLDLIST);

   #Now, write over the old subscription list file
   #Compare each entry to the email address that was submitted by
   #the form - if there is a match, do not write this entry into the
   #new list.

   open(NEWLIST,">$subscription");
      foreach $subscriber(@subscribers) {
        chomp($subscriber);
            unless($subscriber eq $FORM{'email'}) {
                print NEWLIST "$subscriber\n";
            }

      }

   close(NEWLIST);

   #Print out to the browser/screen that the email address was
   #removed
   print "Email address $FORM{'email'} was removed from the list!";

   #Note that this routine does not check if the email address was initially
   #present in the list - if you need to make sure this is the case, instead
   #of using "unless", use an if/else, such as:
   #if($subscriber eq $FORM{'email'} { $subscribed = true; }
   #else { print NEWLIST "$subscriber\n"; }
   #Follow this up by printing out the appropriate response based on the
   #"validity" of the $subscribed variable:
   #if($subscribed) { print "Your email address was successfully removed!"; }
   #else { print "That email address is not subscribed to this mailing list!"; }

}


Since this more advanced ("feature rich") script, make sure you have each extra file that is necessary for this script: the subscription list and the welcome message. You might also notice that upon printing out a message to the browser, I just placed a simple success or error message. You can of course add layout, information and more content, etc.

How would I tell the script to "doThis" (add,send,delete)?

In your form, add some hidden data. <INPUT Type="hidden" Name="doThis" Value="subscribe">. The NAME of the form field will always correspond with the key in the FORM hash. That example I just mentioned would result in passing this expression/value into the program: $FORM{'doThis'} = "subscribe";

I hope you got the gist of this case study by going through the code I penned as I wrote the tutorial. The mailing routine itself is very small, but this is probably one of the most common applications of it. I've demonstrated using it as a simple one-way mailing list. Plus, you can integrate it with any Perl script that will email someone who interacts with your Perl script in other ways (as long as an email address is available as information).

What about autoresponders? (like when someone sends a message to a certain email address)

Unfortunately, that's a completely different matter. The easiest way to do that is to use procmail instead of sendmail. Due to the fact that I don't have access to use this program, this site doesn't have any case study or tutorial available on this topic currently.


Perl Case Study - Unlimited Subdomains CGI

©1999 Team 26297 "Ad Infinitum Web." All rights reserved. Any reproduction of this document for commercial or redistribution purposes without the permission of the author is forbidden.