replicant-packages_apps_Email/emailcommon/src/com/android/emailcommon/provider/HostAuth.java

442 lines
16 KiB
Java

/*
* Copyright (C) 2011 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.emailcommon.provider;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import com.android.emailcommon.provider.EmailContent.HostAuthColumns;
import com.android.emailcommon.utility.SSLUtils;
import com.android.emailcommon.utility.Utility;
import java.net.URI;
import java.net.URISyntaxException;
public final class HostAuth extends EmailContent implements HostAuthColumns, Parcelable {
public static final String TABLE_NAME = "HostAuth";
@SuppressWarnings("hiding")
public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/hostauth");
// TODO the three following constants duplicate constants in Store.java; remove those and
// just reference these.
public static final String SCHEME_IMAP = "imap";
public static final String SCHEME_POP3 = "pop3";
public static final String SCHEME_EAS = "eas";
public static final String SCHEME_SMTP = "smtp";
public static final String SCHEME_TRUST_ALL_CERTS = "trustallcerts";
public static final int PORT_UNKNOWN = -1;
public static final int FLAG_NONE = 0x00; // No flags
public static final int FLAG_SSL = 0x01; // Use SSL
public static final int FLAG_TLS = 0x02; // Use TLS
public static final int FLAG_AUTHENTICATE = 0x04; // Use name/password for authentication
public static final int FLAG_TRUST_ALL = 0x08; // Trust all certificates
// Mask of settings directly configurable by the user
public static final int USER_CONFIG_MASK = 0x0b;
public String mProtocol;
public String mAddress;
public int mPort;
public int mFlags;
public String mLogin;
public String mPassword;
public String mDomain;
public String mClientCertAlias = null;
public static final int CONTENT_ID_COLUMN = 0;
public static final int CONTENT_PROTOCOL_COLUMN = 1;
public static final int CONTENT_ADDRESS_COLUMN = 2;
public static final int CONTENT_PORT_COLUMN = 3;
public static final int CONTENT_FLAGS_COLUMN = 4;
public static final int CONTENT_LOGIN_COLUMN = 5;
public static final int CONTENT_PASSWORD_COLUMN = 6;
public static final int CONTENT_DOMAIN_COLUMN = 7;
public static final int CONTENT_CLIENT_CERT_ALIAS_COLUMN = 8;
public static final String[] CONTENT_PROJECTION = new String[] {
RECORD_ID, HostAuthColumns.PROTOCOL, HostAuthColumns.ADDRESS, HostAuthColumns.PORT,
HostAuthColumns.FLAGS, HostAuthColumns.LOGIN,
HostAuthColumns.PASSWORD, HostAuthColumns.DOMAIN, HostAuthColumns.CLIENT_CERT_ALIAS
};
/**
* no public constructor since this is a utility class
*/
public HostAuth() {
mBaseUri = CONTENT_URI;
// other defaults policy)
mPort = PORT_UNKNOWN;
}
/**
* Restore a HostAuth from the database, given its unique id
* @param context
* @param id
* @return the instantiated HostAuth
*/
public static HostAuth restoreHostAuthWithId(Context context, long id) {
return EmailContent.restoreContentWithId(context, HostAuth.class,
HostAuth.CONTENT_URI, HostAuth.CONTENT_PROJECTION, id);
}
/**
* Returns the scheme for the specified flags.
*/
public static String getSchemeString(String protocol, int flags) {
return getSchemeString(protocol, flags, null);
}
/**
* Builds a URI scheme name given the parameters for a {@code HostAuth}.
* If a {@code clientAlias} is provided, this indicates that a secure connection must be used.
*/
public static String getSchemeString(String protocol, int flags, String clientAlias) {
String security = "";
switch (flags & USER_CONFIG_MASK) {
case FLAG_SSL:
security = "+ssl+";
break;
case FLAG_SSL | FLAG_TRUST_ALL:
security = "+ssl+trustallcerts";
break;
case FLAG_TLS:
security = "+tls+";
break;
case FLAG_TLS | FLAG_TRUST_ALL:
security = "+tls+trustallcerts";
break;
}
if (!TextUtils.isEmpty(clientAlias)) {
if (TextUtils.isEmpty(security)) {
throw new IllegalArgumentException(
"Can't specify a certificate alias for a non-secure connection");
}
if (!security.endsWith("+")) {
security += "+";
}
security += SSLUtils.escapeForSchemeName(clientAlias);
}
return protocol + security;
}
/**
* Returns the flags for the specified scheme.
*/
public static int getSchemeFlags(String scheme) {
String[] schemeParts = scheme.split("\\+");
int flags = HostAuth.FLAG_NONE;
if (schemeParts.length >= 2) {
String part1 = schemeParts[1];
if ("ssl".equals(part1)) {
flags |= HostAuth.FLAG_SSL;
} else if ("tls".equals(part1)) {
flags |= HostAuth.FLAG_TLS;
}
if (schemeParts.length >= 3) {
String part2 = schemeParts[2];
if (SCHEME_TRUST_ALL_CERTS.equals(part2)) {
flags |= HostAuth.FLAG_TRUST_ALL;
}
}
}
return flags;
}
@Override
public void restore(Cursor cursor) {
mBaseUri = CONTENT_URI;
mId = cursor.getLong(CONTENT_ID_COLUMN);
mProtocol = cursor.getString(CONTENT_PROTOCOL_COLUMN);
mAddress = cursor.getString(CONTENT_ADDRESS_COLUMN);
mPort = cursor.getInt(CONTENT_PORT_COLUMN);
mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN);
mLogin = cursor.getString(CONTENT_LOGIN_COLUMN);
mPassword = cursor.getString(CONTENT_PASSWORD_COLUMN);
mDomain = cursor.getString(CONTENT_DOMAIN_COLUMN);
mClientCertAlias = cursor.getString(CONTENT_CLIENT_CERT_ALIAS_COLUMN);
}
@Override
public ContentValues toContentValues() {
ContentValues values = new ContentValues();
values.put(HostAuthColumns.PROTOCOL, mProtocol);
values.put(HostAuthColumns.ADDRESS, mAddress);
values.put(HostAuthColumns.PORT, mPort);
values.put(HostAuthColumns.FLAGS, mFlags);
values.put(HostAuthColumns.LOGIN, mLogin);
values.put(HostAuthColumns.PASSWORD, mPassword);
values.put(HostAuthColumns.DOMAIN, mDomain);
values.put(HostAuthColumns.CLIENT_CERT_ALIAS, mClientCertAlias);
values.put(HostAuthColumns.ACCOUNT_KEY, 0); // Need something to satisfy the DB
return values;
}
/**
* Sets the user name and password from URI user info string
*/
public void setLogin(String userInfo) {
String userName = null;
String userPassword = null;
if (!TextUtils.isEmpty(userInfo)) {
String[] userInfoParts = userInfo.split(":", 2);
userName = userInfoParts[0];
if (userInfoParts.length > 1) {
userPassword = userInfoParts[1];
}
}
setLogin(userName, userPassword);
}
/**
* Sets the user name and password
*/
public void setLogin(String userName, String userPassword) {
mLogin = userName;
mPassword = userPassword;
if (mLogin == null) {
mFlags &= ~FLAG_AUTHENTICATE;
} else {
mFlags |= FLAG_AUTHENTICATE;
}
}
/**
* Returns the login information. [0] is the username and [1] is the password. If
* {@link #FLAG_AUTHENTICATE} is not set, {@code null} is returned.
*/
public String[] getLogin() {
if ((mFlags & FLAG_AUTHENTICATE) != 0) {
String trimUser = (mLogin != null) ? mLogin.trim() : "";
String password = (mPassword != null) ? mPassword : "";
return new String[] { trimUser, password };
}
return null;
}
public void setConnection(String protocol, String address, int port, int flags) {
setConnection(protocol, address, port, flags, null);
}
/**
* Sets the internal connection parameters based on the specified parameter values.
* @param protocol the mail protocol to use (e.g. "eas", "imap").
* @param address the address of the server
* @param port the port for the connection
* @param flags flags indicating the security and type of the connection
* @param clientCertAlias an optional alias to use if a client user certificate is to be
* presented during connection establishment. If this is non-empty, it must be the case
* that flags indicates use of a secure connection
*/
public void setConnection(String protocol, String address,
int port, int flags, String clientCertAlias) {
// Set protocol, security, and additional flags based on uri scheme
mProtocol = protocol;
mFlags &= ~(FLAG_SSL | FLAG_TLS | FLAG_TRUST_ALL);
mFlags |= (flags & USER_CONFIG_MASK);
boolean useSecureConnection = (flags & (FLAG_SSL | FLAG_TLS)) != 0;
if (!useSecureConnection && !TextUtils.isEmpty(clientCertAlias)) {
throw new IllegalArgumentException("Can't use client alias on non-secure connections");
}
mAddress = address;
mPort = port;
if (mPort == PORT_UNKNOWN) {
boolean useSSL = ((mFlags & FLAG_SSL) != 0);
// infer port# from protocol + security
// SSL implies a different port - TLS runs in the "regular" port
// NOTE: Although the port should be setup in the various setup screens, this
// block cannot easily be moved because we get process URIs from other sources
// (e.g. for tests, provider templates and account restore) that may or may not
// have a port specified.
if (SCHEME_POP3.equals(mProtocol)) {
mPort = useSSL ? 995 : 110;
} else if (SCHEME_IMAP.equals(mProtocol)) {
mPort = useSSL ? 993 : 143;
} else if (SCHEME_EAS.equals(mProtocol)) {
mPort = useSSL ? 443 : 80;
} else if (SCHEME_SMTP.equals(mProtocol)) {
mPort = useSSL ? 465 : 587;
}
}
mClientCertAlias = clientCertAlias;
}
/** Returns {@code true} if this is an EAS connection; otherwise, {@code false}. */
public boolean isEasConnection() {
return SCHEME_EAS.equals(mProtocol);
}
/** Convenience method to determine if SSL is used. */
public boolean shouldUseSsl() {
return (mFlags & FLAG_SSL) != 0;
}
/** Convenience method to determine if all server certs should be used. */
public boolean shouldTrustAllServerCerts() {
return (mFlags & FLAG_TRUST_ALL) != 0;
}
/**
* Supports Parcelable
*/
@Override
public int describeContents() {
return 0;
}
/**
* Supports Parcelable
*/
public static final Parcelable.Creator<HostAuth> CREATOR
= new Parcelable.Creator<HostAuth>() {
@Override
public HostAuth createFromParcel(Parcel in) {
return new HostAuth(in);
}
@Override
public HostAuth[] newArray(int size) {
return new HostAuth[size];
}
};
/**
* Supports Parcelable
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
// mBaseUri is not parceled
dest.writeLong(mId);
dest.writeString(mProtocol);
dest.writeString(mAddress);
dest.writeInt(mPort);
dest.writeInt(mFlags);
dest.writeString(mLogin);
dest.writeString(mPassword);
dest.writeString(mDomain);
dest.writeString(mClientCertAlias);
}
/**
* Supports Parcelable
*/
public HostAuth(Parcel in) {
mBaseUri = CONTENT_URI;
mId = in.readLong();
mProtocol = in.readString();
mAddress = in.readString();
mPort = in.readInt();
mFlags = in.readInt();
mLogin = in.readString();
mPassword = in.readString();
mDomain = in.readString();
mClientCertAlias = in.readString();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof HostAuth)) {
return false;
}
HostAuth that = (HostAuth)o;
return mPort == that.mPort
&& mFlags == that.mFlags
&& Utility.areStringsEqual(mProtocol, that.mProtocol)
&& Utility.areStringsEqual(mAddress, that.mAddress)
&& Utility.areStringsEqual(mLogin, that.mLogin)
&& Utility.areStringsEqual(mPassword, that.mPassword)
&& Utility.areStringsEqual(mDomain, that.mDomain)
&& Utility.areStringsEqual(mClientCertAlias, that.mClientCertAlias);
}
/**
* The flag, password, and client cert alias are the only items likely to change after a
* HostAuth is created
*/
@Override
public int hashCode() {
int hashCode = 29;
if (mPassword != null) {
hashCode += mPassword.hashCode();
}
if (mClientCertAlias != null) {
hashCode += (mClientCertAlias.hashCode() << 8);
}
return (hashCode << 8) + mFlags;
}
/**
* Legacy URI parser. Used in parsing template from provider.xml
* Example string:
* "eas+ssl+trustallcerts://user:password@server/domain:123"
*
* Note that the use of client certificate is specified in the URI, a secure connection type
* must be used.
*/
public static void setHostAuthFromString(HostAuth auth, String uriString)
throws URISyntaxException {
URI uri = new URI(uriString);
String path = uri.getPath();
String domain = null;
if (!TextUtils.isEmpty(path)) {
// Strip off the leading slash that begins the path.
domain = path.substring(1);
}
auth.mDomain = domain;
auth.setLogin(uri.getUserInfo());
String scheme = uri.getScheme();
auth.setConnection(scheme, uri.getHost(), uri.getPort());
}
/**
* Legacy code for setting connection values from a "scheme" (see above)
*/
public void setConnection(String scheme, String host, int port) {
String[] schemeParts = scheme.split("\\+");
String protocol = schemeParts[0];
String clientCertAlias = null;
int flags = getSchemeFlags(scheme);
// Example scheme: "eas+ssl+trustallcerts" or "eas+tls+trustallcerts+client-cert-alias"
if (schemeParts.length > 3) {
clientCertAlias = schemeParts[3];
} else if (schemeParts.length > 2) {
if (!SCHEME_TRUST_ALL_CERTS.equals(schemeParts[2])) {
mClientCertAlias = schemeParts[2];
}
}
setConnection(protocol, host, port, flags, clientCertAlias);
}
}