Initial checkin of AOSP Messaging app.
b/23110861 Change-Id: I9aa980d7569247d6b2ca78f5dcb4502e1eaadb8a
This commit is contained in:
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright (C) 2015 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.messaging.util;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
|
||||
/**
|
||||
* Parsing the email address
|
||||
*/
|
||||
public final class EmailAddress {
|
||||
private static final CharMatcher ANY_WHITESPACE = CharMatcher.anyOf(
|
||||
" \t\n\r\f\u000B\u0085\u2028\u2029\u200D\uFFEF\uFFFD\uFFFE\uFFFF");
|
||||
private static final CharMatcher EMAIL_ALLOWED_CHARS = CharMatcher.inRange((char) 0, (char) 31)
|
||||
.or(CharMatcher.is((char) 127))
|
||||
.or(CharMatcher.anyOf(" @,:<>"))
|
||||
.negate();
|
||||
|
||||
/**
|
||||
* Helper method that checks whether the input text is valid email address.
|
||||
* TODO: This creates a new EmailAddress object each time
|
||||
* Need to make it more lightweight by pulling out the validation code into a static method.
|
||||
*/
|
||||
public static boolean isValidEmail(final String emailText) {
|
||||
return new EmailAddress(emailText).isValid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the specified email address. Internationalized addresses are treated as invalid.
|
||||
*
|
||||
* @param emailString A string representing just an email address. It should
|
||||
* not contain any other tokens. <code>"Name<foo@example.org>"</code> won't be valid.
|
||||
*/
|
||||
public EmailAddress(final String emailString) {
|
||||
this(emailString, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the specified email address.
|
||||
*
|
||||
* @param emailString A string representing just an email address. It should
|
||||
* not contain any other tokens. <code>"Name<foo@example.org>"</code> won't be valid.
|
||||
* @param i18n Accept an internationalized address if it is true.
|
||||
*/
|
||||
public EmailAddress(final String emailString, final boolean i18n) {
|
||||
allowI18n = i18n;
|
||||
valid = parseEmail(emailString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the specified email address. Internationalized addresses are treated as invalid.
|
||||
*
|
||||
* @param user A string representing the username in the email prior to the '@' symbol
|
||||
* @param host A string representing the host following the '@' symbol
|
||||
*/
|
||||
public EmailAddress(final String user, final String host) {
|
||||
this(user, host, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the specified email address.
|
||||
*
|
||||
* @param user A string representing the username in the email prior to the '@' symbol
|
||||
* @param host A string representing the host following the '@' symbol
|
||||
* @param i18n Accept an internationalized address if it is true.
|
||||
*/
|
||||
public EmailAddress(final String user, final String host, final boolean i18n) {
|
||||
allowI18n = i18n;
|
||||
this.user = user;
|
||||
setHost(host);
|
||||
}
|
||||
|
||||
protected boolean parseEmail(final String emailString) {
|
||||
// check for null
|
||||
if (emailString == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for an '@' character. Get the last one, in case the local part is
|
||||
// quoted. See http://b/1944742.
|
||||
final int atIndex = emailString.lastIndexOf('@');
|
||||
if ((atIndex <= 0) || // no '@' character in the email address
|
||||
// or @ on the first position
|
||||
(atIndex == (emailString.length() - 1))) { // last character, no host
|
||||
return false;
|
||||
}
|
||||
|
||||
user = emailString.substring(0, atIndex);
|
||||
host = emailString.substring(atIndex + 1);
|
||||
|
||||
return isValidInternal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return user + "@" + host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the email address is valid, conforming to current RFC2821 and
|
||||
* RFC2822 guidelines (although some iffy characters, like ! and ;, are
|
||||
* allowed because they are not technically prohibited in the RFC)
|
||||
*/
|
||||
private boolean isValidInternal() {
|
||||
if ((user == null) || (host == null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((user.length() == 0) || (host.length() == 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check for white space in the host
|
||||
if (ANY_WHITESPACE.indexIn(host) >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// ensure the host is above the minimum length
|
||||
if (host.length() < 4) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int firstDot = host.indexOf('.');
|
||||
|
||||
// ensure host contains at least one dot
|
||||
if (firstDot == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if the host contains two continuous dots.
|
||||
if (host.indexOf("..") >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if the first host char is a dot.
|
||||
if (host.charAt(0) == '.') {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int secondDot = host.indexOf(".", firstDot + 1);
|
||||
|
||||
// if there's a dot at the end, there needs to be a second dot
|
||||
if (host.charAt(host.length() - 1) == '.' && secondDot == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Host must not have any disallowed characters; allowI18n dictates whether
|
||||
// host must be ASCII.
|
||||
if (!EMAIL_ALLOWED_CHARS.matchesAllOf(host)
|
||||
|| (!allowI18n && !CharMatcher.ASCII.matchesAllOf(host))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (user.startsWith("\"")) {
|
||||
if (!isQuotedUserValid()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// check for white space in the user
|
||||
if (ANY_WHITESPACE.indexIn(user) >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the user cannot contain two continuous dots
|
||||
if (user.indexOf("..") >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// User must not have any disallowed characters; allow I18n dictates whether
|
||||
// user must be ASCII.
|
||||
if (!EMAIL_ALLOWED_CHARS.matchesAllOf(user)
|
||||
|| (!allowI18n && !CharMatcher.ASCII.matchesAllOf(user))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isQuotedUserValid() {
|
||||
final int limit = user.length() - 1;
|
||||
if (limit < 1 || !user.endsWith("\"")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unusual loop bounds (looking only at characters between the outer quotes,
|
||||
// not at either quote character). Plus, i is manipulated within the loop.
|
||||
for (int i = 1; i < limit; ++i) {
|
||||
final char ch = user.charAt(i);
|
||||
if (ch == '"' || ch == 127
|
||||
// No non-whitespace control chars:
|
||||
|| (ch < 32 && !ANY_WHITESPACE.matches(ch))
|
||||
// No non-ASCII chars, unless i18n is in effect:
|
||||
|| (ch >= 128 && !allowI18n)) {
|
||||
return false;
|
||||
} else if (ch == '\\') {
|
||||
if (i + 1 < limit) {
|
||||
++i; // Skip the quoted character
|
||||
} else {
|
||||
// We have a trailing backslash -- so it can't be quoting anything.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object otherObject) {
|
||||
// Do an instance check first as an optimization.
|
||||
if (this == otherObject) {
|
||||
return true;
|
||||
}
|
||||
if (otherObject instanceof EmailAddress) {
|
||||
final EmailAddress otherAddress = (EmailAddress) otherObject;
|
||||
return toString().equals(otherAddress.toString());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
// Arbitrary hash code as a function of both host and user.
|
||||
return toString().hashCode();
|
||||
}
|
||||
|
||||
// accessors
|
||||
public boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
public String getUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
// used to change the host on an email address and rechecks validity
|
||||
|
||||
/**
|
||||
* Changes the host name of the email address and rechecks the address'
|
||||
* validity. Exercise caution when storing EmailAddress instances in
|
||||
* hash-keyed collections. Calling setHost() with a different host name will
|
||||
* change the return value of hashCode.
|
||||
*
|
||||
* @param hostName The new host name of the email address.
|
||||
*/
|
||||
public void setHost(final String hostName) {
|
||||
host = hostName;
|
||||
valid = isValidInternal();
|
||||
}
|
||||
|
||||
protected boolean valid = false;
|
||||
protected String user = null;
|
||||
protected String host = null;
|
||||
protected boolean allowI18n = false;
|
||||
}
|
||||
Reference in New Issue
Block a user