001: /* ====================================================================
002: * The LateralNZ Software License, Version 1.0
003: *
004: * Copyright (c) 2003 LateralNZ. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by
021: * LateralNZ (http://www.lateralnz.org/) and other third parties."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. The names "LateralNZ" must not be used to endorse or promote
026: * products derived from this software without prior written
027: * permission. For written permission, please
028: * contact oss@lateralnz.org.
029: *
030: * 5. Products derived from this software may not be called "Panther",
031: * or "Lateral" or "LateralNZ", nor may "PANTHER" or "LATERAL" or
032: * "LATERALNZ" appear in their name, without prior written
033: * permission of LateralNZ.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of LateralNZ. For more
051: * information on Lateral, please see http://www.lateralnz.com/ or
052: * http://www.lateralnz.org
053: *
054: */
055: package org.lateralnz.common.util;
056:
057: import java.util.StringTokenizer;
058:
059: /**
060: * RFC 821 compliant email checking (I think... at the very least it
061: * seems to work on all valid email addresses I poke at it)
062: *
063: * @author J R Briggs
064: */
065: public final class EmailUtils implements Constants {
066: private static final String INVALID_CHARS = " <>()[]\\,;:\"@ ";
067:
068: private EmailUtils() {
069: }
070:
071: /**
072: * returns true if the specified string is a valid (RFC821) email address
073: * @param email the string representation of an email address
074: * @returns true if the email address is valid
075: */
076: public static final boolean isValidEmail(String email) {
077: int pos = email.indexOf('@');
078: if (pos <= 0 || pos == email.length() - 1) {
079: return false;
080: } else {
081: String localpart = email.substring(0, pos);
082: String domain = email.substring(pos + 1);
083:
084: if (!isValidLocalPart(localpart)) {
085: return false;
086: } else {
087: return isValidDomain(domain);
088: }
089: }
090: }
091:
092: private static final boolean isValidLocalPart(String localpart) {
093: if (localpart == null || localpart.length() < 1) {
094: return false;
095: } else {
096: if (localpart.startsWith(DOT) || localpart.endsWith(DOT)) {
097: return false;
098: } else {
099: StringTokenizer st = new StringTokenizer(localpart, DOT);
100: while (st.hasMoreTokens()) {
101: if (!isValidString(st.nextToken())) {
102: return false;
103: }
104: }
105: return true;
106: }
107: }
108: }
109:
110: private static final boolean isValidDomain(String domain) {
111: if (domain.startsWith(DOT) || domain.endsWith(DOT)) {
112: return false;
113: } else {
114: StringTokenizer st = new StringTokenizer(domain, DOT);
115: while (st.hasMoreTokens()) {
116: String token = st.nextToken();
117: // check for valid 'element' which is a name, number or dotnum
118: if (token.startsWith(SQ_LEFT_BRACKET)) {
119: while (!token.endsWith(SQ_RIGHT_BRACKET)
120: && st.hasMoreTokens()) {
121: token += DOT + st.nextToken();
122: }
123:
124: if (!isValidDotNum(token)) {
125: return false;
126: }
127: } else if (!isValidName(token) && !isValidNumber(token)) {
128: return false;
129: }
130: }
131:
132: return true;
133: }
134: }
135:
136: private static final boolean isValidName(String name) {
137: int len = name.length();
138: if (len == 0) {
139: return false;
140: }
141:
142: char start = name.charAt(0);
143: char end = name.charAt(len - 1);
144:
145: if (!isValidAlpha(start)) {
146: return false;
147: } else if (!isValidAlpha(end) && !isValidDigit(end)) {
148: return false;
149: } else {
150: for (int i = 1; i < name.length() - 1; i++) {
151: char c = name.charAt(i);
152: if (!isValidAlpha(c) && !isValidDigit(c) && c != '-') {
153: return false;
154: }
155: }
156: }
157: return true;
158: }
159:
160: private static final boolean isValidNumber(String number) {
161: int len = number.length();
162: if (!number.startsWith(HASH) || len <= 1) {
163: return false;
164: } else {
165: for (int i = 1; i < len; i++) {
166: if (!isValidDigit(number.charAt(i))) {
167: return false;
168: }
169: }
170: return true;
171: }
172: }
173:
174: private static final boolean isValidDotNum(String dotnum) {
175: int len = dotnum.length();
176:
177: if (len <= 2 || dotnum.charAt(0) != '['
178: || dotnum.charAt(len - 1) != ']') {
179: return false;
180: } else {
181: StringTokenizer st = new StringTokenizer(dotnum, DOT);
182:
183: while (st.hasMoreTokens()) {
184: String s = st.nextToken();
185:
186: if (s.length() > 3) {
187: return false;
188: } else {
189: for (int i = 0; i < s.length(); i++) {
190: if (!isValidDigit(s.charAt(i))) {
191: return false;
192: }
193: }
194: }
195: }
196: return true;
197: }
198: }
199:
200: private static final boolean isValidString(String s) {
201: for (int i = 0; i < s.length(); i++) {
202: char c = s.charAt(i);
203: if (c == '\\') {
204: i++;
205: c = s.charAt(i);
206: if (c < 0 || c > 127) {
207: return false;
208: }
209: } else {
210: if (c < 32 || c > 126 || INVALID_CHARS.indexOf(c) >= 0) {
211: return false;
212: }
213: }
214: }
215: return true;
216: }
217:
218: private static final boolean isValidAlpha(char c) {
219: return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
220: }
221:
222: private static final boolean isValidDigit(char c) {
223: return (c >= '0' && c <= '9');
224: }
225:
226: }
|