Welcome Guest!
Please login
If you do not have an account yet on The Web Squeeze forums, please Register! It’s FREE and there are many benefits:
- Receive Fast Advice
- Learn Programming Languages
- Get Professional Website Reviews
- Quick Troubleshooting Assistance
|
|
Php Contact Form
This is a discussion on Php Contact Form, within the PHP section. This forum and the thread "Php Contact Form" are both part of the Programming Your Website category.
![]() ![]() |
Mar 6 2008, 09:38 AM
Post
#1
|
|
![]() Squeeze Machine ![]() ![]() ![]() ![]() ![]() Group: Administrators Posts: 650 Joined: 13-February 08 From: Catching the squeezed drips downunder. Member No.: 13 |
The issue of contact forms with PHP is one that often comes up around the forums and there are of course many ways of achieving this very popular goal. Throughout this post, I will take you through the method I use for processing a form with PHP and having PHP send an email with the details.
The method that c010depunkk uses is very similar to what I use so I will deliberately add some differences into my method to add some diversity to the contact form code found on The Web Squeeze. Requirements of this script : · Register_globals is OFF · PHP is configured to use sendmail or sendmail wrapper · Magic quote is OFF · You don’t mind reading some poor jokes scattered throughout PHP comments and this tutorial Firstly some differences between this method and the other Pinned Thread on the subject :
Okay, so having explained the differences between this method and the equally well-written method of c010depunkk, I will begin... A HTML form please : Well, considering you asked so nicely I guess I can organise that for you. Remember that this is fully customisable and that this for is for example purposes only. It is a mix of all the elements needed to illustrate what this method is capable of. Due to pending patents, I cannot add the code that has this script make you cups of coffee or bake brownies -- I apologise for any inconvenience this causes. Things to note in this form
HTML <form method="post" action="emailMe.php" enctype="multipart/form-data"> <!-- Let's get their name //--> <select name="title"> <option value="mr">Mr</option> <option value="mrs">Mrs</option> <option value="miss">Miss</option> <option value="ms">Ms</option> <option value="martian">Martian</option> </select> <br /> The name that comes before your last name: <br /> <input type="text" name="firstname" /> <br /><br /> The name that comes after the name you entered that comes before your last name :<br /> <input type="text" name="lastname" /> <br /><br /> Email address:<br /> <input type="text" name="email" /> <br /><br /> Phone number:<br /> <input type="text" name="phone" /> <br /><br /> Your favourite palindrome : <br /> <select name="pali"> <option value="noon">Noon</option> <option value="boob">Boob</option> <option value="strawwarts">Straw Warts</option> </select> <br /><br /> Favourite Insects?<br /> (Tick all that apply)<br /><br /> <input type="checkbox" name="insect[]" value="fly"> Fly<br /> <input type="checkbox" name="insect[]" value="grasshopper"> Grasshopper<br /> <input type="checkbox" name="insect[]" value="beetle"> Beetle<br /> <input type="checkbox" name="insect[]" value="flea"> Flea<br /> <input type="checkbox" name="insect[]" value="tick"> Tick<br /> <input type="checkbox" name="insect[]" value="Wasp"> wasp<br /> <br /><br /> <input type="submit" name="submit" value="Send the info (I'm dying to know)" /> </form> Okay, so now we will look at each part of the script that will add up to make this PHP contact form processor do its 'thang'. I have attached the full PHP script to my final post if you would rather not listen to me raving on. -------------------- |
|
|
Mar 6 2008, 09:39 AM
Post
#2
|
|
![]() Squeeze Machine ![]() ![]() ![]() ![]() ![]() Group: Administrators Posts: 650 Joined: 13-February 08 From: Catching the squeezed drips downunder. Member No.: 13 |
The Top Part (Latin Name: Topius Partius)
CODE /**** Some configurable variables, will put them up the top here so I don't hurt my back bending down in the code to get them later****/ $returnURL = 'http://www.example.com'; // The url to send the user back to if something went wrong $successURL = 'http://www.example.com/success.html'; // The url to send the user to if all goes well $allowedReferer = array($returnURL); // What URL's would you like to allow to use this script? If the form is sent from elsewhere we can givem the boot $arrivalMethod = 'post'; // What method will the form be sent using? Choice of 'get', 'post' or 'request' (request is a combination of get and post variables in case you wanted the option to use both) $submitName = 'submit'; // What is the name of the submit button? $mailerName = 'Online Contact Form'; // Name the x-mailer $Temail = 'rakuli@example.com';// Change it to anything you like -- is where the email will be sent to $TemailBcc = 'random2@example.com'; // If you want to copy anyone in on all emails sent from the form, add them here separated by commas $Semail = '"My golly gosh, PHP has contacted me" <%s>'; // The subject line of the email %s will be used in sprintf() to add the senders email as the "from" contact $fVars = ($arrivalMethod == 'post') ? $_POST : ($arrivalMethod == 'get' ? $_GET : $_REQUEST); // Store a reference to where the variables are coming from This first part of the PHP script is just a bit of housekeeping and like housekeeping you're free to change things around as required. If you plan to use this script as a way of processing forms from multiple domains you may wish to use a more secure means of checking where the form was submitted from but to outline what I have here. · $returnURL : When all goes toes up, this is the URL that the user will be sent back to. For the purposes of this script, it will be a link at the bottom of the error messages directing them back to the form. · $successURL : When all goes to plan, this is the URL that the user will be sent to so they can receive their well deserved pat on the back or at least a page advising that email has been sent. Again I am using this to send the user to a new page but it is more than possible to have the success message displayed by this script. · $allowedReferer : An array of URL's that we will allow to use this script – a very rudimentary way of checking credentials as HTTP headers can be modified by the user but for the sake of this script it offers a small way of checking that the user has come from somewhere the script expects. · $arrivalMethod : Will this script be working with $_GET,$_POST or both sets of data? I am using $_POST for this script · $submitName : What is the name of the submit button in the contact form? It is better to keep this uniform across all of the forms using this script. · $Temail : This is the email address that PHP will send the processed form data to · $TemailBcc : Copy in some friends and family so everyone can enjoy the fun of PHP contact forms · $Semail : The subject line of the email when it arrives at the above addresses. The <%s> is a formatting string that will add the form user's email address later in the script · $fVars : Based on the arrival method, I am storing a reference to either $_GET, $_POST or $_REQUEST The Input Checking (Latin Name: Whattheysentya isvalidus) Now we will do a bit more housekeeping to make sure that guests don't come in and start tracking dirt all over our nice clean floor (I hate that!). In other words, we will boot anyone that is tyre-kicking and not correctly using our form. CODE # First things first, well actually, it's the second thing if you count the conifguration variables above as the first thing # Check to make sure that the 'submit' button has been sent and has a value -- if not? Send them to the $return URL if (!isset($fVars[$submitName]) || empty($fVars[$submitName])) { header("Location: $returnURL"); exit(); } # Next, we'll check if the user has submitted this form from one of the allowed referers # If not, we'll give them a fruit basket and send them on their way $pattern = str_replace('\|', '|', preg_quote(implode('|', $allowedReferer), '/'));// Implode the referer array into a regex string this|this|this|this if (!preg_match('/^(' . $pattern . ').*/',$_SERVER['HTTP_REFERER'])) { header("Location: $returnURL"); exit(); } As I hinted at earlier, we are doing a fairly basic check to ensure the form is coming from the right place the first conditional is simply checking that we have received the variable set by the user clicking the submit button on a form. $pattern is obtained by taking the array of allowed referrers and imploding them into a Regex escaped string. They are separated by '|' which tells the regular expression that we want either of what is on each side of the '|'. The following conditional checks that the domain name of the referring URL is one of the allowed one and boots the user if not. The Form Fields (Latin Name: Partius that User Choosius) So, the house is spotless – except for that one glass on the table that used to hold the juice you rewarded yourself with after cleaning up – which means it is time to get into the actual engine of this script. This is the $formInputs array, it holds all the instructions that PHP will carry out on the form data. CODE /* # This is the array of form fields that PHP is expecting to receive, the structure is as follows $formInputs = array (formInputName => arrayOfDetails ( 0 => displayed name of field (string), 1 => is element mandatory? (boolean), 2 => allowed values (array(mixed)), 3 => function to call if required (string), 4 => input is an array of values? (boolean), 5 => customised error message (string) ) ) */ $formInputs = array( 'title' => array('Title', true, array('mr', 'mrs', 'miss', 'ms', 'martian')), 'firstname' => array('First Name', true), 'lastname' => array('Last Name', true), 'email' => array('Email Address', true, 3 => 'check_email', 5 => 'Your email address is invalid!'), 'phone' => array('Phone Number', false, 3 => 'ctype_digit', 5 => 'Phone field may only contain numbers'), 'pali' => array('Favourite Palindrome', false, array('noon', 'boob', 'strawwarts')), 'insect' => array('Favourite Insect', false, array('fly', 'grasshopper', 'flea', 'tick', 'wasp'), 4 => true), ); So... What is so fantastic about this array of form fields? Well, not that much really, it's just a simple set of instructions. I will go into a bit more detail: $formInputs is an associative array of arrays. The key of each array element is the name of one of the form fields that this script will process. Each form field is itself an array of values that describes what it is and what it requires; each form field can have up to 6 array elements (indexed from 0 – 5). Each number can be described as follows. These are required by each form field 0. Displayed name : In the event that the form field is entered incorrectly and winds up in an error message, this is the "Human Readable" name that you can assign to the form field. Instead of just 'email' which is what is found in the name="email" part of the form, you can name it 'Email Address' 1. Is Element Mandatory : Does the form require this value to be entered before sending the email? A simple true or false will suffice from here. The following are optional for each form field 2. Allowed Values : This is an array of values that the form field may contain (ie. for a select element, this would be an array of the options within that select field) – you can use it for text fields also but generally speaking if you have specific allowed values, you should show the user all of these via radio buttons and select boxes 3. Callback Function : A string name of the function you wish to pass the value to when validating it. The function can be a PHP function or a custom function. Whatever function it is you choose it should return false for an invalid value and true for a valid value 4. Array of Values : If the form field is a group of checkboxes or a multi-line select box (which allow the user to select numerous options for which PHP can then convert into an array) add this key as true, it will mean that all values are correctly captured by PHP 5. Customised error : Sometimes it is helpful for an error to say something other than "Please check the value you entered for X". You can add a custom error message to this key So with the array set up, it is simply a matter of looping through the values sent to PHP and validating them based on the $formInputs array. The Logic Validation (Latin Name: Makius Shoritius Makingius Census) Please note that the following code is extensively commented so no explanation will follow this snippet. Essentially it is a group of conditionals checking each of the possible variants found in $formInputs CODE # It's time to loop through all the values and check that they fit the requirements defined
# in the $formInputs array $errors = array(); // Sumfins up? Shove it in the $errors array to berate the user with later $errorMsg = 'Please check that you entered <strong>%s</strong> correctly'; // This is the default, legal department approved standard error message $messageArray = array(); // This will hold all the parts of the email message as the form fields validate # So we don't loop through $_POST, $_GET or $_REQUEST varibales unnecessarily, loop through the forminputs array and check each that way foreach ($formInputs as $pk => $pa) { // The first check that will render the input immediately invalid is // checking against the "is required?" directive of the $formInputs array if (!isset($fVars[$pk]) && $pa[1] === true) { // Is there a custom error? If not, just use the default one $errors[] = $pa[5] ? $pa[5] : sprintf($errorMsg, $pa[0]); continue; } // This next check is for form fields that have numerous available options // that have been sent to us as an array. if ($pa[4] === true && is_array($fVars[$pk])) { $msg = ''; // As this will be more than just a one-line message, set up the variable to hold the string // Loop through each value, if there is a list of allowed values // check that it is in that array, otherwise add all of them to $msg foreach($fVars[$pk] as $val) { $msg .= isset($pa[2]) /* List of allowed values? */ ? in_array($val, $pa[2]) ? "\t\t" . htmlentities($val) . "<br />\n" : '' /* No List of defaults */ : "\t\t" . htmlentities($val) . "<br />\n"; } // If we've been sent a bung array or an array of useless political rants // $msg will be empty, this means that it will trigger an error if it is // a mandatory value if (!empty($msg)) $messageArray[] = '<strong>' . $pa[0] . "</strong> : <br />\n\n<br />" . $msg; else if ($pa[1] === true) $errors[] = $pa[5] ? $pa[5] : sprintf($errorMsg, $pa[0]); continue; // If the value is not an array and also a mandatory element, this will spark an error message } else if ($pa[1] === true && $pa[4] === true) { $errors[] = $pa[5] ? $pa[5] : sprintf($errorMsg, $pa[0]); continue; } // Form fields that have a list of allowed values eg. select elements or radio buttons // This check ensures the sent value is one of those allowed if (isset($pa[2])) { // Is the value in the array? If yes, add it to the $message array if (in_array($fVars[$pk], $pa[2])) { $messageArray[] = '<strong>' . $pa[0] . '</strong> : ' . htmlentities($fVars[$pk]); // Oh no!!, it's just some crazy sales pitch from an entrepeneurial spammer, that means error message time // Only if its a mandatory value though } else if ($pa[1] === true) $errors[] = $pa[5] ? $pa[5] : sprintf($errorMsg, $pa[0]); continue; } // Now we're dealing with values typed in by the user -- gasp!! // If we trim the value of whitespace, we can see if we've // just been sent a string of spaces $fVars[$pk] = trim($fVars[$pk]); if (empty($fVars[$pk]) && $pa[1] === true) { // Mandatory value? Error it $errors[] = $pa[5] ? $pa[5] : sprintf($errorMsg, $pa[0]); continue; // Empty but not mandatory? Just turn the other cheek } else if (empty($fVars[$pk])) continue; // If this is a value we would like to perform an additional check on // via a callback function (which returns true for a good value and false for a bad one if (isset($pa[3])) { if ($pa[3]($fVars[$pk])) // Nice one brudda! Add it to the message $messageArray[] = '<strong>' . $pa[0] . '</strong> : ' . htmlentities($fVars[$pk]); // Oh? I guess that you have a dodgy character in there somewhere so if this is a mandatory value // You is gonna get an error message else if ($pa[1] == true) $errors[] = sprintf($errorMsg, $pa[0]); continue; } // Yawn!... just a boring old input with no special characteristics -- just add it to the message please. $messageArray[] = '<strong>' . $pa[0] . '</strong> : ' . htmlentities($fVars[$pk]); } -------------------- |
|
|
Mar 6 2008, 09:41 AM
Post
#3
|
|
![]() Squeeze Machine ![]() ![]() ![]() ![]() ![]() Group: Administrators Posts: 650 Joined: 13-February 08 From: Catching the squeezed drips downunder. Member No.: 13 |
The Email Validation (Latin Name: Howwouldiya Contactembackius)
In the case of this script, I have two form fields taking advantage of the callback function. The phone number field uses the in-build ctype_digit function (this returns true if the passed argument is all numeric values). The other field using a callback function is the email address. This uses a custom function to check if the email address is valid. Below is the function that checks the email – I won’t explain it as it is commented but it will return true for a valid email and false if not. You can create any functions you like or use any PHP function that returns a Boolean value as a callback to validate your data. CODE function check_email($email) { // First, we check that there's one @ symbol, // and that the lengths are right. if (!ereg("^[^@]{1,64}@[^@]{1,255}$", $email)) { // Email invalid because wrong number of characters // in one section or wrong number of @ symbols. return false; } // Split it into sections to make life easier $email_array = explode("@", $email); $local_array = explode(".", $email_array[0]); for ($i = 0; $i < sizeof($local_array); $i++) { if (!ereg("^(([A-Za-z0-9!#$%&'*+/=?^_`{|}~-][A-Za-z0-9!#$%& ↪'*+/=?^_`{|}~\.-]{0,63})|(\"[^(\\|\")]{0,62}\"))$", $local_array[$i])) { return false; } } // Check if domain is IP. If not, // it should be valid domain name if (!ereg("^\[?[0-9\.]+\]?$", $email_array[1])) { $domain_array = explode(".", $email_array[1]); if (sizeof($domain_array) < 2) { return false; // Not enough parts to domain } for ($i = 0; $i < sizeof($domain_array); $i++) { if (!ereg("^(([A-Za-z0-9][A-Za-z0-9-]{0,61}[A-Za-z0-9])| ↪([A-Za-z0-9]+))$", $domain_array[$i])) { return false; } } } return true; } Sending the Email or Displaying an Error (Latin Name: No Translation Available) Okay, so if you read through the comments above, you will see that I have looped through the $formElements array checking each value against the directives for each form field. Once the loop is finished there are three possible outcomes : 1. There are no errors and $messageArray has some values for PHP to email 2. There are no errors but $messageArray has no workable values 3. There are some nasty, nasty errors raining on everyone’s parade and bringing down the mood. Let’s address each scenario individually: Scenario 1 – All is well It’s time to take the values from $messageArray and email them out – jolly good show! CODE # No Errors? Check - $MessageArray has some values? if check, then we can set up the email if (!count($errors) && count($messageArray)) { // Take the subject declared above and add the sender's email to it // If for some reason, this form is not getting an email off the user // we can ignore it if (isset($fVars['email'])) $Semail = sprintf($Semail, $fVars['email']); // Because we are creating a MIME email (HTML & Plain Text) we need to define some boundaries // To separate the multiple parts of the message... // Let's do that now and create an outer and inner boundary $ob = md5(time() . 'boundary'); $ib = md5(time() . 'boundary2'); $headers = "From: ".$Semail."\n"; // Add the subject (even if it has no sender email address) if (!empty($TemailBcc)) $headers .= "Bcc: ".$TemailBcc."\n"; // Carbon copy in the required users if (isset($fVars['email'])) { // Set the reply to as the user's email (if available) $headers .= "Reply-To: <". $fVars['email'].">\n"; $headers .= "Return-Path: <" . $fVars['email'] . ">\n"; } $headers .= 'Date: ' . gmdate('D, d M Y H:i:s') . ' +0000' . "\n"; // Um, what day is it? $headers .= 'X-Mailer: ' . $mailerName . "\n"; // Set the mailing application name, $headers .= 'Mime-Version: 1.0' . "\n"; // Multi type MIME -- hoorays $headers .= 'Content-Type: multipart/alternative; boundary="' . $ob . '"' . "\n"; // Define the boundary $headers .= 'Content-Transfer-Encoding: 7bit' . "\n"; // Let's start the content.. this is some very sloppy HTML, you can correct if you like $content = "<title>$Semail</title>\n\n\n"; $content .= "<body style='font-family: verdana; font-size: 9pt;'>\n\n"; $content .= "<h1 style='font-size:1.1em'>Someone Has Contacted you!</h1>\n\n\n"; // loop through the messageArray and add each line to the email foreach ($messageArray as $msg) $content .= $msg . "\n\n<br /><br />"; $content .= "Your Sincerely,\n\n<br /><br />"; // Be polite and sign off :) $content .= "Your Website"; $message = strip_tags($content) . "\n" . '--' . $ob . "\n"; // Add the HTML part boundary and start the message // Plain text first $message .= 'Content-Type: text/plain; charset=iso-8859-1' . "\n"; $message .= 'Content-Transfer-Encoding: 7bit' . "\n\n"; $message .= strip_tags($content) . "\n" . '--' . $ob . "\n"; // Now the HTML part $message .= 'Content-Type: text/html; charset=charset=iso-8859-1' . "\n"; $message .= 'Content-Transfer-Encoding: 7bit' . "\n\n"; $message .= $content . "\n" . '--' . $ob . '--'; mail($Temail, $subject, $message, $headers); // Givem their kudos header('Location: ' . $successURL); } else { $errors[] = ‘No valid information was received from the form!’; } This part of the script is creating the email message that will be sent. I am using MIME encoding which allows me to send the email in two parts – HTML and Plain text. You can spruce up the HTML a bit as I typed this up for you pretty quickly. You can use inline style declarations (this tutorial doesn’t hold the scope to attach a style sheet to use in the email). SMTP and MIME are both very simple protocols and it is fairly easy to digest the headers just by reading them. I will detail the points that the comments might not cover in enough detail: · Boundaries : This email message is using MIME format which means and also Content-Type: Multipart/Alternative. To allow as many users as possible to receive and read the email, it is broken down into parts which need to be separated by a boundary. The boundary (as defined by the protocol) needs to be unique within the email message and it is an accepted practice to use a boundary that would never be repeated in the text normally found in an email. To define my boundaries, I had taken the time and concatenated with the words boundary and boundary2 and then taken an md5 hash of it. This ensures a character string that is highly unlikely to appear in any email – let alone the one being sent from this script. · X-Mailer : The X-Mailer header allows you to ‘clone’ the identification of popular mail clients, this is not foolproof but reduces some of the point flags added by junk mail filters. The PHP default is often blocked by spam stoppers. Scenario 2 – Nothing in $messageArray – Huh!, go figure Scenario 2 is also covered in the above snippet, the first conditional is checking to ensure that $messageArray contains some info and adds an element to the $errors array.. This flows nicely into the last scenario Scenario 3 – It’s all going wrong! Calm down, deep breaths! It’s not all bad – it just means that there is an error or two in the information provided by the user. This last snippet of code checks the existence of errors. If any are found, it loops through the $errors array and prints a list of problems to the page. Finishing it off with a link back to the form. CODE if (count($errors)) { // Break out of php to write the HTML ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Untitled Document</title> </head> <body> <?php echo '<div style="border: solid 1px red;padding: 2em;"> <h1 style="font-size: 1.2em;">Some Errors Occurred</h1> <ul>'; foreach ($errors as $err) echo '<li>', $err, '</li>'; echo '</ul> <a href="', $returnURL , '" onclick="window.history.back(); return false;">Go Back and try again</a> </div>'; ?> </body> </html> <?php } ?> Well well well. That pretty much covers the lot there. Let’s recap on what the above has done: · Declared some configurable variables at the top of the script · Check to ensure the user is coming from a friendly location · Defined an array of expected field inputs and directives for what values they should contain and how they should be validated · Check all the field inputs against the defined directives · Send the HTML/MIME email with posted information OR Bail out with a list of the errors that were encountered -------------------- |
|
|
Mar 6 2008, 09:47 AM
Post
#4
|
|
![]() Squeeze Machine ![]() ![]() ![]() ![]() ![]() Group: Administrators Posts: 650 Joined: 13-February 08 From: Catching the squeezed drips downunder. Member No.: 13 |
Now the whole code and nothing but the code...
Attached to this post is the entire script... Some Food For Thought: With a little bit of customisation you can pimp this script out to do many things, consider some of the following changes. · Rather than define the $formFields array within the script, consider passing this variable from the form so that you can use the one script for many forms. On the same token, save the $formFields array for each form in its own include file so that it can be brought in by each form as needed. · Use this as a recursive form (HTML Form and PHP are both in the same file – c010depunkk gives one such example in his contact form) – allowing you to redisplay the form each time an error occurs. · Instead of having PHP email the form details, consider reworking it to add it to a database · Anything else you may want it to do. I have tested this code and it works on my testing server but please feel free to ask any questions in this thread... I hope this helps. Rakuli
Attached File(s)
-------------------- |
|
|
Oct 13 2008, 07:04 AM
Post
#5
|
|
![]() Rapid Squeezer ![]() ![]() ![]() ![]() Group: Team Leaders Posts: 288 Joined: 7-October 08 From: Australia Member No.: 385 |
Awesome!
Could I ask a small favour though? I must be viewing this on a smaller screen than you composed it on, and the code snippets in the second post are wider than my screen... this wouldn't be such a problem, but no horizontal scrollbar appears! Just thought I'd say in case anyone else has the same trouble Great posts though! Nice one, Rakuli! -------------------- The more you visit, the more I'll post: http://japheththomson.com/
|
|
|
Oct 13 2008, 06:08 PM
Post
#6
|
|
![]() Co-Founder ![]() ![]() ![]() ![]() ![]() ![]() Group: Co-Founders Posts: 2,410 Joined: 13-February 08 From: On the forum! Member No.: 1 |
Awesome! Could I ask a small favour though? I must be viewing this on a smaller screen than you composed it on, and the code snippets in the second post are wider than my screen... this wouldn't be such a problem, but no horizontal scrollbar appears! Just thought I'd say in case anyone else has the same trouble Great posts though! Nice one, Rakuli! That's a bug in our forum design. Karinne will look at it once she gets the time, it has to do with the CSS. -------------------- Thanks,
Jacob Haug |
|
|
Oct 13 2008, 06:20 PM
Post
#7
|
|
![]() Rapid Squeezer ![]() ![]() ![]() ![]() Group: Team Leaders Posts: 288 Joined: 7-October 08 From: Australia Member No.: 385 |
Well, sort of, if the lines were shorter it wouldn't be an issue either
-------------------- The more you visit, the more I'll post: http://japheththomson.com/
|






Mar 6 2008, 09:38 AM








