1d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/*
2d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Copyright (C) 2015 The Android Open Source Project
3d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
4d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Licensed under the Apache License, Version 2.0 (the "License");
5d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * you may not use this file except in compliance with the License.
6d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * You may obtain a copy of the License at
7d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
8d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *      http://www.apache.org/licenses/LICENSE-2.0
9d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd *
10d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Unless required by applicable law or agreed to in writing, software
11d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * distributed under the License is distributed on an "AS IS" BASIS,
12d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * See the License for the specific language governing permissions and
14d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * limitations under the License.
15d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
16d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
17d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpackage com.android.messaging.util;
18d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
19d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddimport com.google.common.base.CharMatcher;
20d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
21d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd/**
22d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd * Parsing the email address
23d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd */
24d3b009ae55651f1e60950342468e3c37fdeb0796Mike Doddpublic final class EmailAddress {
25d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final CharMatcher ANY_WHITESPACE = CharMatcher.anyOf(
26d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            " \t\n\r\f\u000B\u0085\u2028\u2029\u200D\uFFEF\uFFFD\uFFFE\uFFFF");
27d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private static final CharMatcher EMAIL_ALLOWED_CHARS = CharMatcher.inRange((char) 0, (char) 31)
28d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            .or(CharMatcher.is((char) 127))
29d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            .or(CharMatcher.anyOf(" @,:<>"))
30d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            .negate();
31d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
32d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
33d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Helper method that checks whether the input text is valid email address.
34d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * TODO: This creates a new EmailAddress object each time
35d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Need to make it more lightweight by pulling out the validation code into a static method.
36d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
37d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public static boolean isValidEmail(final String emailText) {
38d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return new EmailAddress(emailText).isValid();
39d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
40d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
41d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
42d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Parses the specified email address. Internationalized addresses are treated as invalid.
43d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
44d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param emailString A string representing just an email address. It should
45d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * not contain any other tokens. <code>"Name&lt;foo@example.org>"</code> won't be valid.
46d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
47d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public EmailAddress(final String emailString) {
48d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        this(emailString, false);
49d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
50d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
51d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
52d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Parses the specified email address.
53d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
54d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param emailString A string representing just an email address. It should
55d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * not contain any other tokens. <code>"Name&lt;foo@example.org>"</code> won't be valid.
56d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param i18n Accept an internationalized address if it is true.
57d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
58d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public EmailAddress(final String emailString, final boolean i18n) {
59d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        allowI18n = i18n;
60d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        valid = parseEmail(emailString);
61d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
62d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
63d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
64d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Parses the specified email address. Internationalized addresses are treated as invalid.
65d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
66d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param user A string representing the username in the email prior to the '@' symbol
67d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param host A string representing the host following the '@' symbol
68d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
69d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public EmailAddress(final String user, final String host) {
70d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        this(user, host, false);
71d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
72d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
73d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
74d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Parses the specified email address.
75d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
76d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param user A string representing the username in the email prior to the '@' symbol
77d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param host A string representing the host following the '@' symbol
78d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param i18n Accept an internationalized address if it is true.
79d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
80d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public EmailAddress(final String user, final String host, final boolean i18n) {
81d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        allowI18n = i18n;
82d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        this.user = user;
83d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        setHost(host);
84d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
85d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
86d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected boolean parseEmail(final String emailString) {
87d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // check for null
88d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (emailString == null) {
89d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
90d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
91d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
92d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // Check for an '@' character. Get the last one, in case the local part is
93d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // quoted. See http://b/1944742.
94d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int atIndex = emailString.lastIndexOf('@');
95d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if ((atIndex <= 0) || // no '@' character in the email address
96d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                              // or @ on the first position
97d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                (atIndex == (emailString.length() - 1))) { // last character, no host
98d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
99d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
100d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
101d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        user = emailString.substring(0, atIndex);
102d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        host = emailString.substring(atIndex + 1);
103d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
104d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return isValidInternal();
105d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
106d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
107d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @Override
108d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public String toString() {
109d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return user + "@" + host;
110d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
111d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
112d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
113d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Ensure the email address is valid, conforming to current RFC2821 and
114d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * RFC2822 guidelines (although some iffy characters, like ! and ;, are
115d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * allowed because they are not technically prohibited in the RFC)
116d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
117d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private boolean isValidInternal() {
118d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if ((user == null) || (host == null)) {
119d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
120d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
121d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
122d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if ((user.length() == 0) || (host.length() == 0)) {
123d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
124d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
125d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
126d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // check for white space in the host
127d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (ANY_WHITESPACE.indexIn(host) >= 0) {
128d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
129d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
130d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
131d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // ensure the host is above the minimum length
132d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (host.length() < 4) {
133d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
134d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
135d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
136d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int firstDot = host.indexOf('.');
137d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
138d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // ensure host contains at least one dot
139d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (firstDot == -1) {
140d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
141d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
142d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
143d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // check if the host contains two continuous dots.
144d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (host.indexOf("..") >= 0) {
145d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
146d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
147d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
148d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // check if the first host char is a dot.
149d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (host.charAt(0) == '.') {
150d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
151d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
152d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
153d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int secondDot = host.indexOf(".", firstDot + 1);
154d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
155d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // if there's a dot at the end, there needs to be a second dot
156d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (host.charAt(host.length() - 1) == '.' && secondDot == -1) {
157d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
158d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
159d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
160d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // Host must not have any disallowed characters; allowI18n dictates whether
161d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // host must be ASCII.
162d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (!EMAIL_ALLOWED_CHARS.matchesAllOf(host)
163d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                || (!allowI18n && !CharMatcher.ASCII.matchesAllOf(host))) {
164d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
165d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
166d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
167d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (user.startsWith("\"")) {
168d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (!isQuotedUserValid()) {
169d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return false;
170d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
171d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        } else {
172d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // check for white space in the user
173d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (ANY_WHITESPACE.indexIn(user) >= 0) {
174d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return false;
175d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
176d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
177d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // the user cannot contain two continuous dots
178d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (user.indexOf("..") >= 0) {
179d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return false;
180d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
181d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
182d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // User must not have any disallowed characters; allow I18n dictates whether
183d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            // user must be ASCII.
184d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (!EMAIL_ALLOWED_CHARS.matchesAllOf(user)
185d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    || (!allowI18n && !CharMatcher.ASCII.matchesAllOf(user))) {
186d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return false;
187d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
188d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
189d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return true;
190d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
191d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
192d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    private boolean isQuotedUserValid() {
193d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        final int limit = user.length() - 1;
194d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (limit < 1 || !user.endsWith("\"")) {
195d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return false;
196d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
197d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
198d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // Unusual loop bounds (looking only at characters between the outer quotes,
199d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // not at either quote character). Plus, i is manipulated within the loop.
200d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        for (int i = 1; i < limit; ++i) {
201d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final char ch = user.charAt(i);
202d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            if (ch == '"' || ch == 127
203d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // No non-whitespace control chars:
204d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    || (ch < 32 && !ANY_WHITESPACE.matches(ch))
205d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // No non-ASCII chars, unless i18n is in effect:
206d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    || (ch >= 128 && !allowI18n)) {
207d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                return false;
208d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            } else if (ch == '\\') {
209d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                if (i + 1 < limit) {
210d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    ++i; // Skip the quoted character
211d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                } else {
212d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    // We have a trailing backslash -- so it can't be quoting anything.
213d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                    return false;
214d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd                }
215d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            }
216d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
217d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
218d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return true;
219d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
220d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
221d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @Override
222d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public boolean equals(final Object otherObject) {
223d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // Do an instance check first as an optimization.
224d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (this == otherObject) {
225d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return true;
226d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
227d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        if (otherObject instanceof EmailAddress) {
228d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            final EmailAddress otherAddress = (EmailAddress) otherObject;
229d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd            return toString().equals(otherAddress.toString());
230d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        }
231d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return false;
232d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
233d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
234d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    @Override
235d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public int hashCode() {
236d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        // Arbitrary hash code as a function of both host and user.
237d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return toString().hashCode();
238d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
239d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
240d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    // accessors
241d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public boolean isValid() {
242d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return valid;
243d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
244d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
245d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public String getUser() {
246d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return user;
247d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
248d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
249d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public String getHost() {
250d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        return host;
251d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
252d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
253d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    // used to change the host on an email address and rechecks validity
254d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
255d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    /**
256d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * Changes the host name of the email address and rechecks the address'
257d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * validity. Exercise caution when storing EmailAddress instances in
258d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * hash-keyed collections. Calling setHost() with a different host name will
259d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * change the return value of hashCode.
260d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     *
261d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     * @param hostName The new host name of the email address.
262d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd     */
263d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    public void setHost(final String hostName) {
264d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        host = hostName;
265d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd        valid = isValidInternal();
266d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    }
267d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd
268d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected boolean valid = false;
269d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected String user = null;
270d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected String host = null;
271d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd    protected boolean allowI18n = false;
272d3b009ae55651f1e60950342468e3c37fdeb0796Mike Dodd}
273