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