summaryrefslogtreecommitdiffstats
path: root/provider_src/com/android/email/activity/setup/AccountSettingsUtils.java
diff options
context:
space:
mode:
Diffstat (limited to 'provider_src/com/android/email/activity/setup/AccountSettingsUtils.java')
-rw-r--r--provider_src/com/android/email/activity/setup/AccountSettingsUtils.java433
1 files changed, 433 insertions, 0 deletions
diff --git a/provider_src/com/android/email/activity/setup/AccountSettingsUtils.java b/provider_src/com/android/email/activity/setup/AccountSettingsUtils.java
new file mode 100644
index 000000000..dbbd51ee7
--- /dev/null
+++ b/provider_src/com/android/email/activity/setup/AccountSettingsUtils.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright (C) 2009 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.email.activity.setup;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import com.android.email.R;
+import com.android.email.provider.AccountBackupRestore;
+import com.android.emailcommon.Logging;
+import com.android.emailcommon.VendorPolicyLoader;
+import com.android.emailcommon.VendorPolicyLoader.OAuthProvider;
+import com.android.emailcommon.VendorPolicyLoader.Provider;
+import com.android.emailcommon.provider.Account;
+import com.android.emailcommon.provider.EmailContent.AccountColumns;
+import com.android.emailcommon.provider.QuickResponse;
+import com.android.emailcommon.service.PolicyServiceProxy;
+import com.android.emailcommon.utility.Utility;
+import com.android.mail.utils.LogUtils;
+import com.google.common.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AccountSettingsUtils {
+
+ /** Pattern to match any part of a domain */
+ private final static String WILD_STRING = "*";
+ /** Will match any, single character */
+ private final static char WILD_CHARACTER = '?';
+ private final static String DOMAIN_SEPARATOR = "\\.";
+
+ /**
+ * Commits the UI-related settings of an account to the provider. This is static so that it
+ * can be used by the various account activities. If the account has never been saved, this
+ * method saves it; otherwise, it just saves the settings.
+ * @param context the context of the caller
+ * @param account the account whose settings will be committed
+ */
+ public static void commitSettings(Context context, Account account) {
+ if (!account.isSaved()) {
+ account.save(context);
+
+ if (account.mPolicy != null) {
+ // TODO: we need better handling for unsupported policies
+ // For now, just clear the unsupported policies, as the server will (hopefully)
+ // just reject our sync attempts if it's not happy with half-measures
+ if (account.mPolicy.mProtocolPoliciesUnsupported != null) {
+ LogUtils.d(LogUtils.TAG, "Clearing unsupported policies "
+ + account.mPolicy.mProtocolPoliciesUnsupported);
+ account.mPolicy.mProtocolPoliciesUnsupported = null;
+ }
+ PolicyServiceProxy.setAccountPolicy2(context,
+ account.getId(),
+ account.mPolicy,
+ account.mSecuritySyncKey == null ? "" : account.mSecuritySyncKey,
+ false /* notify */);
+ }
+
+ // Set up default quick responses here...
+ String[] defaultQuickResponses =
+ context.getResources().getStringArray(R.array.default_quick_responses);
+ ContentValues cv = new ContentValues();
+ cv.put(QuickResponse.ACCOUNT_KEY, account.mId);
+ ContentResolver resolver = context.getContentResolver();
+ for (String quickResponse: defaultQuickResponses) {
+ // Allow empty entries (some localizations may not want to have the maximum
+ // number)
+ if (!TextUtils.isEmpty(quickResponse)) {
+ cv.put(QuickResponse.TEXT, quickResponse);
+ resolver.insert(QuickResponse.CONTENT_URI, cv);
+ }
+ }
+ } else {
+ ContentValues cv = getAccountContentValues(account);
+ account.update(context, cv);
+ }
+
+ // Update the backup (side copy) of the accounts
+ AccountBackupRestore.backup(context);
+ }
+
+ /**
+ * Returns a set of content values to commit account changes (not including the foreign keys
+ * for the two host auth's and policy) to the database. Does not actually commit anything.
+ */
+ public static ContentValues getAccountContentValues(Account account) {
+ ContentValues cv = new ContentValues();
+ cv.put(AccountColumns.DISPLAY_NAME, account.getDisplayName());
+ cv.put(AccountColumns.SENDER_NAME, account.getSenderName());
+ cv.put(AccountColumns.SIGNATURE, account.getSignature());
+ cv.put(AccountColumns.SYNC_INTERVAL, account.mSyncInterval);
+ cv.put(AccountColumns.FLAGS, account.mFlags);
+ cv.put(AccountColumns.SYNC_LOOKBACK, account.mSyncLookback);
+ cv.put(AccountColumns.SECURITY_SYNC_KEY, account.mSecuritySyncKey);
+ return cv;
+ }
+
+ /**
+ * Create the request to get the authorization code.
+ *
+ * @param context
+ * @param provider The OAuth provider to register with
+ * @param emailAddress Email address to send as a hint to the oauth service.
+ * @return
+ */
+ public static Uri createOAuthRegistrationRequest(final Context context,
+ final OAuthProvider provider, final String emailAddress) {
+ final Uri.Builder b = Uri.parse(provider.authEndpoint).buildUpon();
+ b.appendQueryParameter("response_type", provider.responseType);
+ b.appendQueryParameter("client_id", provider.clientId);
+ b.appendQueryParameter("redirect_uri", provider.redirectUri);
+ b.appendQueryParameter("scope", provider.scope);
+ b.appendQueryParameter("state", provider.state);
+ b.appendQueryParameter("login_hint", emailAddress);
+ return b.build();
+ }
+
+ /**
+ * Search for a single resource containing known oauth provider definitions.
+ *
+ * @param context
+ * @param id String Id of the oauth provider.
+ * @return The OAuthProvider if found, null if not.
+ */
+ public static OAuthProvider findOAuthProvider(final Context context, final String id) {
+ return findOAuthProvider(context, id, R.xml.oauth);
+ }
+
+ public static List<OAuthProvider> getAllOAuthProviders(final Context context) {
+ try {
+ List<OAuthProvider> providers = new ArrayList<OAuthProvider>();
+ final XmlResourceParser xml = context.getResources().getXml(R.xml.oauth);
+ int xmlEventType;
+ OAuthProvider provider = null;
+ while ((xmlEventType = xml.next()) != XmlResourceParser.END_DOCUMENT) {
+ if (xmlEventType == XmlResourceParser.START_TAG
+ && "provider".equals(xml.getName())) {
+ try {
+ provider = new OAuthProvider();
+ provider.id = getXmlAttribute(context, xml, "id");
+ provider.label = getXmlAttribute(context, xml, "label");
+ provider.authEndpoint = getXmlAttribute(context, xml, "auth_endpoint");
+ provider.tokenEndpoint = getXmlAttribute(context, xml, "token_endpoint");
+ provider.refreshEndpoint = getXmlAttribute(context, xml,
+ "refresh_endpoint");
+ provider.responseType = getXmlAttribute(context, xml, "response_type");
+ provider.redirectUri = getXmlAttribute(context, xml, "redirect_uri");
+ provider.scope = getXmlAttribute(context, xml, "scope");
+ provider.state = getXmlAttribute(context, xml, "state");
+ provider.clientId = getXmlAttribute(context, xml, "client_id");
+ provider.clientSecret = getXmlAttribute(context, xml, "client_secret");
+ providers.add(provider);
+ } catch (IllegalArgumentException e) {
+ LogUtils.w(Logging.LOG_TAG, "providers line: " + xml.getLineNumber() +
+ "; Domain contains multiple globals");
+ }
+ }
+ }
+ return providers;
+ } catch (Exception e) {
+ LogUtils.e(Logging.LOG_TAG, "Error while trying to load provider settings.", e);
+ }
+ return null;
+ }
+
+ /**
+ * Search for a single resource containing known oauth provider definitions.
+ *
+ * @param context
+ * @param id String Id of the oauth provider.
+ * @param resourceId ResourceId of the xml file to search.
+ * @return The OAuthProvider if found, null if not.
+ */
+ public static OAuthProvider findOAuthProvider(final Context context, final String id,
+ final int resourceId) {
+ // TODO: Consider adding a way to cache this file during new account setup, so that we
+ // don't need to keep loading the file over and over.
+ // TODO: need a mechanism to get a list of all supported OAuth providers so that we can
+ // offer the user a choice of who to authenticate with.
+ try {
+ final XmlResourceParser xml = context.getResources().getXml(resourceId);
+ int xmlEventType;
+ OAuthProvider provider = null;
+ while ((xmlEventType = xml.next()) != XmlResourceParser.END_DOCUMENT) {
+ if (xmlEventType == XmlResourceParser.START_TAG
+ && "provider".equals(xml.getName())) {
+ String providerId = getXmlAttribute(context, xml, "id");
+ try {
+ if (TextUtils.equals(id, providerId)) {
+ provider = new OAuthProvider();
+ provider.id = id;
+ provider.label = getXmlAttribute(context, xml, "label");
+ provider.authEndpoint = getXmlAttribute(context, xml, "auth_endpoint");
+ provider.tokenEndpoint = getXmlAttribute(context, xml, "token_endpoint");
+ provider.refreshEndpoint = getXmlAttribute(context, xml,
+ "refresh_endpoint");
+ provider.responseType = getXmlAttribute(context, xml, "response_type");
+ provider.redirectUri = getXmlAttribute(context, xml, "redirect_uri");
+ provider.scope = getXmlAttribute(context, xml, "scope");
+ provider.state = getXmlAttribute(context, xml, "state");
+ provider.clientId = getXmlAttribute(context, xml, "client_id");
+ provider.clientSecret = getXmlAttribute(context, xml, "client_secret");
+ return provider;
+ }
+ } catch (IllegalArgumentException e) {
+ LogUtils.w(Logging.LOG_TAG, "providers line: " + xml.getLineNumber() +
+ "; Domain contains multiple globals");
+ }
+ }
+ }
+ } catch (Exception e) {
+ LogUtils.e(Logging.LOG_TAG, "Error while trying to load provider settings.", e);
+ }
+ return null;
+ }
+
+ /**
+ * Search the list of known Email providers looking for one that matches the user's email
+ * domain. We check for vendor supplied values first, then we look in providers_product.xml,
+ * and finally by the entries in platform providers.xml. This provides a nominal override
+ * capability.
+ *
+ * A match is defined as any provider entry for which the "domain" attribute matches.
+ *
+ * @param domain The domain portion of the user's email address
+ * @return suitable Provider definition, or null if no match found
+ */
+ public static Provider findProviderForDomain(Context context, String domain) {
+ Provider p = VendorPolicyLoader.getInstance(context).findProviderForDomain(domain);
+ if (p == null) {
+ p = findProviderForDomain(context, domain, R.xml.providers_product);
+ }
+ if (p == null) {
+ p = findProviderForDomain(context, domain, R.xml.providers);
+ }
+ return p;
+ }
+
+ /**
+ * Search a single resource containing known Email provider definitions.
+ *
+ * @param domain The domain portion of the user's email address
+ * @param resourceId Id of the provider resource to scan
+ * @return suitable Provider definition, or null if no match found
+ */
+ /*package*/ static Provider findProviderForDomain(
+ Context context, String domain, int resourceId) {
+ try {
+ XmlResourceParser xml = context.getResources().getXml(resourceId);
+ int xmlEventType;
+ Provider provider = null;
+ while ((xmlEventType = xml.next()) != XmlResourceParser.END_DOCUMENT) {
+ if (xmlEventType == XmlResourceParser.START_TAG
+ && "provider".equals(xml.getName())) {
+ String providerDomain = getXmlAttribute(context, xml, "domain");
+ try {
+ if (matchProvider(domain, providerDomain)) {
+ provider = new Provider();
+ provider.id = getXmlAttribute(context, xml, "id");
+ provider.label = getXmlAttribute(context, xml, "label");
+ provider.domain = domain.toLowerCase();
+ provider.note = getXmlAttribute(context, xml, "note");
+ // TODO: Maybe this should actually do a lookup of the OAuth provider
+ // here, and keep a pointer to it rather than a textual key.
+ // To do this probably requires caching oauth.xml, otherwise the lookup
+ // is expensive and likely to happen repeatedly.
+ provider.oauth = getXmlAttribute(context, xml, "oauth");
+ }
+ } catch (IllegalArgumentException e) {
+ LogUtils.w(Logging.LOG_TAG, "providers line: " + xml.getLineNumber() +
+ "; Domain contains multiple globals");
+ }
+ }
+ else if (xmlEventType == XmlResourceParser.START_TAG
+ && "incoming".equals(xml.getName())
+ && provider != null) {
+ provider.incomingUriTemplate = getXmlAttribute(context, xml, "uri");
+ provider.incomingUsernameTemplate = getXmlAttribute(context, xml, "username");
+ }
+ else if (xmlEventType == XmlResourceParser.START_TAG
+ && "outgoing".equals(xml.getName())
+ && provider != null) {
+ provider.outgoingUriTemplate = getXmlAttribute(context, xml, "uri");
+ provider.outgoingUsernameTemplate = getXmlAttribute(context, xml, "username");
+ }
+ else if (xmlEventType == XmlResourceParser.START_TAG
+ && "incoming-fallback".equals(xml.getName())
+ && provider != null) {
+ provider.altIncomingUriTemplate = getXmlAttribute(context, xml, "uri");
+ provider.altIncomingUsernameTemplate =
+ getXmlAttribute(context, xml, "username");
+ }
+ else if (xmlEventType == XmlResourceParser.START_TAG
+ && "outgoing-fallback".equals(xml.getName())
+ && provider != null) {
+ provider.altOutgoingUriTemplate = getXmlAttribute(context, xml, "uri");
+ provider.altOutgoingUsernameTemplate =
+ getXmlAttribute(context, xml, "username");
+ }
+ else if (xmlEventType == XmlResourceParser.END_TAG
+ && "provider".equals(xml.getName())
+ && provider != null) {
+ return provider;
+ }
+ }
+ }
+ catch (Exception e) {
+ LogUtils.e(Logging.LOG_TAG, "Error while trying to load provider settings.", e);
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the string <code>s1</code> matches the string <code>s2</code>. The string
+ * <code>s2</code> may contain any number of wildcards -- a '?' character -- and/or asterisk
+ * characters -- '*'. Wildcards match any single character, while the asterisk matches a domain
+ * part (i.e. substring demarcated by a period, '.')
+ */
+ @VisibleForTesting
+ public static boolean matchProvider(String testDomain, String providerDomain) {
+ String[] testParts = testDomain.split(DOMAIN_SEPARATOR);
+ String[] providerParts = providerDomain.split(DOMAIN_SEPARATOR);
+ if (testParts.length != providerParts.length) {
+ return false;
+ }
+ for (int i = 0; i < testParts.length; i++) {
+ String testPart = testParts[i].toLowerCase();
+ String providerPart = providerParts[i].toLowerCase();
+ if (!providerPart.equals(WILD_STRING) &&
+ !matchWithWildcards(testPart, providerPart)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean matchWithWildcards(String testPart, String providerPart) {
+ int providerLength = providerPart.length();
+ if (testPart.length() != providerLength){
+ return false;
+ }
+ for (int i = 0; i < providerLength; i++) {
+ char testChar = testPart.charAt(i);
+ char providerChar = providerPart.charAt(i);
+ if (testChar != providerChar && providerChar != WILD_CHARACTER) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Attempts to get the given attribute as a String resource first, and if it fails
+ * returns the attribute as a simple String value.
+ * @param xml
+ * @param name
+ * @return the requested resource
+ */
+ private static String getXmlAttribute(Context context, XmlResourceParser xml, String name) {
+ int resId = xml.getAttributeResourceValue(null, name, 0);
+ if (resId == 0) {
+ return xml.getAttributeValue(null, name);
+ }
+ else {
+ return context.getString(resId);
+ }
+ }
+
+ /**
+ * Infer potential email server addresses from domain names
+ *
+ * Incoming: Prepend "imap" or "pop3" to domain, unless "pop", "pop3",
+ * "imap", or "mail" are found.
+ * Outgoing: Prepend "smtp" if domain starts with any in the host prefix array
+ *
+ * @param server name as we know it so far
+ * @param incoming "pop3" or "imap" (or null)
+ * @param outgoing "smtp" or null
+ * @return the post-processed name for use in the UI
+ */
+ public static String inferServerName(Context context, String server, String incoming,
+ String outgoing) {
+ // Default values cause entire string to be kept, with prepended server string
+ int keepFirstChar = 0;
+ int firstDotIndex = server.indexOf('.');
+ if (firstDotIndex != -1) {
+ // look at first word and decide what to do
+ String firstWord = server.substring(0, firstDotIndex).toLowerCase();
+ String[] hostPrefixes =
+ context.getResources().getStringArray(R.array.smtp_host_prefixes);
+ boolean canSubstituteSmtp = Utility.arrayContains(hostPrefixes, firstWord);
+ boolean isMail = "mail".equals(firstWord);
+ // Now decide what to do
+ if (incoming != null) {
+ // For incoming, we leave imap/pop/pop3/mail alone, or prepend incoming
+ if (canSubstituteSmtp || isMail) {
+ return server;
+ }
+ } else {
+ // For outgoing, replace imap/pop/pop3 with outgoing, leave mail alone, or
+ // prepend outgoing
+ if (canSubstituteSmtp) {
+ keepFirstChar = firstDotIndex + 1;
+ } else if (isMail) {
+ return server;
+ } else {
+ // prepend
+ }
+ }
+ }
+ return ((incoming != null) ? incoming : outgoing) + '.' + server.substring(keepFirstChar);
+ }
+
+}