MyInternetTutorials.
com
                   Free Autoresponder Tutorial
                   Find more of this tutorial at:
             http://www.MyInternetTutorials.com
©2008, Michael Suddes – MyInternetTutorials.com
All Rights Reserved
                             http://www.MyInternetTutorials.com
   Autoresponder tutorial
   An autoresponder is on the surface a simple program. Take a list of email addresses and send a message. But you
   can do that with an email program with a bcc: field. In its most common incarnation, an autoresponder sends out
   messages to a subscriber in a pre-defined sequence and timing. Additionally, to comply with todays anti-spam laws
   an autoresponder must include opt-out links in each message. To be safest, you can use a double opt-in system for
   subscribers. But, this basic system is a single opt-in approach, and a manual unsubscribe system.
   If theres enough interest, I will cover an add-on script that will handle double opt-ins and automatic unsubscribes.
   Most, if not all, autoresponder programs use a database system. And, almost all of those use mySQL. If you're not
   familiar with mySQL it would be beneficial to find a tutorial on it and at least get an idea of how it works. I have a
   mySQL tutorial planned, but it will assume a basic knowledge of the program.
   Additionally, a basic knowledge of php is assumed for this tutorial.
   If you really need to get this program up and running, follow this link to download the finished product, but I
   recommend you follow along with me to learn how it works.
   So, lets get started:
   Form handler script
   The first thing we need to do is get subscribers information into the database. At minimum we need their name and
   email address. We need a form and a way to process that form. The easiest way to do this is a page with a form and
   a php script that gets called when the form is submitted.
   Its a very simple form, two fields and a submit button. At least on the surface. if you want to prevent someone
   from hacking your form, it takes a bit more planning.
   Heres a basic form:
    
       <form action="<?php echo $editFormAction; ?>" name="form1" method="post">
       <p>Name:<br />
       <input name="name" id="name" type="text" />
       </p>
       <p>email:<br />
       <input name="email" id="email" type="text" /></p>
       <p> <input name="Submit" value="Free access" type="submit" /> </p>
       <input name="MM_insert" value="form1" type="hidden" /></form>
    
   Th action of the form is a variable that is calculated from the name of the page the script is on, and any query
   strings. The two fields are self explanatory. Then a submit button. Next, a hidden field that tells the script what we
   want to do with the data. Not a required step, but it makes things easier if you have multiple forms.
   This form gets placed in the page where you want it. The rest of the script goes at the top of the page.
    
    <?php
     $hostname_mailer = "localhost";
     $database_mailer = "database";
     $username_mailer = "user";
     $password_mailer = "password";
     $mailer = mysql_pconnect($hostname_mailer, $username_mailer, $password_mailer) or die
   (mysql_error());
    
   First, we open the script with <?php. Then we open our database. Hostname tells php where to connect, database
   tells it which database, and user and password for access. The function mysql_pconnect uses the supplied variables
©2007 Michael Suddes - MyInternetTutorials.com
All Rights Reserved                                                                                                         1
                             http://www.MyInternetTutorials.com
   and returns a variable $mailer that we can use to access the database directly throughout the script.The next few
   sections list the functions that we declare before we get to the code that calls them.
    
     function GetSQLValueString($theValue, $theType, $theDefinedValue = "",
   $theNotDefinedValue = "")
     {
     $theValue = (!get_magic_quotes_gpc()) ? addslashes($theValue) : $theValue;
    switch ($theType) {
     case "text":
     $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
     break;
     case "long":
     case "int":
     $theValue = ($theValue != "") ? intval($theValue) : "NULL";
     break;
     case "double":
     $theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL";
     break;
     case "date":
     $theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
     break;
     case "defined":
     $theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue;
     break;
     }
     return $theValue;
     }
    
   This function checks the user supplied variables to make sure they aren't malicious. And typecasts the submission
   to the type expected by the database.
    
         $dodgy_strings = array(
       "content-type:"
         ,"mime-version:"
        ,"multipart/mixed"
        ,"bcc:"
         );
       function contains_bad_str($str_to_test) {
         $bad_strings = array(
         "content-type:"
         ,"mime-version:"
         ,"multipart/mixed"
         ,"Content-Transfer-Encoding:"
         ,"bcc:"
         ,"cc:"
         ,"to:"
         );
        foreach($bad_strings as $bad_string) {
        if(eregi($bad_string, strtolower($str_to_test))) {
        echo "$bad_string found. Suspected injection attempt - mail not being sent.";
        exit;
        }
        }
©2007 Michael Suddes - MyInternetTutorials.com
All Rights Reserved                                                                                                    2
                               http://www.MyInternetTutorials.com
   function contains_newlines($str_to_test) {
     if(preg_match("/(%0A|%0D|\\n+|\\r+)/i", $str_to_test) != 0) {
     echo "newline found in $str_to_test. Suspected injection attempt - mail not being sent.";
     exit;
     }
     }
    
   These three functions are more of the safeties against malicious attacks. First we define an array of strings we don't
   want in our database. Next, the function will compare a string to this list to see if it contains the forbidden strings.
   The next function will check to see if the submitted information includes newlines, these can also indicate a
   malicious attack.
    
           function is_valid($email){
             $qtext = '[^\\x0d\\x22\\x5c\\x80-\\xff]';
             $dtext = '[^\\x0d\\x5b-\\x5d\\x80-\\xff]';
             $atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c'.
                       '\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+';
             $quoted_pair = '\\x5c[\\x00-\\x7f]';
             $domain_literal = "\\x5b($dtext|$quoted_pair)*\\x5d";
             $quoted_string = "\\x22($qtext|$quoted_pair)*\\x22";
             $domain_ref = $atom; $sub_domain = "($domain_ref|$domain_literal)";
             $word = "($atom|$quoted_string)"; $domain = "$sub_domain(\\x2e$sub_domain)*";
             $local_part = "$word(\\x2e$word)*"; $addr_spec = "$local_part\\x40$domain";
           return preg_match("!^$addr_spec$!", $email) ? 1 : 0;
               }
    
   This function is specifically to check if the email address is plausibly valid, I tried some different routines that
   check the actual address by contacting the domains mail server, but that's a lot of server load. If you give some
   incentive to put in a correct email address, like freebies in the email, then you stand a much better chance of
   getting a valid email.
    
           if (!(empty($_POST))){
              $email = GetSQLValueString($_POST['email'], "text");
              $name = GetSQLValueString($_POST['name'], "text");
           if (!is_valid($email)) {
               echo 'Invalid email submitted - mail not being sent.';
               exit;
           }
               contains_bad_str($email);
               contains_bad_str($name);
               contains_newlines($email);
               contains_newlines($name);
       }
    
   Now we are getting into the code. This first section is an if loop that checks to see if the post variable from the
   form has data in it. If it does it proceeds to load the data into variables $name and $email. As we do this, we send
©2007 Michael Suddes - MyInternetTutorials.com
All Rights Reserved                                                                                                           3
                              http://www.MyInternetTutorials.com
   the data to the GetSQLValueString function we described above with the data type we want it converted to. Next,
   we send the email address to the is_valid function to check the email address. We embed this in an if statement so
   that if the email isn't valid, we print an error message and exit the program.
   The next four lines send the data to the other validator functions we looked at earlier, the functions have built in
   mechanisms that print an error messages and exit the program if an error occurs. If some of this seems
   inconsistent, it is. Mainly becausen I found these functions on other tutorial and sample code sites and used them
   here. Good programmers write their own code, great programmers steal great code.
    
       $editFormAction = $_SERVER['PHP_SELF'];
         if (isset($_SERVER['QUERY_STRING'])) {
               $editFormAction .= "?" . htmlentities($_SERVER['QUERY_STRING']);
       }
    
   As I mentioned above when I showed the code for the form, the script constructs the action for the form
   dynamically. $_SERVER['PHP_SELF'] is a system variable you can use in any php script to get the name and path
   of the script. We then check to see if the query string is set and add that to the variable if it is set. This preserves
   data from one page to another in certain cases. In this case it isn't necessary, but good programming habit.
    
       if ((isset($_POST["MM_insert"])) && ($_POST["MM_insert"] == "form1")) {
           $dstamp = date("Y-m-d H:i:s");
           $insertSQL = sprintf("INSERT INTO info (name, email, date) VALUES (%s, %s, '$dstamp')",
           GetSQLValueString($_POST['name'], "text"),
           GetSQLValueString($_POST['email'], "text"));
           mysql_select_db($database_mailer, $mailer);
           if(mysql_query($insertSQL, $mailer) ){
       }    
   This segment begins by checking the hidden field from the form and whether it is the correct form. Once again this
   is Dreamweaver's setup in case of multiple forms and actions in a single script. If both of these conditions are true,
   we drop into the script. We get todays date and current time from the date() function, in an easy to read format.
   Now we have the three pieces of data for the record: the users name, email address, and the datestamp. We
   construct the SQL statement and place it into the variable $insertSQL. Next, we tell mySQL which database we're
   using with the mysql_select_db function.
   Next, we run the query on the database with the mysql_query function feeding it the $insertSQL variable we built
   earlier, and the database variable $mailer. The if statement checks the result of mysql_query. If it returns true then
   we do nothing and contnue the script. This next code section covers what we do if it fails.    
       else{
       echo "<h2>Sorry, we don't allow duplicate email adresses in the database.<BR>";
       echo "Please use your back button to go back and try again.<BR>";
       echo "Thanks!</h2>";
       die(mysql_error());
       }
       mailit ();
    
   This is the second part of the if statement outlined above, its what happens if the if statement returns the value of
   false because of an error. The only likely common error is a duplicate email address since we've already checked
   other malicious entries in the preceeding code. ( You would be amazed at how many people put their information
©2007 Michael Suddes - MyInternetTutorials.com
All Rights Reserved                                                                                                           4
                             http://www.MyInternetTutorials.com
   in two or even three time for some reason! )
   If someone enters an email address that already exists in the database, mySQL won't insert the record and returns
   false as the result. When this happens, the above code prints the error message and exits. The die(mysql_error())
   function prints the actual error code from mySQL for debugging purposes.
   So yes, we are making an assumption that the error is a duplicate email address. Not the best programming
   practice, but we can add more error checking here in the future.
   The next function we call is mailit(), this function will take a bit to explain, as it emails the thankyou message and
   the free report. Before we get to that, we have a few more lines of code to cover.    
       $insertGoTo = "join.html";
       if (isset($_SERVER['QUERY_STRING'])) {
           $insertGoTo .= (strpos($insertGoTo, '?')) ? "&" : "?";
           $insertGoTo .= $_SERVER['QUERY_STRING'];
       }
       header(sprintf("Location: %s", $insertGoTo));
    
   This section creates a redirect header to send the user to your thank you or download page. It also checks to see if
   there's a query string to pass along to the new page and adds it to the redirect. Then it prints the header which send
   the subscribers browser to the new page. This basically ends the form handling section of the code. But we have
   the mailit() function to explore before we're done.
    
       function mailit(){
         $name = $_POST["name"];
         $email = $_POST["email"];
           $to = "$name <$email>";
           $from = "FastEasyWorkouts.com <webmaster@fasteasyworkouts.com>";
           $subject = "Here is your requested information from FastEasyWorkouts.com";
           $fileatt = "thermogenicfoods.pdf";
           $fileatttype = "application/pdf";
           $fileattname = "thermogenicfoods.pdf";
           $headers = "From: $from";
    
   We just called this function, and now lets step through the code. We load in the name and email into variables and
   then declare several other variables needed to send to the email program. $to, $from, and $subject are self
   explanatory. The next three variables are needed for the file we're attaching to the email. $fileatt is the path and
   local name of the file. $fileatttype is the mime code for the file type. $fileattname is the name you want the file to
   have when you attach it. We also declare the $headers variable, we'll be adding more to it in the next section.
    
       $file = fopen( $fileatt, 'rb' );
       $data = fread( $file, filesize( $fileatt ) );
       fclose( $file );
       $semi_rand = md5( time() );
       $mime_boundary = "==Multipart_Boundary_x{$semi_rand}x";
       $headers .= "\nMIME-Version: 1.0\n" .
        "Content-Type: multipart/mixed;\n" .
        " boundary=\"{$mime_boundary}\"";
©2007 Michael Suddes - MyInternetTutorials.com
All Rights Reserved                                                                                                         5
                              http://www.MyInternetTutorials.com
    
   Here we open the file for reading with fopen which returns a variable that points to the open file. Then we use
   fread to load the actual data into $data. To keep things cleaned up and neat we then use fclose to close the file and
   free up the memory.
   Next we create a large random number that acts as a bookend for the attached file data. We declare the rest of the
   bookend as $mime_boundary. This will be used in several places. Now we add this to $headers with the required
   mime protocols of mime version, type of email which is multipart/mixed, and what the boundary code is.
    
       $message = "This is a multi-part message in MIME format.\n\n" .
       "--{$mime_boundary}\n" .
       "Content-Type: text/plain; charset=\"iso-8859-1\"\n" .
       "Content-Transfer-Encoding: 7bit\n\n" .
       $message .= "Thank you!\n\n" .
       "Your requested information is included in this email.\n".
       "For any questions, please feel free to email us at\n".
       "webmaster@fasteasyworkouts.com\n\n";
       $message .= "To access the signup page directly please use the link below:\n\n" .
       "http://fasteasyworkouts.com/amember/signup.php\n\n".
       "To see the information page again please use this link:\n\n".
       "http://www.fasteasyworkouts.com/join.html\n\n\n\n";
    
   This is a large chunk of code but very simple, so bear with me. We start by declaring the $message variable. Please
   make note of the "\n" newlines and the concatenation periods between quotation marks. we could put all of this in
   one big quote, but this is easier to read. The newlines are important, note where there are one, and where there are
   two at the top of the message, this is important, as the email programs look for these. Everything before the Thank
   you! is telling the email program that this is a mime message and we're starting with a text segment.
   After this we write our message. I wanted double spacing for this message so you will see lots of double newlines
   in my text. At the end of my message there's four newlines to make some space for the attachment.
    
   $data = chunk_split( base64_encode( $data ) );
   $message .= "--{$mime_boundary}\n" .
    "Content-Type: {$fileatttype};\n" .
    " name=\"{$fileattname}\"\n" .
    "Content-Disposition: attachment;\n" .
    " filename=\"{$fileattname}\"\n" .
    "Content-Transfer-Encoding: base64\n\n" .
    $data . "\n\n" .
    "--{$mime_boundary}--\n";
    
   This section adds the next part of the message. First we split the file and encode it to plain text so it can be sent in
   the email with chunk_split( base64_encode( $data ) ). Then we add the headers, the data, and the final bookend.
    
        if( mail( $to, $subject, $message, $headers ) ) {
        }
        else {
          echo "<p>There was an error sending the mail.</p>";
©2007 Michael Suddes - MyInternetTutorials.com
All Rights Reserved                                                                                                           6
                              http://www.MyInternetTutorials.com
            exit;
        }
   }
    
   As we send the email message using mail(), a built in php functionality, we check and make sure it sends properly
   with the if statement. If mail() returns true we don't do anything and we go back to where the function was called.
   If an error occurs, we print an error message and exit the program.
    
   Autoresponder script
   Now that we have a way to get names and addresses into the database, we need to get them out and send messages
   to them. In the last section, we inserted the name and email into the database, datestamped the record, and set their
   message counter to zero. Now, we need to compare their datestamp to today to see how many days they've been in
   the system and send the appropriate message if any. If we send a message, we increment their counter.
   Those of you who have used Dreamweaver will recognize some of the code here, I use their utilities to access the
   mySQL database.
   Ready? Here we go:
    <?php
     $hostname_mailer = "localhost";
     $database_mailer = "database";
     $username_mailer = "user";
     $password_mailer = "password";
     $mailer = mysql_pconnect($hostname_mailer, $username_mailer, $password_mailer) or die
   (mysql_error());
    
   This code serves the same function as the identical section in the form script above.
    
        mysql_select_db($database_mailer, $mailer);
        $query_Recordset1 = "SELECT * FROM info";
        $Recordset1 = mysql_query($query_Recordset1, $mailer) or die(mysql_error());
        $row_Recordset1 = mysql_fetch_assoc($Recordset1);
        $totalRows_Recordset1 = mysql_num_rows($Recordset1);
        ?>
    
   In the first line we tell mySQL which database we're using. In the next line we use a variable to hold our mySQL
   query. The query tells mySQL to SELECT all the fields from the table named info. We then feed this variable and
   the variable for the database to mysql_query. This returns a variable or, if an error occurs, ends the script.
   In the absence of an error, the next two rows fetch a variable for the first record in the query, and the total number
   of records returned by the query. The ?> tag closes the php script so we can handle some plain html.
    
       <html >
       <head>
       <title>Autoresponder</title>
       </head>
       <body>
    
   Just basic html headers, no meta tags are necessary because its for our use only, not for the public.
    
©2007 Michael Suddes - MyInternetTutorials.com
All Rights Reserved                                                                                                         7
                              http://www.MyInternetTutorials.com
    <?php
   $count = 0;
   function elapsedDays($dt)
     {
     $yr=strval(substr($dt,0,4));
     $mo=strval(substr($dt,5,2));
     $da=strval(substr($dt,8,2));
     $hr=strval(substr($dt,11,2));
     $mi=strval(substr($dt,14,2));
     $se=strval(substr($dt,17,2));
       $time = mktime($hr,$mi,$se,$mo,$da,$yr);
       $diff = time()-$time;
       return floor($diff/60/60/24);
       }
    
   This section starts by re-opening the php script, then we initialize a counter to track how many record we process.
   Next, we have a function that converts the date format we use in the database as a timestamp to UNIX time, and
   calculates the difference in days between when the record was entered and now. The function returns the number
   of elapsed days to the part of the script that called it.
    
     do {
   $time1 = $row_Recordset1['date'];
   $days = elapsedDays($time1);
   $query_message = "SELECT * from messages WHERE sequence = $days";
   $Recordset2 = mysql_query($query_message, $mailer) or die(mysql_error());
    
   The first line starts a loop that will continue until al the records are processed. Next, we get the elapsed days for the
   current record. Once we have the elapsed days we query the database to see if there's a message that corresponds
   to the number of elapsed days.
    
     if (($row_Recordset2 = mysql_fetch_assoc($Recordset2)) && ($row_Recordset1['sequence'] !
   = $days)){
   $name = $row_Recordset1['name'];
     $email = $row_Recordset1['email'];
     $from = "FastEasyWorkouts.com <webmaster@fasteasyworkouts.com>";
     $headers = "From: $from";
    
   First we check to see if there was a message that matched our records elapsed days, and that the sequence of the
   message matches the sequence of the record as a safeguard to sending the same message twice. If all these factors
   are in place, we pull al the fields from the message record and proceed to process the record. We pull the records
   name and email address and put them in $name and $email variables respectively. We assign our from information
   as well.
    
       $message = preg_replace("/\{([^\{]{1,100}?)\}/e","$$1", $row_Recordset2['message']);
©2007 Michael Suddes - MyInternetTutorials.com
All Rights Reserved                                                                                                            8
                              http://www.MyInternetTutorials.com
        $subject = $row_Recordset2['subject'];
        $recepient = "$name <$email>";
   if( mail( $recepient, $subject, $message, $headers) ) { }
   else { echo "<p>There was an error sending the mail.</p>"; }
   echo "Processing record #$count, sending message #$days to $email<BR>";
    
   This section starts with probably the most difficult piece of code in the entire script. Its called a regular expression,
   and its one of the most difficult types of code to learn. Suffice it to say that this line of code replaces anything in
   curly brackets with the contents of a variable with the same name. Example in the stored message text {name}
   would be replaced by the contents of variable $name. This same line places the modified message into the variable
   $message.
   Next, we place the message subject from the database into a variable, as well as the email address of the
   subscriber.
   Now all of the proper parts are in place and we can send them to the mail() function, this uses sendmail on your
   server to send the message. We put it inside an if statement to make sure it doesn't throw an error. If it does, we
   print an error message and continue. The last line in this section prints an informational message telling us what
   message was sent to whom. This line could be commented out on larger lists.
    
     $query_message = "UPDATE `info` SET `sequence` = '$days' WHERE `name` = '$name'
   AND `email` = '$email' LIMIT 1 ";
     $Recordset3 = mysql_query($query_message, $mailer) or die(mysql_error());
   mysql_free_result($Recordset2);
   $count = $count + 1;
     }
       } while ($row_Recordset1 = mysql_fetch_assoc($Recordset1));
    
   We're almost done! In this section we take care of housekeeping and close the processing loop. The first line
   updates the sequence field in the subscribers record to confirm we sent that particular message to that address.
   mysql_free_result releases the address to the queried record to free up memory. Then we update the counter to
   track the total number of messages we're sending.
   The first curly bracket closes the if statement from above where we checked to see if there was a message to send.
   And, the second curly bracket closes the do loop that processes each email record. You can see the while statement
   that will continue this loop until we run out of email records. After this we'll finish up and close the script.
    
        mysql_free_result($Recordset1);
©2007 Michael Suddes - MyInternetTutorials.com
All Rights Reserved                                                                                                            9
                            http://www.MyInternetTutorials.com
       echo "<BR><HR><BR>Processing done. Processed $count messages.";
        ?>
       </body>
       </html>
    
   This section releases the mySQL query with all of the email records, reports the total number of records processed
   from our counter, and closes the body and html tags. Thats it, one simple autoresponder.
   Now, I'm going to show you the code that will create the mySQL tables needed for this script to work.
    
        CREATE TABLE `info` (
        `name` varchar(50) NOT NULL default '',
        `email` varchar(50) NOT NULL default '',
        `sequence` int(11) NOT NULL default '0',
        `key` int(11) NOT NULL auto_increment,
        `date` datetime NOT NULL default '0000-00-00 00:00:00',
        PRIMARY KEY (`key`),
        UNIQUE KEY `email` (`email`)
        ) TYPE=MyISAM;
   CREATE TABLE `messages` (
     `message_ID` int(11) NOT NULL auto_increment,
     `subject` text NOT NULL,
     `message` text NOT NULL,
     `sequence` int(11) NOT NULL default '0',
     UNIQUE KEY `sequence` (`sequence`),
     KEY `message_ID` (`message_ID`)
     ) TYPE=MyISAM;
    
   These two statements create the mySQL tables you will need to store the data. If you don't know if your server has
   mySQL, or how to create a database, I suggest you contact your hosting company and check with them. As I
   mentioned above, I am planning a mySQL tutorial, and I will cover things like this.
   Installation
©2007 Michael Suddes - MyInternetTutorials.com
All Rights Reserved                                                                                                     10