Subversion Repositories javautils

Rev

Rev 14 | Blame | Last modification | View Log | RSS feed

  1. package com.dominicsayers.isemail;
  2.  
  3. import javax.naming.NamingException;
  4.  
  5. /**
  6.  * This class checks if email addresses are valid or not.
  7.  *
  8.  * @package isemail
  9.  * @author Dominic Sayers <dominic_sayers@hotmail.com>; Translated from PHP into
  10.  *         Java by Daniel Marschall [www.daniel-marschall.de]
  11.  * @copyright 2010 Dominic Sayers; Java-Translation 2010 by Daniel Marschall
  12.  * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  13.  * @see http://www.dominicsayers.com/isemail
  14.  * @version 1.17 - Upper length limit corrected to 254 characters;
  15.  *          Java-Translation 2010-06-14
  16.  */
  17.  
  18. /*
  19.  * Copyright (c) 2008-2010, Dominic Sayers All rights reserved.
  20.  *
  21.  * Redistribution and use in source and binary forms, with or without
  22.  * modification, are permitted provided that the following conditions are met:
  23.  *
  24.  * Redistributions of source code must retain the above copyright notice, this
  25.  * list of conditions and the following disclaimer. Redistributions in binary
  26.  * form must reproduce the above copyright notice, this list of conditions and
  27.  * the following disclaimer in the documentation and/or other materials provided
  28.  * with the distribution. Neither the name of Dominic Sayers nor the names of
  29.  * its contributors may be used to endorse or promote products derived from this
  30.  * software without specific prior written permission.
  31.  *
  32.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  33.  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  34.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  35.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  36.  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  37.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  38.  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  39.  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  40.  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  42.  * POSSIBILITY OF SUCH DAMAGE.
  43.  */
  44.  
  45. public class IsEMail {
  46.  
  47.         /**
  48.          * Checks the syntax of an email address without DNS check.
  49.          *
  50.          * @param email
  51.          *            The email address to be checked.
  52.          * @return True if the email address is valid.
  53.          */
  54.         public static boolean is_email(String email) {
  55.                 return (is_email_diagnosis(email, false) == EMailSyntaxDiagnosis.ISEMAIL_VALID);
  56.         }
  57.  
  58.         /**
  59.          * Checks the syntax of an email address.
  60.          *
  61.          * @param email
  62.          *            The email address to be checked.
  63.          * @param checkDNS
  64.          *            Whether a DNS check should be performed or not.
  65.          * @return True if the email address is valid.
  66.          */
  67.         public static boolean is_email(String email, boolean checkDNS) {
  68.                 return (is_email_diagnosis(email, checkDNS) == EMailSyntaxDiagnosis.ISEMAIL_VALID);
  69.         }
  70.  
  71.         /**
  72.          * Checks the syntax of an email address with diagnosis and without DNS
  73.          * check.
  74.          *
  75.          * @param email
  76.          *            The email address to be checked.
  77.          * @return A diagnosis of the email syntax.
  78.          */
  79.         public static EMailSyntaxDiagnosis is_email_diagnosis(String email) {
  80.                 return is_email_diagnosis(email, false);
  81.         }
  82.  
  83.         /**
  84.          * Checks the syntax of an email address with diagnosis.
  85.          *
  86.          * @param email
  87.          *            The email address to be checked.
  88.          * @param checkDNS
  89.          *            Whether a DNS check should be performed or not.
  90.          * @return A diagnosis of the email syntax.
  91.          */
  92.         public static EMailSyntaxDiagnosis is_email_diagnosis(String email,
  93.                         boolean checkDNS) {
  94.  
  95.                 if (email == null)
  96.                         email = "";
  97.  
  98.                 // Check that 'email' is a valid address. Read the following RFCs to
  99.                 // understand the constraints:
  100.                 // (http://tools.ietf.org/html/rfc5322)
  101.                 // (http://tools.ietf.org/html/rfc3696)
  102.                 // (http://tools.ietf.org/html/rfc5321)
  103.                 // (http://tools.ietf.org/html/rfc4291#section-2.2)
  104.                 // (http://tools.ietf.org/html/rfc1123#section-2.1)
  105.  
  106.                 // the upper limit on address lengths should normally be considered to
  107.                 // be 254
  108.                 // (http://www.rfc-editor.org/errata_search.php?rfc=3696)
  109.                 // NB My erratum has now been verified by the IETF so the correct answer
  110.                 // is 254
  111.                 //
  112.                 // The maximum total length of a reverse-path or forward-path is 256
  113.                 // characters (including the punctuation and element separators)
  114.                 // (http://tools.ietf.org/html/rfc5321#section-4.5.3.1.3)
  115.                 // NB There is a mandatory 2-character wrapper round the actual address
  116.                 int emailLength = email.length();
  117.                 // revision 1.17: Max length reduced to 254 (see above)
  118.                 if (emailLength > 254) {
  119.                         return EMailSyntaxDiagnosis.ISEMAIL_TOOLONG; // Too long
  120.                 }
  121.  
  122.                 // Contemporary email addresses consist of a "local part" separated from
  123.                 // a "domain part" (a fully-qualified domain name) by an at-sign ("@").
  124.                 // (http://tools.ietf.org/html/rfc3696#section-3)
  125.                 int atIndex = email.lastIndexOf('@');
  126.  
  127.                 if (atIndex == -1) {
  128.                         return EMailSyntaxDiagnosis.ISEMAIL_NOAT; // No at-sign
  129.                 }
  130.                 if (atIndex == 0) {
  131.                         return EMailSyntaxDiagnosis.ISEMAIL_NOLOCALPART; // No local part
  132.                 }
  133.                 if (atIndex == emailLength - 1) {
  134.                         // No domain part
  135.                         return EMailSyntaxDiagnosis.ISEMAIL_NODOMAIN;
  136.                         // revision 1.14: Length test bug suggested by Andrew Campbell of
  137.                         // Gloucester, MA
  138.                 }
  139.  
  140.                 // Sanitize comments
  141.                 // - remove nested comments, quotes and dots in comments
  142.                 // - remove parentheses and dots from quoted strings
  143.                 int braceDepth = 0;
  144.                 boolean inQuote = false;
  145.                 boolean escapeThisChar = false;
  146.  
  147.                 for (int i = 0; i < emailLength; ++i) {
  148.                         char charX = email.charAt(i);
  149.                         boolean replaceChar = false;
  150.  
  151.                         if (charX == '\\') {
  152.                                 escapeThisChar = !escapeThisChar; // Escape the next character?
  153.                         } else {
  154.                                 switch (charX) {
  155.                                 case '(':
  156.                                         if (escapeThisChar) {
  157.                                                 replaceChar = true;
  158.                                         } else {
  159.                                                 if (inQuote) {
  160.                                                         replaceChar = true;
  161.                                                 } else {
  162.                                                         if (braceDepth++ > 0) {
  163.                                                                 replaceChar = true; // Increment brace depth
  164.                                                         }
  165.                                                 }
  166.                                         }
  167.  
  168.                                         break;
  169.                                 case ')':
  170.                                         if (escapeThisChar) {
  171.                                                 replaceChar = true;
  172.                                         } else {
  173.                                                 if (inQuote) {
  174.                                                         replaceChar = true;
  175.                                                 } else {
  176.                                                         if (--braceDepth > 0)
  177.                                                                 replaceChar = true; // Decrement brace depth
  178.                                                         if (braceDepth < 0) {
  179.                                                                 braceDepth = 0;
  180.                                                         }
  181.                                                 }
  182.                                         }
  183.  
  184.                                         break;
  185.                                 case '"':
  186.                                         if (escapeThisChar) {
  187.                                                 replaceChar = true;
  188.                                         } else {
  189.                                                 if (braceDepth == 0) {
  190.                                                         // Are we inside a quoted string?
  191.                                                         inQuote = !inQuote;
  192.                                                 } else {
  193.                                                         replaceChar = true;
  194.                                                 }
  195.                                         }
  196.  
  197.                                         break;
  198.                                 case '.': // Dots don't help us either
  199.                                         if (escapeThisChar) {
  200.                                                 replaceChar = true;
  201.                                         } else {
  202.                                                 if (braceDepth > 0)
  203.                                                         replaceChar = true;
  204.                                         }
  205.  
  206.                                         break;
  207.                                 default:
  208.                                 }
  209.  
  210.                                 escapeThisChar = false;
  211.                                 if (replaceChar) {
  212.                                         // Replace the offending character with something harmless
  213.                                         // revision 1.12: Line above replaced because PHPLint
  214.                                         // doesn't like that syntax
  215.                                         email = replaceCharAt(email, i, 'x');
  216.                                 }
  217.  
  218.                         }
  219.                 }
  220.  
  221.                 String localPart = PHPFunctions.substr(email, 0, atIndex);
  222.                 String domain = PHPFunctions.substr(email, atIndex + 1);
  223.                 // Folding white space
  224.                 final String FWS = "(?:(?:(?:[ \\t]*(?:\\r\\n))?[ \\t]+)|(?:[ \\t]+(?:(?:\\r\\n)[ \\t]+)*))";
  225.                 // Let's check the local part for RFC compliance...
  226.                 //
  227.                 // local-part = dot-atom / quoted-string / obs-local-part
  228.                 // obs-local-part = word *("." word)
  229.                 // (http://tools.ietf.org/html/rfc5322#section-3.4.1)
  230.                 //
  231.                 // Problem: need to distinguish between "first.last" and "first"."last"
  232.                 // (i.e. one element or two). And I suck at regular expressions.
  233.  
  234.                 String[] dotArray = PHPFunctions.preg_split(
  235.                                 "(?m)\\.(?=(?:[^\\\"]*\\\"[^\\\"]*\\\")*(?![^\\\"]*\\\"))",
  236.                                 localPart);
  237.                 int partLength = 0;
  238.  
  239.                 for (String element : dotArray) {
  240.                         // Remove any leading or trailing FWS
  241.                         element = PHPFunctions.preg_replace("^" + FWS + "|" + FWS + "$",
  242.                                         "", element);
  243.                         int elementLength = element.length();
  244.  
  245.                         if (elementLength == 0) {
  246.                                 // Can't have empty element (consecutive dots or
  247.                                 // dots at the start or end)
  248.                                 return EMailSyntaxDiagnosis.ISEMAIL_ZEROLENGTHELEMENT;
  249.                         }
  250.                         // revision 1.15: Speed up the test and get rid of
  251.                         // "uninitialized string offset" notices from PHP
  252.  
  253.                         // We need to remove any valid comments (i.e. those at the start or
  254.                         // end of the element)
  255.                         if (element.charAt(0) == '(') {
  256.                                 int indexBrace = element.indexOf(')');
  257.                                 if (indexBrace != -1) {
  258.                                         if (PHPFunctions.preg_match("(?<!\\\\)[\\(\\)]",
  259.                                                         PHPFunctions.substr(element, 1, indexBrace - 1)) > 0) {
  260.                                                 // Illegal characters in comment
  261.                                                 return EMailSyntaxDiagnosis.ISEMAIL_BADCOMMENT_START;
  262.                                         }
  263.                                         element = PHPFunctions.substr(element, indexBrace + 1,
  264.                                                         elementLength - indexBrace - 1);
  265.                                         elementLength = element.length();
  266.                                 }
  267.                         }
  268.  
  269.                         if (element.charAt(elementLength - 1) == ')') {
  270.                                 int indexBrace = element.lastIndexOf('(');
  271.                                 if (indexBrace != -1) {
  272.                                         if (PHPFunctions.preg_match("(?<!\\\\)(?:[\\(\\)])",
  273.                                                         PHPFunctions.substr(element, indexBrace + 1,
  274.                                                                         elementLength - indexBrace - 2)) > 0) {
  275.                                                 // Illegal characters in comment
  276.                                                 return EMailSyntaxDiagnosis.ISEMAIL_BADCOMMENT_END;
  277.                                         }
  278.                                         element = PHPFunctions.substr(element, 0, indexBrace);
  279.                                         elementLength = element.length();
  280.                                 }
  281.                         }
  282.  
  283.                         // Remove any leading or trailing FWS around the element (inside any
  284.                         // comments)
  285.                         element = PHPFunctions.preg_replace("^" + FWS + "|" + FWS + "$",
  286.                                         "", element);
  287.  
  288.                         // What's left counts towards the maximum length for this part
  289.                         if (partLength > 0)
  290.                                 partLength++; // for the dot
  291.                         partLength += element.length();
  292.  
  293.                         // Each dot-delimited component can be an atom or a quoted string
  294.                         // (because of the obs-local-part provision)
  295.  
  296.                         if (PHPFunctions.preg_match("(?s)^\"(?:.)*\"$", element) > 0) {
  297.                                 // Quoted-string tests:
  298.                                 //
  299.                                 // Remove any FWS
  300.                                 element = PHPFunctions.preg_replace("(?<!\\\\)" + FWS, "",
  301.                                                 element);
  302.                                 // My regular expression skills aren't up to distinguishing
  303.                                 // between \" \\" \\\" \\\\" etc.
  304.                                 // So remove all \\ from the string first...
  305.                                 element = PHPFunctions.preg_replace("\\\\\\\\", " ", element);
  306.                                 if (PHPFunctions
  307.                                                 .preg_match(
  308.                                                                 "(?<!\\\\|^)[\"\\r\\n\\x00](?!$)|\\\\\"$|\"\"",
  309.                                                                 element) > 0) {
  310.                                         // ", CR, LF and NUL must be escaped, "" is too short
  311.                                         return EMailSyntaxDiagnosis.ISEMAIL_UNESCAPEDDELIM;
  312.                                 }
  313.                         } else {
  314.                                 // Unquoted string tests:
  315.                                 //
  316.                                 // Period (".") may...appear, but may not be used to start or
  317.                                 // end the
  318.                                 // local part, nor may two or more consecutive periods appear.
  319.                                 // (http://tools.ietf.org/html/rfc3696#section-3)
  320.                                 //
  321.                                 // A zero-length element implies a period at the beginning or
  322.                                 // end of the
  323.                                 // local part, or two periods together. Either way it's not
  324.                                 // allowed.
  325.                                 if (element.isEmpty()) {
  326.                                         // Dots in wrong place
  327.                                         return EMailSyntaxDiagnosis.ISEMAIL_EMPTYELEMENT;
  328.                                 }
  329.  
  330.                                 // Any ASCII graphic (printing) character other than the
  331.                                 // at-sign ("@"), backslash, double quote, comma, or square
  332.                                 // brackets may
  333.                                 // appear without quoting. If any of that list of excluded
  334.                                 // characters
  335.                                 // are to appear, they must be quoted
  336.                                 // (http://tools.ietf.org/html/rfc3696#section-3)
  337.                                 //
  338.                                 // Any excluded characters? i.e. 0x00-0x20, (, ), <, >, [, ], :,
  339.                                 // ;, @, \, comma, period, "
  340.                                 if (PHPFunctions.preg_match(
  341.                                                 "[\\x00-\\x20\\(\\)<>\\[\\]:;@\\\\,\\.\"]", element) > 0) {
  342.                                         // These characters must be in a quoted string
  343.                                         return EMailSyntaxDiagnosis.ISEMAIL_UNESCAPEDSPECIAL;
  344.                                 }
  345.                         }
  346.                 }
  347.  
  348.                 if (partLength > 64) {
  349.                         // Local part must be 64 characters or less
  350.                         return EMailSyntaxDiagnosis.ISEMAIL_LOCALTOOLONG;
  351.                 }
  352.  
  353.                 // Now let's check the domain part...
  354.  
  355.                 // The domain name can also be replaced by an IP address in square
  356.                 // brackets
  357.                 // (http://tools.ietf.org/html/rfc3696#section-3)
  358.                 // (http://tools.ietf.org/html/rfc5321#section-4.1.3)
  359.                 // (http://tools.ietf.org/html/rfc4291#section-2.2)
  360.  
  361.                 if (PHPFunctions.preg_match("^\\[(.)+]$", domain) == 1) {
  362.                         // It's an address-literal
  363.                         String addressLiteral = PHPFunctions.substr(domain, 1, domain
  364.                                         .length() - 2);
  365.  
  366.                         String IPv6;
  367.                         int groupMax;
  368.  
  369.                         // Extract IPv4 part from the end of the address-literal (if there
  370.                         // is one)
  371.                         String[] matchesIP = PHPFunctions
  372.                                         .preg_match_to_array(
  373.                                                         "\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
  374.                                                         addressLiteral);
  375.                         if (matchesIP.length > 0) {
  376.                                 int index = addressLiteral.lastIndexOf(matchesIP[0]);
  377.  
  378.                                 if (index == 0) {
  379.                                         // Nothing there except a valid IPv4 address, so...
  380.                                         return EMailSyntaxDiagnosis.ISEMAIL_VALID;
  381.                                 } else {
  382.                                         // Assume it's an attempt at a mixed address (IPv6 + IPv4)
  383.                                         if (addressLiteral.charAt(index - 1) != ':') {
  384.                                                 // Character preceding IPv4 address must be ':'
  385.                                                 return EMailSyntaxDiagnosis.ISEMAIL_IPV4BADPREFIX;
  386.                                         }
  387.                                         if (!addressLiteral.startsWith("IPv6:")) {
  388.                                                 // RFC5321 section 4.1.3
  389.                                                 return EMailSyntaxDiagnosis.ISEMAIL_IPV6BADPREFIXMIXED;
  390.                                         }
  391.  
  392.                                         IPv6 = PHPFunctions.substr(addressLiteral, 5,
  393.                                                         (index == 7) ? 2 : index - 6);
  394.                                         groupMax = 6;
  395.                                 }
  396.                         } else {
  397.                                 // It must be an attempt at pure IPv6
  398.                                 if (!addressLiteral.startsWith("IPv6:")) {
  399.                                         // RFC5321 section 4.1.3
  400.                                         return EMailSyntaxDiagnosis.ISEMAIL_IPV6BADPREFIX;
  401.                                 }
  402.                                 IPv6 = PHPFunctions.substr(addressLiteral, 5);
  403.                                 groupMax = 8;
  404.                         }
  405.  
  406.                         String[][] matchesIP6 = PHPFunctions.preg_match_all(
  407.                                         "^[0-9a-fA-F]{0,4}|\\:[0-9a-fA-F]{0,4}|(.)", IPv6);
  408.                         int groupCount = 0;
  409.                         if (matchesIP6.length > 0) {
  410.                                 groupCount = matchesIP6[0].length;
  411.                         } // else: Undefined state (should never be reached)
  412.                         int index = IPv6.indexOf("::");
  413.  
  414.                         if (index == -1) {
  415.                                 // We need exactly the right number of groups
  416.                                 if (groupCount != groupMax) {
  417.                                         // RFC5321 section 4.1.3
  418.                                         return EMailSyntaxDiagnosis.ISEMAIL_IPV6GROUPCOUNT;
  419.                                 }
  420.                         } else {
  421.                                 if (index != IPv6.lastIndexOf("::")) {
  422.                                         // More than one '::'
  423.                                         return EMailSyntaxDiagnosis.ISEMAIL_IPV6DOUBLEDOUBLECOLON;
  424.                                 }
  425.                                 groupMax = (index == 0 || index == (IPv6.length() - 2)) ? groupMax
  426.                                                 : groupMax - 1;
  427.                                 if (groupCount > groupMax) {
  428.                                         // Too many IPv6 groups in address
  429.                                         return EMailSyntaxDiagnosis.ISEMAIL_IPV6TOOMANYGROUPS;
  430.                                 }
  431.                         }
  432.  
  433.                         // Daniel Marschall: For the Java translation, I optimized
  434.                         // the process. Instead of sorting the array (which needs
  435.                         // null-pointer checks and array-length checks) and then
  436.                         // checking element [0], I decided to directly check every
  437.                         // element.
  438.  
  439.                         // Check for unmatched characters
  440.                         // array_multisort(matchesIP6[1], SORT_DESC);
  441.                         // if ($matchesIP6[1][0] !== '')) {
  442.                         // return EMailResultState.ISEMAIL_IPV6BADCHAR;
  443.                         // }
  444.  
  445.                         // Check for unmatched characters
  446.                         if (matchesIP6.length > 1) {
  447.                                 for (String s : matchesIP6[1]) {
  448.                                         if ((s != null) && (!s.isEmpty())) {
  449.                                                 return EMailSyntaxDiagnosis.ISEMAIL_IPV6BADCHAR;
  450.                                         }
  451.                                 }
  452.                         } // else: Undefined state (should never be reached)
  453.  
  454.                         // It's a valid IPv6 address, so...
  455.                         return EMailSyntaxDiagnosis.ISEMAIL_VALID;
  456.                 } else {
  457.                         // It's a domain name...
  458.  
  459.                         // The syntax of a legal Internet host name was specified in RFC-952
  460.                         // One aspect of host name syntax is hereby changed: the
  461.                         // restriction on the first character is relaxed to allow either a
  462.                         // letter or a digit.
  463.                         // (http://tools.ietf.org/html/rfc1123#section-2.1)
  464.                         //
  465.                         // NB RFC 1123 updates RFC 1035, but this is not currently apparent
  466.                         // from reading RFC 1035.
  467.                         //
  468.                         // Most common applications, including email and the Web, will
  469.                         // generally not
  470.                         // permit...escaped strings
  471.                         // (http://tools.ietf.org/html/rfc3696#section-2)
  472.                         //
  473.                         // the better strategy has now become to make the
  474.                         // "at least one period" test,
  475.                         // to verify LDH conformance (including verification that the
  476.                         // apparent TLD name
  477.                         // is not all-numeric)
  478.                         // (http://tools.ietf.org/html/rfc3696#section-2)
  479.                         //
  480.                         // Characters outside the set of alphabetic characters, digits, and
  481.                         // hyphen MUST NOT appear in domain name
  482.                         // labels for SMTP clients or servers
  483.                         // (http://tools.ietf.org/html/rfc5321#section-4.1.2)
  484.                         //
  485.                         // RFC5321 precludes the use of a trailing dot in a domain name for
  486.                         // SMTP purposes
  487.                         // (http://tools.ietf.org/html/rfc5321#section-4.1.2)
  488.  
  489.                         dotArray = PHPFunctions.preg_split(
  490.                                         "(?m)\\.(?=(?:[^\\\"]*\\\"[^\\\"]*\\\")*(?![^\\\"]*\\\"))",
  491.                                         domain);
  492.                         partLength = 0;
  493.                         // Since we use 'element' after the foreach
  494.                         // loop let's make sure it has a value
  495.                         String lastElement = "";
  496.                         // revision 1.13: Line above added because PHPLint now checks for
  497.                         // Definitely Assigned Variables
  498.  
  499.                         if (dotArray.length == 1) {
  500.                                 // Mail host can't be a TLD (cite? What about localhost?)
  501.                                 return EMailSyntaxDiagnosis.ISEMAIL_TLD;
  502.                         }
  503.  
  504.                         for (String element : dotArray) {
  505.                                 lastElement = element;
  506.                                 // Remove any leading or trailing FWS
  507.                                 element = PHPFunctions.preg_replace(
  508.                                                 "^" + FWS + "|" + FWS + "$", "", element);
  509.                                 int elementLength = element.length();
  510.  
  511.                                 // Each dot-delimited component must be of type atext
  512.                                 // A zero-length element implies a period at the beginning or
  513.                                 // end of the
  514.                                 // local part, or two periods together. Either way it's not
  515.                                 // allowed.
  516.                                 if (elementLength == 0) {
  517.                                         // Dots in wrong place
  518.                                         return EMailSyntaxDiagnosis.ISEMAIL_DOMAINEMPTYELEMENT;
  519.                                 }
  520.                                 // revision 1.15: Speed up the test and get rid of
  521.                                 // "uninitialized string offset" notices from PHP
  522.  
  523.                                 // Then we need to remove all valid comments (i.e. those at the
  524.                                 // start or end of the element
  525.                                 if (element.charAt(0) == '(') {
  526.                                         int indexBrace = element.indexOf(')');
  527.                                         if (indexBrace != -1) {
  528.                                                 if (PHPFunctions
  529.                                                                 .preg_match("(?<!\\\\)[\\(\\)]", PHPFunctions
  530.                                                                                 .substr(element, 1, indexBrace - 1)) > 0) {
  531.                                                         // revision 1.17: Fixed name of constant (also
  532.                                                         // spotted by turboflash - thanks!)
  533.                                                         // Illegal characters in comment
  534.                                                         return EMailSyntaxDiagnosis.ISEMAIL_BADCOMMENT_START;
  535.                                                 }
  536.                                                 element = PHPFunctions.substr(element, indexBrace + 1,
  537.                                                                 elementLength - indexBrace - 1);
  538.                                                 elementLength = element.length();
  539.                                         }
  540.                                 }
  541.  
  542.                                 if (element.charAt(elementLength - 1) == ')') {
  543.                                         int indexBrace = element.lastIndexOf('(');
  544.                                         if (indexBrace != -1) {
  545.                                                 if (PHPFunctions.preg_match("(?<!\\\\)(?:[\\(\\)])",
  546.                                                                 PHPFunctions.substr(element, indexBrace + 1,
  547.                                                                                 elementLength - indexBrace - 2)) > 0) {
  548.                                                         // revision 1.17: Fixed name of constant (also
  549.                                                         // spotted by turboflash - thanks!)
  550.                                                         // Illegal characters in comment
  551.                                                         return EMailSyntaxDiagnosis.ISEMAIL_BADCOMMENT_END;
  552.                                                 }
  553.  
  554.                                                 element = PHPFunctions.substr(element, 0, indexBrace);
  555.                                                 elementLength = element.length();
  556.                                         }
  557.                                 }
  558.  
  559.                                 // Remove any leading or trailing FWS around the element (inside
  560.                                 // any comments)
  561.                                 element = PHPFunctions.preg_replace(
  562.                                                 "^" + FWS + "|" + FWS + "$", "", element);
  563.  
  564.                                 // What's left counts towards the maximum length for this part
  565.                                 if (partLength > 0)
  566.                                         partLength++; // for the dot
  567.                                 partLength += element.length();
  568.  
  569.                                 // The DNS defines domain name syntax very generally -- a
  570.                                 // string of labels each containing up to 63 8-bit octets,
  571.                                 // separated by dots, and with a maximum total of 255
  572.                                 // octets.
  573.                                 // (http://tools.ietf.org/html/rfc1123#section-6.1.3.5)
  574.                                 if (elementLength > 63) {
  575.                                         // Label must be 63 characters or less
  576.                                         return EMailSyntaxDiagnosis.ISEMAIL_DOMAINELEMENTTOOLONG;
  577.                                 }
  578.  
  579.                                 // Any ASCII graphic (printing) character other than the
  580.                                 // at-sign ("@"), backslash, double quote, comma, or square
  581.                                 // brackets may
  582.                                 // appear without quoting. If any of that list of excluded
  583.                                 // characters
  584.                                 // are to appear, they must be quoted
  585.                                 // (http://tools.ietf.org/html/rfc3696#section-3)
  586.                                 //
  587.                                 // If the hyphen is used, it is not permitted to appear at
  588.                                 // either the beginning or end of a label.
  589.                                 // (http://tools.ietf.org/html/rfc3696#section-2)
  590.                                 //
  591.                                 // Any excluded characters? i.e. 0x00-0x20, (, ), <, >, [, ], :,
  592.                                 // ;, @, \, comma, period, "
  593.  
  594.                                 if (PHPFunctions.preg_match(
  595.                                                 "[\\x00-\\x20\\(\\)<>\\[\\]:;@\\\\,\\.\"]|^-|-$",
  596.                                                 element) > 0) {
  597.                                         return EMailSyntaxDiagnosis.ISEMAIL_DOMAINBADCHAR;
  598.                                 }
  599.                         }
  600.  
  601.                         if (partLength > 255) {
  602.                                 // Domain part must be 255 characters or less
  603.                                 // (http://tools.ietf.org/html/rfc1123#section-6.1.3.5)
  604.                                 return EMailSyntaxDiagnosis.ISEMAIL_DOMAINTOOLONG;
  605.                         }
  606.  
  607.                         if (PHPFunctions.preg_match("^[0-9]+$", lastElement) > 0) {
  608.                                 // TLD can't be all-numeric
  609.                                 // (http://www.apps.ietf.org/rfc/rfc3696.html#sec-2)
  610.                                 return EMailSyntaxDiagnosis.ISEMAIL_TLDNUMERIC;
  611.                         }
  612.  
  613.                         // Check DNS?
  614.                         if (checkDNS) {
  615.                                 try {
  616.                                         if (!((DNSLookup.doLookup(domain, DNSType.A) > 0) || (DNSLookup
  617.                                                         .doLookup(domain, DNSType.MX) > 0))) {
  618.                                                 // Domain doesn't actually exist
  619.                                                 return EMailSyntaxDiagnosis.ISEMAIL_DOMAINNOTFOUND;
  620.                                         }
  621.                                 } catch (NamingException e) {
  622.                                         // Resp.: Internal error
  623.                                         return EMailSyntaxDiagnosis.ISEMAIL_DOMAINNOTFOUND;
  624.                                 }
  625.                         }
  626.                 }
  627.  
  628.                 // Eliminate all other factors, and the one which remains must be the
  629.                 // truth. (Sherlock Holmes, The Sign of Four)
  630.                 return EMailSyntaxDiagnosis.ISEMAIL_VALID;
  631.         }
  632.  
  633.         /**
  634.          * Replaces a char in a String
  635.          *
  636.          * @param s
  637.          *            The input string
  638.          * @param pos
  639.          *            The position of the char to be replaced
  640.          * @param c
  641.          *            The new char
  642.          * @return The new String
  643.          * @see Source: http://www.rgagnon.com/javadetails/java-0030.html
  644.          */
  645.         public static String replaceCharAt(String s, int pos, char c) {
  646.                 return s.substring(0, pos) + c + s.substring(pos + 1);
  647.         }
  648.  
  649.         private IsEMail() {
  650.         }
  651. }
  652.