001/*
002 * Copyright 2008 Les Hazlewood Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
003 * file except in compliance with the License. You may obtain a copy of the License at
004 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
005 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
006 * either express or implied. See the License for the specific language governing permissions and limitations under the
007 * License.
008 */
009package org.codemonkey.simplejavamail;
010
011import java.util.regex.Pattern;
012
013/**
014 * Validates an email address according to <a href="http://www.ietf.org/rfc/rfc2822.txt">RFC 2822</a>, using regular expressions.
015 * <p>
016 * From the original author: <br>
017 * <blockquote> If you use this code, please keep the author information in tact and reference my site at <a
018 * href="http://www.leshazlewood.com">leshazlewood.com</a>. Thanks! </blockquote>
019 * <p>
020 * Code sanitized by Benny Bottema (kept validation 100% in tact).
021 * 
022 * @author Les Hazlewood, Benny Bottema
023 * @see EmailAddressValidationCriteria
024 */
025public final class EmailValidationUtil {
026
027        /**
028         * Private constructor; this is a utility class with static methods only, not designed for extension.
029         */
030        private EmailValidationUtil() {
031                //
032        }
033
034        /**
035         * Validates an e-mail with default validation flags that remains <code>true</code> to RFC 2822. This means allowing both domain
036         * literals and quoted identifiers.
037         * 
038         * @param email A complete email address.
039         * @return Whether the e-mail address is compliant with RFC 2822.
040         * @see EmailAddressValidationCriteria#RFC_COMPLIANT
041         */
042        public static boolean isValid(final String email) {
043                return isValid(email, EmailAddressValidationCriteria.RFC_COMPLIANT);
044        }
045
046        /**
047         * Validates an e-mail with given validation flags.
048         * 
049         * @param email A complete email address.
050         * @param emailAddressValidationCriteria A set of flags that restrict or relax RFC 2822 compliance.
051         * @return Whether the e-mail address is compliant with RFC 2822, configured using the passed in {@link EmailAddressValidationCriteria}.
052         * @see EmailAddressValidationCriteria#RFC_COMPLIANT
053         */
054        public static boolean isValid(final String email, final EmailAddressValidationCriteria emailAddressValidationCriteria) {
055                return buildValidEmailPattern(emailAddressValidationCriteria).matcher(email).matches();
056        }
057
058        protected static Pattern buildValidEmailPattern(EmailAddressValidationCriteria parameterObject) {
059                // RFC 2822 2.2.2 Structured Header Field Bodies
060                final String wsp = "[ \\t]"; // space or tab
061                final String fwsp = wsp + "*";
062                // RFC 2822 3.2.1 Primitive tokens
063                final String dquote = "\\\"";
064                // ASCII Control characters excluding white space:
065                final String noWsCtl = "\\x01-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F";
066                // all ASCII characters except CR and LF:
067                final String asciiText = "[\\x01-\\x09\\x0B\\x0C\\x0E-\\x7F]";
068                // RFC 2822 3.2.2 Quoted characters:
069                // single backslash followed by a text char
070                final String quotedPair = "(\\\\" + asciiText + ")";
071                // RFC 2822 3.2.4 Atom:
072                final String atext = "[a-zA-Z0-9\\!\\#\\$\\%\\&\\'\\*\\+\\-\\/\\=\\?\\^\\_\\`\\{\\|\\}\\~]";
073                final String atom = fwsp + atext + "+" + fwsp;
074                final String dotAtomText = atext + "+" + "(" + "\\." + atext + "+)*";
075                final String dotAtom = fwsp + "(" + dotAtomText + ")" + fwsp;
076                // RFC 2822 3.2.5 Quoted strings:
077                // noWsCtl and the rest of ASCII except the doublequote and backslash characters:
078                final String qtext = "[" + noWsCtl + "\\x21\\x23-\\x5B\\x5D-\\x7E]";
079                final String qcontent = "(" + qtext + "|" + quotedPair + ")";
080                final String quotedString = dquote + "(" + fwsp + qcontent + ")*" + fwsp + dquote;
081                // RFC 2822 3.2.6 Miscellaneous tokens
082                final String word = "((" + atom + ")|(" + quotedString + "))";
083                final String phrase = word + "+"; // one or more words.
084                // RFC 1035 tokens for domain names:
085                final String letter = "[a-zA-Z]";
086                final String letDig = "[a-zA-Z0-9]";
087                final String letDigHyp = "[a-zA-Z0-9-]";
088                final String rfcLabel = letDig + "(" + letDigHyp + "{0,61}" + letDig + ")?";
089                final String rfc1035DomainName = rfcLabel + "(\\." + rfcLabel + ")*\\." + letter + "{2,6}";
090                // RFC 2822 3.4 Address specification
091                // domain text - non white space controls and the rest of ASCII chars not including [, ], or \:
092                final String dtext = "[" + noWsCtl + "\\x21-\\x5A\\x5E-\\x7E]";
093                final String dcontent = dtext + "|" + quotedPair;
094                final String domainLiteral = "\\[" + "(" + fwsp + dcontent + "+)*" + fwsp + "\\]";
095                final String rfc2822Domain = "(" + dotAtom + "|" + domainLiteral + ")";
096                final String domain = parameterObject.isAllowDomainLiterals() ? rfc2822Domain : rfc1035DomainName;
097                final String localPart = "((" + dotAtom + ")|(" + quotedString + "))";
098                final String addrSpec = localPart + "@" + domain;
099                final String angleAddr = "<" + addrSpec + ">";
100                final String nameAddr = "(" + phrase + ")?" + fwsp + angleAddr;
101                final String mailbox = nameAddr + "|" + addrSpec;
102                // now compile a pattern for efficient re-use:
103                // if we're allowing quoted identifiers or not:
104                final String patternString = parameterObject.isAllowQuotedIdentifiers() ? mailbox : addrSpec;
105                return Pattern.compile(patternString);
106        }
107}