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.openstreetmap.josm.data.validation.routines; 018 019import static org.openstreetmap.josm.tools.I18n.tr; 020 021import java.util.regex.Matcher; 022import java.util.regex.Pattern; 023 024/** 025 * <p>Perform email validations.</p> 026 * <p> 027 * Based on a script by <a href="mailto:stamhankar@hotmail.com">Sandeep V. Tamhankar</a> 028 * http://javascript.internet.com 029 * </p> 030 * <p> 031 * This implementation is not guaranteed to catch all possible errors in an email address. 032 * </p>. 033 * 034 * @version $Revision: 1741724 $ 035 * @since Validator 1.4 036 */ 037public class EmailValidator extends AbstractValidator { 038 039 private static final String SPECIAL_CHARS = "\\p{Cntrl}\\(\\)<>@,;:'\\\\\\\"\\.\\[\\]"; 040 private static final String VALID_CHARS = "(\\\\.)|[^\\s" + SPECIAL_CHARS + "]"; 041 private static final String QUOTED_USER = "(\"(\\\\\"|[^\"])*\")"; 042 private static final String WORD = "((" + VALID_CHARS + "|')+|" + QUOTED_USER + ")"; 043 044 private static final String EMAIL_REGEX = "^\\s*?(.+)@(.+?)\\s*$"; 045 private static final String IP_DOMAIN_REGEX = "^\\[(.*)\\]$"; 046 private static final String USER_REGEX = "^\\s*" + WORD + "(\\." + WORD + ")*$"; 047 048 private static final Pattern EMAIL_PATTERN = Pattern.compile(EMAIL_REGEX); 049 private static final Pattern IP_DOMAIN_PATTERN = Pattern.compile(IP_DOMAIN_REGEX); 050 private static final Pattern USER_PATTERN = Pattern.compile(USER_REGEX); 051 052 private static final int MAX_USERNAME_LEN = 64; 053 054 private final boolean allowLocal; 055 private final boolean allowTld; 056 057 /** 058 * Singleton instance of this class, which 059 * doesn't consider local addresses as valid. 060 */ 061 private static final EmailValidator EMAIL_VALIDATOR = new EmailValidator(false, false); 062 063 /** 064 * Singleton instance of this class, which 065 * doesn't consider local addresses as valid. 066 */ 067 private static final EmailValidator EMAIL_VALIDATOR_WITH_TLD = new EmailValidator(false, true); 068 069 /** 070 * Singleton instance of this class, which does 071 * consider local addresses valid. 072 */ 073 private static final EmailValidator EMAIL_VALIDATOR_WITH_LOCAL = new EmailValidator(true, false); 074 075 /** 076 * Returns the Singleton instance of this validator. 077 * 078 * @return singleton instance of this validator. 079 */ 080 public static EmailValidator getInstance() { 081 return EMAIL_VALIDATOR; 082 } 083 084 /** 085 * Returns the Singleton instance of this validator, 086 * with local validation as required. 087 * 088 * @param allowLocal Should local addresses be considered valid? 089 * @param allowTld Should TLDs be allowed? 090 * @return singleton instance of this validator 091 */ 092 public static EmailValidator getInstance(boolean allowLocal, boolean allowTld) { 093 if (allowLocal) { 094 return EMAIL_VALIDATOR_WITH_LOCAL; 095 } else { 096 if (allowTld) { 097 return EMAIL_VALIDATOR_WITH_TLD; 098 } else { 099 return EMAIL_VALIDATOR; 100 } 101 } 102 } 103 104 /** 105 * Returns the Singleton instance of this validator, 106 * with local validation as required. 107 * 108 * @param allowLocal Should local addresses be considered valid? 109 * @return singleton instance of this validator 110 */ 111 public static EmailValidator getInstance(boolean allowLocal) { 112 return getInstance(allowLocal, false); 113 } 114 115 /** 116 * Protected constructor for subclasses to use. 117 * 118 * @param allowLocal Should local addresses be considered valid? 119 * @param allowTld Should TLDs be allowed? 120 */ 121 protected EmailValidator(boolean allowLocal, boolean allowTld) { 122 super(); 123 this.allowLocal = allowLocal; 124 this.allowTld = allowTld; 125 } 126 127 /** 128 * <p>Checks if a field has a valid e-mail address.</p> 129 * 130 * @param email The value validation is being performed on. A <code>null</code> 131 * value is considered invalid. 132 * @return true if the email address is valid. 133 */ 134 @Override 135 public boolean isValid(String email) { 136 if (email == null) { 137 return false; 138 } 139 140 if (email.endsWith(".")) { // check this first - it's cheap! 141 setErrorMessage(tr("E-mail address is invalid")); 142 return false; 143 } 144 145 // Check the whole email address structure 146 Matcher emailMatcher = EMAIL_PATTERN.matcher(email); 147 if (!emailMatcher.matches()) { 148 setErrorMessage(tr("E-mail address is invalid")); 149 return false; 150 } 151 152 String username = emailMatcher.group(1); 153 if (!isValidUser(username)) { 154 setErrorMessage(tr("E-mail address contains an invalid username: {0}", username)); 155 return false; 156 } 157 158 String domain = emailMatcher.group(2); 159 if (!isValidDomain(domain)) { 160 setErrorMessage(tr("E-mail address contains an invalid domain: {0}", domain)); 161 return false; 162 } 163 164 return true; 165 } 166 167 @Override 168 public String getValidatorName() { 169 return tr("Email validator"); 170 } 171 172 /** 173 * Returns true if the domain component of an email address is valid. 174 * 175 * @param domain being validated, may be in IDN format 176 * @return true if the email address's domain is valid. 177 */ 178 protected boolean isValidDomain(String domain) { 179 // see if domain is an IP address in brackets 180 Matcher ipDomainMatcher = IP_DOMAIN_PATTERN.matcher(domain); 181 182 if (ipDomainMatcher.matches()) { 183 InetAddressValidator inetAddressValidator = 184 InetAddressValidator.getInstance(); 185 return inetAddressValidator.isValid(ipDomainMatcher.group(1)); 186 } 187 // Domain is symbolic name 188 DomainValidator domainValidator = 189 DomainValidator.getInstance(allowLocal); 190 if (allowTld) { 191 return domainValidator.isValid(domain) || (!domain.startsWith(".") && domainValidator.isValidTld(domain)); 192 } else { 193 return domainValidator.isValid(domain); 194 } 195 } 196 197 /** 198 * Returns true if the user component of an email address is valid. 199 * 200 * @param user being validated 201 * @return true if the user name is valid. 202 */ 203 protected boolean isValidUser(String user) { 204 205 if (user == null || user.length() > MAX_USERNAME_LEN) { 206 return false; 207 } 208 209 return USER_PATTERN.matcher(user).matches(); 210 } 211 212}