blob: bb7750894d8f9b12d499d19f76ed7629637a9d14 [file] [log] [blame]
/*
* Copyright (C) 2008 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.common;
import android.text.TextUtils;
import android.text.util.Rfc822Token;
import android.text.util.Rfc822Tokenizer;
import android.util.Patterns;
import android.widget.AutoCompleteTextView;
import java.util.regex.Pattern;
/**
* This class works as a Validator for AutoCompleteTextView for
* email addresses. If a token does not appear to be a valid address,
* it is trimmed of characters that cannot legitimately appear in one
* and has the specified domain name added. It is meant for use with
* {@link Rfc822Token} and {@link Rfc822Tokenizer}.
*
* @deprecated In the future make sure we don't quietly alter the user's
* text in ways they did not intend. Meanwhile, hide this
* class from the public API because it does not even have
* a full understanding of the syntax it claims to correct.
* @hide
*/
@Deprecated
public class Rfc822Validator implements AutoCompleteTextView.Validator {
/**
* Expression that matches the local part of an email address.
* This expression does not follow the constraints of the RFC towards the dots, because the
* de facto standard is to allow them anywhere.
*
* It is however a simplification and it will not validate the double-quote syntax.
*/
private static final String EMAIL_ADDRESS_LOCALPART_REGEXP =
"((?!\\s)[\\.\\w!#$%&'*+\\-/=?^`{|}~\u0080-\uFFFE])+";
/**
* Alias of characters that can be used in IRI, as per RFC 3987.
*/
private static final String GOOD_IRI_CHAR = Patterns.GOOD_IRI_CHAR;
/**
* Regular expression for a domain label, as per RFC 3490.
* Its total length must not exceed 63 octets, according to RFC 5890.
*/
private static final String LABEL_REGEXP =
"([" + GOOD_IRI_CHAR + "][" + GOOD_IRI_CHAR + "\\-]{0,61})?[" + GOOD_IRI_CHAR + "]";
/**
* Expression that matches a domain name, including international domain names in Punycode or
* Unicode.
*/
private static final String DOMAIN_REGEXP =
"("+ LABEL_REGEXP + "\\.)+" // Subdomains and domain
// Top-level domain must be at least 2 chars
+ "[" + GOOD_IRI_CHAR + "][" + GOOD_IRI_CHAR + "\\-]{0,61}[" + GOOD_IRI_CHAR + "]";
/**
* Pattern for an email address.
*
* It is similar to {@link android.util.Patterns#EMAIL_ADDRESS}, but also accepts Unicode
* characters.
*/
private static final Pattern EMAIL_ADDRESS_PATTERN =
Pattern.compile(EMAIL_ADDRESS_LOCALPART_REGEXP + "@" + DOMAIN_REGEXP);
private String mDomain;
private boolean mRemoveInvalid = false;
/**
* Constructs a new validator that uses the specified domain name as
* the default when none is specified.
*/
public Rfc822Validator(String domain) {
mDomain = domain;
}
/**
* {@inheritDoc}
*/
public boolean isValid(CharSequence text) {
Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(text);
return tokens.length == 1 &&
EMAIL_ADDRESS_PATTERN.
matcher(tokens[0].getAddress()).matches();
}
/**
* Specify if the validator should remove invalid tokens instead of trying
* to fix them. This can be used to strip results of incorrectly formatted
* tokens.
*
* @param remove true to remove tokens with the wrong format, false to
* attempt to fix them
*/
public void setRemoveInvalid(boolean remove) {
mRemoveInvalid = remove;
}
/**
* @return a string in which all the characters that are illegal for the username
* or the domain name part of the email address have been removed.
*/
private String removeIllegalCharacters(String s) {
StringBuilder result = new StringBuilder();
int length = s.length();
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
/*
* An RFC822 atom can contain any ASCII printing character
* except for periods and any of the following punctuation.
* A local-part can contain multiple atoms, concatenated by
* periods, so do allow periods here.
*/
if (c <= ' ' || c > '~') {
continue;
}
if (c == '(' || c == ')' || c == '<' || c == '>' ||
c == '@' || c == ',' || c == ';' || c == ':' ||
c == '\\' || c == '"' || c == '[' || c == ']') {
continue;
}
result.append(c);
}
return result.toString();
}
/**
* {@inheritDoc}
*/
public CharSequence fixText(CharSequence cs) {
// Return an empty string if the email address only contains spaces, \n or \t
if (TextUtils.getTrimmedLength(cs) == 0) return "";
Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(cs);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < tokens.length; i++) {
String text = tokens[i].getAddress();
if (mRemoveInvalid && !isValid(text)) {
continue;
}
int index = text.indexOf('@');
if (index < 0) {
// append the domain of the account if it exists
if (mDomain != null) {
tokens[i].setAddress(removeIllegalCharacters(text) + "@" + mDomain);
}
} else {
// Otherwise, remove the illegal characters on both sides of the '@'
String fix = removeIllegalCharacters(text.substring(0, index));
if (TextUtils.isEmpty(fix)) {
// if the address is empty after removing invalid chars
// don't use it
continue;
}
String domain = removeIllegalCharacters(text.substring(index + 1));
boolean emptyDomain = domain.length() == 0;
if (!emptyDomain || mDomain != null) {
tokens[i].setAddress(fix + "@" + (!emptyDomain ? domain : mDomain));
}
}
sb.append(tokens[i].toString());
if (i + 1 < tokens.length) {
sb.append(", ");
}
}
return sb;
}
}