View Full Version : Simple Email Form
b_ball_player9
09-25-2005, 10:36 AM
Hi im absolutly new to PHP and was wondering how do i go about making a simple form on my site
which just has "Name" "E-mail" "Feedback" and so when the user hits submit, sends me an email and shows the user a "Messege has been sent" text
any help would be greatly appricted
thanks
scrypter
09-26-2005, 11:51 AM
Google for "formmail.php", there is one from tectite (i think, or something similar). It has much explanation on how to use it. If you can't find that one, am sure there are many other examples on the web. Google for "form email php" without the quotes.
Paul
b_ball_player9
09-26-2005, 12:46 PM
hey thanks alot, that "tectite " site i exectly what i was looking for
thanks
mindlessLemming
09-26-2005, 12:52 PM
I made exactly that yesterday...
Features:
Checks all submitted values
Safe from email injection attacks
User feedback -- tells them which field is incomplete on error, says "Thanks for your message" when complete
Accessible, valid XHTML markup.
Sample CSS included
Enjoy :)
lol, I knew I should have hit post before I had dinner
updated: see below
<?php
class SimpleContact {
var $state; // 0 = fail, 1 = ready, 2 = attempted injection attack, 3 = mail sent
var $errorMsg; // human readable version of $this->state with specific detail of error's cause
var $_to;
var $_from;
var $_name;
var $_subject;
var $_msg;
function _isEmail($email) {
if (preg_match("#^(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}$#", $email))
{ return true; }
else
{ return false; }
}
function _isInjection($text) {
$text = strtolower($text);
if (preg_match('#(content\s*-\s*disposition)|(bcc\:)|(cc\:)|(content\s*-\s*transfer\s*-\s*encoding)|(mime\s*-\s*version)|(multipart\s*/\s*mixed)|(multipart\s*/\s*alternative)|(multipart\s*/\s*related)|(reply\s*-\s*to)|(x\s*-\s*mailer)|(x\s*-\s*sender)|(x\s*-\s*uidl)#is',$text))
{ return true; }
else
{ return false;}
}
function init($to, $from, $name, $subject, $msg) {
// start with least expensive process, end with regex madness.
if( empty($subject) ) {
$this->state = 0;
$this->errorMsg = 'All mail must contain a subject';
return false;
}
if( empty($name) ) {
$this->state = 0;
$this->errorMsg = 'Please enter your name';
return false;
}
if( empty($msg) ) {
$this->state = 0;
$this->errorMsg = 'All mail must contain a message body';
return false;
}
if( empty($from) || !$this->_isEmail($from) ) {
$this->state = 0;
$this->errorMsg = 'Invalid sender email address supplied';
return false;
}
if( empty($to) || !$this->_isEmail($to) ) {
$this->state = 0;
$this->errorMsg = 'Invalid recipient email address supplied';
return false;
}
if( $this->_isInjection($from) || $this->_isInjection($to) || $this->_isInjection($msg)) {
$this->state = 2;
$this->errorMsg = 'This message has been identified as an email injection attempt';
return false;
}
$this->_to = $to;
$this->_from = $from;
$this->_name = $name;
$this->_subject = $subject;
$this->_msg = $msg;
$this->state = 1;
return true;
}
function sendMail() {
if($this->state !== 1) {
trigger_error('SimpleMail Object is not initialized or contains invalid data - Mail sending cancelled', E_USER_WARNING);
} else {
$header = "Return-Path: {$this->_from}\n";
$header .= "X-Sender: {$this->_from}\n";
$header .= "From: '{$this->_name}' <{$this->_from}>\n";
$header .= "MIME-Version: 1.0\n";
$header .= 'X-Mailer: PHP/'. phpversion();
ini_set(sendmail_from, $this->_from); // stops gmail from putting (unknown sender) in From field
$sent = mail($this->_to, $this->_subject, $this->_msg, $header);
ini_restore( sendmail_from );
if (!$sent) {
trigger_error('SimpleContact failed due to php mail()', E_USER_WARNING);
} else {
$this->state = 3;
}
}
}
}
// bot attacks may miss hidden input elements
if (isset($_POST['confirm'])) {
$to = 'test@testmail.com.com'; // administrator or comma deliminated group of administrators
$from = $_POST['email']; // must be email only, not "'John Smith' <john@smith.name>"
$msg = $_POST['message'];
$subject = $_POST['subject'];
$name = $_POST['name'];
$fin = FALSE; // is the entire user process complete?
$userOutput = ''; // what the user will see at the end
//get the ball rolling...
$mailer = new SimpleContact;
$mailReady = $mailer->init( $to, $from, $name, $subject, $msg);
//ensure input is good
if(!$mailReady) {
$userOutput = $mailer->errorMsg;
} else {
$mailer->sendMail();
if($mailer->state !== 3) { //something went wrong, mail() probably failed :|
$userOutput = $mailer->errorMsg;
} else {
$userOutput = "Hi {$_POST['name']}, thanks for taking the time to contact us!";
$fin = TRUE;
}
}
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Contact Us</title>
<style type="text/css" media="screen,projection">
form {width:500px; margin:20px; font:76% tahoma, verda, sans-serif; padding:0;}
legend {font: bold 110% tahoma, verdana, sans-serif; }
label {display:block;margin:10px 0; border-bottom:1px solid #aaa;padding:5px 0 5px 2px;}
label input { float:right; width:300px; clear:right; border:1px solid #000; padding:1px; }
label input:focus, textarea:focus {border-color:#382; background:#fff;}
textarea { border:1px solid #000; width:474px; magin:5px 5px 0; }
p.note, p.alert { margin:1em 0; padding:.5em 2%; border-bottom:1px solid #ccc; }
p.alert { font-weight:bold; color:#882; border:2px groove #882; }
</style>
</head>
<body>
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
<fieldset>
<legend>Contact Us</legend>
<?php
if(empty($userOutput)) {
echo '<p class="note"><strong>We appreciate your feedback!</strong><br />Please fill in all fields</p>';
} else {
echo "<p class='alert'>$userOutput</p>";
}
?>
<label for="name"><input type="text" name="name" id="name" value="<?php if(!$fin && isset($_POST['name'])) echo $_POST['name']; ?>" /> Name</label>
<label for="email"><input type="text" name="email" id="email" value="<?php if(!$fin && isset($_POST['email'])) echo $_POST['email']; ?>" />E-mail</label>
<label for="subject"><input type="text" name="subject" id="subject" value="<?php if(!$fin && isset($_POST['subject'])) echo $_POST['subject']; ?>" />Subject</label>
<label for="message" class="commentlabel">Your Message<br />
<textarea name="message" cols="30" id="message" rows="8"><?php if(!$fin && isset($_POST['message'])) echo $_POST['message']; ?></textarea>
</label>
<input type="hidden" name="confirm" value="true" />
<input name="submit" type="submit" class="btn" value="Send Mail!" />
</fieldset>
</form>
</body>
</html>
matthijs
09-26-2005, 01:12 PM
Andrew, that looks very good. Trying the script at this moment. However, the first run shows that an empty subject or name is not notified by the script. Only an empty email field gives an error message. Probably is just some small typo or something. Looking closer at the script now, but wanted to let you know.
[edit]
Ok, maybe it has something to do with:
if (isset($_POST['confirm'])) {
...
$subject = 'Site Feedback: '.$_POST['subject'];
while in the class:
if( empty($subject) )
However, $subject is never empty, so never returns false, even if $_POST['subject'] is empty. Or am I missing something?
mindlessLemming
09-26-2005, 01:51 PM
Bingo :)
So it begs the question; poor class design or merely poor implementation? :o
Probably both.
Ideally, the Name field should be appended to the From: field in the mail header using the "John Smith" <john@smith.name> format, thereby allowing the message to be a direct copy of the textarea input. At the same time though, depending on empty input is porr design too, so that's not a good solution...
This is becoming one of those horrid 'class that does everything' pieces. I'll clean up the implementation for now and come back to the class in a week when I return from out of town
Thanks for the heads up; and for the compliment :)
previously posted code is now updated
matthijs
09-26-2005, 02:18 PM
Andrew, don't be so hard on yourself! Some easy switching of some lines could (would?) be enough to make it work. Something I just tried in 5mins:
if (isset($_POST['confirm'])) {
$to = 'mymail@gmail.com';
$from = $_POST['email'];
$name = $_POST['name'];
$subject = $_POST['subject'];
$msg = $_POST['message'];
$fin = FALSE; // is the entire user process complete?
$userOutput = ''; // what the user will see at the end
//get the ball rolling...
$mailer = new SimpleContact;
$mailReady = $mailer->init( $to, $from, $subject, $msg);
//ensure input is good
if(!$mailReady) {
$userOutput = $mailer->errorMsg;
} else {
$to = 'mymail@gmail.com'; // administrator or comma deliminated group of administrators
$from = $from; // must be email only, not "'John Smith' <john@smith.name>"
$msg = "From: ".$from ."\n".$message; // Add any extra message content here
$subject = 'Site Feedback: '.$subject;
$mailer->sendMail();
A bit messy, so it'll need some cleaning up, but it seemed to work correctly.
[EDIT:]
Ok, you were faster than me (or I'm slow). Nevermind my code. Everybody, take a good look at the code posted above by Andrew!
Andrew, thanks again.
mindlessLemming
09-26-2005, 02:28 PM
No, thank you.
Cronstructive criticism is the most I could hope for when posting code samples :thumbsup:
matthijs
09-26-2005, 02:35 PM
You're welcome. You're code is appreciated. It wasn't untill I saw your signature that I realised you're THE Andrew from leftjustified :) I visit your site regularly. What a small world..
Good luck/have fun out of town,
Matthijs
mindlessLemming
09-26-2005, 02:48 PM
Thanks for visiting my site Matthijs, but I wouldn't know squat if it wasn't for codingforums. :D
Out of town (http://we05.com/) is going to be awesome. Quite awesome indeed :)
matthijs
09-26-2005, 10:23 PM
I think i found another bug. (or I have been behind my pc too long and do not think clearly anymore..)
As the code is now the headers are made like this:
else {
$header = "Return-Path: {$this->_from}\n";
$header .= "X-Sender: {$this->_from}\n";
$header .= "From: '{$this->_name}' <{$this->_from}>\n";
$header .= "MIME-Version: 1.0\n";
$header .= 'X-Mailer: PHP/'. phpversion();
However, the email injection filtering is done like:
if( $this->_isInjection($from) || $this->_isInjection($to) || $this->_isInjection($msg)) {
$this->state = 2;
$this->errorMsg = 'This message has been identified as an email injection attempt';
return false;
}
while
$this->_name = $name;
$sent = mail($this->_to, $this->_subject, $this->_msg, $header);
So if I see it correctly, $name does not get filtered for email injection attempts, but sneaks into the headers!
The reason this small, but fatal mistake has sneaked into the code, is that the original code given was:
$msg = "From: ".$_POST['name']."\n".$_POST['message']; // Add any extra message content here
So in the original code the $name var was put in the $msg var. The $msg was filtered afterwards, so no problem there.
To summerise, my guess is that the best thing to do is to not include the $name in the headers, in the following rule:
$header = "Return-Path: {$this->_from}\n";
$header .= "X-Sender: {$this->_from}\n";
$header .= "From: <{$this->_from}>\n";
$header .= "MIME-Version: 1.0\n";
$header .= 'X-Mailer: PHP/'. phpversion();
Please correct me if I'm wrong ;) it's too late, need some sleep...
matthijs
10-08-2005, 09:22 AM
Ok, I'll give the complete code, with the small updates:
<?php
class SimpleContact {
var $state; // 0 = fail, 1 = ready, 2 = attempted injection attack, 3 = mail sent
var $errorMsg; // human readable version of $this->state with specific detail of error's cause
var $_to;
var $_from;
var $_subject;
var $_msg;
function _isEmail($email) {
if (preg_match("#^(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}$#", $email))
{ return true; }
else
{ return false; }
}
function _isInjection($text) {
$text = strtolower($text);
if (preg_match('#(content\s*-\s*disposition)|(bcc\:)|(cc\:)|(content\s*-\s*transfer\s*-\s*encoding)|(mime\s*-\s*version)|(multipart\s*/\s*mixed)|(multipart\s*/\s*alternative)|(multipart\s*/\s*related)|(reply\s*-\s*to)|(x\s*-\s*mailer)|(x\s*-\s*sender)|(x\s*-\s*uidl)#is',$text))
{ return true; }
else
{ return false;}
}
function init($to, $from, $name, $subject, $msg) {
// start with least expensive process, end with regex madness.
if( empty($name) ) {
$this->state = 0;
$this->errorMsg = 'Please fill in your name';
return false;
}
if( empty($subject) ) {
$this->state = 0;
$this->errorMsg = 'All mail must contain a subject';
return false;
}
if( empty($msg) ) {
$this->state = 0;
$this->errorMsg = 'All mail must contain a message body';
return false;
}
if( empty($from) || !$this->_isEmail($from) ) {
$this->state = 0;
$this->errorMsg = 'Invalid sender email address supplied';
return false;
}
if( empty($to) || !$this->_isEmail($to) ) {
$this->state = 0;
$this->errorMsg = 'Invalid recipient email address supplied';
return false;
}
if( $this->_isInjection($to) || $this->_isInjection($name) || $this->_isInjection($from) || $this->_isInjection($subject) || $this->_isInjection($msg)) {
$this->state = 2;
$this->errorMsg = 'This message has been identified as an email injection attempt';
return false;
}
$this->_to = $to;
$this->_from = $from;
$this->_name = $name;
$this->_subject = $subject;
$this->_msg = $msg;
$this->state = 1;
return true;
}
function sendMail() {
if($this->state !== 1) {
trigger_error('SimpleMail Object is not initialized or contains invalid data - Mail sending cancelled', E_USER_WARNING);
} else {
$header = "Return-Path: {$this->_from}\n";
$header .= "X-Sender: {$this->_from}\n";
$header .= "From: '{$this->_name}' <{$this->_from}>\n";
$header .= "MIME-Version: 1.0\n";
$header .= 'X-Mailer: PHP/'. phpversion();
ini_set(sendmail_from, $this->_from); // stops gmail from putting (unknown sender) in From field
$sent = mail($this->_to, $this->_subject, $this->_msg, $header);
ini_restore( sendmail_from );
if (!$sent) {
trigger_error('SimpleContact failed due to php mail()', E_USER_WARNING);
} else {
$this->state = 3;
}
}
}
}
// bot attacks may miss hidden input elements
if (isset($_POST['confirm'])) {
$to = 'mymail@mail.com'; // administrator or comma deliminated group of administrators
$name = $_POST['name'];
$from = $_POST['email'];
$subject = $_POST['subject'];
$msg = $_POST['message'];
$fin = FALSE; // is the entire user process complete?
$userOutput = ''; // what the user will see at the end
//get the ball rolling...
$mailer = new SimpleContact;
$mailReady = $mailer->init($to, $from, $name, $subject, $msg);
//ensure input is good
if(!$mailReady) {
$userOutput = $mailer->errorMsg;
} else {
$mailer->sendMail();
if($mailer->state !== 3) { //something went wrong, mail() probably failed :|
$userOutput = $mailer->errorMsg;
} else {
$userOutput = "Hi {$_POST['name']}, thanks for taking the time to contact us!";
$fin = TRUE;
}
}
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Contact Us</title>
<style type="text/css" media="screen,projection">
form {width:500px; margin:20px; font:76% tahoma, verda, sans-serif; padding:0;}
legend {font: bold 110% tahoma, verdana, sans-serif; }
label {display:block;margin:10px 0; border-bottom:1px solid #aaa;padding:5px 0 5px 2px;}
label input { float:right; width:300px; clear:right; border:1px solid #000; padding:1px; }
label input:focus, textarea:focus {border-color:#382; background:#fff;}
textarea { border:1px solid #000; width:474px; magin:5px 5px 0; }
p.note, p.alert { margin:1em 0; padding:.5em 2%; border-bottom:1px solid #ccc; }
p.alert { font-weight:bold; color:#882; border:2px groove #882; }
</style>
</head>
<body>
<form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="post">
<fieldset>
<legend>Contact Us</legend>
<?php
if(empty($userOutput)) {
echo '<p class="note"><strong>We appreciate your feedback!</strong><br />Please fill in all fields</p>';
} else {
echo "<p class='alert'>$userOutput</p>";
}
?>
<label for="name"><input type="text" name="name" id="name" value="<?php if(!$fin && isset($_POST['name'])) echo $_POST['name']; ?>" /> Name</label>
<label for="email"><input type="text" name="email" id="email" value="<?php if(!$fin && isset($_POST['email'])) echo $_POST['email']; ?>" />E-mail</label>
<label for="subject"><input type="text" name="subject" id="subject" value="<?php if(!$fin && isset($_POST['subject'])) echo $_POST['subject']; ?>" />Subject</label>
<label for="message" class="commentlabel">Your Message<br />
<textarea name="message" cols="30" id="message" rows="8"><?php if(!$fin && isset($_POST['message'])) echo $_POST['message']; ?></textarea>
</label>
<input type="hidden" name="confirm" value="true" />
<input name="submit" type="submit" class="btn" value="Send Mail!" />
</fieldset>
</form>
</body>
</html>
The update I made was to put all the vars in the injection function (see below). Certainly the $name var must be filtered, because that one is ending up in the headers of the mail()
if( $this->_isInjection($to) || $this->_isInjection($name) || $this->_isInjection($from) || $this->_isInjection($subject) || $this->_isInjection($msg)) {
$this->state = 2;
$this->errorMsg = 'This message has been identified as an email injection attempt';
return false;
}
Hope this one is compleet and error free.
Love*
10-08-2005, 09:25 AM
may i suggest aMailzing (http://scripts.inexistent.org) ? It's what I use, and it is wonderful.
matthijs
10-08-2005, 09:49 AM
Off course you can. But I'm not so sure if I personally would use that script. First of all, the need to have credit line in the mail. Of course, if you use a script from someone else, you want to credit them. But for something as 'simple' as a contact form, why not write your own, maybe with some help on a few forums :)
Secondly, I'm not sure if this script is not vulnarable to the email-injection problem we're talking about here. I quickly looked at the code and couldn't find any filtering for emailinjection attempts. (but maybe I overlooked something in my quick scan).
Love*
10-08-2005, 10:09 AM
ahh, I'm not really an expert with these things, :) I get easily impressed with talks like the one you made up there^ LOL :p
kiwibrit
08-11-2006, 06:47 PM
I've had a look at this - modifying it for use with HTML 4.01 strict, rather than
XHTML 1.0 strict.
Apart from changes to the CSS (for my own styling) and the DOCTYPE, all that I did was to change the code so that " />" was replaced by ">".
I had wondered about:
$header .= "MIME-Version: 1.0\n"; Bearing in mind I am using HTML rather than XHTML, should I leave it in?
Also, the correct mail recipient is getting club\\\'s when the test message read club's - and \\\"email injection\\\" when the test message read "email injection".
Looks like I need to use stripslashes - and I will experiment.
OK, I have got rid of the unwanted slashes
$msg = $_POST['message'];
if (get_magic_quotes_gpc()) {
$msg = stripslashes( $msg );
}
Does it.
Would still like some advice on the MIME thing, though
Sorry for jumping on the wagon, but how would I add non-required fields to Matthijs'/MindlessLemming's e-mail form (http://www.codingforums.com/showpost.php?p=362752&postcount=12)? I just can't figure that one out and would really appreciate some help! :)
kiwibrit
08-12-2006, 08:42 AM
Sorry for jumping on the wagon, but how would I add non-required fields to Matthijs'/MindlessLemming's e-mail form (http://www.codingforums.com/showpost.php?p=362752&postcount=12)? I just can't figure that one out and would really appreciate some help! :)
I don't think it should be tricky. You would have to change the "Please fill in all fields" note to something like please fill in all fields marked *.
For the html part of the form, simply add more inputs as required. So if you ant one, say, "diet" - the extra input would be:
<label for="name"><input type="text" name="diet" id="diet" value="<?php if(!$fin && isset($_POST['diet'])) echo $_POST['diet']; ?>" > Diet</label>
For the php
if( empty($diet) ) {
$this->state = 0;
$this->errorMsg = 'Please let us know your diet';
return false;
}
Senior moment. I was treating it as a mandatory field. I wonder if it should not have a filter for malicious script. Will look at that - but not today - it's grandson's birthday.:thumbsup:
and
$name = $_POST['name'];
$from = $_POST['email'];
$diet = $_POST['diet']
$subject = $_POST['subject'];
$msg = $_POST['message'];
if (get_magic_quotes_gpc()) {
$name = stripslashes( $name );
$diet = stripslashes( $diet );
$msg = stripslashes( $msg );
}
Don't forget to amend the thank-you message to allow for the stripslashes :
$userOutput = "{$name}, thanks for contacting us.";
My suggestions are untested - but I will be doing something lik this very soon. If I have goofed, I'll post.
kiwibrit: Thanks for taking the time. If you do figure out how to add non-required field I'd be glad if you could share it. I know HTML and CSS and have some but limited knowledge in PHP, but I can't for the life of me figure this one out... Cheers! :)
kiwibrit
08-14-2006, 05:26 PM
Well, I am struggling.
The HTML side is pretty straightforward:
<label for="adr2"><input type="text" name="addr2" id="addr2" value="<?php if(!$fin && isset($_POST['addr2'])) echo $_POST['addr2']; ?>" maxlength="50" >Organization</label>
<textarea name="commnents" cols="30" id="commnents" rows="8"><?php if(!$fin && isset($_POST['commnents'])) echo $_POST['comments']; ?></textarea>
should do it, I think, for an optional field Address 2.
As $msg will eventually have the information about Address 2 in it, I have made the value for the text area "comments".
Now look at the php
if( empty($comments) ) {
$this->state = 0;
$this->errorMsg = 'Please enter your comments';
return false;
replaces the empty $msg check.
and we then have
$to='anyone@mydomain.com';}
$name = $_POST['name'];
$from = $_POST['email'];
$subject = $_POST['subject'];
$addr2 =$_POST['addr2'];
$comments = $_POST['comments'];
$msg =
"This message was sent from: $name\n" .
"Address (line 2) : $addr2\n" .
"E-mail : $email\n" .
"------------------------- COMMENTS -------------------------\n\n" .
$comments .
"\n\n------------------------------------------------------------\n" ;
if (get_magic_quotes_gpc()) {
$msg = stripslashes( $msg );
$name = stripslashes( $name );
}
But I am still working on it :(
vBulletin® v3.8.2, Copyright ©2000-2012, Jelsoft Enterprises Ltd.