    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017package org.apache.commons.validator;
    018
    019import org.apache.commons.validator.routines.InetAddressValidator;
    020
    021import java.util.regex.Matcher;
    022import java.util.regex.Pattern;
    023
    024/**
    025 * <p>Perform email validations.</p>
    026 * <p>
    027 * This class is a Singleton; you can retrieve the instance via the getInstance() method.
    028 * </p>
    029 * <p>
    030 * Based on a script by <a href="mailto:stamhankar@hotmail.com">Sandeep V. Tamhankar</a>
    031 * http://javascript.internet.com
    032 * </p>
    033 * <p>
    034 * This implementation is not guaranteed to catch all possible errors in an email address.
    035 * For example, an address like nobody@noplace.somedog will pass validator, even though there
    036 * is no TLD "somedog"
    037 * </p>.
    038 *
    039 * @version $Revision$
    040 * @since Validator 1.1
    041 * @deprecated Use the new EmailValidator in the routines package. This class
    042 * will be removed in a future release.
    043 */
    044@Deprecated
    045public class EmailValidator {
    046
    047    private static final String SPECIAL_CHARS = "\\p{Cntrl}\\(\\)<>@,;:'\\\\\\\"\\.\\[\\]";
    048    private static final String VALID_CHARS = "[^\\s" + SPECIAL_CHARS + "]";
    049    private static final String QUOTED_USER = "(\"[^\"]*\")";
    050    private static final String ATOM = VALID_CHARS + '+';
    051    private static final String WORD = "((" + VALID_CHARS + "|')+|" + QUOTED_USER + ")";
    052
    053// NOT USED   private static final Pattern LEGAL_ASCII_PATTERN = Pattern.compile("^\\p{ASCII}+$");
    054// NOT USED   private static final Pattern EMAIL_PATTERN = Pattern.compile("^(.+)@(.+)$");
    055    private static final Pattern IP_DOMAIN_PATTERN = Pattern.compile("^\\[(.*)\\]$");
    056    private static final Pattern TLD_PATTERN = Pattern.compile("^([a-zA-Z]+)$");
    057
    058    private static final Pattern USER_PATTERN = Pattern.compile("^\\s*" + WORD + "(\\." + WORD + ")*$");
    059    private static final Pattern DOMAIN_PATTERN = Pattern.compile("^" + ATOM + "(\\." + ATOM + ")*\\s*$");
    060    private static final Pattern ATOM_PATTERN = Pattern.compile("(" + ATOM + ")");
    061
    062    /**
    063     * Singleton instance of this class.
    064     */
    065    private static final EmailValidator EMAIL_VALIDATOR = new EmailValidator();
    066
    067    /**
    068     * Returns the Singleton instance of this validator.
    069     * @return singleton instance of this validator.
    070     */
    071    public static EmailValidator getInstance() {
    072        return EMAIL_VALIDATOR;
    073    }
    074
    075    /**
    076     * Protected constructor for subclasses to use.
    077     */
    078    protected EmailValidator() {
    079        super();
    080    }
    081
    082    /**
    083     * <p>Checks if a field has a valid e-mail address.</p>
    084     *
    085     * @param email The value validation is being performed on.  A <code>null</code>
    086     * value is considered invalid.
    087     * @return true if the email address is valid.
    088     */
    089    public boolean isValid(String email) {
    090        return org.apache.commons.validator.routines.EmailValidator.getInstance().isValid(email);
    091    }
    092
    093    /**
    094     * Returns true if the domain component of an email address is valid.
    095     * @param domain being validated.
    096     * @return true if the email address's domain is valid.
    097     */
    098    protected boolean isValidDomain(String domain) {
    099        boolean symbolic = false;
    100
    101        // see if domain is an IP address in brackets
    102        Matcher ipDomainMatcher = IP_DOMAIN_PATTERN.matcher(domain);
    103
    104        if (ipDomainMatcher.matches()) {
    105            InetAddressValidator inetAddressValidator =
    106                    InetAddressValidator.getInstance();
    107            if (inetAddressValidator.isValid(ipDomainMatcher.group(1))) {
    108                return true;
    109            }
    110        } else {
    111            // Domain is symbolic name
    112            symbolic = DOMAIN_PATTERN.matcher(domain).matches();
    113        }
    114
    115        if (symbolic) {
    116            if (!isValidSymbolicDomain(domain)) {
    117                return false;
    118            }
    119        } else {
    120            return false;
    121        }
    122
    123        return true;
    124    }
    125
    126    /**
    127     * Returns true if the user component of an email address is valid.
    128     * @param user being validated
    129     * @return true if the user name is valid.
    130     */
    131    protected boolean isValidUser(String user) {
    132        return USER_PATTERN.matcher(user).matches();
    133    }
    134
    135    /**
    136     * Validates an IP address. Returns true if valid.
    137     * @param ipAddress IP address
    138     * @return true if the ip address is valid.
    139     */
    140    protected boolean isValidIpAddress(String ipAddress) {
    141        Matcher ipAddressMatcher = IP_DOMAIN_PATTERN.matcher(ipAddress);
    142        for (int i = 1; i <= 4; i++) { // CHECKSTYLE IGNORE MagicNumber
    143            String ipSegment = ipAddressMatcher.group(i);
    144            if (ipSegment == null || ipSegment.length() <= 0) {
    145                return false;
    146            }
    147
    148            int iIpSegment = 0;
    149
    150            try {
    151                iIpSegment = Integer.parseInt(ipSegment);
    152            } catch(NumberFormatException e) {
    153                return false;
    154            }
    155
    156            if (iIpSegment > 255) { // CHECKSTYLE IGNORE MagicNumber
    157                return false;
    158            }
    159
    160        }
    161        return true;
    162    }
    163
    164    /**
    165     * Validates a symbolic domain name.  Returns true if it's valid.
    166     * @param domain symbolic domain name
    167     * @return true if the symbolic domain name is valid.
    168     */
    169    protected boolean isValidSymbolicDomain(String domain) {
    170        String[] domainSegment = new String[10]; // CHECKSTYLE IGNORE MagicNumber
    171        boolean match = true;
    172        int i = 0;
    173        Matcher atomMatcher = ATOM_PATTERN.matcher(domain);
    174        while (match) {
    175            match = atomMatcher.matches();
    176            if (match) {
    177                domainSegment[i] = atomMatcher.group(1);
    178                int l = domainSegment[i].length() + 1;
    179                domain =
    180                        (l >= domain.length())
    181                        ? ""
    182                        : domain.substring(l);
    183
    184                i++;
    185            }
    186        }
    187
    188        int len = i;
    189
    190        // Make sure there's a host name preceding the domain.
    191        if (len < 2) {
    192            return false;
    193        }
    194
    195        String tld = domainSegment[len - 1];
    196        if (tld.length() > 1) {
    197            if (! TLD_PATTERN.matcher(tld).matches()) {
    198                return false;
    199            }
    200        } else {
    201            return false;
    202        }
    203
    204        return true;
    205    }
    206    /**
    207     *   Recursively remove comments, and replace with a single space.  The simpler
    208     *   regexps in the Email Addressing FAQ are imperfect - they will miss escaped
    209     *   chars in atoms, for example.
    210     *   Derived From    Mail::RFC822::Address
    211     * @param emailStr The email address
    212     * @return address with comments removed.
    213    */
    214    protected String stripComments(String emailStr)  {
    215     String result = emailStr;
    216     String commentPat = "^((?:[^\"\\\\]|\\\\.)*(?:\"(?:[^\"\\\\]|\\\\.)*\"(?:[^\"\\\\]|\111111\\\\.)*)*)\\((?:[^()\\\\]|\\\\.)*\\)/";
    217     Pattern commentMatcher = Pattern.compile(commentPat);
    218
    219     while (commentMatcher.matcher(result).matches()) {
    220        result = result.replaceFirst(commentPat, "\1 ");
    221     }
    222     return result;
    223    }
    224}