1823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang/* 2823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * Copyright (C) 2008 The Android Open Source Project 3823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * 4823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * Licensed under the Apache License, Version 2.0 (the "License"); 5823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * you may not use this file except in compliance with the License. 6823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * You may obtain a copy of the License at 7823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * 8823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * http://www.apache.org/licenses/LICENSE-2.0 9823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * 10823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * Unless required by applicable law or agreed to in writing, software 11823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * distributed under the License is distributed on an "AS IS" BASIS, 12823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * See the License for the specific language governing permissions and 14823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * limitations under the License. 15823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang */ 16823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 17823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangpackage com.android.common; 18823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 19823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangimport android.text.TextUtils; 20823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangimport android.text.util.Rfc822Token; 21823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangimport android.text.util.Rfc822Tokenizer; 224c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décampsimport android.util.Patterns; 23823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangimport android.widget.AutoCompleteTextView; 24823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 25823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangimport java.util.regex.Pattern; 26823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 27823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang/** 28823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * This class works as a Validator for AutoCompleteTextView for 29823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * email addresses. If a token does not appear to be a valid address, 30823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * it is trimmed of characters that cannot legitimately appear in one 31823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * and has the specified domain name added. It is meant for use with 32823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * {@link Rfc822Token} and {@link Rfc822Tokenizer}. 33823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * 34823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * @deprecated In the future make sure we don't quietly alter the user's 35823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * text in ways they did not intend. Meanwhile, hide this 36823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * class from the public API because it does not even have 37823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * a full understanding of the syntax it claims to correct. 38823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * @hide 39823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang */ 40490556a764a879cd0eaff358e90705cc1335c92eErik@Deprecated 41823b6f3516076b92f78c3fc27037d24bb514e653Ying Wangpublic class Rfc822Validator implements AutoCompleteTextView.Validator { 424c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps /** 434c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps * Expression that matches the local part of an email address. 444c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps * This expression does not follow the constraints of the RFC towards the dots, because the 454c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps * de facto standard is to allow them anywhere. 464c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps * 474c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps * It is however a simplification and it will not validate the double-quote syntax. 484c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps */ 494c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps private static final String EMAIL_ADDRESS_LOCALPART_REGEXP = 504c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps "((?!\\s)[\\.\\w!#$%&'*+\\-/=?^`{|}~\u0080-\uFFFE])+"; 514c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps 524c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps /** 534c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps * Alias of characters that can be used in IRI, as per RFC 3987. 544c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps */ 554c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps private static final String GOOD_IRI_CHAR = Patterns.GOOD_IRI_CHAR; 564c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps 574c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps /** 584c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps * Regular expression for a domain label, as per RFC 3490. 594c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps * Its total length must not exceed 63 octets, according to RFC 5890. 604c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps */ 614c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps private static final String LABEL_REGEXP = 624c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps "([" + GOOD_IRI_CHAR + "][" + GOOD_IRI_CHAR + "\\-]{0,61})?[" + GOOD_IRI_CHAR + "]"; 634c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps 644c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps /** 654c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps * Expression that matches a domain name, including international domain names in Punycode or 664c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps * Unicode. 674c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps */ 684c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps private static final String DOMAIN_REGEXP = 694c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps "("+ LABEL_REGEXP + "\\.)+" // Subdomains and domain 704c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps // Top-level domain must be at least 2 chars 714c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps + "[" + GOOD_IRI_CHAR + "][" + GOOD_IRI_CHAR + "\\-]{0,61}[" + GOOD_IRI_CHAR + "]"; 724c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps 734c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps /** 744c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps * Pattern for an email address. 754c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps * 764c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps * It is similar to {@link android.util.Patterns#EMAIL_ADDRESS}, but also accepts Unicode 774c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps * characters. 78823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang */ 79823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang private static final Pattern EMAIL_ADDRESS_PATTERN = 804c384ab3ae7621e602a95aeac6e527a52bb5598cRégis Décamps Pattern.compile(EMAIL_ADDRESS_LOCALPART_REGEXP + "@" + DOMAIN_REGEXP); 81823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 82823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang private String mDomain; 83490556a764a879cd0eaff358e90705cc1335c92eErik private boolean mRemoveInvalid = false; 84823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 85823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang /** 86823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * Constructs a new validator that uses the specified domain name as 87823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * the default when none is specified. 88823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang */ 89823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang public Rfc822Validator(String domain) { 90823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang mDomain = domain; 91823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang } 92823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 93823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang /** 94823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * {@inheritDoc} 95823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang */ 96823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang public boolean isValid(CharSequence text) { 97823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(text); 98823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang return tokens.length == 1 && 99823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang EMAIL_ADDRESS_PATTERN. 100823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang matcher(tokens[0].getAddress()).matches(); 101823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang } 102823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 103823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang /** 104490556a764a879cd0eaff358e90705cc1335c92eErik * Specify if the validator should remove invalid tokens instead of trying 105490556a764a879cd0eaff358e90705cc1335c92eErik * to fix them. This can be used to strip results of incorrectly formatted 106490556a764a879cd0eaff358e90705cc1335c92eErik * tokens. 107490556a764a879cd0eaff358e90705cc1335c92eErik * 108490556a764a879cd0eaff358e90705cc1335c92eErik * @param remove true to remove tokens with the wrong format, false to 109490556a764a879cd0eaff358e90705cc1335c92eErik * attempt to fix them 110490556a764a879cd0eaff358e90705cc1335c92eErik */ 111490556a764a879cd0eaff358e90705cc1335c92eErik public void setRemoveInvalid(boolean remove) { 112490556a764a879cd0eaff358e90705cc1335c92eErik mRemoveInvalid = remove; 113490556a764a879cd0eaff358e90705cc1335c92eErik } 114490556a764a879cd0eaff358e90705cc1335c92eErik 115490556a764a879cd0eaff358e90705cc1335c92eErik /** 116823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * @return a string in which all the characters that are illegal for the username 117823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * or the domain name part of the email address have been removed. 118823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang */ 119823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang private String removeIllegalCharacters(String s) { 120823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang StringBuilder result = new StringBuilder(); 121823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang int length = s.length(); 122823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang for (int i = 0; i < length; i++) { 123823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang char c = s.charAt(i); 124823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 125823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang /* 126823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * An RFC822 atom can contain any ASCII printing character 127823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * except for periods and any of the following punctuation. 128823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * A local-part can contain multiple atoms, concatenated by 129823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * periods, so do allow periods here. 130823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang */ 131823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 132823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang if (c <= ' ' || c > '~') { 133823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang continue; 134823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang } 135823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 136823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang if (c == '(' || c == ')' || c == '<' || c == '>' || 137823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang c == '@' || c == ',' || c == ';' || c == ':' || 138823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang c == '\\' || c == '"' || c == '[' || c == ']') { 139823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang continue; 140823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang } 141823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 142823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang result.append(c); 143823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang } 144823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang return result.toString(); 145823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang } 146823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 147823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang /** 148823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang * {@inheritDoc} 149823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang */ 150823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang public CharSequence fixText(CharSequence cs) { 151823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang // Return an empty string if the email address only contains spaces, \n or \t 152823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang if (TextUtils.getTrimmedLength(cs) == 0) return ""; 153823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 154823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(cs); 155823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang StringBuilder sb = new StringBuilder(); 156823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 157823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang for (int i = 0; i < tokens.length; i++) { 158823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang String text = tokens[i].getAddress(); 159490556a764a879cd0eaff358e90705cc1335c92eErik 160490556a764a879cd0eaff358e90705cc1335c92eErik if (mRemoveInvalid && !isValid(text)) { 161490556a764a879cd0eaff358e90705cc1335c92eErik continue; 162490556a764a879cd0eaff358e90705cc1335c92eErik } 163823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang int index = text.indexOf('@'); 164823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang if (index < 0) { 165490556a764a879cd0eaff358e90705cc1335c92eErik // append the domain of the account if it exists 166490556a764a879cd0eaff358e90705cc1335c92eErik if (mDomain != null) { 167490556a764a879cd0eaff358e90705cc1335c92eErik tokens[i].setAddress(removeIllegalCharacters(text) + "@" + mDomain); 168490556a764a879cd0eaff358e90705cc1335c92eErik } 169823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang } else { 170823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang // Otherwise, remove the illegal characters on both sides of the '@' 171823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang String fix = removeIllegalCharacters(text.substring(0, index)); 172490556a764a879cd0eaff358e90705cc1335c92eErik if (TextUtils.isEmpty(fix)) { 173490556a764a879cd0eaff358e90705cc1335c92eErik // if the address is empty after removing invalid chars 174490556a764a879cd0eaff358e90705cc1335c92eErik // don't use it 175490556a764a879cd0eaff358e90705cc1335c92eErik continue; 176490556a764a879cd0eaff358e90705cc1335c92eErik } 177823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang String domain = removeIllegalCharacters(text.substring(index + 1)); 178490556a764a879cd0eaff358e90705cc1335c92eErik boolean emptyDomain = domain.length() == 0; 179490556a764a879cd0eaff358e90705cc1335c92eErik if (!emptyDomain || mDomain != null) { 180490556a764a879cd0eaff358e90705cc1335c92eErik tokens[i].setAddress(fix + "@" + (!emptyDomain ? domain : mDomain)); 181490556a764a879cd0eaff358e90705cc1335c92eErik } 182823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang } 183823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 184823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang sb.append(tokens[i].toString()); 185823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang if (i + 1 < tokens.length) { 186823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang sb.append(", "); 187823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang } 188823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang } 189823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang 190823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang return sb; 191823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang } 192823b6f3516076b92f78c3fc27037d24bb514e653Ying Wang} 193