Subversion Repositories javautils

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

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