That's roughly correct, but it's actually even simpler than that. You needn't think about injection, headers or storing the attachment anywhere. PHP and Swift will both do most of that work for you. All you need to do is put the POST data into the relevant methods Swift uses. Since formmail seems to be the only thing out there for sending emails from forms I'm semi-considering writing a wrapper myself and putting it in the library.
Here's an option for the procedural programmer...
First you need to create the form. If it will send attachments, make sure to set the
enctype to "multipart/form-data". Set the
action attribute to point to a PHP script which we'll create in a few moments. I'm making the attachment upload optional here. I'll call the file
form.php and it will send data to
mail_handler.php.
Code: Select all
<form action="mail_handler.php" method="post" enctype"multipart/form-data">
<div class="row">
<div class="label">Your name*:</div>
<div class="field"><input type="text" name="user_name" /></div>
</div>
<div class="row">
<div class="label">Your e-mail*:</div>
<div class="field"><input type="text" name="email" /></div>
</div>
<div class="row">
<div class="label">Subject*:</div>
<div class="field"><input type="text" name="subject" /></div>
</div>
<div class="row">
<div class="label">Attachment:</div>
<div class="field"><input type="file" name="attachment" /></div>
</div>
<div class="row">
<div class="label">Comments*:</div>
<div class="field"><textarea name="comments"></textarea></div>
</div>
<div class="row">
<div class="label"> </div>
<div class="field"><input type="submit" name="submit" value="Send" />
</div>
</form>
Now you need to create the script which will send the email. Make sure you have
uploaded the Swift library to the web server so that you can use it in your script.
The script will:
- 1. Start a session so if we redirect we don't lose entered data in the form
2. Fix any mess PHP made with magic_quotes on
3. Copy POST data into the session so we can use it on the form page again if there's a problem
4. Load in the Swift files
5. Check if all POST data was sent that was required
6. Check if the email address is in a valid format
7. Check if the file upload was performed and no errors occured (file too big etc)
8. Connect to the SMTP server
9. Construct the message body using the POST data
10. Attach the file if it was uploaded
11. Add the message body
12. Send the email to your address
13. Redirect to a success page if everything worked or go back to the form page with an error if there was a problem
In the script, we'll open a <?php tag immediately and we *won't* output anything at all because we are going to send HTTP headers to redirect the browser once our work is done.
Code: Select all
<?php
/** 1 **/
session_start();
/** 2 **/
//See if evil magic_quotes is enabled, and fix problems if it is
$quotes_on = (get_magic_quotes_gpc() || get_magic_quotes_runtime());
if ($quotes_on)
{
foreach ($_POST as $key => $value)
{
$_POST[$key] = stripslashes($value);
}
}
/** 3 **/
$_SESSION["post"] = $_POST;
/** 4 **/
//Load in the required Swift files
require_once "classes/Swift.php";
require_once "classes/Swift/Connection/SMTP.php";
/** 5 **/
//Create an empty array where we can catch any fields which were not filled in
$fields_not_set = array();
//Check if all POST data was sent, redirect with an error if not
if (empty($_POST["user_name"])) $fields_not_set[] = "user_name";
if (empty($_POST["email"])) $fields_not_set[] = "email";
if (empty($_POST["subject"])) $fields_not_set[] = "subject";
if (empty($_POST["comments"])) $fields_not_set[] = "comments";
//If $fields_not_set contains any values, then something wasn't filled in. Time to redirect.
if (!empty($fields_not_set))
{
//Read further down to see how we'll modify form.php to handle the error
header("Location: form.php?error=incomplete&fields=" . implode(",", $fields_not_set));
exit();
}
//Copy the POST data to standard globals
$user_name = $_POST["user_name"];
$email = $_POST["email"];
$subject = $_POST["subject"];
$comments = $_POST["comments"];
/** 6 **/
//This is a RegExp I've adopted for validating email addresses. NOTE that it's NOT RFC compliant.
// Use another regexp at your own choice
$email_re = '(?#Start of dot-atom
)[-!#\$%&\'\*\+\/=\?\^_`{}\|~0-9A-Za-z]+(?:\.[-!#\$%&\'\*\+\/=\?\^_`{}\|~0-9A-Za-z]+)*(?#
End of dot-atom)(?:@(?#Start of domain)[-0-9A-Za-z]+(?:\.[-0-9A-Za-z]+)*(?#End of domain))?';
//Now check if the email address they gave is valid, redirect back to the form if not
if (!preg_match($email_re, $email))
{
header("Location: form.php?error=email_invalid");
exit();
}
/** 7 **/
$attachment_data = array();
//Now check if there's an attachment they've sent
if (!empty($_FILES["attachment"]))
{
//If an attachment was sent, but there was an error, redirect
if ($_FILES["attachment"]["error"] != 0)
{
header("Location: form.php?error=attachment_failed");
exit();
}
else $attachment_data = $_FILES["attachment"];
}
/** 8 **/
//Everything looks ok to send an email, create an instance of Swift
$swift = new Swift(new Swift_Connection_SMTP("your.smtp.server.tld"));
/** 9 **/
//Now build your message body
$body = "A message was sent from " . $user_name . " with the title '" . $subject . "'";
if (!empty($attachment_data))
{
$body .= "\r\nAn attachment with the name '" . $attachment_data["name"] . "' was added";
}
/** 10 **/
//Attach any files if they were sent
// PHP stores files in a temporary location and cleans up itself, so we'll just read the temporary file
if (!empty($attachment_data))
{
$attachment_str = file_get_contents($attachment_data["tmp_name"]);
//Check if we need to remove slashes (again!!)
if ($quotes_on)
{
$attachment_str = stripslashes($attachment_str);
}
$swift->addAttachment(
$attachment_str, $attachment_data["name"], $attachment_data["type"]);
}
/** 11 **/
//Add the email body
$swift->addPart($body);
/** 12 **/
//Try sending the email.
// Redirect to success page on success, or form on failure
if ($swift->send("your@address.com", $email, $subject))
{
unset($_SESSION["post"]); //It worked, we have no reason to keep this data
$swift->close();
header("Location: success.php"); /** 13 **/
exit();
}
else
{
$swift->close();
header("Location: form.php?error=runtime_error"); /** 13 **/
exit();
}
//End of script
For the sake of clarity and brevity, earlier I didn't mention how we'd make the form deal with errors, so lets look at modifying it to do that.
First we'll make sure we start a session at the top of the page, because we don't want the user to have to type everything out again.
Make sure there's no whitespace at all before that opening <?php tag or it won't work.
Now, we'll make a function to "paint" any already submitted values into the form value calling it in the form's
value attributes.
Code: Select all
function paint_value($field)
{
if (!empty($_SESSION["post"][$field]))
{
echo $_SESSION["post"][$field];
}
else { echo ""; }
}
We also need to display error messages on the page, so we'll add some code for that too. Putting it all together, the form now looks like this.
Code: Select all
<?php
//Start session
session_start();
//Function to display the values in the fields
function paint_value($field)
{
if (!empty($_SESSION["post"][$field]))
{
echo $_SESSION["post"][$field];
}
else { echo ""; }
}
//Check if errors were sent
if (!empty($_GET["error"]))
{
switch ($_GET["error"])
{
case "incomplete":
$field_labels = array("user_name" => "Name", "email" => "E-mail address", "subject" => "Subject", "comments" => "Comments");
$incomplete = explode(",", $_GET["fields"]);
?><div class="error">All required fields (*) were not completed. Please check the following fields:
<ul>
<?php foreach ($incomplete as $field) {
if (isset($field_labels[$field])) { echo "<li>" . $field_labels[$field] . "</li>"; }
} ?>
</ul></div><?php
break;
case "email_invalid":
?><div class="error">The email address entered does not appear to be a valid format</div><?php
break;
case "attachment_failed":
?><div class="error">The attachment upload failed. Perhaps the file is too large?</div><?php
break;
case "runtime_error":
?><div class="error">There was a problem processing your request. Please try again later.</div><?php
break;
}
}
?>
<form action="mail_handler.php" method="post" enctype"multipart/form-data">
<div class="row">
<div class="label">Your name*:</div>
<div class="field"><input type="text" name="user_name" value="<?php paint_value("user_name"); ?>" /></div>
</div>
<div class="row">
<div class="label">Your e-mail*:</div>
<div class="field"><input type="text" name="email" value="<?php paint_value("email"); ?>" /></div>
</div>
<div class="row">
<div class="label">Subject*:</div>
<div class="field"><input type="text" name="subject" value="<?php paint_value("subject"); ?>" /></div>
</div>
<div class="row">
<div class="label">Attachment:</div>
<div class="field"><input type="file" name="attachment" /></div>
</div>
<div class="row">
<div class="label">Comments*:</div>
<div class="field"><textarea name="comments"><?php paint_value("comments"); ?></textarea></div>
</div>
<div class="row">
<div class="label"> </div>
<div class="field"><input type="submit" name="submit" value="Send" />
</div>
</form>
The above should give you a fairly solid working form-mail set up. Obviously, I could have gone into a lot more detail, but if you don't know much about PHP development it would be too overwhelming as the above is fairly lengthy as it is.
Hopefully it will clear a few things up though.
Cheers,
Chris