1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.common; 18 19import android.text.TextUtils; 20import android.text.util.Rfc822Token; 21import android.text.util.Rfc822Tokenizer; 22import android.widget.AutoCompleteTextView; 23 24import java.util.regex.Pattern; 25 26/** 27 * This class works as a Validator for AutoCompleteTextView for 28 * email addresses. If a token does not appear to be a valid address, 29 * it is trimmed of characters that cannot legitimately appear in one 30 * and has the specified domain name added. It is meant for use with 31 * {@link Rfc822Token} and {@link Rfc822Tokenizer}. 32 * 33 * @deprecated In the future make sure we don't quietly alter the user's 34 * text in ways they did not intend. Meanwhile, hide this 35 * class from the public API because it does not even have 36 * a full understanding of the syntax it claims to correct. 37 * @hide 38 */ 39@Deprecated 40public class Rfc822Validator implements AutoCompleteTextView.Validator { 41 /* 42 * Regex.EMAIL_ADDRESS_PATTERN hardcodes the TLD that we accept, but we 43 * want to make sure we will keep accepting email addresses with TLD's 44 * that don't exist at the time of this writing, so this regexp relaxes 45 * that constraint by accepting any kind of top level domain, not just 46 * ".com", ".fr", etc... 47 */ 48 private static final Pattern EMAIL_ADDRESS_PATTERN = 49 Pattern.compile("[^\\s@]+@([^\\s@\\.]+\\.)+[a-zA-z][a-zA-Z][a-zA-Z]*"); 50 51 private String mDomain; 52 private boolean mRemoveInvalid = false; 53 54 /** 55 * Constructs a new validator that uses the specified domain name as 56 * the default when none is specified. 57 */ 58 public Rfc822Validator(String domain) { 59 mDomain = domain; 60 } 61 62 /** 63 * {@inheritDoc} 64 */ 65 public boolean isValid(CharSequence text) { 66 Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(text); 67 68 return tokens.length == 1 && 69 EMAIL_ADDRESS_PATTERN. 70 matcher(tokens[0].getAddress()).matches(); 71 } 72 73 /** 74 * Specify if the validator should remove invalid tokens instead of trying 75 * to fix them. This can be used to strip results of incorrectly formatted 76 * tokens. 77 * 78 * @param remove true to remove tokens with the wrong format, false to 79 * attempt to fix them 80 */ 81 public void setRemoveInvalid(boolean remove) { 82 mRemoveInvalid = remove; 83 } 84 85 /** 86 * @return a string in which all the characters that are illegal for the username 87 * or the domain name part of the email address have been removed. 88 */ 89 private String removeIllegalCharacters(String s) { 90 StringBuilder result = new StringBuilder(); 91 int length = s.length(); 92 for (int i = 0; i < length; i++) { 93 char c = s.charAt(i); 94 95 /* 96 * An RFC822 atom can contain any ASCII printing character 97 * except for periods and any of the following punctuation. 98 * A local-part can contain multiple atoms, concatenated by 99 * periods, so do allow periods here. 100 */ 101 102 if (c <= ' ' || c > '~') { 103 continue; 104 } 105 106 if (c == '(' || c == ')' || c == '<' || c == '>' || 107 c == '@' || c == ',' || c == ';' || c == ':' || 108 c == '\\' || c == '"' || c == '[' || c == ']') { 109 continue; 110 } 111 112 result.append(c); 113 } 114 return result.toString(); 115 } 116 117 /** 118 * {@inheritDoc} 119 */ 120 public CharSequence fixText(CharSequence cs) { 121 // Return an empty string if the email address only contains spaces, \n or \t 122 if (TextUtils.getTrimmedLength(cs) == 0) return ""; 123 124 Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(cs); 125 StringBuilder sb = new StringBuilder(); 126 127 for (int i = 0; i < tokens.length; i++) { 128 String text = tokens[i].getAddress(); 129 130 if (mRemoveInvalid && !isValid(text)) { 131 continue; 132 } 133 int index = text.indexOf('@'); 134 if (index < 0) { 135 // append the domain of the account if it exists 136 if (mDomain != null) { 137 tokens[i].setAddress(removeIllegalCharacters(text) + "@" + mDomain); 138 } 139 } else { 140 // Otherwise, remove the illegal characters on both sides of the '@' 141 String fix = removeIllegalCharacters(text.substring(0, index)); 142 if (TextUtils.isEmpty(fix)) { 143 // if the address is empty after removing invalid chars 144 // don't use it 145 continue; 146 } 147 String domain = removeIllegalCharacters(text.substring(index + 1)); 148 boolean emptyDomain = domain.length() == 0; 149 if (!emptyDomain || mDomain != null) { 150 tokens[i].setAddress(fix + "@" + (!emptyDomain ? domain : mDomain)); 151 } 152 } 153 154 sb.append(tokens[i].toString()); 155 if (i + 1 < tokens.length) { 156 sb.append(", "); 157 } 158 } 159 160 return sb; 161 } 162} 163