Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / android / core / src / org / xwalk / core / HttpAuthDatabase.java
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.xwalk.core;
6
7 import android.content.ContentValues;
8 import android.content.Context;
9 import android.database.Cursor;
10 import android.database.sqlite.SQLiteDatabase;
11 import android.database.sqlite.SQLiteException;
12 import android.util.Log;
13
14 /**
15  * This database is used to support the methods of
16  * setHttpAuthUsernamePassword() and getHttpAuthUsernamePassword()
17  * clearHttpAuthUsernamePassword() and hasHttpAuthUsernamePassword().
18  *
19  * While this class is intended to be used as a singleton, this property is not enforced in this
20  * layer, primarily for ease of testing. To line up with the classic implementation and behavior,
21  * there is no specific handling and reporting when SQL errors occur.
22  *
23  * Note on thread-safety: As per the classic implementation, most API functions have thread safety
24  * provided by the underlying SQLiteDatabase instance. The exception is database opening: this
25  * is handled in the dedicated background thread, which also provides a performance gain
26  * if triggered early on (e.g. as a side effect of CookieSyncManager.createInstance() call),
27  * sufficiently in advance of the first blocking usage of the API.
28  */
29 class HttpAuthDatabase {
30
31     private static final String LOGTAG = HttpAuthDatabase.class.getName();
32
33     private static final int DATABASE_VERSION = 1;
34
35     private SQLiteDatabase mDatabase = null;
36
37     private static final String ID_COL = "_id";
38
39     private static final String[] ID_PROJECTION = new String[] {
40         ID_COL
41     };
42
43     // column id strings for "httpauth" table
44     private static final String HTTPAUTH_TABLE_NAME = "httpauth";
45     private static final String HTTPAUTH_HOST_COL = "host";
46     private static final String HTTPAUTH_REALM_COL = "realm";
47     private static final String HTTPAUTH_USERNAME_COL = "username";
48     private static final String HTTPAUTH_PASSWORD_COL = "password";
49
50     /**
51      * Initially false until the background thread completes.
52      */
53     private boolean mInitialized = false;
54
55     /**
56      * Create an instance of HttpAuthDatabase for the named file, and kick-off background
57      * initialization of that database.
58      *
59      * @param context the Context to use for opening the database
60      * @param databaseFile Name of the file to be initialized.
61      */
62     public HttpAuthDatabase(final Context context, final String databaseFile) {
63         new Thread() {
64             @Override
65             public void run() {
66                 initOnBackgroundThread(context, databaseFile);
67             }
68         }.start();
69     }
70
71     /**
72      * Initializes the databases and notifies any callers waiting on waitForInit.
73      *
74      * @param context the Context to use for opening the database
75      * @param databaseFile Name of the file to be initialized.
76      */
77     private synchronized void initOnBackgroundThread(Context context, String databaseFile) {
78         if (mInitialized) {
79             return;
80         }
81
82         initDatabase(context, databaseFile);
83
84         // Thread done, notify.
85         mInitialized = true;
86         notifyAll();
87     }
88
89     /**
90      * Opens the database, and upgrades it if necessary.
91      *
92      * @param context the Context to use for opening the database
93      * @param databaseFile Name of the file to be initialized.
94      */
95     private void initDatabase(Context context, String databaseFile) {
96         try {
97             mDatabase = context.openOrCreateDatabase(databaseFile, 0, null);
98         } catch (SQLiteException e) {
99             // try again by deleting the old db and create a new one
100             if (context.deleteDatabase(databaseFile)) {
101                 mDatabase = context.openOrCreateDatabase(databaseFile, 0, null);
102             }
103         }
104
105         if (mDatabase == null) {
106             // Not much we can do to recover at this point
107             Log.e(LOGTAG, "Unable to open or create " + databaseFile);
108             return;
109         }
110
111         if (mDatabase.getVersion() != DATABASE_VERSION) {
112             mDatabase.beginTransactionNonExclusive();
113             try {
114                 createTable();
115                 mDatabase.setTransactionSuccessful();
116             } finally {
117                 mDatabase.endTransaction();
118             }
119         }
120     }
121
122     private void createTable() {
123         mDatabase.execSQL("CREATE TABLE " + HTTPAUTH_TABLE_NAME
124                 + " (" + ID_COL + " INTEGER PRIMARY KEY, "
125                 + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL
126                 + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, "
127                 + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE ("
128                 + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL
129                 + ") ON CONFLICT REPLACE);");
130
131         mDatabase.setVersion(DATABASE_VERSION);
132     }
133
134     /**
135      * Waits for the background initialization thread to complete and check the database creation
136      * status.
137      *
138      * @return true if the database was initialized, false otherwise
139      */
140     private boolean waitForInit() {
141         synchronized (this) {
142             while (!mInitialized) {
143                 try {
144                     wait();
145                 } catch (InterruptedException e) {
146                     Log.e(LOGTAG, "Caught exception while checking initialization", e);
147                 }
148             }
149         }
150         return mDatabase != null;
151     }
152
153     /**
154      * Sets the HTTP authentication password. Tuple (HTTPAUTH_HOST_COL, HTTPAUTH_REALM_COL,
155      * HTTPAUTH_USERNAME_COL) is unique.
156      *
157      * @param host the host for the password
158      * @param realm the realm for the password
159      * @param username the username for the password.
160      * @param password the password
161      */
162     public void setHttpAuthUsernamePassword(String host, String realm, String username,
163             String password) {
164         if (host == null || realm == null || !waitForInit()) {
165             return;
166         }
167
168         final ContentValues c = new ContentValues();
169         c.put(HTTPAUTH_HOST_COL, host);
170         c.put(HTTPAUTH_REALM_COL, realm);
171         c.put(HTTPAUTH_USERNAME_COL, username);
172         c.put(HTTPAUTH_PASSWORD_COL, password);
173         mDatabase.insert(HTTPAUTH_TABLE_NAME, HTTPAUTH_HOST_COL, c);
174     }
175
176     /**
177      * Retrieves the HTTP authentication username and password for a given host and realm pair. If
178      * there are multiple username/password combinations for a host/realm, only the first one will
179      * be returned.
180      *
181      * @param host the host the password applies to
182      * @param realm the realm the password applies to
183      * @return a String[] if found where String[0] is username (which can be null) and
184      *         String[1] is password.  Null is returned if it can't find anything.
185      */
186     public String[] getHttpAuthUsernamePassword(String host, String realm) {
187         if (host == null || realm == null || !waitForInit()){
188             return null;
189         }
190
191         final String[] columns = new String[] {
192             HTTPAUTH_USERNAME_COL, HTTPAUTH_PASSWORD_COL
193         };
194         final String selection = "(" + HTTPAUTH_HOST_COL + " == ?) AND " +
195                 "(" + HTTPAUTH_REALM_COL + " == ?)";
196
197         String[] ret = null;
198         Cursor cursor = null;
199         try {
200             cursor = mDatabase.query(HTTPAUTH_TABLE_NAME, columns, selection,
201                     new String[] { host, realm }, null, null, null);
202             if (cursor.moveToFirst()) {
203                 ret = new String[] {
204                         cursor.getString(cursor.getColumnIndex(HTTPAUTH_USERNAME_COL)),
205                         cursor.getString(cursor.getColumnIndex(HTTPAUTH_PASSWORD_COL)),
206                 };
207             }
208         } catch (IllegalStateException e) {
209             Log.e(LOGTAG, "getHttpAuthUsernamePassword", e);
210         } finally {
211             if (cursor != null) cursor.close();
212         }
213         return ret;
214     }
215
216     /**
217      * Determines if there are any HTTP authentication passwords saved.
218      *
219      * @return true if there are passwords saved
220      */
221     public boolean hasHttpAuthUsernamePassword() {
222         if (!waitForInit()) {
223             return false;
224         }
225
226         Cursor cursor = null;
227         boolean ret = false;
228         try {
229             cursor = mDatabase.query(HTTPAUTH_TABLE_NAME, ID_PROJECTION, null, null, null, null,
230                     null);
231             ret = cursor.moveToFirst();
232         } catch (IllegalStateException e) {
233             Log.e(LOGTAG, "hasEntries", e);
234         } finally {
235             if (cursor != null) cursor.close();
236         }
237         return ret;
238     }
239
240     /**
241      * Clears the HTTP authentication password database.
242      */
243     public void clearHttpAuthUsernamePassword() {
244         if (!waitForInit()) {
245             return;
246         }
247         mDatabase.delete(HTTPAUTH_TABLE_NAME, null, null);
248     }
249 }