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 */ 017 package org.apache.commons.validator; 018 019 import org.apache.commons.validator.routines.InetAddressValidator; 020 import org.apache.oro.text.perl.Perl5Util; 021 022 import java.util.regex.Matcher; 023 import java.util.regex.Pattern; 024 025 /** 026 * <p>Perform email validations.</p> 027 * <p> 028 * This class is a Singleton; you can retrieve the instance via the getInstance() method. 029 * </p> 030 * <p> 031 * Based on a script by <a href="mailto:stamhankar@hotmail.com">Sandeep V. Tamhankar</a> 032 * http://javascript.internet.com 033 * </p> 034 * <p> 035 * This implementation is not guaranteed to catch all possible errors in an email address. 036 * For example, an address like nobody@noplace.somedog will pass validator, even though there 037 * is no TLD "somedog" 038 * </p>. 039 * 040 * @version $Revision: 658832 $ $Date: 2008-05-21 21:57:21 +0200 (Mi, 21. Mai 2008) $ 041 * @since Validator 1.1 042 * @deprecated Use the new EmailValidator in the routines package. This class 043 * will be removed in a future release. 044 */ 045 public class EmailValidator { 046 047 private static final String SPECIAL_CHARS = "\\000-\\037\\(\\)<>@,;:'\\\\\\\"\\.\\[\\]\\177"; 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 // Each pattern must be surrounded by / 054 private static final String LEGAL_ASCII_PATTERN = "/^[\\000-\\177]+$/"; 055 private static final String EMAIL_PATTERN = "/^(.+)@(.+)$/"; 056 private static final String IP_DOMAIN_PATTERN = "^\\[(.*)\\]$"; 057 private static final String TLD_PATTERN = "/^([a-zA-Z]+)$/"; 058 059 private static final String USER_PATTERN = "/^\\s*" + WORD + "(\\." + WORD + ")*$/"; 060 private static final String DOMAIN_PATTERN = "/^" + ATOM + "(\\." + ATOM + ")*\\s*$/"; 061 private static final String ATOM_PATTERN = "/(" + ATOM + ")/"; 062 063 /** 064 * Singleton instance of this class. 065 */ 066 private static final EmailValidator EMAIL_VALIDATOR = new EmailValidator(); 067 068 /** 069 * Returns the Singleton instance of this validator. 070 * @return singleton instance of this validator. 071 */ 072 public static EmailValidator getInstance() { 073 return EMAIL_VALIDATOR; 074 } 075 076 /** 077 * Protected constructor for subclasses to use. 078 */ 079 protected EmailValidator() { 080 super(); 081 } 082 083 /** 084 * <p>Checks if a field has a valid e-mail address.</p> 085 * 086 * @param email The value validation is being performed on. A <code>null</code> 087 * value is considered invalid. 088 * @return true if the email address is valid. 089 */ 090 public boolean isValid(String email) { 091 return org.apache.commons.validator.routines.EmailValidator.getInstance().isValid(email); 092 } 093 094 /** 095 * Returns true if the domain component of an email address is valid. 096 * @param domain being validated. 097 * @return true if the email address's domain is valid. 098 */ 099 protected boolean isValidDomain(String domain) { 100 boolean symbolic = false; 101 102 // see if domain is an IP address in brackets 103 Pattern ipDomainPattern = Pattern.compile(IP_DOMAIN_PATTERN); 104 Matcher ipDomainMatcher = ipDomainPattern.matcher(domain); 105 106 if (ipDomainMatcher.matches()) { 107 InetAddressValidator inetAddressValidator = 108 InetAddressValidator.getInstance(); 109 if (inetAddressValidator.isValid(ipDomainMatcher.group(1))) { 110 return true; 111 } 112 } else { 113 // Domain is symbolic name 114 Perl5Util domainMatcher = new Perl5Util(); 115 symbolic = domainMatcher.match(DOMAIN_PATTERN, domain); 116 } 117 118 if (symbolic) { 119 if (!isValidSymbolicDomain(domain)) { 120 return false; 121 } 122 } else { 123 return false; 124 } 125 126 return true; 127 } 128 129 /** 130 * Returns true if the user component of an email address is valid. 131 * @param user being validated 132 * @return true if the user name is valid. 133 */ 134 protected boolean isValidUser(String user) { 135 Perl5Util userMatcher = new Perl5Util(); 136 return userMatcher.match(USER_PATTERN, user); 137 } 138 139 /** 140 * Validates an IP address. Returns true if valid. 141 * @param ipAddressMatcher Pattren matcher 142 * @return true if the ip address is valid. 143 */ 144 protected boolean isValidIpAddress(Perl5Util ipAddressMatcher) { 145 for (int i = 1; i <= 4; i++) { 146 String ipSegment = ipAddressMatcher.group(i); 147 if (ipSegment == null || ipSegment.length() <= 0) { 148 return false; 149 } 150 151 int iIpSegment = 0; 152 153 try { 154 iIpSegment = Integer.parseInt(ipSegment); 155 } catch(NumberFormatException e) { 156 return false; 157 } 158 159 if (iIpSegment > 255) { 160 return false; 161 } 162 163 } 164 return true; 165 } 166 167 /** 168 * Validates a symbolic domain name. Returns true if it's valid. 169 * @param domain symbolic domain name 170 * @return true if the symbolic domain name is valid. 171 */ 172 protected boolean isValidSymbolicDomain(String domain) { 173 String[] domainSegment = new String[10]; 174 boolean match = true; 175 int i = 0; 176 Perl5Util atomMatcher = new Perl5Util(); 177 while (match) { 178 match = atomMatcher.match(ATOM_PATTERN, domain); 179 if (match) { 180 domainSegment[i] = atomMatcher.group(1); 181 int l = domainSegment[i].length() + 1; 182 domain = 183 (l >= domain.length()) 184 ? "" 185 : domain.substring(l); 186 187 i++; 188 } 189 } 190 191 int len = i; 192 193 // Make sure there's a host name preceding the domain. 194 if (len < 2) { 195 return false; 196 } 197 198 // TODO: the tld should be checked against some sort of configurable 199 // list 200 String tld = domainSegment[len - 1]; 201 if (tld.length() > 1) { 202 Perl5Util matchTldPat = new Perl5Util(); 203 if (!matchTldPat.match(TLD_PATTERN, tld)) { 204 return false; 205 } 206 } else { 207 return false; 208 } 209 210 return true; 211 } 212 /** 213 * Recursively remove comments, and replace with a single space. The simpler 214 * regexps in the Email Addressing FAQ are imperfect - they will miss escaped 215 * chars in atoms, for example. 216 * Derived From Mail::RFC822::Address 217 * @param emailStr The email address 218 * @return address with comments removed. 219 */ 220 protected String stripComments(String emailStr) { 221 String input = emailStr; 222 String result = emailStr; 223 String commentPat = "s/^((?:[^\"\\\\]|\\\\.)*(?:\"(?:[^\"\\\\]|\\\\.)*\"(?:[^\"\\\\]|\111111\\\\.)*)*)\\((?:[^()\\\\]|\\\\.)*\\)/$1 /osx"; 224 Perl5Util commentMatcher = new Perl5Util(); 225 result = commentMatcher.substitute(commentPat,input); 226 // This really needs to be =~ or Perl5Matcher comparison 227 while (!result.equals(input)) { 228 input = result; 229 result = commentMatcher.substitute(commentPat,input); 230 } 231 return result; 232 233 } 234 }