1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.apache.commons.validator;
23
24 import org.apache.oro.text.perl.Perl5Util;
25
26 /***
27 * <p>Perform email validations.</p>
28 * <p>
29 * This class is a Singleton; you can retrieve the instance via the getInstance() method.
30 * </p>
31 * <p>
32 * Based on a script by <a href="mailto:stamhankar@hotmail.com">Sandeep V. Tamhankar</a>
33 * http://javascript.internet.com
34 * </p>
35 * <p>
36 * This implementation is not guaranteed to catch all possible errors in an email address.
37 * For example, an address like nobody@noplace.somedog will pass validator, even though there
38 * is no TLD "somedog"
39 * </p>.
40 * @since Validator 1.1
41 */
42 public class EmailValidator {
43
44 private static final String SPECIAL_CHARS = "//(//)<>@,;:'//////\"//.//[//]";
45 private static final String VALID_CHARS = "[^//s" + SPECIAL_CHARS + "]";
46 private static final String QUOTED_USER = "(\"[^\"]*\")";
47 private static final String ATOM = VALID_CHARS + '+';
48 private static final String WORD = "((" + VALID_CHARS + "|')+|" + QUOTED_USER + ")";
49
50
51 private static final String LEGAL_ASCII_PATTERN = "/^[//000-//177]+$/";
52 private static final String EMAIL_PATTERN = "/^(.+)@(.+)$/";
53 private static final String IP_DOMAIN_PATTERN =
54 "/^//[(//d{1,3})[.](//d{1,3})[.](//d{1,3})[.](//d{1,3})//]$/";
55 private static final String TLD_PATTERN = "/^([a-zA-Z]+)$/";
56
57 private static final String USER_PATTERN = "/^//s*" + WORD + "(//." + WORD + ")*$/";
58 private static final String DOMAIN_PATTERN = "/^" + ATOM + "(//." + ATOM + ")*//s*$/";
59 private static final String ATOM_PATTERN = "/(" + ATOM + ")/";
60
61 /***
62 * Singleton instance of this class.
63 */
64 private static final EmailValidator EMAIL_VALIDATOR = new EmailValidator();
65
66 /***
67 * Returns the Singleton instance of this validator.
68 * @return singleton instance of this validator.
69 */
70 public static EmailValidator getInstance() {
71 return EMAIL_VALIDATOR;
72 }
73
74 /***
75 * Protected constructor for subclasses to use.
76 */
77 protected EmailValidator() {
78 super();
79 }
80
81 /***
82 * <p>Checks if a field has a valid e-mail address.</p>
83 *
84 * @param email The value validation is being performed on. A <code>null</code>
85 * value is considered invalid.
86 * @return true if the email address is valid.
87 */
88 public boolean isValid(String email) {
89 if (email == null) {
90 return false;
91 }
92
93 Perl5Util matchAsciiPat = new Perl5Util();
94 if (!matchAsciiPat.match(LEGAL_ASCII_PATTERN, email)) {
95 return false;
96 }
97
98 email = stripComments(email);
99
100
101 Perl5Util emailMatcher = new Perl5Util();
102 if (!emailMatcher.match(EMAIL_PATTERN, email)) {
103 return false;
104 }
105
106 if (email.endsWith(".")) {
107 return false;
108 }
109
110 if (!isValidUser(emailMatcher.group(1))) {
111 return false;
112 }
113
114 if (!isValidDomain(emailMatcher.group(2))) {
115 return false;
116 }
117
118 return true;
119 }
120
121 /***
122 * Returns true if the domain component of an email address is valid.
123 * @param domain being validatied.
124 * @return true if the email address's domain is valid.
125 */
126 protected boolean isValidDomain(String domain) {
127 boolean symbolic = false;
128 Perl5Util ipAddressMatcher = new Perl5Util();
129
130 if (ipAddressMatcher.match(IP_DOMAIN_PATTERN, domain)) {
131 if (!isValidIpAddress(ipAddressMatcher)) {
132 return false;
133 } else {
134 return true;
135 }
136 } else {
137
138 Perl5Util domainMatcher = new Perl5Util();
139 symbolic = domainMatcher.match(DOMAIN_PATTERN, domain);
140 }
141
142 if (symbolic) {
143 if (!isValidSymbolicDomain(domain)) {
144 return false;
145 }
146 } else {
147 return false;
148 }
149
150 return true;
151 }
152
153 /***
154 * Returns true if the user component of an email address is valid.
155 * @param user being validated
156 * @return true if the user name is valid.
157 */
158 protected boolean isValidUser(String user) {
159 Perl5Util userMatcher = new Perl5Util();
160 return userMatcher.match(USER_PATTERN, user);
161 }
162
163 /***
164 * Validates an IP address. Returns true if valid.
165 * @param ipAddressMatcher Pattren matcher
166 * @return true if the ip address is valid.
167 */
168 protected boolean isValidIpAddress(Perl5Util ipAddressMatcher) {
169 for (int i = 1; i <= 4; i++) {
170 String ipSegment = ipAddressMatcher.group(i);
171 if (ipSegment == null || ipSegment.length() <= 0) {
172 return false;
173 }
174
175 int iIpSegment = 0;
176
177 try {
178 iIpSegment = Integer.parseInt(ipSegment);
179 } catch(NumberFormatException e) {
180 return false;
181 }
182
183 if (iIpSegment > 255) {
184 return false;
185 }
186
187 }
188 return true;
189 }
190
191 /***
192 * Validates a symbolic domain name. Returns true if it's valid.
193 * @param domain symbolic domain name
194 * @return true if the symbolic domain name is valid.
195 */
196 protected boolean isValidSymbolicDomain(String domain) {
197 String[] domainSegment = new String[10];
198 boolean match = true;
199 int i = 0;
200 Perl5Util atomMatcher = new Perl5Util();
201 while (match) {
202 match = atomMatcher.match(ATOM_PATTERN, domain);
203 if (match) {
204 domainSegment[i] = atomMatcher.group(1);
205 int l = domainSegment[i].length() + 1;
206 domain =
207 (l >= domain.length())
208 ? ""
209 : domain.substring(l);
210
211 i++;
212 }
213 }
214
215 int len = i;
216
217
218 if (len < 2) {
219 return false;
220 }
221
222
223
224 String tld = domainSegment[len - 1];
225 if (tld.length() > 1) {
226 Perl5Util matchTldPat = new Perl5Util();
227 if (!matchTldPat.match(TLD_PATTERN, tld)) {
228 return false;
229 }
230 } else {
231 return false;
232 }
233
234 return true;
235 }
236 /***
237 * Recursively remove comments, and replace with a single space. The simpler
238 * regexps in the Email Addressing FAQ are imperfect - they will miss escaped
239 * chars in atoms, for example.
240 * Derived From Mail::RFC822::Address
241 * @param emailStr The email address
242 * @return address with comments removed.
243 */
244 protected String stripComments(String emailStr) {
245 String input = emailStr;
246 String result = emailStr;
247 String commentPat = "s/^((?:[^\"////]|////.)*(?:\"(?:[^\"////]|////.)*\"(?:[^\"////]|\111111////.)*)*)//((?:[^()////]|////.)*//)/$1 /osx";
248 Perl5Util commentMatcher = new Perl5Util();
249 result = commentMatcher.substitute(commentPat,input);
250
251 while (!result.equals(input)) {
252 input = result;
253 result = commentMatcher.substitute(commentPat,input);
254 }
255 return result;
256
257 }
258 }