1. /*
  2. * $Header: /home/cvs/jakarta-commons/validator/src/share/org/apache/commons/validator/EmailValidator.java,v 1.12 2004/02/21 17:10:29 rleland Exp $
  3. * $Revision: 1.12 $
  4. * $Date: 2004/02/21 17:10:29 $
  5. *
  6. * ====================================================================
  7. * Copyright 2001-2004 The Apache Software Foundation
  8. *
  9. * Licensed under the Apache License, Version 2.0 (the "License");
  10. * you may not use this file except in compliance with the License.
  11. * You may obtain a copy of the License at
  12. *
  13. * http://www.apache.org/licenses/LICENSE-2.0
  14. *
  15. * Unless required by applicable law or agreed to in writing, software
  16. * distributed under the License is distributed on an "AS IS" BASIS,
  17. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. * See the License for the specific language governing permissions and
  19. * limitations under the License.
  20. */
  21. package org.apache.commons.validator;
  22. import org.apache.oro.text.perl.Perl5Util;
  23. /**
  24. * <p>Perform email validations.</p>
  25. * <p>
  26. * This class is a Singleton; you can retrieve the instance via the getInstance() method.
  27. * </p>
  28. * <p>
  29. * Based on a script by <a href="mailto:stamhankar@hotmail.com">Sandeep V. Tamhankar</a>
  30. * http://javascript.internet.com
  31. * </p>
  32. *
  33. * @since Validator 1.1
  34. */
  35. public class EmailValidator {
  36. private static final String SPECIAL_CHARS = "\\(\\)<>@,;:\\\\\\\"\\.\\[\\]";
  37. private static final String VALID_CHARS = "[^\\s" + SPECIAL_CHARS + "]";
  38. private static final String QUOTED_USER = "(\"[^\"]*\")";
  39. private static final String ATOM = VALID_CHARS + '+';
  40. private static final String WORD = "(" + ATOM + "|" + QUOTED_USER + ")";
  41. // Each pattern must be surrounded by /
  42. private static final String LEGAL_ASCII_PATTERN = "/^[\\000-\\177]+$/";
  43. private static final String EMAIL_PATTERN = "/^(.+)@(.+)$/";
  44. private static final String IP_DOMAIN_PATTERN =
  45. "/^(\\d{1,3})[.](\\d{1,3})[.](\\d{1,3})[.](\\d{1,3})$/";
  46. private static final String USER_PATTERN = "/^" + WORD + "(\\." + WORD + ")*$/";
  47. private static final String DOMAIN_PATTERN = "/^" + ATOM + "(\\." + ATOM + ")*$/";
  48. private static final String ATOM_PATTERN = "/(" + ATOM + ")/";
  49. /**
  50. * Singleton instance of this class.
  51. */
  52. private static final EmailValidator instance = new EmailValidator();
  53. /**
  54. * Returns the Singleton instance of this validator.
  55. */
  56. public static EmailValidator getInstance() {
  57. return instance;
  58. }
  59. /**
  60. * Protected constructor for subclasses to use.
  61. */
  62. protected EmailValidator() {
  63. super();
  64. }
  65. /**
  66. * <p>Checks if a field has a valid e-mail address.</p>
  67. *
  68. * @param email The value validation is being performed on. A <code>null</code>
  69. * value is considered invalid.
  70. */
  71. public boolean isValid(String email) {
  72. if (email == null) {
  73. return false;
  74. }
  75. Perl5Util matchAsciiPat = new Perl5Util();
  76. if (!matchAsciiPat.match(LEGAL_ASCII_PATTERN, email)) {
  77. return false;
  78. }
  79. // Check the whole email address structure
  80. Perl5Util emailMatcher = new Perl5Util();
  81. if (!emailMatcher.match(EMAIL_PATTERN, email)) {
  82. return false;
  83. }
  84. if (email.endsWith(".")) {
  85. return false;
  86. }
  87. if (!isValidUser(emailMatcher.group(1))) {
  88. return false;
  89. }
  90. if (!isValidDomain(emailMatcher.group(2))) {
  91. return false;
  92. }
  93. return true;
  94. }
  95. /**
  96. * Returns true if the domain component of an email address is valid.
  97. * @param domain being validatied.
  98. */
  99. protected boolean isValidDomain(String domain) {
  100. boolean symbolic = false;
  101. Perl5Util ipAddressMatcher = new Perl5Util();
  102. if (ipAddressMatcher.match(IP_DOMAIN_PATTERN, domain)) {
  103. if (!isValidIpAddress(ipAddressMatcher)) {
  104. return false;
  105. }
  106. } else {
  107. // Domain is symbolic name
  108. Perl5Util domainMatcher = new Perl5Util();
  109. symbolic = domainMatcher.match(DOMAIN_PATTERN, domain);
  110. }
  111. if (symbolic) {
  112. if (!isValidSymbolicDomain(domain)) {
  113. return false;
  114. }
  115. } else {
  116. return false;
  117. }
  118. return true;
  119. }
  120. /**
  121. * Returns true if the user component of an email address is valid.
  122. * @param user being validated
  123. */
  124. protected boolean isValidUser(String user) {
  125. Perl5Util userMatcher = new Perl5Util();
  126. return userMatcher.match(USER_PATTERN, user);
  127. }
  128. /**
  129. * Validates an IP address. Returns true if valid.
  130. * @param ipAddressMatcher Pattren matcher
  131. */
  132. protected boolean isValidIpAddress(Perl5Util ipAddressMatcher) {
  133. for (int i = 1; i <= 4; i++) {
  134. String ipSegment = ipAddressMatcher.group(i);
  135. if (ipSegment == null || ipSegment.length() <= 0) {
  136. return false;
  137. }
  138. int iIpSegment = 0;
  139. try {
  140. iIpSegment = Integer.parseInt(ipSegment);
  141. } catch(NumberFormatException e) {
  142. return false;
  143. }
  144. if (iIpSegment > 255) {
  145. return false;
  146. }
  147. }
  148. return true;
  149. }
  150. /**
  151. * Validates a symbolic domain name. Returns true if it's valid.
  152. * @param domain symbolic domain name
  153. */
  154. protected boolean isValidSymbolicDomain(String domain) {
  155. String[] domainSegment = new String[10];
  156. boolean match = true;
  157. int i = 0;
  158. Perl5Util atomMatcher = new Perl5Util();
  159. while (match) {
  160. match = atomMatcher.match(ATOM_PATTERN, domain);
  161. if (match) {
  162. domainSegment[i] = atomMatcher.group(1);
  163. int l = domainSegment[i].length() + 1;
  164. domain =
  165. (l >= domain.length())
  166. ? ""
  167. : domain.substring(l);
  168. i++;
  169. }
  170. }
  171. int len = i;
  172. if (domainSegment[len - 1].length() < 2
  173. || domainSegment[len - 1].length() > 4) {
  174. return false;
  175. }
  176. // Make sure there's a host name preceding the domain.
  177. if (len < 2) {
  178. return false;
  179. }
  180. return true;
  181. }
  182. }