onefish

Validate Email Address CF

Discussion created by onefish on Jul 10, 2018
Latest reply on Jul 12, 2018 by onefish

Hi community,

 

Been busy for a while so haven't checked in here lately. I just had a need today to validate manually entered email address format and syntax. Supposing this would be a simple matter of heading over to BrianDunning.com and grabbing a CF some brilliant dev had uploaded, I was surprised to find that there was only 3, all of which reject perfectly legal and valid email addresses. So I set out to write my own CF to handle this task.

 

Below is what I came up with. It still has limits though. It only partially validates IPv6 domains and only partially validates the top level domain syntax. I also didn't allow bracketed comments in the address as this is somewhat deprecated and gMail's smtp sever doesn't accept them anymore.

 

Wouldn't mind your feedback if there's a better way to do any of it. Requires the parameter "email" in the CF. Boolean result 1 = pass, 0 = fail.

 

/* Use to validate email address format and syntax. This function handles quoted text and special characters within the local-part. It validates the format of IPv4 IP addresses in the domain part and partially validates IPv6 addresses in the domain part by ensuring that it is appropriately wrapped in "[ ]" and contains at least 2x " : ".  Top-level domain syntax and format is only partially validated, i.e. at least one "." must exist to the right of the last "@" and at least 2 characters exist to the right of the last "." . This function does not allow bracketed comments. While technically a valid email syntax, many popular email servers reject emails containing comments.*/

 

Let ( [

EM = Lower ( email ) ; /* the input parameter, email address to be evaluated */
AT = Position ( EM ; "@" ; 1 ; PatternCount ( EM ; "@" ) ) ; /* position of the last @ symbol, multiples are allowed if quoted */
TL = Position ( EM ; "." ; AT ; 1 ) ; /* position of the first "." to right of last "@", used to separate domain from top level domain */
LP = Left ( EM ; AT - 1) ; /* local part of email address */
IP = If ( (Middle ( EM ; AT + 1 ; 1 ) = "[") and (Right ( EM ; 1 ) = "]") ; 1 ; 0 ) ; /* check if domain is IPv4 or IPv6 "[ ]" */
RI = Right ( EM ; Length ( EM ) - AT ) ; /* all text to right of last "@" */
DM = If ( IP = 1 ; Middle ( RI ; 2 ; Length ( RI ) - 2 ) ; Left ( RI ; Position ( RI ; "." ; 1 ; 1 ) - 1 ) ) ; /* domain, different if IPv4/6 or standard */
AL = "abcdefghijklmnopqrstuvwxyz" ; /* allowable alphabet in LP */
NU = "0123456789" ; /* allowable numeric in LP and IP addresses */
SP1 = ".!#$%&'*+-/=?^_`{|}~" ; /* allowable special characters in LP */
SP2 = " (),:;<>@[\]" ; /* allowable special characters in LP if quoted */
SP3 = "\"" ; /* double quote " for count functions later */
SP4 = "\\\"" ; /* escaped double quote \" required if double quotes exist inside quoted LP */
SP5 = "-" ; /* allowable special character in domain */
cSP3 = PatternCount ( LP ; SP3 ) ; /* count of double quotes " in LP */
cSP4 = PatternCount ( LP ; SP4 ) ] ; /* count of escaped double quotes \" in LP */

 

/* check if there is a value to be evaluated */

If ( IsEmpty ( EM ) ; 0 ;

/* value exists */

Case (

 

(cSP3 > 0) and /* pass = this is a quoted LP */
(cSP4 = cSP3 - 2) and /* pass = any double quotes inside the quoted LP are properly escaped */
(Left ( LP ; 1 ) = SP3) and /* pass = the quoted LP begins with a double quote as required*/
(Right ( LP ; 1 ) = SP3) and /* pass = the quoted LP ends with a double quote as required*/
(Right ( LP ; 2 )  ≠ SP4) and /* pass = the quoted LP does not end with an escaped double quote, not allowed */
(Filter ( LP ; AL & NU & SP1 & SP2 & SP3 ) = LP) ; 1 ; /* pass = the quoted LP does not contain special characters which are not allowed within a quoted LP */

 

(cSP3 = 0) and /* pass = this is not a quoted LP */
(Left ( LP ; 1 ) ≠ "." ) and /* pass = the LP does not begin with a "." */
(Right ( LP ; 1 ) ≠ "." ) and /* pass = the LP does not end with a "." */

(PatternCount ( LP ; ".." ) = 0 ) and /* pass = the LP does not contain a double ".." */
(Filter ( LP ; AL & NU & SP1 ) = LP ) ; 1 ; /* pass = the LP does not contain special characters that are not allowed in a non-quoted LP */

 

0 ) /* default case function result - failed */

 

* /* multiply Boolean results, must pass all: 1*1*0*1*1 = 0 (fail) */

 

( AT > 0 ) /* There is an @ symbol in the string */

 

* /* multiply Boolean results, must pass all: 1*1*0*1*1 = 0 (fail) */

 

If ( IP = 1 ; /* this is an IP formatted domain */

 

Case (

 

(PatternCount ( DM ; ":" ) > 1 ) ; 1 ; /* this is an IPv6 formatted domain and contains at least 2 ":" */

 

(PatternCount ( DM ; "." ) = 3) and /* this is an IPv4 formatted domain and contains 3 "." */
Let ( [
DMval = Substitute ( DM ; "." ; "¶" ) ; /* turn IP address into a 4 value list */
val1 = GetValue ( DMval ; 1 ) ; /* set variables for each value */
val2 = GetValue ( DMval ; 2 ) ;
val3 = GetValue ( DMval ; 3 ) ;
val4 = GetValue ( DMval ; 4 ) ] ;

IsEmpty ( Filter ( DMval ; AL & SP1 & SP2 ) ) and /* check the list contains only numerical values, 0 is allowed */
not IsEmpty ( val1 ) and /* check each value is not empty */
not IsEmpty ( val2 ) and
not IsEmpty ( val3 ) and
not IsEmpty ( val4 )
) ; 1 ;

0 ) ; /* default case function result - failed */

 

/* value if domain is not IP formatted */

(Filter ( DM ; AL & NU & SP5 ) = DM) and /* domain does not contain special characters that are not allowed */
(TL > 0) and /*at least one "." exists to the right of the last @ symbol */
(Length ( Right ( RI ; Length ( RI ) - Position ( RI ; "." ; 1 ; PatternCount ( RI ; "." ) ) ) ) > 1 ) /* at least 2 characters exist to the right of the last "." in the top level domain */

 

)
)
)

 

------- Edited IPv4 check to remove a few lines of code  --------

------- Edit 2 realised case sensitivity issue with AL, added Lower() to EM (as opposed to adding caps string to AL) -------

P.S. I know all the extra ( brackets ) aren't necessary, just added them for readability :-)

Outcomes