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.routines; 018 019import java.io.Serializable; 020import java.util.regex.Matcher; 021import java.util.regex.Pattern; 022 023/** 024 * <p>Perform email validations.</p> 025 * <p> 026 * This class is a Singleton; you can retrieve the instance via the getInstance() method. 027 * </p> 028 * <p> 029 * Based on a script by <a href="mailto:stamhankar@hotmail.com">Sandeep V. Tamhankar</a> 030 * http://javascript.internet.com 031 * </p> 032 * <p> 033 * This implementation is not guaranteed to catch all possible errors in an email address. 034 * For example, an address like nobody@noplace.somedog will pass validator, even though there 035 * is no TLD "somedog" 036 * </p>. 037 * 038 * @version $Revision: 1227719 $ $Date: 2012-01-05 12:45:51 -0500 (Thu, 05 Jan 2012) $ 039 * @since Validator 1.4 040 */ 041public class EmailValidator implements Serializable { 042 043 private static final long serialVersionUID = 1705927040799295880L; 044 045 private static final String SPECIAL_CHARS = "\\p{Cntrl}\\(\\)<>@,;:'\\\\\\\"\\.\\[\\]"; 046 private static final String VALID_CHARS = "[^\\s" + SPECIAL_CHARS + "]"; 047 private static final String QUOTED_USER = "(\"[^\"]*\")"; 048 private static final String WORD = "((" + VALID_CHARS + "|')+|" + QUOTED_USER + ")"; 049 050 private static final String LEGAL_ASCII_REGEX = "^\\p{ASCII}+$"; 051 private static final String EMAIL_REGEX = "^\\s*?(.+)@(.+?)\\s*$"; 052 private static final String IP_DOMAIN_REGEX = "^\\[(.*)\\]$"; 053 private static final String USER_REGEX = "^\\s*" + WORD + "(\\." + WORD + ")*$"; 054 055 private static final Pattern MATCH_ASCII_PATTERN = Pattern.compile(LEGAL_ASCII_REGEX); 056 private static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REGEX); 057 private static final Pattern IP_DOMAIN_PATTERN = Pattern.compile(IP_DOMAIN_REGEX); 058 private static final Pattern USER_PATTERN = Pattern.compile(USER_REGEX); 059 060 private final boolean allowLocal; 061 062 /** 063 * Singleton instance of this class, which 064 * doesn't consider local addresses as valid. 065 */ 066 private static final EmailValidator EMAIL_VALIDATOR = new EmailValidator(false); 067 068 /** 069 * Singleton instance of this class, which does 070 * consider local addresses valid. 071 */ 072 private static final EmailValidator EMAIL_VALIDATOR_WITH_LOCAL = new EmailValidator(true); 073 074 /** 075 * Returns the Singleton instance of this validator. 076 * 077 * @return singleton instance of this validator. 078 */ 079 public static EmailValidator getInstance() { 080 return EMAIL_VALIDATOR; 081 } 082 083 /** 084 * Returns the Singleton instance of this validator, 085 * with local validation as required. 086 * 087 * @param allowLocal Should local addresses be considered valid? 088 * @return singleton instance of this validator 089 */ 090 public static EmailValidator getInstance(boolean allowLocal) { 091 if(allowLocal) { 092 return EMAIL_VALIDATOR_WITH_LOCAL; 093 } 094 return EMAIL_VALIDATOR; 095 } 096 097 /** 098 * Protected constructor for subclasses to use. 099 * 100 * @param allowLocal Should local addresses be considered valid? 101 */ 102 protected EmailValidator(boolean allowLocal) { 103 super(); 104 this.allowLocal = allowLocal; 105 } 106 107 /** 108 * <p>Checks if a field has a valid e-mail address.</p> 109 * 110 * @param email The value validation is being performed on. A <code>null</code> 111 * value is considered invalid. 112 * @return true if the email address is valid. 113 */ 114 public boolean isValid(String email) { 115 if (email == null) { 116 return false; 117 } 118 119 Matcher asciiMatcher = MATCH_ASCII_PATTERN.matcher(email); 120 if (!asciiMatcher.matches()) { 121 return false; 122 } 123 124 // Check the whole email address structure 125 Matcher emailMatcher = EMAIL_PATTERN.matcher(email); 126 if (!emailMatcher.matches()) { 127 return false; 128 } 129 130 if (email.endsWith(".")) { 131 return false; 132 } 133 134 if (!isValidUser(emailMatcher.group(1))) { 135 return false; 136 } 137 138 if (!isValidDomain(emailMatcher.group(2))) { 139 return false; 140 } 141 142 return true; 143 } 144 145 /** 146 * Returns true if the domain component of an email address is valid. 147 * 148 * @param domain being validated. 149 * @return true if the email address's domain is valid. 150 */ 151 protected boolean isValidDomain(String domain) { 152 // see if domain is an IP address in brackets 153 Matcher ipDomainMatcher = IP_DOMAIN_PATTERN.matcher(domain); 154 155 if (ipDomainMatcher.matches()) { 156 InetAddressValidator inetAddressValidator = 157 InetAddressValidator.getInstance(); 158 return inetAddressValidator.isValid(ipDomainMatcher.group(1)); 159 } else { 160 // Domain is symbolic name 161 DomainValidator domainValidator = 162 DomainValidator.getInstance(allowLocal); 163 return domainValidator.isValid(domain); 164 } 165 } 166 167 /** 168 * Returns true if the user component of an email address is valid. 169 * 170 * @param user being validated 171 * @return true if the user name is valid. 172 */ 173 protected boolean isValidUser(String user) { 174 return USER_PATTERN.matcher(user).matches(); 175 } 176 177}