Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 4 | |
michael@0 | 5 | package org.mozilla.gecko.db; |
michael@0 | 6 | |
michael@0 | 7 | import java.util.HashMap; |
michael@0 | 8 | |
michael@0 | 9 | import org.mozilla.gecko.GeckoApp; |
michael@0 | 10 | import org.mozilla.gecko.GeckoAppShell; |
michael@0 | 11 | import org.mozilla.gecko.GeckoEvent; |
michael@0 | 12 | import org.mozilla.gecko.NSSBridge; |
michael@0 | 13 | import org.mozilla.gecko.db.BrowserContract.DeletedPasswords; |
michael@0 | 14 | import org.mozilla.gecko.db.BrowserContract.Passwords; |
michael@0 | 15 | import org.mozilla.gecko.mozglue.GeckoLoader; |
michael@0 | 16 | import org.mozilla.gecko.sqlite.MatrixBlobCursor; |
michael@0 | 17 | import org.mozilla.gecko.sqlite.SQLiteBridge; |
michael@0 | 18 | import org.mozilla.gecko.sync.Utils; |
michael@0 | 19 | |
michael@0 | 20 | import android.content.ContentValues; |
michael@0 | 21 | import android.content.Intent; |
michael@0 | 22 | import android.content.UriMatcher; |
michael@0 | 23 | import android.database.Cursor; |
michael@0 | 24 | import android.net.Uri; |
michael@0 | 25 | import android.text.TextUtils; |
michael@0 | 26 | import android.util.Log; |
michael@0 | 27 | |
michael@0 | 28 | public class PasswordsProvider extends SQLiteBridgeContentProvider { |
michael@0 | 29 | static final String TABLE_PASSWORDS = "moz_logins"; |
michael@0 | 30 | static final String TABLE_DELETED_PASSWORDS = "moz_deleted_logins"; |
michael@0 | 31 | |
michael@0 | 32 | private static final String TELEMETRY_TAG = "SQLITEBRIDGE_PROVIDER_PASSWORDS"; |
michael@0 | 33 | |
michael@0 | 34 | private static final int PASSWORDS = 100; |
michael@0 | 35 | private static final int DELETED_PASSWORDS = 101; |
michael@0 | 36 | |
michael@0 | 37 | static final String DEFAULT_PASSWORDS_SORT_ORDER = Passwords.HOSTNAME + " ASC"; |
michael@0 | 38 | static final String DEFAULT_DELETED_PASSWORDS_SORT_ORDER = DeletedPasswords.TIME_DELETED + " ASC"; |
michael@0 | 39 | |
michael@0 | 40 | private static final UriMatcher URI_MATCHER; |
michael@0 | 41 | |
michael@0 | 42 | private static HashMap<String, String> PASSWORDS_PROJECTION_MAP; |
michael@0 | 43 | private static HashMap<String, String> DELETED_PASSWORDS_PROJECTION_MAP; |
michael@0 | 44 | |
michael@0 | 45 | // this should be kept in sync with the version in toolkit/components/passwordmgr/storage-mozStorage.js |
michael@0 | 46 | private static final int DB_VERSION = 5; |
michael@0 | 47 | private static final String DB_FILENAME = "signons.sqlite"; |
michael@0 | 48 | private static final String WHERE_GUID_IS_NULL = BrowserContract.DeletedPasswords.GUID + " IS NULL"; |
michael@0 | 49 | private static final String WHERE_GUID_IS_VALUE = BrowserContract.DeletedPasswords.GUID + " = ?"; |
michael@0 | 50 | |
michael@0 | 51 | private static final String LOG_TAG = "GeckPasswordsProvider"; |
michael@0 | 52 | |
michael@0 | 53 | static { |
michael@0 | 54 | URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); |
michael@0 | 55 | |
michael@0 | 56 | // content://org.mozilla.gecko.providers.browser/passwords/# |
michael@0 | 57 | URI_MATCHER.addURI(BrowserContract.PASSWORDS_AUTHORITY, "passwords", PASSWORDS); |
michael@0 | 58 | |
michael@0 | 59 | PASSWORDS_PROJECTION_MAP = new HashMap<String, String>(); |
michael@0 | 60 | PASSWORDS_PROJECTION_MAP.put(Passwords.ID, Passwords.ID); |
michael@0 | 61 | PASSWORDS_PROJECTION_MAP.put(Passwords.HOSTNAME, Passwords.HOSTNAME); |
michael@0 | 62 | PASSWORDS_PROJECTION_MAP.put(Passwords.HTTP_REALM, Passwords.HTTP_REALM); |
michael@0 | 63 | PASSWORDS_PROJECTION_MAP.put(Passwords.FORM_SUBMIT_URL, Passwords.FORM_SUBMIT_URL); |
michael@0 | 64 | PASSWORDS_PROJECTION_MAP.put(Passwords.USERNAME_FIELD, Passwords.USERNAME_FIELD); |
michael@0 | 65 | PASSWORDS_PROJECTION_MAP.put(Passwords.PASSWORD_FIELD, Passwords.PASSWORD_FIELD); |
michael@0 | 66 | PASSWORDS_PROJECTION_MAP.put(Passwords.ENCRYPTED_USERNAME, Passwords.ENCRYPTED_USERNAME); |
michael@0 | 67 | PASSWORDS_PROJECTION_MAP.put(Passwords.ENCRYPTED_PASSWORD, Passwords.ENCRYPTED_PASSWORD); |
michael@0 | 68 | PASSWORDS_PROJECTION_MAP.put(Passwords.GUID, Passwords.GUID); |
michael@0 | 69 | PASSWORDS_PROJECTION_MAP.put(Passwords.ENC_TYPE, Passwords.ENC_TYPE); |
michael@0 | 70 | PASSWORDS_PROJECTION_MAP.put(Passwords.TIME_CREATED, Passwords.TIME_CREATED); |
michael@0 | 71 | PASSWORDS_PROJECTION_MAP.put(Passwords.TIME_LAST_USED, Passwords.TIME_LAST_USED); |
michael@0 | 72 | PASSWORDS_PROJECTION_MAP.put(Passwords.TIME_PASSWORD_CHANGED, Passwords.TIME_PASSWORD_CHANGED); |
michael@0 | 73 | PASSWORDS_PROJECTION_MAP.put(Passwords.TIMES_USED, Passwords.TIMES_USED); |
michael@0 | 74 | |
michael@0 | 75 | URI_MATCHER.addURI(BrowserContract.PASSWORDS_AUTHORITY, "deleted-passwords", DELETED_PASSWORDS); |
michael@0 | 76 | |
michael@0 | 77 | DELETED_PASSWORDS_PROJECTION_MAP = new HashMap<String, String>(); |
michael@0 | 78 | DELETED_PASSWORDS_PROJECTION_MAP.put(DeletedPasswords.ID, DeletedPasswords.ID); |
michael@0 | 79 | DELETED_PASSWORDS_PROJECTION_MAP.put(DeletedPasswords.GUID, DeletedPasswords.GUID); |
michael@0 | 80 | DELETED_PASSWORDS_PROJECTION_MAP.put(DeletedPasswords.TIME_DELETED, DeletedPasswords.TIME_DELETED); |
michael@0 | 81 | |
michael@0 | 82 | // We don't use .loadMozGlue because we're in a different process, |
michael@0 | 83 | // and we just want to reuse code rather than use the loader lock etc. |
michael@0 | 84 | GeckoLoader.doLoadLibrary("mozglue"); |
michael@0 | 85 | } |
michael@0 | 86 | |
michael@0 | 87 | public PasswordsProvider() { |
michael@0 | 88 | super(LOG_TAG); |
michael@0 | 89 | } |
michael@0 | 90 | |
michael@0 | 91 | @Override |
michael@0 | 92 | protected String getDBName(){ |
michael@0 | 93 | return DB_FILENAME; |
michael@0 | 94 | } |
michael@0 | 95 | |
michael@0 | 96 | @Override |
michael@0 | 97 | protected String getTelemetryPrefix() { |
michael@0 | 98 | return TELEMETRY_TAG; |
michael@0 | 99 | } |
michael@0 | 100 | |
michael@0 | 101 | @Override |
michael@0 | 102 | protected int getDBVersion(){ |
michael@0 | 103 | return DB_VERSION; |
michael@0 | 104 | } |
michael@0 | 105 | |
michael@0 | 106 | @Override |
michael@0 | 107 | public String getType(Uri uri) { |
michael@0 | 108 | final int match = URI_MATCHER.match(uri); |
michael@0 | 109 | |
michael@0 | 110 | switch (match) { |
michael@0 | 111 | case PASSWORDS: |
michael@0 | 112 | return Passwords.CONTENT_TYPE; |
michael@0 | 113 | |
michael@0 | 114 | case DELETED_PASSWORDS: |
michael@0 | 115 | return DeletedPasswords.CONTENT_TYPE; |
michael@0 | 116 | |
michael@0 | 117 | default: |
michael@0 | 118 | throw new UnsupportedOperationException("Unknown type " + uri); |
michael@0 | 119 | } |
michael@0 | 120 | } |
michael@0 | 121 | |
michael@0 | 122 | @Override |
michael@0 | 123 | public String getTable(Uri uri) { |
michael@0 | 124 | final int match = URI_MATCHER.match(uri); |
michael@0 | 125 | switch (match) { |
michael@0 | 126 | case DELETED_PASSWORDS: |
michael@0 | 127 | return TABLE_DELETED_PASSWORDS; |
michael@0 | 128 | |
michael@0 | 129 | case PASSWORDS: |
michael@0 | 130 | return TABLE_PASSWORDS; |
michael@0 | 131 | |
michael@0 | 132 | default: |
michael@0 | 133 | throw new UnsupportedOperationException("Unknown table " + uri); |
michael@0 | 134 | } |
michael@0 | 135 | } |
michael@0 | 136 | |
michael@0 | 137 | @Override |
michael@0 | 138 | public String getSortOrder(Uri uri, String aRequested) { |
michael@0 | 139 | if (!TextUtils.isEmpty(aRequested)) { |
michael@0 | 140 | return aRequested; |
michael@0 | 141 | } |
michael@0 | 142 | |
michael@0 | 143 | final int match = URI_MATCHER.match(uri); |
michael@0 | 144 | switch (match) { |
michael@0 | 145 | case DELETED_PASSWORDS: |
michael@0 | 146 | return DEFAULT_DELETED_PASSWORDS_SORT_ORDER; |
michael@0 | 147 | |
michael@0 | 148 | case PASSWORDS: |
michael@0 | 149 | return DEFAULT_PASSWORDS_SORT_ORDER; |
michael@0 | 150 | |
michael@0 | 151 | default: |
michael@0 | 152 | throw new UnsupportedOperationException("Unknown URI " + uri); |
michael@0 | 153 | } |
michael@0 | 154 | } |
michael@0 | 155 | |
michael@0 | 156 | @Override |
michael@0 | 157 | public void setupDefaults(Uri uri, ContentValues values) |
michael@0 | 158 | throws IllegalArgumentException { |
michael@0 | 159 | int match = URI_MATCHER.match(uri); |
michael@0 | 160 | long now = System.currentTimeMillis(); |
michael@0 | 161 | switch (match) { |
michael@0 | 162 | case DELETED_PASSWORDS: |
michael@0 | 163 | values.put(DeletedPasswords.TIME_DELETED, now); |
michael@0 | 164 | |
michael@0 | 165 | // Deleted passwords must contain a guid |
michael@0 | 166 | if (!values.containsKey(Passwords.GUID)) { |
michael@0 | 167 | throw new IllegalArgumentException("Must provide a GUID for a deleted password"); |
michael@0 | 168 | } |
michael@0 | 169 | break; |
michael@0 | 170 | |
michael@0 | 171 | case PASSWORDS: |
michael@0 | 172 | values.put(Passwords.TIME_CREATED, now); |
michael@0 | 173 | |
michael@0 | 174 | // Generate GUID for new password. Don't override specified GUIDs. |
michael@0 | 175 | if (!values.containsKey(Passwords.GUID)) { |
michael@0 | 176 | String guid = Utils.generateGuid(); |
michael@0 | 177 | values.put(Passwords.GUID, guid); |
michael@0 | 178 | } |
michael@0 | 179 | String nowString = new Long(now).toString(); |
michael@0 | 180 | DBUtils.replaceKey(values, null, Passwords.HOSTNAME, ""); |
michael@0 | 181 | DBUtils.replaceKey(values, null, Passwords.HTTP_REALM, ""); |
michael@0 | 182 | DBUtils.replaceKey(values, null, Passwords.FORM_SUBMIT_URL, ""); |
michael@0 | 183 | DBUtils.replaceKey(values, null, Passwords.USERNAME_FIELD, ""); |
michael@0 | 184 | DBUtils.replaceKey(values, null, Passwords.PASSWORD_FIELD, ""); |
michael@0 | 185 | DBUtils.replaceKey(values, null, Passwords.ENCRYPTED_USERNAME, ""); |
michael@0 | 186 | DBUtils.replaceKey(values, null, Passwords.ENCRYPTED_PASSWORD, ""); |
michael@0 | 187 | DBUtils.replaceKey(values, null, Passwords.ENC_TYPE, "0"); |
michael@0 | 188 | DBUtils.replaceKey(values, null, Passwords.TIME_LAST_USED, nowString); |
michael@0 | 189 | DBUtils.replaceKey(values, null, Passwords.TIME_PASSWORD_CHANGED, nowString); |
michael@0 | 190 | DBUtils.replaceKey(values, null, Passwords.TIMES_USED, "0"); |
michael@0 | 191 | break; |
michael@0 | 192 | |
michael@0 | 193 | default: |
michael@0 | 194 | throw new UnsupportedOperationException("Unknown URI " + uri); |
michael@0 | 195 | } |
michael@0 | 196 | } |
michael@0 | 197 | |
michael@0 | 198 | @Override |
michael@0 | 199 | public void initGecko() { |
michael@0 | 200 | GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Passwords:Init", null)); |
michael@0 | 201 | Intent initIntent = new Intent(GeckoApp.ACTION_INIT_PW); |
michael@0 | 202 | mContext.sendBroadcast(initIntent); |
michael@0 | 203 | } |
michael@0 | 204 | |
michael@0 | 205 | private String doCrypto(String initialValue, Uri uri, Boolean encrypt) { |
michael@0 | 206 | String profilePath = null; |
michael@0 | 207 | if (uri != null) { |
michael@0 | 208 | profilePath = uri.getQueryParameter(BrowserContract.PARAM_PROFILE_PATH); |
michael@0 | 209 | } |
michael@0 | 210 | |
michael@0 | 211 | String result = ""; |
michael@0 | 212 | try { |
michael@0 | 213 | if (encrypt) { |
michael@0 | 214 | if (profilePath != null) { |
michael@0 | 215 | result = NSSBridge.encrypt(mContext, profilePath, initialValue); |
michael@0 | 216 | } else { |
michael@0 | 217 | result = NSSBridge.encrypt(mContext, initialValue); |
michael@0 | 218 | } |
michael@0 | 219 | } else { |
michael@0 | 220 | if (profilePath != null) { |
michael@0 | 221 | result = NSSBridge.decrypt(mContext, profilePath, initialValue); |
michael@0 | 222 | } else { |
michael@0 | 223 | result = NSSBridge.decrypt(mContext, initialValue); |
michael@0 | 224 | } |
michael@0 | 225 | } |
michael@0 | 226 | } catch (Exception ex) { |
michael@0 | 227 | Log.e(LOG_TAG, "Error in NSSBridge"); |
michael@0 | 228 | throw new RuntimeException(ex); |
michael@0 | 229 | } |
michael@0 | 230 | return result; |
michael@0 | 231 | } |
michael@0 | 232 | |
michael@0 | 233 | @Override |
michael@0 | 234 | public void onPreInsert(ContentValues values, Uri uri, SQLiteBridge db) { |
michael@0 | 235 | if (values.containsKey(Passwords.GUID)) { |
michael@0 | 236 | String guid = values.getAsString(Passwords.GUID); |
michael@0 | 237 | if (guid == null) { |
michael@0 | 238 | db.delete(TABLE_DELETED_PASSWORDS, WHERE_GUID_IS_NULL, null); |
michael@0 | 239 | return; |
michael@0 | 240 | } |
michael@0 | 241 | String[] args = new String[] { guid }; |
michael@0 | 242 | db.delete(TABLE_DELETED_PASSWORDS, WHERE_GUID_IS_VALUE, args); |
michael@0 | 243 | } |
michael@0 | 244 | |
michael@0 | 245 | if (values.containsKey(Passwords.ENCRYPTED_PASSWORD)) { |
michael@0 | 246 | String res = doCrypto(values.getAsString(Passwords.ENCRYPTED_PASSWORD), uri, true); |
michael@0 | 247 | values.put(Passwords.ENCRYPTED_PASSWORD, res); |
michael@0 | 248 | values.put(Passwords.ENC_TYPE, Passwords.ENCTYPE_SDR); |
michael@0 | 249 | } |
michael@0 | 250 | |
michael@0 | 251 | if (values.containsKey(Passwords.ENCRYPTED_USERNAME)) { |
michael@0 | 252 | String res = doCrypto(values.getAsString(Passwords.ENCRYPTED_USERNAME), uri, true); |
michael@0 | 253 | values.put(Passwords.ENCRYPTED_USERNAME, res); |
michael@0 | 254 | values.put(Passwords.ENC_TYPE, Passwords.ENCTYPE_SDR); |
michael@0 | 255 | } |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | @Override |
michael@0 | 259 | public void onPreUpdate(ContentValues values, Uri uri, SQLiteBridge db) { |
michael@0 | 260 | if (values.containsKey(Passwords.ENCRYPTED_PASSWORD)) { |
michael@0 | 261 | String res = doCrypto(values.getAsString(Passwords.ENCRYPTED_PASSWORD), uri, true); |
michael@0 | 262 | values.put(Passwords.ENCRYPTED_PASSWORD, res); |
michael@0 | 263 | values.put(Passwords.ENC_TYPE, Passwords.ENCTYPE_SDR); |
michael@0 | 264 | } |
michael@0 | 265 | |
michael@0 | 266 | if (values.containsKey(Passwords.ENCRYPTED_USERNAME)) { |
michael@0 | 267 | String res = doCrypto(values.getAsString(Passwords.ENCRYPTED_USERNAME), uri, true); |
michael@0 | 268 | values.put(Passwords.ENCRYPTED_USERNAME, res); |
michael@0 | 269 | values.put(Passwords.ENC_TYPE, Passwords.ENCTYPE_SDR); |
michael@0 | 270 | } |
michael@0 | 271 | } |
michael@0 | 272 | |
michael@0 | 273 | @Override |
michael@0 | 274 | public void onPostQuery(Cursor cursor, Uri uri, SQLiteBridge db) { |
michael@0 | 275 | int passwordIndex = -1; |
michael@0 | 276 | int usernameIndex = -1; |
michael@0 | 277 | String profilePath = null; |
michael@0 | 278 | |
michael@0 | 279 | try { |
michael@0 | 280 | passwordIndex = cursor.getColumnIndexOrThrow(Passwords.ENCRYPTED_PASSWORD); |
michael@0 | 281 | } catch(Exception ex) { } |
michael@0 | 282 | try { |
michael@0 | 283 | usernameIndex = cursor.getColumnIndexOrThrow(Passwords.ENCRYPTED_USERNAME); |
michael@0 | 284 | } catch(Exception ex) { } |
michael@0 | 285 | |
michael@0 | 286 | if (passwordIndex > -1 || usernameIndex > -1) { |
michael@0 | 287 | MatrixBlobCursor m = (MatrixBlobCursor)cursor; |
michael@0 | 288 | if (cursor.moveToFirst()) { |
michael@0 | 289 | do { |
michael@0 | 290 | if (passwordIndex > -1) { |
michael@0 | 291 | String decrypted = doCrypto(cursor.getString(passwordIndex), uri, false);; |
michael@0 | 292 | m.set(passwordIndex, decrypted); |
michael@0 | 293 | } |
michael@0 | 294 | |
michael@0 | 295 | if (usernameIndex > -1) { |
michael@0 | 296 | String decrypted = doCrypto(cursor.getString(usernameIndex), uri, false); |
michael@0 | 297 | m.set(usernameIndex, decrypted); |
michael@0 | 298 | } |
michael@0 | 299 | } while(cursor.moveToNext()); |
michael@0 | 300 | } |
michael@0 | 301 | } |
michael@0 | 302 | } |
michael@0 | 303 | } |