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 java.util.regex.Matcher; 020import java.util.regex.Pattern; 021 022import org.apache.commons.validator.routines.InetAddressValidator; 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 * https://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 * @since 1.1 040 * @deprecated Use the new EmailValidator in the routines package. This class 041 * will be removed in a future release. 042 */ 043@Deprecated 044public class EmailValidator { 045 046 private static final String SPECIAL_CHARS = "\\p{Cntrl}\\(\\)<>@,;:'\\\\\\\"\\.\\[\\]"; 047 private static final String VALID_CHARS = "[^\\s" + SPECIAL_CHARS + "]"; 048 private static final String QUOTED_USER = "(\"[^\"]*\")"; 049 private static final String ATOM = VALID_CHARS + '+'; 050 private static final String WORD = "((" + VALID_CHARS + "|')+|" + QUOTED_USER + ")"; 051 052// NOT USED private static final Pattern LEGAL_ASCII_PATTERN = Pattern.compile("^\\p{ASCII}+$"); 053// NOT USED private static final Pattern EMAIL_PATTERN = Pattern.compile("^(.+)@(.+)$"); 054 private static final Pattern IP_DOMAIN_PATTERN = Pattern.compile("^\\[(.*)\\]$"); 055 private static final Pattern TLD_PATTERN = Pattern.compile("^([a-zA-Z]+)$"); 056 057 private static final Pattern USER_PATTERN = Pattern.compile("^\\s*" + WORD + "(\\." + WORD + ")*$"); 058 private static final Pattern DOMAIN_PATTERN = Pattern.compile("^" + ATOM + "(\\." + ATOM + ")*\\s*$"); 059 private static final Pattern ATOM_PATTERN = Pattern.compile("(" + ATOM + ")"); 060 061 /** 062 * Singleton instance of this class. 063 */ 064 private static final EmailValidator EMAIL_VALIDATOR = new EmailValidator(); 065 066 /** 067 * Returns the Singleton instance of this validator. 068 * @return singleton instance of this validator. 069 */ 070 public static EmailValidator getInstance() { 071 return EMAIL_VALIDATOR; 072 } 073 074 /** 075 * Protected constructor for subclasses to use. 076 */ 077 protected EmailValidator() { 078 } 079 080 /** 081 * <p>Checks if a field has a valid e-mail address.</p> 082 * 083 * @param email The value validation is being performed on. A {@code null} 084 * value is considered invalid. 085 * @return true if the email address is valid. 086 */ 087 public boolean isValid(final String email) { 088 return org.apache.commons.validator.routines.EmailValidator.getInstance().isValid(email); 089 } 090 091 /** 092 * Returns true if the domain component of an email address is valid. 093 * @param domain being validated. 094 * @return true if the email address's domain is valid. 095 */ 096 protected boolean isValidDomain(final String domain) { 097 boolean symbolic = false; 098 099 // see if domain is an IP address in brackets 100 final Matcher ipDomainMatcher = IP_DOMAIN_PATTERN.matcher(domain); 101 102 if (ipDomainMatcher.matches()) { 103 final InetAddressValidator inetAddressValidator = 104 InetAddressValidator.getInstance(); 105 if (inetAddressValidator.isValid(ipDomainMatcher.group(1))) { 106 return true; 107 } 108 } else { 109 // Domain is symbolic name 110 symbolic = DOMAIN_PATTERN.matcher(domain).matches(); 111 } 112 113 if (!symbolic) { 114 return false; 115 } 116 if (!isValidSymbolicDomain(domain)) { 117 return false; 118 } 119 120 return true; 121 } 122 123 /** 124 * Validates an IP address. Returns true if valid. 125 * @param ipAddress IP address 126 * @return true if the ip address is valid. 127 */ 128 protected boolean isValidIpAddress(final String ipAddress) { 129 final Matcher ipAddressMatcher = IP_DOMAIN_PATTERN.matcher(ipAddress); 130 for (int i = 1; i <= 4; i++) { // CHECKSTYLE IGNORE MagicNumber 131 final String ipSegment = ipAddressMatcher.group(i); 132 if (ipSegment == null || ipSegment.isEmpty()) { 133 return false; 134 } 135 136 int iIpSegment = 0; 137 138 try { 139 iIpSegment = Integer.parseInt(ipSegment); 140 } catch (final NumberFormatException e) { 141 return false; 142 } 143 144 if (iIpSegment > 255) { // CHECKSTYLE IGNORE MagicNumber 145 return false; 146 } 147 148 } 149 return true; 150 } 151 152 /** 153 * Validates a symbolic domain name. Returns true if it's valid. 154 * @param domain symbolic domain name 155 * @return true if the symbolic domain name is valid. 156 */ 157 protected boolean isValidSymbolicDomain(String domain) { 158 final String[] domainSegment = new String[10]; // CHECKSTYLE IGNORE MagicNumber 159 boolean match = true; 160 int i = 0; 161 final Matcher atomMatcher = ATOM_PATTERN.matcher(domain); 162 while (match) { 163 match = atomMatcher.matches(); 164 if (match) { 165 domainSegment[i] = atomMatcher.group(1); 166 final int l = domainSegment[i].length() + 1; 167 domain = 168 l >= domain.length() 169 ? "" 170 : domain.substring(l); 171 172 i++; 173 } 174 } 175 176 final int len = i; 177 178 // Make sure there's a host name preceding the domain. 179 if (len < 2) { 180 return false; 181 } 182 183 final String tld = domainSegment[len - 1]; 184 if (tld.length() <= 1) { 185 return false; 186 } 187 if (! TLD_PATTERN.matcher(tld).matches()) { 188 return false; 189 } 190 191 return true; 192 } 193 194 /** 195 * Returns true if the user component of an email address is valid. 196 * @param user being validated 197 * @return true if the user name is valid. 198 */ 199 protected boolean isValidUser(final String user) { 200 return USER_PATTERN.matcher(user).matches(); 201 } 202 203 /** 204 * Recursively remove comments, and replace with a single space. The simpler regexps in the Email Addressing FAQ are imperfect - they will miss escaped 205 * chars in atoms, for example. Derived From Mail::RFC822::Address 206 * 207 * @param emailStr The email address 208 * @return address with comments removed. 209 */ 210 protected String stripComments(final String emailStr) { 211 String result = emailStr; 212 final String commentPat = "^((?:[^\"\\\\]|\\\\.)*(?:\"(?:[^\"\\\\]|\\\\.)*\"(?:[^\"\\\\]|\111111\\\\.)*)*)\\((?:[^()\\\\]|\\\\.)*\\)/"; 213 final Pattern commentMatcher = Pattern.compile(commentPat); 214 215 while (commentMatcher.matcher(result).matches()) { 216 result = result.replaceFirst(commentPat, "\1 "); 217 } 218 return result; 219 } 220}