[FIX] Validate email address input for customer registration

For articles specific to version 5.x
Martin
Site Admin
Site Admin
Posts: 1854
Joined: Wed Jun 17, 2009 6:30 pm
Location: South Yorkshire UK
Contact:

[FIX] Validate email address input for customer registration

Post by Martin »

This fix includes some optional extras in the second and third posts but the first modification is required to get any new customer registrations checked for valid email addresses (including spaces).

Until now, the only time the function is_email_address() in general.php was used, was to validate guest checkout orders and not customer registrations. Stupid, stupid mistake but this fix resolves that and passes everything through.


IMPORTANT: The quoted code below only works with 5.5.x and above... For 5.0.6 see last post.

Open: includes/classes/class.customer.php

Find:

Code: Select all

			// Does an account with this email address already exist?
			if ($this->AccountWithEmailAlreadyExists($customerData['custconemail'])) {
				$this->CreateAccountStep1("already_exists");
			}
After, Add

Code: Select all

			// MOD Force validation of the email using existing function
			else if(!is_email_address($customerData['custconemail'])) {
				$this->CreateAccountStep1("invalid_email");
			}
			// MOD END
Find:

Code: Select all

			if ($Error == "already_exists") {
				// The email address is taken, they have to choose another one
				$GLOBALS['ErrorMessage'] = sprintf(GetLang('AccountEmailTaken'), isc_html_escape($emailAddress));
			}
After, Add

Code: Select all

			//MOD validate email address
			else if ($Error == "invalid_email") {
				$GLOBALS['ErrorMessage'] = GetLang('AccountEnterValidEmail');
			}
Martin
Site Admin
Site Admin
Posts: 1854
Joined: Wed Jun 17, 2009 6:30 pm
Location: South Yorkshire UK
Contact:

Re: Space at end of customer email

Post by Martin »

Something else you may want to do as well to make life a little easier..

1. Consider replacing the is_email_address() function in /lib/general.php as follows:
Credit: http://www.linuxjournal.com/article/9585?page=0,3

Open: /lib/general.php

Find:

Code: Select all

	/**
	 * Checks if the passed string is a valid email address.
	 *
	 * @param string The email address to check.
	 * @return boolean True if the email is a valid format, false if not.
	 */
	function is_email_address($email)
	{
		// If the email is empty it can't be valid
		if (empty($email)) {
			return false;
		}

		// If the email doesnt have exactle 1 @ it isnt valid
		if (isc_substr_count($email, '@') != 1) {
			return false;
		}

		$matches = array();
		$local_matches = array();
		preg_match(':^([^@]+)@([a-zA-Z0-9\-][a-zA-Z0-9\-\.]{0,254})$:', $email, $matches);

		if (count($matches) != 3) {
			return false;
		}

		$local = $matches[1];
		$domain = $matches[2];

		// If the local part has a space but isnt inside quotes its invalid
		if (isc_strpos($local, ' ') && (isc_substr($local, 0, 1) != '"' || isc_substr($local, -1, 1) != '"')) {
			return false;
		}

		// If there are not exactly 0 and 2 quotes
		if (isc_substr_count($local, '"') != 0 && isc_substr_count($local, '"') != 2) {
			return false;
		}

		// if the local part starts or ends with a dot (.)
		if (isc_substr($local, 0, 1) == '.' || isc_substr($local, -1, 1) == '.') {
			return false;
		}

		// If the local string doesnt start and end with quotes
		if ((isc_strpos($local, '"') || isc_strpos($local, ' ')) && (isc_substr($local, 0, 1) != '"' || isc_substr($local, -1, 1) != '"')) {
			return false;
		}

		preg_match(':^([\ \"\w\!\#\$\%\&\'\*\+\-\/\=\?\^\_\`\{\|\}\~\.]{1,64})$:', $local, $local_matches);

		// Check the domain has at least 1 dot in it
		if (isc_strpos($domain, '.') === false) {
			return false;
		}

		if (!empty($local_matches) ) {
			return true;
		} else {
			return false;
		}
	}
Replace with:

Code: Select all

	/**
	 * Checks if the passed string is a valid email address.
	 *
	 * @param string The email address to check.
	 * @return boolean True if the email is a valid format, false if not.
	 */
	function is_email_address_OLD($email)
	{
		// If the email is empty it can't be valid
		if (empty($email)) {
			return false;
		}

		// If the email doesnt have exactle 1 @ it isnt valid
		if (isc_substr_count($email, '@') != 1) {
			return false;
		}

		$matches = array();
		$local_matches = array();
		preg_match(':^([^@]+)@([a-zA-Z0-9\-][a-zA-Z0-9\-\.]{0,254})$:', $email, $matches);

		if (count($matches) != 3) {
			return false;
		}

		$local = $matches[1];
		$domain = $matches[2];

		// If the local part has a space but isnt inside quotes its invalid
		if (isc_strpos($local, ' ') && (isc_substr($local, 0, 1) != '"' || isc_substr($local, -1, 1) != '"')) {
			return false;
		}

		// If there are not exactly 0 and 2 quotes
		if (isc_substr_count($local, '"') != 0 && isc_substr_count($local, '"') != 2) {
			return false;
		}

		// if the local part starts or ends with a dot (.)
		if (isc_substr($local, 0, 1) == '.' || isc_substr($local, -1, 1) == '.') {
			return false;
		}

		// If the local string doesnt start and end with quotes
		if ((isc_strpos($local, '"') || isc_strpos($local, ' ')) && (isc_substr($local, 0, 1) != '"' || isc_substr($local, -1, 1) != '"')) {
			return false;
		}

		preg_match(':^([\ \"\w\!\#\$\%\&\'\*\+\-\/\=\?\^\_\`\{\|\}\~\.]{1,64})$:', $local, $local_matches);

		// Check the domain has at least 1 dot in it
		if (isc_strpos($domain, '.') === false) {
			return false;
		}

		if (!empty($local_matches) ) {
			return true;
		} else {
			return false;
		}
	}
	
// MOD Alternate email validation routine
	/**
	 * Checks if the passed string is a valid email address.
	 *
	 * @param string The email address to check.
	 * @return boolean True if the email is a valid format, false if not.
	 */
	function is_email_address($email) {
		$isValid = true;
		$atIndex = strrpos($email, "@");
		if (is_bool($atIndex) && !$atIndex)
		{
			$isValid = false;
		}
		else
		{
			$domain = substr($email, $atIndex+1);
			$local = substr($email, 0, $atIndex);
			$localLen = strlen($local);
			$domainLen = strlen($domain);
			if ($localLen < 1 || $localLen > 64)
			{
				// local part length exceeded
				$isValid = false;
			}
			else if ($domainLen < 1 || $domainLen > 255) {
				// domain part length exceeded
				$isValid = false;
			}
			else if ($local[0] == '.' || $local[$localLen-1] == '.')
			{
				// local part starts or ends with '.'
				$isValid = false;
			}
			else if (preg_match('/\\.\\./', $local))
			{
				// local part has two consecutive dots
				$isValid = false;
			}
			else if (!preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain))
			{
				// character not valid in domain part
				$isValid = false;
			}
			else if (preg_match('/\\.\\./', $domain))
			{
				// domain part has two consecutive dots
				$isValid = false;
			}
			else if(!preg_match('/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$/', str_replace("\\\\","",$local)))
			{
				// character not valid in local part unless 
				// local part is quoted
				if (!preg_match('/^"(\\\\"|[^"])+"$/',
					 str_replace("\\\\","",$local)))
				{
					$isValid = false;
				}
			}
			if ($isValid && !(checkdnsrr($domain,"MX") || checkdnsrr($domain,"A")))
			{
				// domain not found in DNS
				$isValid = false;
			}
		}
		return $isValid;
	}
	
// MOD END Alternate email validation routine
This provides a different checking function that actually tests to see if the domain exists, has a dns record and responds to MX queries.
Martin
Site Admin
Site Admin
Posts: 1854
Joined: Wed Jun 17, 2009 6:30 pm
Location: South Yorkshire UK
Contact:

Re: Space at end of customer email

Post by Martin »

Finally... make the error phrase a little more helpful so that those customers inputting a space, actually get told that's what they may have done so they can check.

Open: /language/en/front_language.ini

Find:

Code: Select all

AccountEnterValidEmail = "Please type in a valid email address, such as joe@aol.com"
Replace with:

Code: Select all

AccountEnterValidEmail = "Invalid email address: Please check the address is correct and delete any spaces at the beginning/end of the address."
Tony Barnes
Posts: 744
Joined: Thu Jun 18, 2009 8:59 am

Re: [FIX] Validate email address input for customer registra

Post by Tony Barnes »

Bloody hell, nice one Martin!! Will implement this all now, mucho thanks :D
Tony Barnes
Posts: 744
Joined: Thu Jun 18, 2009 8:59 am

Re: [FIX] Validate email address input for customer registra

Post by Tony Barnes »

Works perfectly, thanks Martin
Martin
Site Admin
Site Admin
Posts: 1854
Joined: Wed Jun 17, 2009 6:30 pm
Location: South Yorkshire UK
Contact:

Re: [FIX] Validate email address input for customer registra

Post by Martin »

You're welcome... I'm more than slightly gobsmacked that this oversight has sat for so long as I know we raised it as an issue a year or more ago and were told it was working and must be my eyesight or something.

Either way, it's fixed good n' proper now but it would be a nice addition to add in proper JS checking on the front end so you don't have to submit the form to lose you password information... Something that checked once the email field was exited, if that's possible...

Anyway... Score one to moi... :ugeek: :mrgreen:
Martin
Site Admin
Site Admin
Posts: 1854
Joined: Wed Jun 17, 2009 6:30 pm
Location: South Yorkshire UK
Contact:

Re: [FIX] Validate email address input for customer registra

Post by Martin »

IMPORTANT: The quoted code below is the only change to the modification above to make it work with 5.0.6 (everything else is the same)

Open: includes/classes/class.customer.php

Find:

Code: Select all

			// Does an account with this email address already exist?
			if ($this->AccountWithEmailAlreadyExists($customerData['custconemail'])) {
				$this->CreateAccountStep1("already_exists");
			}
5.5.x After, Add

Code: Select all

			// MOD Force validation of the email using existing function
			else if(!is_email_address($customerData['custconemail'])) {
				$this->CreateAccountStep1("invalid_email");
			}
			// MOD END
5.0.x After, Add

Code: Select all

			// MOD Force validation of the email using existing function
			else if(!is_email_address($customerData['email'])) {
				$this->CreateAccountStep1("invalid_email");
			}
			// MOD END
CharlieFoxtrot
Confirmed
Confirmed
Posts: 413
Joined: Sun Aug 09, 2009 1:23 pm

Re: [FIX] Validate email address input for customer registra

Post by CharlieFoxtrot »

Thank you!

Image
ISC 4.0.7

"... and let's be honest that whole "by design" thing is getting old too."
CharlieFoxtrot
Confirmed
Confirmed
Posts: 413
Joined: Sun Aug 09, 2009 1:23 pm

Re: [FIX] Validate email address input for customer registra

Post by CharlieFoxtrot »

Martin wrote: IMPORTANT: The quoted code below only works with 5.5.x ...
FYI: This also works with version 4.07
ISC 4.0.7

"... and let's be honest that whole "by design" thing is getting old too."
CharlieFoxtrot
Confirmed
Confirmed
Posts: 413
Joined: Sun Aug 09, 2009 1:23 pm

Re: [FIX] Validate email address input for customer registra

Post by CharlieFoxtrot »

Further Modifications: Users can also enter an invalid email address when editing their account details. Based on Martin's instructions above, I made the following modifications.

Tested With: ISC version 4.07
REQUIRES: You must have already added Martin's modified email verification function to general.php
Disclaimers: At your own risk. Make backups. Test before going live.. etc etc.

STEP ONE: You'll be making most of the changes to the class.account.php file

Open: /includes/classes/class.account.php

======================================
FIND:

Code: Select all

$phone_invalid = false;
Below that, ADD:

Code: Select all

$email_invalid = false; // MOD: Initialize variable. Assume valid email.
=====================================
FIND:

Code: Select all

// Are they updating their email address? If so is the new email address available?
if ($_POST['current_email'] != $_POST['account_email']) {
	if ($GLOBALS['ISC_CLASS_CUSTOMER']->AccountWithEmailAlreadyExists($_POST['account_email'])) {
		$email_taken = true;
	}
}
Below that, ADD:

Code: Select all

// MOD CHECK FOR VALID EMAIL
if (!is_email_address($_POST['account_email'])) {
	$email_invalid = true;
	}
// MOD END
=====================================
FIND:

Code: Select all

if (!$email_taken && !$phone_invalid) {
*REPLACE* WITH:

Code: Select all

// ORIGINAL CODE: if (!$email_taken && !$phone_invalid) {
// Has been modified to also confirm valid email
if (!$email_taken && !$phone_invalid && !$email_invalid) {
=====================================
FIND:

Code: Select all

else if ($phone_invalid) {
	// Phone number is invalid
	$this->EditAccount(sprintf(GetLang('AccountUpdateValidPhone'), $_POST['account_phone']), MSG_ERROR);
}
Below that, ADD:

Code: Select all

// MOD EMAIL ADDRESS WAS INVALID
else if ($email_invalid) {
	// Email address is invalid
	$this->EditAccount(sprintf(GetLang('AccountUpdateEmailInvalid'), $_POST['account_email']), MSG_ERROR);
}
// MOD END


STEP TWO: You'll need to add an additional error message to the language file.

Open: /language/en/front_language.ini

=====================================
FIND:

Code: Select all

AccountUpdateEmailTaken = "Sorry... The email address '%s' is already in use by another customer. Please enter a different one."
VERSION 1 (Generic Message)
Below that, ADD:

Code: Select all

AccountUpdateEmailInvalid = "<b>Invalid Email:</b> Sorry, there appears to be a problem with the email address you entered. Please double-check to be sure everything is correct.<br/><br/>TIP: Delete any extra spaces at the beginning or end of the email address. Valid email addresses should be in the following format: <i>username@domain.com</i>"
OR... VERSION 2 (Message includes offending email address)
Below that, ADD:

Code: Select all

AccountUpdateEmailInvalid = "<b>Invalid Email:</b> Sorry, there appears to be a problem with the email address <i>'%s'</i> -- Please double-check to be sure everything is correct.<br/><br/>TIP: Delete any extra spaces at the beginning or end of the email address. Valid email addresses should be in the following format: <i>username@domain.com</i>"
Obviously, use one or the other of these two error messages... but not both... or edit the wording to suit your preferences.

A final note... I've tested this and it appears to work fine on ISC 4.07. I've reviewed these instructions for accuracy, but if you find any typos or errors, please post a correction or let me know and I'll edit this post.

Good luck,
Charlie

PS: Many thanks, Martin!
ISC 4.0.7

"... and let's be honest that whole "by design" thing is getting old too."
Post Reply