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