View Javadoc

    1   /*
    2    * Copyright 2004-2009 the Seasar Foundation and the Others.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *     http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
    13   * either express or implied. See the License for the specific language
    14   * governing permissions and limitations under the License.
    15   */
    16  package org.seasar.cubby.validator.validators;
    17
    18  import java.util.ArrayList;
    19  import java.util.List;
    20  import java.util.regex.Matcher;
    21  import java.util.regex.Pattern;
    22
    23  import org.seasar.cubby.action.MessageInfo;
    24  import org.seasar.cubby.internal.util.StringUtils;
    25  import org.seasar.cubby.validator.ScalarFieldValidator;
    26  import org.seasar.cubby.validator.ValidationContext;
    27
    28  /**
    29   * <a href="http://www.ietf.org/rfc/rfc2821.txt">RFC2821</a>
    30   * に準拠したメールアドレスかを検証します。
    31   * <p>
    32   * <table>
    33   * <caption>検証エラー時に設定するエラーメッセージ</caption> <tbody>
    34   * <tr>
    35   * <th scope="row">デフォルトのキー</th>
    36   * <td>valid.email</td>
    37   * </tr>
    38   * <tr>
    39   * <th scope="row">置換文字列</th>
    40   * <td>
    41   * <ol start="0">
    42   * <li>フィールド名</li>
    43   * </ol></td>
    44   * </tr>
    45   * </tbody>
    46   * </table>
    47   * </p>
    48   *
    49   * @see <a href="http://www.ietf.org/rfc/rfc2821.txt">RFC2821</a>
    50   * @author agata
    51   * @author baba
    52   */
    53  public class EmailValidator implements ScalarFieldValidator {
    54
    55  	private static final String SPECIAL_CHARS = "\\(\\)<>@,;:\\\\\\\"\\.\\[\\]";
    56  	private static final String VALID_CHARS = "[^\\s" + SPECIAL_CHARS + "]";
    57  	private static final String QUOTED_USER = "(\"[^\"]*\")";
    58  	private static final String ATOM = VALID_CHARS + '+';
    59  	private static final String WORD = "(" + ATOM + "|" + QUOTED_USER + ")";
    60
    61  	private static final String LEGAL_ASCII_PATTERN = "^[\\x00-\\x7F]+$";
    62  	private static final String EMAIL_PATTERN = "^(.+)@(.+)$";
    63  	private static final String IP_DOMAIN_PATTERN = "^(\\d{1,3})[.](\\d{1,3})[.](\\d{1,3})[.](\\d{1,3})$";
    64
    65  	private static final String USER_PATTERN = "^" + WORD + "(\\." + WORD
    66  			+ ")*$";
    67  	private static final String DOMAIN_PATTERN = "^" + ATOM + "(\\." + ATOM
    68  			+ ")*$";
    69  	private static final String ATOM_PATTERN = "(" + ATOM + ")";
    70
    71  	/**
    72  	 * メッセージキー。
    73  	 */
    74  	private final String messageKey;
    75
    76  	/**
    77  	 * コンストラクタ
    78  	 */
    79  	public EmailValidator() {
    80  		this("valid.email");
    81  	}
    82
    83  	/**
    84  	 * メッセージキーを指定するコンストラクタ
    85  	 *
    86  	 * @param messageKey
    87  	 */
    88  	public EmailValidator(final String messageKey) {
    89  		this.messageKey = messageKey;
    90  	}
    91
    92  	/**
    93  	 * {@inheritDoc}
    94  	 */
    95  	public void validate(final ValidationContext context, final Object value) {
    96  		if (value == null) {
    97  			return;
    98  		}
    99  		if (value instanceof String) {
    100 			final String email = (String) value;
    101 			if (StringUtils.isEmpty(email)) {
    102 				return;
    103 			}
    104
    105 			boolean match = !email.endsWith(".");
    106 			if (match) {
    107 				final Pattern pattern = Pattern.compile(LEGAL_ASCII_PATTERN);
    108 				final Matcher matchAsciiPat = pattern.matcher(email);
    109 				match = matchAsciiPat.matches();
    110 			}
    111
    112 			if (match) {
    113 				final Pattern pattern = Pattern.compile(EMAIL_PATTERN);
    114 				final Matcher matcher = pattern.matcher(email);
    115 				match = matcher.find();
    116 				if (match) {
    117 					if (isValidUser(matcher.group(1))
    118 							&& isValidDomain(matcher.group(2))) {
    119 						return;
    120 					}
    121 				}
    122 			}
    123 		}
    124
    125 		final MessageInfo messageInfo = new MessageInfo();
    126 		messageInfo.setKey(this.messageKey);
    127 		context.addMessageInfo(messageInfo);
    128 	}
    129
    130 	private boolean isValidDomain(final String domain) {
    131 		Pattern pattern = Pattern.compile(IP_DOMAIN_PATTERN);
    132 		final Matcher ipAddressMatcher = pattern.matcher(domain);
    133
    134 		if (ipAddressMatcher.find()) {
    135 			if (isValidIpAddress(ipAddressMatcher)) {
    136 				return true;
    137 			}
    138 		} else {
    139 			pattern = Pattern.compile(DOMAIN_PATTERN);
    140 			final Matcher domainMatcher = pattern.matcher(domain);
    141 			if (domainMatcher.matches()) {
    142 				if (isValidSymbolicDomain(domain)) {
    143 					return true;
    144 				}
    145 			}
    146 		}
    147 		return false;
    148 	}
    149
    150 	private boolean isValidUser(final String user) {
    151 		final Pattern pattern = Pattern.compile(USER_PATTERN);
    152 		final Matcher userMatcher = pattern.matcher(user);
    153 		return userMatcher.matches();
    154 	}
    155
    156 	private boolean isValidIpAddress(final Matcher ipAddressMatcher) {
    157 		for (int i = 1; i <= 4; i++) {
    158 			final String ipSegment = ipAddressMatcher.group(i);
    159 			if (ipSegment == null || ipSegment.length() <= 0) {
    160 				return false;
    161 			}
    162
    163 			int iIpSegment = 0;
    164
    165 			try {
    166 				iIpSegment = Integer.parseInt(ipSegment);
    167 			} catch (final NumberFormatException e) {
    168 				return false;
    169 			}
    170
    171 			if (iIpSegment > 255) {
    172 				return false;
    173 			}
    174
    175 		}
    176 		return true;
    177 	}
    178
    179 	private boolean isValidSymbolicDomain(final String domain) {
    180 		final List<String> domainSegments = new ArrayList<String>();
    181 		boolean match = true;
    182
    183 		final Pattern pattern = Pattern.compile(ATOM_PATTERN);
    184 		String domainSegment;
    185 		String currentDomain = domain;
    186 		while (match) {
    187 			final Matcher atomMatcher = pattern.matcher(currentDomain);
    188 			match = atomMatcher.find();
    189 			if (match) {
    190 				domainSegment = atomMatcher.group(1);
    191 				domainSegments.add(domainSegment);
    192 				final int domainSegmentLength = domainSegment.length() + 1;
    193 				currentDomain = domainSegmentLength >= currentDomain.length() ? ""
    194 						: currentDomain.substring(domainSegmentLength);
    195 			}
    196 		}
    197
    198 		final int size = domainSegments.size();
    199 		if (size > 0) {
    200 			final String end = domainSegments.get(size - 1);
    201 			if (end.length() < 2 || end.length() > 4) {
    202 				return false;
    203 			}
    204 		}
    205
    206 		if (size < 2) {
    207 			return false;
    208 		}
    209
    210 		return true;
    211 	}
    212
    213 }