diff options
Diffstat (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider')
7 files changed, 470 insertions, 173 deletions
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index d8de30b37..e7b31bf65 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -17,11 +17,11 @@ package org.sufficientlysecure.keychain.provider; -import org.sufficientlysecure.keychain.Constants; - import android.net.Uri; import android.provider.BaseColumns; +import org.sufficientlysecure.keychain.Constants; + public class KeychainContract { interface KeyRingsColumns { @@ -57,10 +57,15 @@ public class KeychainContract { interface ApiAppsColumns { String PACKAGE_NAME = "package_name"; String PACKAGE_SIGNATURE = "package_signature"; + } + + interface ApiAppsAccountsColumns { + String ACCOUNT_NAME = "account_name"; String KEY_ID = "key_id"; // not a database id String ENCRYPTION_ALGORITHM = "encryption_algorithm"; String HASH_ALORITHM = "hash_algorithm"; String COMPRESSION = "compression"; + String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name } public static final class KeyTypes { @@ -88,18 +93,26 @@ public class KeychainContract { public static final String PATH_KEYS = "keys"; public static final String BASE_API_APPS = "api_apps"; - public static final String PATH_BY_PACKAGE_NAME = "package_name"; + public static final String PATH_ACCOUNTS = "accounts"; public static class KeyRings implements KeyRingsColumns, BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_KEY_RINGS).build(); - /** Use if multiple items get returned */ + /** + * Use if multiple items get returned + */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key_ring"; - /** Use if a single item is returned */ + /** + * Use if a single item is returned + */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key_ring"; + public static Uri buildUnifiedKeyRingsUri() { + return CONTENT_URI; + } + public static Uri buildPublicKeyRingsUri() { return CONTENT_URI.buildUpon().appendPath(PATH_PUBLIC).build(); } @@ -147,6 +160,7 @@ public class KeychainContract { } public static Uri buildSecretKeyRingsByEmailsUri(String emails) { + // TODO: encoded? return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_EMAILS) .appendPath(emails).build(); } @@ -161,10 +175,14 @@ public class KeychainContract { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_KEY_RINGS).build(); - /** Use if multiple items get returned */ + /** + * Use if multiple items get returned + */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.key"; - /** Use if a single item is returned */ + /** + * Use if a single item is returned + */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.key"; public static Uri buildPublicKeysUri(String keyRingRowId) { @@ -200,10 +218,14 @@ public class KeychainContract { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_KEY_RINGS).build(); - /** Use if multiple items get returned */ + /** + * Use if multiple items get returned + */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.user_id"; - /** Use if a single item is returned */ + /** + * Use if a single item is returned + */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.user_id"; public static Uri buildPublicUserIdsUri(String keyRingRowId) { @@ -239,20 +261,44 @@ public class KeychainContract { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_API_APPS).build(); - /** Use if multiple items get returned */ + /** + * Use if multiple items get returned + */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api_apps"; - /** Use if a single item is returned */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_apps"; + /** + * Use if a single item is returned + */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_app"; - public static Uri buildIdUri(String rowId) { - return CONTENT_URI.buildUpon().appendPath(rowId).build(); + public static Uri buildByPackageNameUri(String packageName) { + return CONTENT_URI.buildUpon().appendEncodedPath(packageName).build(); } + } - public static Uri buildByPackageNameUri(String packageName) { - return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName) + public static class ApiAccounts implements ApiAppsAccountsColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_API_APPS).build(); + + /** + * Use if multiple items get returned + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api_app.accounts"; + + /** + * Use if a single item is returned + */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_app.account"; + + public static Uri buildBaseUri(String packageName) { + return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS) .build(); } + + public static Uri buildByPackageAndAccountUri(String packageName, String accountName) { + return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS) + .appendEncodedPath(accountName).build(); + } } public static class DataStream { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 5f18ed6f9..8c33844b2 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -17,27 +17,29 @@ package org.sufficientlysecure.keychain.provider; +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import android.provider.BaseColumns; + import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns; import org.sufficientlysecure.keychain.util.Log; -import android.content.Context; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.provider.BaseColumns; - public class KeychainDatabase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "apg.db"; - private static final int DATABASE_VERSION = 7; + private static final int DATABASE_VERSION = 8; public interface Tables { String KEY_RINGS = "key_rings"; String KEYS = "keys"; String USER_IDS = "user_ids"; String API_APPS = "api_apps"; + String API_ACCOUNTS = "api_accounts"; } private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS @@ -62,26 +64,35 @@ public class KeychainDatabase extends SQLiteOpenHelper { + KeysColumns.KEY_DATA + " BLOB," + KeysColumns.RANK + " INTEGER, " + KeysColumns.FINGERPRINT + " BLOB, " - + KeysColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY(" - + KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "(" - + BaseColumns._ID + ") ON DELETE CASCADE)"; + + KeysColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, " + + "FOREIGN KEY(" + KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " + + Tables.KEY_RINGS + "(" + BaseColumns._ID + ") ON DELETE CASCADE)"; private static final String CREATE_USER_IDS = "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + UserIdsColumns.USER_ID + " TEXT, " + UserIdsColumns.RANK + " INTEGER, " - + UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY(" - + UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "(" - + BaseColumns._ID + ") ON DELETE CASCADE)"; + + UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, " + + "FOREIGN KEY(" + UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + + Tables.KEY_RINGS + "(" + BaseColumns._ID + ") ON DELETE CASCADE)"; private static final String CREATE_API_APPS = "CREATE TABLE IF NOT EXISTS " + Tables.API_APPS + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + ApiAppsColumns.PACKAGE_NAME + " TEXT UNIQUE, " - + ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB, " - + ApiAppsColumns.KEY_ID + " INT64, " - + ApiAppsColumns.ENCRYPTION_ALGORITHM + " INTEGER, " - + ApiAppsColumns.HASH_ALORITHM + " INTEGER, " - + ApiAppsColumns.COMPRESSION + " INTEGER)"; + + ApiAppsColumns.PACKAGE_NAME + " TEXT NOT NULL UNIQUE, " + + ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB)"; + + private static final String CREATE_API_APPS_ACCOUNTS = "CREATE TABLE IF NOT EXISTS " + Tables.API_ACCOUNTS + + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + ApiAppsAccountsColumns.ACCOUNT_NAME + " TEXT NOT NULL, " + + ApiAppsAccountsColumns.KEY_ID + " INT64, " + + ApiAppsAccountsColumns.ENCRYPTION_ALGORITHM + " INTEGER, " + + ApiAppsAccountsColumns.HASH_ALORITHM + " INTEGER, " + + ApiAppsAccountsColumns.COMPRESSION + " INTEGER, " + + ApiAppsAccountsColumns.PACKAGE_NAME + " TEXT NOT NULL, " + + "UNIQUE(" + ApiAppsAccountsColumns.ACCOUNT_NAME + ", " + + ApiAppsAccountsColumns.PACKAGE_NAME + "), " + + "FOREIGN KEY(" + ApiAppsAccountsColumns.PACKAGE_NAME + ") REFERENCES " + + Tables.API_APPS + "(" + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE)"; KeychainDatabase(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); @@ -95,6 +106,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { db.execSQL(CREATE_KEYS); db.execSQL(CREATE_USER_IDS); db.execSQL(CREATE_API_APPS); + db.execSQL(CREATE_API_APPS_ACCOUNTS); } @Override @@ -134,6 +146,12 @@ public class KeychainDatabase extends SQLiteOpenHelper { db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.FINGERPRINT + " BLOB;"); break; + case 7: + // new db layout for api apps + db.execSQL("DROP TABLE IF EXISTS " + Tables.API_APPS); + db.execSQL(CREATE_API_APPS); + db.execSQL(CREATE_API_APPS_ACCOUNTS); + break; default: break; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 781f36758..1c5e3ab36 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> * Copyright (C) 2010 Thialfihar <thi@thialfihar.org> * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,10 +17,20 @@ package org.sufficientlysecure.keychain.provider; -import java.util.Arrays; -import java.util.HashMap; +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.sqlite.SQLiteConstraintException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteQueryBuilder; +import android.net.Uri; +import android.provider.BaseColumns; +import android.text.TextUtils; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns; @@ -32,17 +42,8 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.util.Log; -import android.content.ContentProvider; -import android.content.ContentValues; -import android.content.UriMatcher; -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.database.sqlite.SQLiteConstraintException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteQueryBuilder; -import android.net.Uri; -import android.provider.BaseColumns; -import android.text.TextUtils; +import java.util.Arrays; +import java.util.HashMap; public class KeychainProvider extends ContentProvider { // public static final String ACTION_BROADCAST_DATABASE_CHANGE = Constants.PACKAGE_NAME @@ -63,6 +64,7 @@ public class KeychainProvider extends ContentProvider { private static final int PUBLIC_KEY_RING_USER_ID = 121; private static final int PUBLIC_KEY_RING_USER_ID_BY_ROW_ID = 122; + private static final int PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID = 123; private static final int SECRET_KEY_RING = 201; private static final int SECRET_KEY_RING_BY_ROW_ID = 202; @@ -78,8 +80,11 @@ public class KeychainProvider extends ContentProvider { private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222; private static final int API_APPS = 301; - private static final int API_APPS_BY_ROW_ID = 302; private static final int API_APPS_BY_PACKAGE_NAME = 303; + private static final int API_ACCOUNTS = 304; + private static final int API_ACCOUNTS_BY_ACCOUNT_NAME = 306; + + private static final int UNIFIED_KEY_RING = 401; // private static final int DATA_STREAM = 401; @@ -95,6 +100,15 @@ public class KeychainProvider extends ContentProvider { String authority = KeychainContract.CONTENT_AUTHORITY; /** + * unified key rings + * + * <pre> + * key_rings + * </pre> + */ + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS, UNIFIED_KEY_RING); + + /** * public key rings * * <pre> @@ -147,6 +161,7 @@ public class KeychainProvider extends ContentProvider { * <pre> * key_rings/public/#/user_ids * key_rings/public/#/user_ids/# + * key_rings/public/master_key_id/#/user_ids * </pre> */ matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" @@ -155,6 +170,10 @@ public class KeychainProvider extends ContentProvider { matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + KeychainContract.PATH_PUBLIC + "/#/" + KeychainContract.PATH_USER_IDS + "/#", PUBLIC_KEY_RING_USER_ID_BY_ROW_ID); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/" + + KeychainContract.PATH_PUBLIC + "/" + + KeychainContract.PATH_BY_MASTER_KEY_ID + "/*/" + KeychainContract.PATH_USER_IDS, + PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID); /** * secret key rings @@ -220,11 +239,22 @@ public class KeychainProvider extends ContentProvider { /** * API apps + * + * <pre> + * api_apps + * api_apps/_ (package name) + * + * api_apps/_/accounts + * api_apps/_/accounts/_ (account name) + * </pre> */ matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS); - matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/#", API_APPS_BY_ROW_ID); - matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/" - + KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_APPS_BY_PACKAGE_NAME); + matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*", API_APPS_BY_PACKAGE_NAME); + + matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/" + + KeychainContract.PATH_ACCOUNTS, API_ACCOUNTS); + matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/" + + KeychainContract.PATH_ACCOUNTS + "/*", API_ACCOUNTS_BY_ACCOUNT_NAME); /** * data stream @@ -238,7 +268,7 @@ public class KeychainProvider extends ContentProvider { return matcher; } - private KeychainDatabase mApgDatabase; + private KeychainDatabase mKeychainDatabase; /** * {@inheritDoc} @@ -246,7 +276,7 @@ public class KeychainProvider extends ContentProvider { @Override public boolean onCreate() { mUriMatcher = buildUriMatcher(); - mApgDatabase = new KeychainDatabase(getContext()); + mKeychainDatabase = new KeychainDatabase(getContext()); return true; } @@ -282,6 +312,7 @@ public class KeychainProvider extends ContentProvider { return Keys.CONTENT_ITEM_TYPE; case PUBLIC_KEY_RING_USER_ID: + case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID: case SECRET_KEY_RING_USER_ID: return UserIds.CONTENT_TYPE; @@ -292,10 +323,15 @@ public class KeychainProvider extends ContentProvider { case API_APPS: return ApiApps.CONTENT_TYPE; - case API_APPS_BY_ROW_ID: case API_APPS_BY_PACKAGE_NAME: return ApiApps.CONTENT_ITEM_TYPE; + case API_ACCOUNTS: + return ApiAccounts.CONTENT_TYPE; + + case API_ACCOUNTS_BY_ACCOUNT_NAME: + return ApiAccounts.CONTENT_ITEM_TYPE; + default: throw new UnsupportedOperationException("Unknown uri: " + uri); } @@ -304,7 +340,7 @@ public class KeychainProvider extends ContentProvider { /** * Returns type of the query (secret/public) * - * @param uri + * @param match * @return */ private int getKeyType(int match) { @@ -319,6 +355,7 @@ public class KeychainProvider extends ContentProvider { case PUBLIC_KEY_RING_KEY: case PUBLIC_KEY_RING_KEY_BY_ROW_ID: case PUBLIC_KEY_RING_USER_ID: + case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID: case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: type = KeyTypes.PUBLIC; break; @@ -356,15 +393,25 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(BaseColumns._ID, Tables.KEY_RINGS + "." + BaseColumns._ID); projectionMap.put(KeyRingsColumns.KEY_RING_DATA, Tables.KEY_RINGS + "." + KeyRingsColumns.KEY_RING_DATA); - projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID); + projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "." + + KeyRingsColumns.MASTER_KEY_ID); // TODO: deprecated master key id //projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEYS + "." + KeysColumns.KEY_ID); + projectionMap.put(KeysColumns.ALGORITHM, Tables.KEYS + "." + KeysColumns.ALGORITHM); + projectionMap.put(KeysColumns.KEY_SIZE, Tables.KEYS + "." + KeysColumns.KEY_SIZE); + projectionMap.put(KeysColumns.CREATION, Tables.KEYS + "." + KeysColumns.CREATION); + projectionMap.put(KeysColumns.EXPIRY, Tables.KEYS + "." + KeysColumns.EXPIRY); + projectionMap.put(KeysColumns.KEY_RING_ROW_ID, Tables.KEYS + "." + KeysColumns.KEY_RING_ROW_ID); projectionMap.put(KeysColumns.FINGERPRINT, Tables.KEYS + "." + KeysColumns.FINGERPRINT); projectionMap.put(KeysColumns.IS_REVOKED, Tables.KEYS + "." + KeysColumns.IS_REVOKED); projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID); + // type attribute is special: if there is any grouping, choose secret over public type + projectionMap.put(KeyRingsColumns.TYPE, + "MAX(" + Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + ") AS " + KeyRingsColumns.TYPE); + return projectionMap; } @@ -395,13 +442,27 @@ public class KeychainProvider extends ContentProvider { return projectionMap; } + private HashMap<String, String> getProjectionMapForUserIds() { + HashMap<String, String> projectionMap = new HashMap<String, String>(); + + projectionMap.put(BaseColumns._ID, Tables.USER_IDS + "." + BaseColumns._ID); + projectionMap.put(UserIdsColumns.USER_ID, Tables.USER_IDS + "." + UserIdsColumns.USER_ID); + projectionMap.put(UserIdsColumns.RANK, Tables.USER_IDS + "." + UserIdsColumns.RANK); + projectionMap.put(KeyRingsColumns.MASTER_KEY_ID, Tables.KEY_RINGS + "." + + KeyRingsColumns.MASTER_KEY_ID); + + return projectionMap; + } + /** * Builds default query for keyRings: KeyRings table is joined with UserIds and Keys */ private SQLiteQueryBuilder buildKeyRingQuery(SQLiteQueryBuilder qb, int match) { - // public or secret keyring - qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); - qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); + if (match != UNIFIED_KEY_RING) { + // public or secret keyring + qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.TYPE + " = "); + qb.appendWhereEscapeString(Integer.toString(getKeyType(match))); + } // join keyrings with keys and userIds // Only get user id and key with rank 0 (main user id and main key) @@ -451,11 +512,26 @@ public class KeychainProvider extends ContentProvider { Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")"); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - SQLiteDatabase db = mApgDatabase.getReadableDatabase(); + SQLiteDatabase db = mKeychainDatabase.getReadableDatabase(); int match = mUriMatcher.match(uri); + // all query() parameters, for good measure + String groupBy = null, having = null; + switch (match) { + case UNIFIED_KEY_RING: + qb = buildKeyRingQuery(qb, match); + + // GROUP BY so we don't get duplicates + groupBy = Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID; + + if (TextUtils.isEmpty(sortOrder)) { + sortOrder = KeyRings.TYPE + " DESC, " + + Tables.USER_IDS + "." + UserIdsColumns.USER_ID + " ASC"; + } + + break; case PUBLIC_KEY_RING: case SECRET_KEY_RING: qb = buildKeyRingQuery(qb, match); @@ -465,7 +541,6 @@ public class KeychainProvider extends ContentProvider { } break; - case PUBLIC_KEY_RING_BY_ROW_ID: case SECRET_KEY_RING_BY_ROW_ID: qb = buildKeyRingQuery(qb, match); @@ -478,7 +553,6 @@ public class KeychainProvider extends ContentProvider { } break; - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: case SECRET_KEY_RING_BY_MASTER_KEY_ID: qb = buildKeyRingQuery(qb, match); @@ -491,7 +565,6 @@ public class KeychainProvider extends ContentProvider { } break; - case SECRET_KEY_RING_BY_KEY_ID: case PUBLIC_KEY_RING_BY_KEY_ID: qb = buildKeyRingQueryWithSpecificKey(qb, match); @@ -504,7 +577,6 @@ public class KeychainProvider extends ContentProvider { } break; - case SECRET_KEY_RING_BY_EMAILS: case PUBLIC_KEY_RING_BY_EMAILS: qb = buildKeyRingQuery(qb, match); @@ -534,7 +606,6 @@ public class KeychainProvider extends ContentProvider { } break; - case SECRET_KEY_RING_BY_LIKE_EMAIL: case PUBLIC_KEY_RING_BY_LIKE_EMAIL: qb = buildKeyRingQuery(qb, match); @@ -550,7 +621,6 @@ public class KeychainProvider extends ContentProvider { + "))"); break; - case PUBLIC_KEY_RING_KEY: case SECRET_KEY_RING_KEY: qb.setTables(Tables.KEYS); @@ -563,7 +633,6 @@ public class KeychainProvider extends ContentProvider { qb.setProjectionMap(getProjectionMapForKeys()); break; - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: case SECRET_KEY_RING_KEY_BY_ROW_ID: qb.setTables(Tables.KEYS); @@ -579,7 +648,16 @@ public class KeychainProvider extends ContentProvider { qb.setProjectionMap(getProjectionMapForKeys()); break; + case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID: + qb.setTables(Tables.USER_IDS + " INNER JOIN " + Tables.KEY_RINGS + " ON " + "(" + + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "." + + KeysColumns.KEY_RING_ROW_ID + " )"); + qb.appendWhere(Tables.KEY_RINGS + "." + KeyRingsColumns.MASTER_KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(3)); + + qb.setProjectionMap(getProjectionMapForUserIds()); + break; case PUBLIC_KEY_RING_USER_ID: case SECRET_KEY_RING_USER_ID: qb.setTables(Tables.USER_IDS); @@ -587,7 +665,6 @@ public class KeychainProvider extends ContentProvider { qb.appendWhereEscapeString(uri.getPathSegments().get(2)); break; - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: case SECRET_KEY_RING_USER_ID_BY_ROW_ID: qb.setTables(Tables.USER_IDS); @@ -598,25 +675,31 @@ public class KeychainProvider extends ContentProvider { qb.appendWhereEscapeString(uri.getLastPathSegment()); break; - case API_APPS: qb.setTables(Tables.API_APPS); break; - case API_APPS_BY_ROW_ID: + case API_APPS_BY_PACKAGE_NAME: qb.setTables(Tables.API_APPS); - - qb.appendWhere(BaseColumns._ID + " = "); + qb.appendWhere(ApiApps.PACKAGE_NAME + " = "); qb.appendWhereEscapeString(uri.getLastPathSegment()); break; - case API_APPS_BY_PACKAGE_NAME: - qb.setTables(Tables.API_APPS); - qb.appendWhere(ApiApps.PACKAGE_NAME + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + case API_ACCOUNTS: + qb.setTables(Tables.API_ACCOUNTS); + qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); break; + case API_ACCOUNTS_BY_ACCOUNT_NAME: + qb.setTables(Tables.API_ACCOUNTS); + qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); + qb.appendWhere(" AND " + Tables.API_ACCOUNTS + "." + ApiAccounts.ACCOUNT_NAME + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + + break; default: throw new IllegalArgumentException("Unknown URI " + uri); @@ -630,7 +713,7 @@ public class KeychainProvider extends ContentProvider { orderBy = sortOrder; } - Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy); + Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, having, orderBy); // Tell the cursor what uri to watch, so it knows when its source data changes c.setNotificationUri(getContext().getContentResolver(), uri); @@ -653,7 +736,7 @@ public class KeychainProvider extends ContentProvider { public Uri insert(Uri uri, ContentValues values) { Log.d(Constants.TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")"); - final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); + final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase(); Uri rowUri = null; long rowId = -1; @@ -673,12 +756,14 @@ public class KeychainProvider extends ContentProvider { values.put(Keys.TYPE, KeyTypes.PUBLIC); rowId = db.insertOrThrow(Tables.KEYS, null, values); + // TODO: this is wrong: rowUri = Keys.buildPublicKeysUri(Long.toString(rowId)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; case PUBLIC_KEY_RING_USER_ID: rowId = db.insertOrThrow(Tables.USER_IDS, null, values); + // TODO: this is wrong: rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); @@ -695,18 +780,33 @@ public class KeychainProvider extends ContentProvider { values.put(Keys.TYPE, KeyTypes.SECRET); rowId = db.insertOrThrow(Tables.KEYS, null, values); + // TODO: this is wrong: rowUri = Keys.buildSecretKeysUri(Long.toString(rowId)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; case SECRET_KEY_RING_USER_ID: rowId = db.insertOrThrow(Tables.USER_IDS, null, values); + // TODO: this is wrong: rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId)); break; case API_APPS: rowId = db.insertOrThrow(Tables.API_APPS, null, values); - rowUri = ApiApps.buildIdUri(Long.toString(rowId)); +// rowUri = ApiApps.buildIdUri(Long.toString(rowId)); + + break; + case API_ACCOUNTS: + // set foreign key automatically based on given uri + // e.g., api_apps/com.example.app/accounts/ + String packageName = uri.getPathSegments().get(1); + values.put(ApiAccounts.PACKAGE_NAME, packageName); + + Log.d(Constants.TAG, "provider packageName: " + packageName); + + rowId = db.insertOrThrow(Tables.API_ACCOUNTS, null, values); + // TODO: this is wrong: +// rowUri = ApiAccounts.buildIdUri(Long.toString(rowId)); break; default: @@ -730,7 +830,7 @@ public class KeychainProvider extends ContentProvider { public int delete(Uri uri, String selection, String[] selectionArgs) { Log.v(Constants.TAG, "delete(uri=" + uri + ")"); - final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); + final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase(); int count; final int match = mUriMatcher.match(uri); @@ -766,12 +866,12 @@ public class KeychainProvider extends ContentProvider { count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection), selectionArgs); break; - case API_APPS_BY_ROW_ID: - count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, false, selection), + case API_APPS_BY_PACKAGE_NAME: + count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, selection), selectionArgs); break; - case API_APPS_BY_PACKAGE_NAME: - count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, true, selection), + case API_ACCOUNTS_BY_ACCOUNT_NAME: + count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, selection), selectionArgs); break; default: @@ -791,7 +891,7 @@ public class KeychainProvider extends ContentProvider { public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")"); - final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); + final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase(); String defaultSelection = null; int count = 0; @@ -836,13 +936,13 @@ public class KeychainProvider extends ContentProvider { count = db.update(Tables.USER_IDS, values, buildDefaultUserIdsSelection(uri, selection), selectionArgs); break; - case API_APPS_BY_ROW_ID: - count = db.update(Tables.API_APPS, values, - buildDefaultApiAppsSelection(uri, false, selection), selectionArgs); - break; case API_APPS_BY_PACKAGE_NAME: count = db.update(Tables.API_APPS, values, - buildDefaultApiAppsSelection(uri, true, selection), selectionArgs); + buildDefaultApiAppsSelection(uri, selection), selectionArgs); + break; + case API_ACCOUNTS_BY_ACCOUNT_NAME: + count = db.update(Tables.API_ACCOUNTS, values, + buildDefaultApiAccountsSelection(uri, selection), selectionArgs); break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); @@ -862,7 +962,8 @@ public class KeychainProvider extends ContentProvider { * Build default selection statement for KeyRings. If no extra selection is specified only build * where clause with rowId * - * @param uri + * @param defaultSelection + * @param keyType * @param selection * @return */ @@ -940,19 +1041,29 @@ public class KeychainProvider extends ContentProvider { * @param selection * @return */ - private String buildDefaultApiAppsSelection(Uri uri, boolean packageSelection, String selection) { - String lastPathSegment = uri.getLastPathSegment(); + private String buildDefaultApiAppsSelection(Uri uri, String selection) { + String packageName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment()); String andSelection = ""; if (!TextUtils.isEmpty(selection)) { andSelection = " AND (" + selection + ")"; } - if (packageSelection) { - return ApiApps.PACKAGE_NAME + "=" + lastPathSegment + andSelection; - } else { - return BaseColumns._ID + "=" + lastPathSegment + andSelection; + return ApiApps.PACKAGE_NAME + "=" + packageName + andSelection; + } + + private String buildDefaultApiAccountsSelection(Uri uri, String selection) { + String packageName = DatabaseUtils.sqlEscapeString(uri.getPathSegments().get(1)); + String accountName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment()); + + String andSelection = ""; + if (!TextUtils.isEmpty(selection)) { + andSelection = " AND (" + selection + ")"; } + + return ApiAccounts.PACKAGE_NAME + "=" + packageName + " AND " + + ApiAccounts.ACCOUNT_NAME + "=" + accountName + + andSelection; } // @Override diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobContract.java index a879d60a8..701ffc6af 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobContract.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobContract.java @@ -17,10 +17,9 @@ package org.sufficientlysecure.keychain.provider; -import org.sufficientlysecure.keychain.Constants; - import android.net.Uri; import android.provider.BaseColumns; +import org.sufficientlysecure.keychain.Constants; public class KeychainServiceBlobContract { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java index fcee76fd7..da1bcb2d9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobDatabase.java @@ -22,7 +22,6 @@ import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.provider.BaseColumns; - import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.BlobsColumns; public class KeychainServiceBlobDatabase extends SQLiteOpenHelper { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java index 5693e6de2..aa30e845d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java @@ -18,11 +18,6 @@ package org.sufficientlysecure.keychain.provider; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.Blobs; -import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.BlobsColumns; -import org.sufficientlysecure.keychain.util.Log; - import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; @@ -31,7 +26,10 @@ import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.provider.BaseColumns; - +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.Blobs; +import org.sufficientlysecure.keychain.provider.KeychainServiceBlobContract.BlobsColumns; +import org.sufficientlysecure.keychain.util.Log; import java.io.File; import java.io.FileNotFoundException; @@ -40,7 +38,7 @@ import java.util.List; import java.util.UUID; public class KeychainServiceBlobProvider extends ContentProvider { - private static final String STORE_PATH = Constants.path.APP_DIR + "/ApgBlobs"; + private static final String STORE_PATH = Constants.Path.APP_DIR + "/KeychainBlobs"; private KeychainServiceBlobDatabase mBlobDatabase = null; @@ -55,7 +53,9 @@ public class KeychainServiceBlobProvider extends ContentProvider { return true; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public Uri insert(Uri uri, ContentValues ignored) { // ContentValues are actually ignored, because we want to store a blob with no more @@ -74,7 +74,9 @@ public class KeychainServiceBlobProvider extends ContentProvider { return Uri.withAppendedPath(insertedUri, password); } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws SecurityException, FileNotFoundException { @@ -91,9 +93,9 @@ public class KeychainServiceBlobProvider extends ContentProvider { // get the data SQLiteDatabase db = mBlobDatabase.getReadableDatabase(); - Cursor result = db.query(KeychainServiceBlobDatabase.TABLE, new String[] { BaseColumns._ID }, + Cursor result = db.query(KeychainServiceBlobDatabase.TABLE, new String[]{BaseColumns._ID}, BaseColumns._ID + " = ? and " + BlobsColumns.KEY + " = ?", - new String[] { id, key }, null, null, null); + new String[]{id, key}, null, null, null); if (result.getCount() == 0) { // either the key is wrong or no id exists @@ -124,26 +126,34 @@ public class KeychainServiceBlobProvider extends ContentProvider { return null; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public String getType(Uri uri) { return null; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { + String sortOrder) { return null; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { return 0; } - /** {@inheritDoc} */ + /** + * {@inheritDoc} + */ @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { return 0; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index fef3d391b..b47c4946f 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -17,10 +17,11 @@ package org.sufficientlysecure.keychain.provider; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Date; +import android.content.*; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.net.Uri; +import android.os.RemoteException; import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.openpgp.PGPKeyRing; @@ -38,19 +39,15 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; -import org.sufficientlysecure.keychain.service.remote.AppSettings; +import org.sufficientlysecure.keychain.remote.AccountSettings; +import org.sufficientlysecure.keychain.remote.AppSettings; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; -import android.content.ContentProviderOperation; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.OperationApplicationException; -import android.database.Cursor; -import android.database.DatabaseUtils; -import android.net.Uri; -import android.os.RemoteException; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; public class ProviderHelper { @@ -87,7 +84,7 @@ public class ProviderHelper { } /** - * Retrieves the actual PGPPublicKeyRing object from the database blob based on the maserKeyId + * Retrieves the actual PGPPublicKeyRing object from the database blob based on the masterKeyId */ public static PGPPublicKeyRing getPGPPublicKeyRingByMasterKeyId(Context context, long masterKeyId) { @@ -110,11 +107,8 @@ public class ProviderHelper { */ public static PGPPublicKey getPGPPublicKeyByKeyId(Context context, long keyId) { PGPPublicKeyRing keyRing = getPGPPublicKeyRingByKeyId(context, keyId); - if (keyRing == null) { - return null; - } - return keyRing.getPublicKey(keyId); + return (keyRing == null) ? null : keyRing.getPublicKey(keyId); } /** @@ -149,11 +143,8 @@ public class ProviderHelper { */ public static PGPSecretKey getPGPSecretKeyByKeyId(Context context, long keyId) { PGPSecretKeyRing keyRing = getPGPSecretKeyRingByKeyId(context, keyId); - if (keyRing == null) { - return null; - } - return keyRing.getSecretKey(keyId); + return (keyRing == null) ? null : keyRing.getSecretKey(keyId); } /** @@ -168,7 +159,8 @@ public class ProviderHelper { // get current _ID of key long currentRowId = -1; - Cursor oldQuery = context.getContentResolver().query(deleteUri, new String[]{KeyRings._ID}, null, null, null); + Cursor oldQuery = context.getContentResolver() + .query(deleteUri, new String[]{KeyRings._ID}, null, null, null); if (oldQuery != null && oldQuery.moveToFirst()) { currentRowId = oldQuery.getLong(0); } else { @@ -184,10 +176,12 @@ public class ProviderHelper { ContentValues values = new ContentValues(); // use exactly the same _ID again to replace key in-place. - // NOTE: If we would not use the same _ID again, getting back to the ViewKeyActivity would result in Nullpointer, + // NOTE: If we would not use the same _ID again, + // getting back to the ViewKeyActivity would result in Nullpointer, // because the currently loaded key would be gone from the database - if (currentRowId != -1) + if (currentRowId != -1) { values.put(KeyRings._ID, currentRowId); + } values.put(KeyRings.MASTER_KEY_ID, masterKeyId); values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded()); @@ -211,8 +205,11 @@ public class ProviderHelper { ++userIdRank; } - for (PGPSignature certification : new IterableIterator<PGPSignature>(masterKey.getSignaturesOfType(PGPSignature.POSITIVE_CERTIFICATION))) { - //TODO: how to do this?? we need to verify the signatures again and again when they are displayed... + for (PGPSignature certification : + new IterableIterator<PGPSignature>( + masterKey.getSignaturesOfType(PGPSignature.POSITIVE_CERTIFICATION))) { + // TODO: how to do this?? + // we need to verify the signatures again and again when they are displayed... // if (certification.verify // operations.add(buildPublicKeyOperations(context, keyRingRowId, key, rank)); } @@ -239,7 +236,8 @@ public class ProviderHelper { // get current _ID of key long currentRowId = -1; - Cursor oldQuery = context.getContentResolver().query(deleteUri, new String[]{KeyRings._ID}, null, null, null); + Cursor oldQuery = context.getContentResolver() + .query(deleteUri, new String[]{KeyRings._ID}, null, null, null); if (oldQuery != null && oldQuery.moveToFirst()) { currentRowId = oldQuery.getLong(0); } else { @@ -255,10 +253,12 @@ public class ProviderHelper { ContentValues values = new ContentValues(); // use exactly the same _ID again to replace key in-place. - // NOTE: If we would not use the same _ID again, getting back to the ViewKeyActivity would result in Nullpointer, + // NOTE: If we would not use the same _ID again, + // getting back to the ViewKeyActivity would result in Nullpointer, // because the currently loaded key would be gone from the database - if (currentRowId != -1) + if (currentRowId != -1) { values.put(KeyRings._ID, currentRowId); + } values.put(KeyRings.MASTER_KEY_ID, masterKeyId); values.put(KeyRings.KEY_RING_DATA, keyRing.getEncoded()); @@ -341,10 +341,10 @@ public class ProviderHelper { long keyRingRowId, PGPSecretKey key, int rank) throws IOException { ContentValues values = new ContentValues(); - boolean has_private = true; + boolean hasPrivate = true; if (key.isMasterKey()) { - if (PgpKeyHelper.isSecretKeyPrivateEmpty(key)) { - has_private = false; + if (key.isPrivateKeyEmpty()) { + hasPrivate = false; } } @@ -352,9 +352,9 @@ public class ProviderHelper { values.put(Keys.IS_MASTER_KEY, key.isMasterKey()); values.put(Keys.ALGORITHM, key.getPublicKey().getAlgorithm()); values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength()); - values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key) && has_private)); - values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key) && has_private)); - values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key) && has_private); + values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key) && hasPrivate)); + values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key) && hasPrivate)); + values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key) && hasPrivate); values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked()); values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000); Date expiryDate = PgpKeyHelper.getExpiryDate(key); @@ -411,6 +411,30 @@ public class ProviderHelper { } /** + * Private helper method + */ + private static ArrayList<Long> getKeyRingsRowIds(Context context, Uri queryUri) { + Cursor cursor = context.getContentResolver().query(queryUri, + new String[]{KeyRings._ID}, null, null, null); + + ArrayList<Long> rowIds = new ArrayList<Long>(); + if (cursor != null) { + int idCol = cursor.getColumnIndex(KeyRings._ID); + if (cursor.moveToFirst()) { + do { + rowIds.add(cursor.getLong(idCol)); + } while (cursor.moveToNext()); + } + } + + if (cursor != null) { + cursor.close(); + } + + return rowIds; + } + + /** * Retrieves ids of all SecretKeyRings */ public static ArrayList<Long> getSecretKeyRingsMasterKeyIds(Context context) { @@ -426,6 +450,22 @@ public class ProviderHelper { return getKeyRingsMasterKeyIds(context, queryUri); } + /** + * Retrieves ids of all SecretKeyRings + */ + public static ArrayList<Long> getSecretKeyRingsRowIds(Context context) { + Uri queryUri = KeyRings.buildSecretKeyRingsUri(); + return getKeyRingsRowIds(context, queryUri); + } + + /** + * Retrieves ids of all PublicKeyRings + */ + public static ArrayList<Long> getPublicKeyRingsRowIds(Context context) { + Uri queryUri = KeyRings.buildPublicKeyRingsUri(); + return getKeyRingsRowIds(context, queryUri); + } + public static void deletePublicKeyRing(Context context, long rowId) { ContentResolver cr = context.getContentResolver(); cr.delete(KeyRings.buildPublicKeyRingsUri(Long.toString(rowId)), null, null); @@ -436,6 +476,15 @@ public class ProviderHelper { cr.delete(KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)), null, null); } + public static void deleteUnifiedKeyRing(Context context, String masterKeyId, boolean isSecretKey) { + ContentResolver cr = context.getContentResolver(); + cr.delete(KeyRings.buildPublicKeyRingsByMasterKeyIdUri(masterKeyId), null, null); + if (isSecretKey) { + cr.delete(KeyRings.buildSecretKeyRingsByMasterKeyIdUri(masterKeyId), null, null); + } + + } + /** * Get master key id of keyring by its row id */ @@ -449,13 +498,14 @@ public class ProviderHelper { */ public static boolean getSecretMasterKeyCanCertify(Context context, long keyRingRowId) { Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId)); - return getMasterKeyCanCertify(context, queryUri, keyRingRowId); + return getMasterKeyCanCertify(context, queryUri); } /** * Private helper method to get master key private empty status of keyring by its row id */ - private static boolean getMasterKeyCanCertify(Context context, Uri queryUri, long keyRingRowId) { + + public static boolean getMasterKeyCanCertify(Context context, Uri queryUri) { String[] projection = new String[]{ KeyRings.MASTER_KEY_ID, "(SELECT COUNT(sign_keys." + Keys._ID + ") FROM " + Tables.KEYS @@ -481,6 +531,12 @@ public class ProviderHelper { return (masterKeyId > 0); } + public static boolean hasSecretKeyByMasterKeyId(Context context, long masterKeyId) { + Uri queryUri = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(Long.toString(masterKeyId)); + // see if we can get our master key id back from the uri + return getMasterKeyId(context, queryUri) == masterKeyId; + } + /** * Get master key id of keyring by its row id */ @@ -512,6 +568,26 @@ public class ProviderHelper { return masterKeyId; } + public static long getRowId(Context context, Uri queryUri) { + String[] projection = new String[]{KeyRings._ID}; + Cursor cursor = context.getContentResolver().query(queryUri, projection, null, null, null); + + long rowId = 0; + try { + if (cursor != null && cursor.moveToFirst()) { + int idCol = cursor.getColumnIndexOrThrow(KeyRings._ID); + + rowId = cursor.getLong(idCol); + } + } finally { + if (cursor != null) { + cursor.close(); + } + } + + return rowId; + } + /** * Get fingerprint of key */ @@ -733,19 +809,28 @@ public class ProviderHelper { ContentValues values = new ContentValues(); values.put(ApiApps.PACKAGE_NAME, appSettings.getPackageName()); values.put(ApiApps.PACKAGE_SIGNATURE, appSettings.getPackageSignature()); - values.put(ApiApps.KEY_ID, appSettings.getKeyId()); - values.put(ApiApps.COMPRESSION, appSettings.getCompression()); - values.put(ApiApps.ENCRYPTION_ALGORITHM, appSettings.getEncryptionAlgorithm()); - values.put(ApiApps.HASH_ALORITHM, appSettings.getHashAlgorithm()); + return values; + } + private static ContentValues contentValueForApiAccounts(AccountSettings accSettings) { + ContentValues values = new ContentValues(); + values.put(KeychainContract.ApiAccounts.ACCOUNT_NAME, accSettings.getAccountName()); + values.put(KeychainContract.ApiAccounts.KEY_ID, accSettings.getKeyId()); + values.put(KeychainContract.ApiAccounts.COMPRESSION, accSettings.getCompression()); + values.put(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM, accSettings.getEncryptionAlgorithm()); + values.put(KeychainContract.ApiAccounts.HASH_ALORITHM, accSettings.getHashAlgorithm()); return values; } public static void insertApiApp(Context context, AppSettings appSettings) { - context.getContentResolver().insert(ApiApps.CONTENT_URI, + context.getContentResolver().insert(KeychainContract.ApiApps.CONTENT_URI, contentValueForApiApps(appSettings)); } + public static void insertApiAccount(Context context, Uri uri, AccountSettings accSettings) { + context.getContentResolver().insert(uri, contentValueForApiAccounts(accSettings)); + } + public static void updateApiApp(Context context, AppSettings appSettings, Uri uri) { if (context.getContentResolver().update(uri, contentValueForApiApps(appSettings), null, null) <= 0) { @@ -753,30 +838,59 @@ public class ProviderHelper { } } + public static void updateApiAccount(Context context, AccountSettings accSettings, Uri uri) { + if (context.getContentResolver().update(uri, contentValueForApiAccounts(accSettings), null, + null) <= 0) { + throw new RuntimeException(); + } + } + + /** + * Must be an uri pointing to an account + * + * @param context + * @param uri + * @return + */ public static AppSettings getApiAppSettings(Context context, Uri uri) { AppSettings settings = null; Cursor cur = context.getContentResolver().query(uri, null, null, null, null); if (cur != null && cur.moveToFirst()) { settings = new AppSettings(); - settings.setPackageName(cur.getString(cur - .getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME))); - settings.setPackageSignature(cur.getBlob(cur - .getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE))); - settings.setKeyId(cur.getLong(cur.getColumnIndex(KeychainContract.ApiApps.KEY_ID))); - settings.setCompression(cur.getInt(cur - .getColumnIndexOrThrow(KeychainContract.ApiApps.COMPRESSION))); - settings.setHashAlgorithm(cur.getInt(cur - .getColumnIndexOrThrow(KeychainContract.ApiApps.HASH_ALORITHM))); - settings.setEncryptionAlgorithm(cur.getInt(cur - .getColumnIndexOrThrow(KeychainContract.ApiApps.ENCRYPTION_ALGORITHM))); + settings.setPackageName(cur.getString( + cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME))); + settings.setPackageSignature(cur.getBlob( + cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE))); + } + + return settings; + } + + public static AccountSettings getApiAccountSettings(Context context, Uri uri) { + AccountSettings settings = null; + + Cursor cur = context.getContentResolver().query(uri, null, null, null, null); + if (cur != null && cur.moveToFirst()) { + settings = new AccountSettings(); + + settings.setAccountName(cur.getString( + cur.getColumnIndex(KeychainContract.ApiAccounts.ACCOUNT_NAME))); + settings.setKeyId(cur.getLong( + cur.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID))); + settings.setCompression(cur.getInt( + cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.COMPRESSION))); + settings.setHashAlgorithm(cur.getInt( + cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.HASH_ALORITHM))); + settings.setEncryptionAlgorithm(cur.getInt( + cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM))); } return settings; } public static byte[] getApiAppSignature(Context context, String packageName) { - Uri queryUri = KeychainContract.ApiApps.buildByPackageNameUri(packageName); + Uri queryUri = ApiApps.buildByPackageNameUri(packageName); String[] projection = new String[]{ApiApps.PACKAGE_SIGNATURE}; |