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 }