EmailValidator.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.validator;

  18. import java.util.regex.Matcher;
  19. import java.util.regex.Pattern;

  20. import org.apache.commons.validator.routines.InetAddressValidator;

  21. /**
  22.  * <p>Perform email validations.</p>
  23.  * <p>
  24.  * This class is a Singleton; you can retrieve the instance via the getInstance() method.
  25.  * </p>
  26.  * <p>
  27.  * Based on a script by <a href="mailto:stamhankar@hotmail.com">Sandeep V. Tamhankar</a>
  28.  * https://javascript.internet.com
  29.  * </p>
  30.  * <p>
  31.  * This implementation is not guaranteed to catch all possible errors in an email address.
  32.  * For example, an address like nobody@noplace.somedog will pass validator, even though there
  33.  * is no TLD "somedog"
  34.  * </p>.
  35.  *
  36.  * @since 1.1
  37.  * @deprecated Use the new EmailValidator in the routines package. This class
  38.  * will be removed in a future release.
  39.  */
  40. @Deprecated
  41. public class EmailValidator {

  42.     private static final String SPECIAL_CHARS = "\\p{Cntrl}\\(\\)<>@,;:'\\\\\\\"\\.\\[\\]";
  43.     private static final String VALID_CHARS = "[^\\s" + SPECIAL_CHARS + "]";
  44.     private static final String QUOTED_USER = "(\"[^\"]*\")";
  45.     private static final String ATOM = VALID_CHARS + '+';
  46.     private static final String WORD = "((" + VALID_CHARS + "|')+|" + QUOTED_USER + ")";

  47. // NOT USED   private static final Pattern LEGAL_ASCII_PATTERN = Pattern.compile("^\\p{ASCII}+$");
  48. // NOT USED   private static final Pattern EMAIL_PATTERN = Pattern.compile("^(.+)@(.+)$");
  49.     private static final Pattern IP_DOMAIN_PATTERN = Pattern.compile("^\\[(.*)\\]$");
  50.     private static final Pattern TLD_PATTERN = Pattern.compile("^([a-zA-Z]+)$");

  51.     private static final Pattern USER_PATTERN = Pattern.compile("^\\s*" + WORD + "(\\." + WORD + ")*$");
  52.     private static final Pattern DOMAIN_PATTERN = Pattern.compile("^" + ATOM + "(\\." + ATOM + ")*\\s*$");
  53.     private static final Pattern ATOM_PATTERN = Pattern.compile("(" + ATOM + ")");

  54.     /**
  55.      * Singleton instance of this class.
  56.      */
  57.     private static final EmailValidator EMAIL_VALIDATOR = new EmailValidator();

  58.     /**
  59.      * Returns the Singleton instance of this validator.
  60.      * @return singleton instance of this validator.
  61.      */
  62.     public static EmailValidator getInstance() {
  63.         return EMAIL_VALIDATOR;
  64.     }

  65.     /**
  66.      * Protected constructor for subclasses to use.
  67.      */
  68.     protected EmailValidator() {
  69.     }

  70.     /**
  71.      * <p>Checks if a field has a valid e-mail address.</p>
  72.      *
  73.      * @param email The value validation is being performed on.  A {@code null}
  74.      * value is considered invalid.
  75.      * @return true if the email address is valid.
  76.      */
  77.     public boolean isValid(final String email) {
  78.         return org.apache.commons.validator.routines.EmailValidator.getInstance().isValid(email);
  79.     }

  80.     /**
  81.      * Returns true if the domain component of an email address is valid.
  82.      * @param domain being validated.
  83.      * @return true if the email address's domain is valid.
  84.      */
  85.     protected boolean isValidDomain(final String domain) {
  86.         boolean symbolic = false;

  87.         // see if domain is an IP address in brackets
  88.         final Matcher ipDomainMatcher = IP_DOMAIN_PATTERN.matcher(domain);

  89.         if (ipDomainMatcher.matches()) {
  90.             final InetAddressValidator inetAddressValidator =
  91.                     InetAddressValidator.getInstance();
  92.             if (inetAddressValidator.isValid(ipDomainMatcher.group(1))) {
  93.                 return true;
  94.             }
  95.         } else {
  96.             // Domain is symbolic name
  97.             symbolic = DOMAIN_PATTERN.matcher(domain).matches();
  98.         }

  99.         if (!symbolic) {
  100.             return false;
  101.         }
  102.         if (!isValidSymbolicDomain(domain)) {
  103.             return false;
  104.         }

  105.         return true;
  106.     }

  107.     /**
  108.      * Validates an IP address. Returns true if valid.
  109.      * @param ipAddress IP address
  110.      * @return true if the ip address is valid.
  111.      */
  112.     protected boolean isValidIpAddress(final String ipAddress) {
  113.         final Matcher ipAddressMatcher = IP_DOMAIN_PATTERN.matcher(ipAddress);
  114.         for (int i = 1; i <= 4; i++) { // CHECKSTYLE IGNORE MagicNumber
  115.             final String ipSegment = ipAddressMatcher.group(i);
  116.             if (ipSegment == null || ipSegment.isEmpty()) {
  117.                 return false;
  118.             }

  119.             int iIpSegment = 0;

  120.             try {
  121.                 iIpSegment = Integer.parseInt(ipSegment);
  122.             } catch (final NumberFormatException e) {
  123.                 return false;
  124.             }

  125.             if (iIpSegment > 255) { // CHECKSTYLE IGNORE MagicNumber
  126.                 return false;
  127.             }

  128.         }
  129.         return true;
  130.     }

  131.     /**
  132.      * Validates a symbolic domain name.  Returns true if it's valid.
  133.      * @param domain symbolic domain name
  134.      * @return true if the symbolic domain name is valid.
  135.      */
  136.     protected boolean isValidSymbolicDomain(String domain) {
  137.         final String[] domainSegment = new String[10]; // CHECKSTYLE IGNORE MagicNumber
  138.         boolean match = true;
  139.         int i = 0;
  140.         final Matcher atomMatcher = ATOM_PATTERN.matcher(domain);
  141.         while (match) {
  142.             match = atomMatcher.matches();
  143.             if (match) {
  144.                 domainSegment[i] = atomMatcher.group(1);
  145.                 final int l = domainSegment[i].length() + 1;
  146.                 domain =
  147.                         l >= domain.length()
  148.                         ? ""
  149.                         : domain.substring(l);

  150.                 i++;
  151.             }
  152.         }

  153.         final int len = i;

  154.         // Make sure there's a host name preceding the domain.
  155.         if (len < 2) {
  156.             return false;
  157.         }

  158.         final String tld = domainSegment[len - 1];
  159.         if (tld.length() <= 1) {
  160.             return false;
  161.         }
  162.         if (! TLD_PATTERN.matcher(tld).matches()) {
  163.             return false;
  164.         }

  165.         return true;
  166.     }

  167.     /**
  168.      * Returns true if the user component of an email address is valid.
  169.      * @param user being validated
  170.      * @return true if the user name is valid.
  171.      */
  172.     protected boolean isValidUser(final String user) {
  173.         return USER_PATTERN.matcher(user).matches();
  174.     }

  175.     /**
  176.      * Recursively remove comments, and replace with a single space. The simpler regexps in the Email Addressing FAQ are imperfect - they will miss escaped
  177.      * chars in atoms, for example. Derived From Mail::RFC822::Address
  178.      *
  179.      * @param emailStr The email address
  180.      * @return address with comments removed.
  181.      */
  182.     protected String stripComments(final String emailStr) {
  183.         String result = emailStr;
  184.         final String commentPat = "^((?:[^\"\\\\]|\\\\.)*(?:\"(?:[^\"\\\\]|\\\\.)*\"(?:[^\"\\\\]|\111111\\\\.)*)*)\\((?:[^()\\\\]|\\\\.)*\\)/";
  185.         final Pattern commentMatcher = Pattern.compile(commentPat);

  186.         while (commentMatcher.matcher(result).matches()) {
  187.             result = result.replaceFirst(commentPat, "\1 ");
  188.         }
  189.         return result;
  190.     }
  191. }