• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2009 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 
17 package com.android.email.activity.setup;
18 
19 import android.content.ContentValues;
20 import android.content.Context;
21 import android.content.res.XmlResourceParser;
22 import android.text.Editable;
23 import android.util.Log;
24 import android.widget.EditText;
25 
26 import com.android.email.R;
27 import com.android.email.VendorPolicyLoader;
28 import com.android.email.provider.AccountBackupRestore;
29 import com.android.emailcommon.Logging;
30 import com.android.emailcommon.provider.Account;
31 import com.android.emailcommon.provider.EmailContent.AccountColumns;
32 import com.google.common.annotations.VisibleForTesting;
33 
34 import java.io.Serializable;
35 
36 public class AccountSettingsUtils {
37 
38     /** Pattern to match any part of a domain */
39     private final static String WILD_STRING = "*";
40     /** Will match any, single character */
41     private final static char WILD_CHARACTER = '?';
42     private final static String DOMAIN_SEPARATOR = "\\.";
43 
44     /**
45      * Commits the UI-related settings of an account to the provider.  This is static so that it
46      * can be used by the various account activities.  If the account has never been saved, this
47      * method saves it; otherwise, it just saves the settings.
48      * @param context the context of the caller
49      * @param account the account whose settings will be committed
50      */
commitSettings(Context context, Account account)51     public static void commitSettings(Context context, Account account) {
52         if (!account.isSaved()) {
53             account.save(context);
54         } else {
55             ContentValues cv = getAccountContentValues(account);
56             account.update(context, cv);
57         }
58         // Update the backup (side copy) of the accounts
59         AccountBackupRestore.backup(context);
60     }
61 
62     /**
63      * Returns a set of content values to commit account changes (not including the foreign keys
64      * for the two host auth's and policy) to the database.  Does not actually commit anything.
65      */
getAccountContentValues(Account account)66     public static ContentValues getAccountContentValues(Account account) {
67         ContentValues cv = new ContentValues();
68         cv.put(AccountColumns.IS_DEFAULT, account.mIsDefault);
69         cv.put(AccountColumns.DISPLAY_NAME, account.getDisplayName());
70         cv.put(AccountColumns.SENDER_NAME, account.getSenderName());
71         cv.put(AccountColumns.SIGNATURE, account.getSignature());
72         cv.put(AccountColumns.SYNC_INTERVAL, account.mSyncInterval);
73         cv.put(AccountColumns.RINGTONE_URI, account.mRingtoneUri);
74         cv.put(AccountColumns.FLAGS, account.mFlags);
75         cv.put(AccountColumns.SYNC_LOOKBACK, account.mSyncLookback);
76         cv.put(AccountColumns.SECURITY_SYNC_KEY, account.mSecuritySyncKey);
77         return cv;
78     }
79 
80     /**
81      * Search the list of known Email providers looking for one that matches the user's email
82      * domain.  We check for vendor supplied values first, then we look in providers_product.xml,
83      * and finally by the entries in platform providers.xml.  This provides a nominal override
84      * capability.
85      *
86      * A match is defined as any provider entry for which the "domain" attribute matches.
87      *
88      * @param domain The domain portion of the user's email address
89      * @return suitable Provider definition, or null if no match found
90      */
findProviderForDomain(Context context, String domain)91     public static Provider findProviderForDomain(Context context, String domain) {
92         Provider p = VendorPolicyLoader.getInstance(context).findProviderForDomain(domain);
93         if (p == null) {
94             p = findProviderForDomain(context, domain, R.xml.providers_product);
95         }
96         if (p == null) {
97             p = findProviderForDomain(context, domain, R.xml.providers);
98         }
99         return p;
100     }
101 
102     /**
103      * Search a single resource containing known Email provider definitions.
104      *
105      * @param domain The domain portion of the user's email address
106      * @param resourceId Id of the provider resource to scan
107      * @return suitable Provider definition, or null if no match found
108      */
findProviderForDomain( Context context, String domain, int resourceId)109     /*package*/ static Provider findProviderForDomain(
110             Context context, String domain, int resourceId) {
111         try {
112             XmlResourceParser xml = context.getResources().getXml(resourceId);
113             int xmlEventType;
114             Provider provider = null;
115             while ((xmlEventType = xml.next()) != XmlResourceParser.END_DOCUMENT) {
116                 if (xmlEventType == XmlResourceParser.START_TAG
117                         && "provider".equals(xml.getName())) {
118                     String providerDomain = getXmlAttribute(context, xml, "domain");
119                     try {
120                         if (matchProvider(domain, providerDomain)) {
121                             provider = new Provider();
122                             provider.id = getXmlAttribute(context, xml, "id");
123                             provider.label = getXmlAttribute(context, xml, "label");
124                             provider.domain = domain.toLowerCase();
125                             provider.note = getXmlAttribute(context, xml, "note");
126                         }
127                     } catch (IllegalArgumentException e) {
128                         Log.w(Logging.LOG_TAG, "providers line: " + xml.getLineNumber() +
129                                 "; Domain contains multiple globals");
130                     }
131                 }
132                 else if (xmlEventType == XmlResourceParser.START_TAG
133                         && "incoming".equals(xml.getName())
134                         && provider != null) {
135                     provider.incomingUriTemplate = getXmlAttribute(context, xml, "uri");
136                     provider.incomingUsernameTemplate = getXmlAttribute(context, xml, "username");
137                 }
138                 else if (xmlEventType == XmlResourceParser.START_TAG
139                         && "outgoing".equals(xml.getName())
140                         && provider != null) {
141                     provider.outgoingUriTemplate = getXmlAttribute(context, xml, "uri");
142                     provider.outgoingUsernameTemplate = getXmlAttribute(context, xml, "username");
143                 }
144                 else if (xmlEventType == XmlResourceParser.END_TAG
145                         && "provider".equals(xml.getName())
146                         && provider != null) {
147                     return provider;
148                 }
149             }
150         }
151         catch (Exception e) {
152             Log.e(Logging.LOG_TAG, "Error while trying to load provider settings.", e);
153         }
154         return null;
155     }
156 
157     /**
158      * Returns true if the string <code>s1</code> matches the string <code>s2</code>. The string
159      * <code>s2</code> may contain any number of wildcards -- a '?' character -- and/or asterisk
160      * characters -- '*'. Wildcards match any single character, while the asterisk matches a domain
161      * part (i.e. substring demarcated by a period, '.')
162      */
163     @VisibleForTesting
matchProvider(String testDomain, String providerDomain)164     static boolean matchProvider(String testDomain, String providerDomain) {
165         String[] testParts = testDomain.split(DOMAIN_SEPARATOR);
166         String[] providerParts = providerDomain.split(DOMAIN_SEPARATOR);
167         if (testParts.length != providerParts.length) {
168             return false;
169         }
170         for (int i = 0; i < testParts.length; i++) {
171             String testPart = testParts[i].toLowerCase();
172             String providerPart = providerParts[i].toLowerCase();
173             if (!providerPart.equals(WILD_STRING) &&
174                     !matchWithWildcards(testPart, providerPart)) {
175                 return false;
176             }
177         }
178         return true;
179     }
180 
matchWithWildcards(String testPart, String providerPart)181     private static boolean matchWithWildcards(String testPart, String providerPart) {
182         int providerLength = providerPart.length();
183         if (testPart.length() != providerLength){
184             return false;
185         }
186         for (int i = 0; i < providerLength; i++) {
187             char testChar = testPart.charAt(i);
188             char providerChar = providerPart.charAt(i);
189             if (testChar != providerChar && providerChar != WILD_CHARACTER) {
190                 return false;
191             }
192         }
193         return true;
194     }
195 
196     /**
197      * Attempts to get the given attribute as a String resource first, and if it fails
198      * returns the attribute as a simple String value.
199      * @param xml
200      * @param name
201      * @return the requested resource
202      */
getXmlAttribute(Context context, XmlResourceParser xml, String name)203     private static String getXmlAttribute(Context context, XmlResourceParser xml, String name) {
204         int resId = xml.getAttributeResourceValue(null, name, 0);
205         if (resId == 0) {
206             return xml.getAttributeValue(null, name);
207         }
208         else {
209             return context.getString(resId);
210         }
211     }
212 
213     public static class Provider implements Serializable {
214         private static final long serialVersionUID = 8511656164616538989L;
215 
216         public String id;
217         public String label;
218         public String domain;
219         public String incomingUriTemplate;
220         public String incomingUsernameTemplate;
221         public String outgoingUriTemplate;
222         public String outgoingUsernameTemplate;
223         public String incomingUri;
224         public String incomingUsername;
225         public String outgoingUri;
226         public String outgoingUsername;
227         public String note;
228 
229         /**
230          * Expands templates in all of the  provider fields that support them. Currently,
231          * templates are used in 4 fields -- incoming and outgoing URI and user name.
232          * @param email user-specified data used to replace template values
233          */
expandTemplates(String email)234         public void expandTemplates(String email) {
235             String[] emailParts = email.split("@");
236             String user = emailParts[0];
237 
238             incomingUri = expandTemplate(incomingUriTemplate, email, user);
239             incomingUsername = expandTemplate(incomingUsernameTemplate, email, user);
240             outgoingUri = expandTemplate(outgoingUriTemplate, email, user);
241             outgoingUsername = expandTemplate(outgoingUsernameTemplate, email, user);
242         }
243 
244         /**
245          * Replaces all parameterized values in the given template. The values replaced are
246          * $domain, $user and $email.
247          */
expandTemplate(String template, String email, String user)248         private String expandTemplate(String template, String email, String user) {
249             String returnString = template;
250             returnString = returnString.replaceAll("\\$email", email);
251             returnString = returnString.replaceAll("\\$user", user);
252             returnString = returnString.replaceAll("\\$domain", domain);
253             return returnString;
254         }
255     }
256 
257     /**
258      * Infer potential email server addresses from domain names
259      *
260      * Incoming: Prepend "imap" or "pop3" to domain, unless "pop", "pop3",
261      *          "imap", or "mail" are found.
262      * Outgoing: Prepend "smtp" if "pop", "pop3", "imap" are found.
263      *          Leave "mail" as-is.
264      * TBD: Are there any useful defaults for exchange?
265      *
266      * @param server name as we know it so far
267      * @param incoming "pop3" or "imap" (or null)
268      * @param outgoing "smtp" or null
269      * @return the post-processed name for use in the UI
270      */
inferServerName(String server, String incoming, String outgoing)271     public static String inferServerName(String server, String incoming, String outgoing) {
272         // Default values cause entire string to be kept, with prepended server string
273         int keepFirstChar = 0;
274         int firstDotIndex = server.indexOf('.');
275         if (firstDotIndex != -1) {
276             // look at first word and decide what to do
277             String firstWord = server.substring(0, firstDotIndex).toLowerCase();
278             boolean isImapOrPop = "imap".equals(firstWord)
279                     || "pop3".equals(firstWord) || "pop".equals(firstWord);
280             boolean isMail = "mail".equals(firstWord);
281             // Now decide what to do
282             if (incoming != null) {
283                 // For incoming, we leave imap/pop/pop3/mail alone, or prepend incoming
284                 if (isImapOrPop || isMail) {
285                     return server;
286                 }
287             } else {
288                 // For outgoing, replace imap/pop/pop3 with outgoing, leave mail alone, or
289                 // prepend outgoing
290                 if (isImapOrPop) {
291                     keepFirstChar = firstDotIndex + 1;
292                 } else if (isMail) {
293                     return server;
294                 } else {
295                     // prepend
296                 }
297             }
298         }
299         return ((incoming != null) ? incoming : outgoing) + '.' + server.substring(keepFirstChar);
300     }
301 
302     /**
303      * Helper to set error status on password fields that have leading or trailing spaces
304      */
checkPasswordSpaces(Context context, EditText passwordField)305     public static void checkPasswordSpaces(Context context, EditText passwordField) {
306         Editable password = passwordField.getText();
307         int length = password.length();
308         if (length > 0) {
309             if (password.charAt(0) == ' ' || password.charAt(length-1) == ' ') {
310                 passwordField.setError(context.getString(R.string.account_password_spaces_error));
311             }
312         }
313     }
314 
315 }
316