mobile/android/base/db/PasswordsProvider.java

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial