Home | History | Annotate | Download | only in provider
      1 /*
      2  * Copyright (C) 2011 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 
     18 package com.android.emailcommon.provider;
     19 
     20 import android.content.ContentValues;
     21 import android.content.Context;
     22 import android.database.Cursor;
     23 import android.net.Uri;
     24 import android.os.Parcel;
     25 import android.os.Parcelable;
     26 import android.text.TextUtils;
     27 
     28 import com.android.emailcommon.provider.EmailContent.HostAuthColumns;
     29 import com.android.emailcommon.utility.SSLUtils;
     30 import com.android.emailcommon.utility.Utility;
     31 
     32 import java.net.URI;
     33 import java.net.URISyntaxException;
     34 
     35 public final class HostAuth extends EmailContent implements HostAuthColumns, Parcelable {
     36     public static final String TABLE_NAME = "HostAuth";
     37     public static Uri CONTENT_URI;
     38 
     39     public static void initHostAuth() {
     40         CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/hostauth");
     41     }
     42 
     43     // These legacy constants should be used in code created prior to Email2
     44     public static final String LEGACY_SCHEME_SMTP = "smtp";
     45 
     46     public static final String SCHEME_TRUST_ALL_CERTS = "trustallcerts";
     47 
     48     public static final int PORT_UNKNOWN = -1;
     49 
     50     public static final int FLAG_NONE         = 0x00;    // No flags
     51     public static final int FLAG_SSL          = 0x01;    // Use SSL
     52     public static final int FLAG_TLS          = 0x02;    // Use TLS
     53     public static final int FLAG_AUTHENTICATE = 0x04;    // Use name/password for authentication
     54     public static final int FLAG_TRUST_ALL    = 0x08;    // Trust all certificates
     55     // Mask of settings directly configurable by the user
     56     public static final int USER_CONFIG_MASK  = 0x0b;
     57 
     58     public String mProtocol;
     59     public String mAddress;
     60     public int mPort;
     61     public int mFlags;
     62     public String mLogin;
     63     public String mPassword;
     64     public String mDomain;
     65     public String mClientCertAlias = null;
     66     // NOTE: The server certificate is NEVER automatically retrieved from EmailProvider
     67     public byte[] mServerCert = null;
     68 
     69     public static final int CONTENT_ID_COLUMN = 0;
     70     public static final int CONTENT_PROTOCOL_COLUMN = 1;
     71     public static final int CONTENT_ADDRESS_COLUMN = 2;
     72     public static final int CONTENT_PORT_COLUMN = 3;
     73     public static final int CONTENT_FLAGS_COLUMN = 4;
     74     public static final int CONTENT_LOGIN_COLUMN = 5;
     75     public static final int CONTENT_PASSWORD_COLUMN = 6;
     76     public static final int CONTENT_DOMAIN_COLUMN = 7;
     77     public static final int CONTENT_CLIENT_CERT_ALIAS_COLUMN = 8;
     78 
     79     public static final String[] CONTENT_PROJECTION = new String[] {
     80         RECORD_ID, HostAuthColumns.PROTOCOL, HostAuthColumns.ADDRESS, HostAuthColumns.PORT,
     81         HostAuthColumns.FLAGS, HostAuthColumns.LOGIN,
     82         HostAuthColumns.PASSWORD, HostAuthColumns.DOMAIN, HostAuthColumns.CLIENT_CERT_ALIAS
     83     };
     84 
     85     /**
     86      * no public constructor since this is a utility class
     87      */
     88     public HostAuth() {
     89         mBaseUri = CONTENT_URI;
     90 
     91         // other defaults policy)
     92         mPort = PORT_UNKNOWN;
     93     }
     94 
     95      /**
     96      * Restore a HostAuth from the database, given its unique id
     97      * @param context
     98      * @param id
     99      * @return the instantiated HostAuth
    100      */
    101     public static HostAuth restoreHostAuthWithId(Context context, long id) {
    102         return EmailContent.restoreContentWithId(context, HostAuth.class,
    103                 HostAuth.CONTENT_URI, HostAuth.CONTENT_PROJECTION, id);
    104     }
    105 
    106 
    107     /**
    108      * Returns the scheme for the specified flags.
    109      */
    110     public static String getSchemeString(String protocol, int flags) {
    111         return getSchemeString(protocol, flags, null);
    112     }
    113 
    114     /**
    115      * Builds a URI scheme name given the parameters for a {@code HostAuth}.
    116      * If a {@code clientAlias} is provided, this indicates that a secure connection must be used.
    117      */
    118     public static String getSchemeString(String protocol, int flags, String clientAlias) {
    119         String security = "";
    120         switch (flags & USER_CONFIG_MASK) {
    121             case FLAG_SSL:
    122                 security = "+ssl+";
    123                 break;
    124             case FLAG_SSL | FLAG_TRUST_ALL:
    125                 security = "+ssl+trustallcerts";
    126                 break;
    127             case FLAG_TLS:
    128                 security = "+tls+";
    129                 break;
    130             case FLAG_TLS | FLAG_TRUST_ALL:
    131                 security = "+tls+trustallcerts";
    132                 break;
    133         }
    134 
    135         if (!TextUtils.isEmpty(clientAlias)) {
    136             if (TextUtils.isEmpty(security)) {
    137                 throw new IllegalArgumentException(
    138                         "Can't specify a certificate alias for a non-secure connection");
    139             }
    140             if (!security.endsWith("+")) {
    141                 security += "+";
    142             }
    143             security += SSLUtils.escapeForSchemeName(clientAlias);
    144         }
    145 
    146         return protocol + security;
    147     }
    148 
    149     /**
    150      * Returns the flags for the specified scheme.
    151      */
    152     public static int getSchemeFlags(String scheme) {
    153         String[] schemeParts = scheme.split("\\+");
    154         int flags = HostAuth.FLAG_NONE;
    155         if (schemeParts.length >= 2) {
    156             String part1 = schemeParts[1];
    157             if ("ssl".equals(part1)) {
    158                 flags |= HostAuth.FLAG_SSL;
    159             } else if ("tls".equals(part1)) {
    160                 flags |= HostAuth.FLAG_TLS;
    161             }
    162             if (schemeParts.length >= 3) {
    163                 String part2 = schemeParts[2];
    164                 if (SCHEME_TRUST_ALL_CERTS.equals(part2)) {
    165                     flags |= HostAuth.FLAG_TRUST_ALL;
    166                 }
    167             }
    168         }
    169         return flags;
    170     }
    171 
    172     @Override
    173     public void restore(Cursor cursor) {
    174         mBaseUri = CONTENT_URI;
    175         mId = cursor.getLong(CONTENT_ID_COLUMN);
    176         mProtocol = cursor.getString(CONTENT_PROTOCOL_COLUMN);
    177         mAddress = cursor.getString(CONTENT_ADDRESS_COLUMN);
    178         mPort = cursor.getInt(CONTENT_PORT_COLUMN);
    179         mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN);
    180         mLogin = cursor.getString(CONTENT_LOGIN_COLUMN);
    181         mPassword = cursor.getString(CONTENT_PASSWORD_COLUMN);
    182         mDomain = cursor.getString(CONTENT_DOMAIN_COLUMN);
    183         mClientCertAlias = cursor.getString(CONTENT_CLIENT_CERT_ALIAS_COLUMN);
    184     }
    185 
    186     @Override
    187     public ContentValues toContentValues() {
    188         ContentValues values = new ContentValues();
    189         values.put(HostAuthColumns.PROTOCOL, mProtocol);
    190         values.put(HostAuthColumns.ADDRESS, mAddress);
    191         values.put(HostAuthColumns.PORT, mPort);
    192         values.put(HostAuthColumns.FLAGS, mFlags);
    193         values.put(HostAuthColumns.LOGIN, mLogin);
    194         values.put(HostAuthColumns.PASSWORD, mPassword);
    195         values.put(HostAuthColumns.DOMAIN, mDomain);
    196         values.put(HostAuthColumns.CLIENT_CERT_ALIAS, mClientCertAlias);
    197         values.put(HostAuthColumns.ACCOUNT_KEY, 0); // Need something to satisfy the DB
    198         return values;
    199     }
    200 
    201     /**
    202      * Sets the user name and password from URI user info string
    203      */
    204     public void setLogin(String userInfo) {
    205         String userName = null;
    206         String userPassword = null;
    207         if (!TextUtils.isEmpty(userInfo)) {
    208             String[] userInfoParts = userInfo.split(":", 2);
    209             userName = userInfoParts[0];
    210             if (userInfoParts.length > 1) {
    211                 userPassword = userInfoParts[1];
    212             }
    213         }
    214         setLogin(userName, userPassword);
    215     }
    216 
    217     /**
    218      * Sets the user name and password
    219      */
    220     public void setLogin(String userName, String userPassword) {
    221         mLogin = userName;
    222         mPassword = userPassword;
    223 
    224         if (mLogin == null) {
    225             mFlags &= ~FLAG_AUTHENTICATE;
    226         } else {
    227             mFlags |= FLAG_AUTHENTICATE;
    228         }
    229     }
    230 
    231     /**
    232      * Returns the login information. [0] is the username and [1] is the password. If
    233      * {@link #FLAG_AUTHENTICATE} is not set, {@code null} is returned.
    234      */
    235     public String[] getLogin() {
    236         if ((mFlags & FLAG_AUTHENTICATE) != 0) {
    237             String trimUser = (mLogin != null) ? mLogin.trim() : "";
    238             String password = (mPassword != null) ? mPassword : "";
    239             return new String[] { trimUser, password };
    240         }
    241         return null;
    242     }
    243 
    244     public void setConnection(String protocol, String address, int port, int flags) {
    245         setConnection(protocol, address, port, flags, null);
    246     }
    247 
    248     /**
    249      * Sets the internal connection parameters based on the specified parameter values.
    250      * @param protocol the mail protocol to use (e.g. "eas", "imap").
    251      * @param address the address of the server
    252      * @param port the port for the connection
    253      * @param flags flags indicating the security and type of the connection
    254      * @param clientCertAlias an optional alias to use if a client user certificate is to be
    255      *     presented during connection establishment. If this is non-empty, it must be the case
    256      *     that flags indicates use of a secure connection
    257      */
    258     public void setConnection(String protocol, String address,
    259             int port, int flags, String clientCertAlias) {
    260         // Set protocol, security, and additional flags based on uri scheme
    261         mProtocol = protocol;
    262 
    263         mFlags &= ~(FLAG_SSL | FLAG_TLS | FLAG_TRUST_ALL);
    264         mFlags |= (flags & USER_CONFIG_MASK);
    265 
    266         boolean useSecureConnection = (flags & (FLAG_SSL | FLAG_TLS)) != 0;
    267         if (!useSecureConnection && !TextUtils.isEmpty(clientCertAlias)) {
    268             throw new IllegalArgumentException("Can't use client alias on non-secure connections");
    269         }
    270 
    271         mAddress = address;
    272         mPort = port;
    273         if (mPort == PORT_UNKNOWN) {
    274             boolean useSSL = ((mFlags & FLAG_SSL) != 0);
    275             if (LEGACY_SCHEME_SMTP.equals(mProtocol)) {
    276                 mPort = useSSL ? 465 : 587;
    277             }
    278         }
    279 
    280         mClientCertAlias = clientCertAlias;
    281     }
    282 
    283 
    284     /** Convenience method to determine if SSL is used. */
    285     public boolean shouldUseSsl() {
    286         return (mFlags & FLAG_SSL) != 0;
    287     }
    288 
    289     /** Convenience method to determine if all server certs should be used. */
    290     public boolean shouldTrustAllServerCerts() {
    291         return (mFlags & FLAG_TRUST_ALL) != 0;
    292     }
    293 
    294     /**
    295      * Supports Parcelable
    296      */
    297     @Override
    298     public int describeContents() {
    299         return 0;
    300     }
    301 
    302     /**
    303      * Supports Parcelable
    304      */
    305     public static final Parcelable.Creator<HostAuth> CREATOR
    306             = new Parcelable.Creator<HostAuth>() {
    307         @Override
    308         public HostAuth createFromParcel(Parcel in) {
    309             return new HostAuth(in);
    310         }
    311 
    312         @Override
    313         public HostAuth[] newArray(int size) {
    314             return new HostAuth[size];
    315         }
    316     };
    317 
    318     /**
    319      * Supports Parcelable
    320      */
    321     @Override
    322     public void writeToParcel(Parcel dest, int flags) {
    323         // mBaseUri is not parceled
    324         dest.writeLong(mId);
    325         dest.writeString(mProtocol);
    326         dest.writeString(mAddress);
    327         dest.writeInt(mPort);
    328         dest.writeInt(mFlags);
    329         dest.writeString(mLogin);
    330         dest.writeString(mPassword);
    331         dest.writeString(mDomain);
    332         dest.writeString(mClientCertAlias);
    333     }
    334 
    335     /**
    336      * Supports Parcelable
    337      */
    338     public HostAuth(Parcel in) {
    339         mBaseUri = CONTENT_URI;
    340         mId = in.readLong();
    341         mProtocol = in.readString();
    342         mAddress = in.readString();
    343         mPort = in.readInt();
    344         mFlags = in.readInt();
    345         mLogin = in.readString();
    346         mPassword = in.readString();
    347         mDomain = in.readString();
    348         mClientCertAlias = in.readString();
    349     }
    350 
    351     @Override
    352     public boolean equals(Object o) {
    353         if (!(o instanceof HostAuth)) {
    354             return false;
    355         }
    356         HostAuth that = (HostAuth)o;
    357         return mPort == that.mPort
    358                 && mId == that.mId
    359                 && mFlags == that.mFlags
    360                 && Utility.areStringsEqual(mProtocol, that.mProtocol)
    361                 && Utility.areStringsEqual(mAddress, that.mAddress)
    362                 && Utility.areStringsEqual(mLogin, that.mLogin)
    363                 && Utility.areStringsEqual(mPassword, that.mPassword)
    364                 && Utility.areStringsEqual(mDomain, that.mDomain)
    365                 && Utility.areStringsEqual(mClientCertAlias, that.mClientCertAlias);
    366                 // We don't care about the server certificate for equals
    367     }
    368 
    369     /**
    370      * The flag, password, and client cert alias are the only items likely to change after a
    371      * HostAuth is created
    372      */
    373     @Override
    374     public int hashCode() {
    375         int hashCode = 29;
    376         if (mPassword != null) {
    377             hashCode += mPassword.hashCode();
    378         }
    379         if (mClientCertAlias != null) {
    380             hashCode += (mClientCertAlias.hashCode() << 8);
    381         }
    382         return (hashCode << 8) + mFlags;
    383     }
    384 
    385     /**
    386      * Legacy URI parser. Used in parsing template from provider.xml
    387      * Example string:
    388      *   "eas+ssl+trustallcerts://user:password@server/domain:123"
    389      *
    390      * Note that the use of client certificate is specified in the URI, a secure connection type
    391      * must be used.
    392      */
    393     public static void setHostAuthFromString(HostAuth auth, String uriString)
    394             throws URISyntaxException {
    395         URI uri = new URI(uriString);
    396         String path = uri.getPath();
    397         String domain = null;
    398         if (!TextUtils.isEmpty(path)) {
    399             // Strip off the leading slash that begins the path.
    400             domain = path.substring(1);
    401         }
    402         auth.mDomain = domain;
    403         auth.setLogin(uri.getUserInfo());
    404 
    405         String scheme = uri.getScheme();
    406         auth.setConnection(scheme, uri.getHost(), uri.getPort());
    407     }
    408 
    409     /**
    410      * Legacy code for setting connection values from a "scheme" (see above)
    411      */
    412     public void setConnection(String scheme, String host, int port) {
    413         String[] schemeParts = scheme.split("\\+");
    414         String protocol = schemeParts[0];
    415         String clientCertAlias = null;
    416         int flags = getSchemeFlags(scheme);
    417 
    418         // Example scheme: "eas+ssl+trustallcerts" or "eas+tls+trustallcerts+client-cert-alias"
    419         if (schemeParts.length > 3) {
    420             clientCertAlias = schemeParts[3];
    421         } else if (schemeParts.length > 2) {
    422             if (!SCHEME_TRUST_ALL_CERTS.equals(schemeParts[2])) {
    423                 mClientCertAlias = schemeParts[2];
    424             }
    425         }
    426 
    427         setConnection(protocol, host, port, flags, clientCertAlias);
    428     }
    429 
    430     @Override
    431     public String toString() {
    432         return "[protocol " + mProtocol + "]";
    433     }
    434 
    435 }
    436