mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-30 12:32:17 -05:00
db-overhaul: reintroduce by-mail and by-subkey queries
the only thing left to fix after the db upgrade (that I can think of right now) are key exports
This commit is contained in:
parent
59f4b4e3e7
commit
d921cca913
@ -83,10 +83,9 @@ public class KeychainContract {
|
|||||||
|
|
||||||
public static final String PATH_UNIFIED = "unified";
|
public static final String PATH_UNIFIED = "unified";
|
||||||
|
|
||||||
public static final String PATH_BY_MASTER_KEY_ID = "master_key_id";
|
public static final String PATH_FIND = "find";
|
||||||
public static final String PATH_BY_KEY_ID = "key_id";
|
public static final String PATH_BY_EMAIL = "email";
|
||||||
public static final String PATH_BY_EMAILS = "emails";
|
public static final String PATH_BY_SUBKEY = "subkey";
|
||||||
public static final String PATH_BY_LIKE_EMAIL = "like_email";
|
|
||||||
|
|
||||||
public static final String PATH_PUBLIC = "public";
|
public static final String PATH_PUBLIC = "public";
|
||||||
public static final String PATH_SECRET = "secret";
|
public static final String PATH_SECRET = "secret";
|
||||||
@ -109,20 +108,24 @@ public class KeychainContract {
|
|||||||
public static Uri buildUnifiedKeyRingsUri() {
|
public static Uri buildUnifiedKeyRingsUri() {
|
||||||
return CONTENT_URI.buildUpon().appendPath(PATH_UNIFIED).build();
|
return CONTENT_URI.buildUpon().appendPath(PATH_UNIFIED).build();
|
||||||
}
|
}
|
||||||
public static Uri buildUnifiedKeyRingsByEmailUri(String email) {
|
|
||||||
return CONTENT_URI.buildUpon().appendPath("email:" + email).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Uri buildGenericKeyRingUri(String masterKeyId) {
|
public static Uri buildGenericKeyRingUri(String masterKeyId) {
|
||||||
return CONTENT_URI.buildUpon().appendPath(masterKeyId).build();
|
return CONTENT_URI.buildUpon().appendPath(masterKeyId).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Uri buildUnifiedKeyRingUri(String masterKeyId) {
|
public static Uri buildUnifiedKeyRingUri(String masterKeyId) {
|
||||||
return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_UNIFIED).build();
|
return CONTENT_URI.buildUpon().appendPath(masterKeyId).appendPath(PATH_UNIFIED).build();
|
||||||
}
|
}
|
||||||
public static Uri buildUnifiedKeyRingUri(Uri uri) {
|
public static Uri buildUnifiedKeyRingUri(Uri uri) {
|
||||||
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_UNIFIED).build();
|
return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_UNIFIED).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Uri buildUnifiedKeyRingsFindByEmailUri(String email) {
|
||||||
|
return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_EMAIL).appendPath(email).build();
|
||||||
|
}
|
||||||
|
public static Uri buildUnifiedKeyRingsFindBySubkeyUri(String subkey) {
|
||||||
|
return CONTENT_URI.buildUpon().appendPath(PATH_FIND).appendPath(PATH_BY_SUBKEY).appendPath(subkey).build();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class KeyRingData implements KeyRingsColumns, BaseColumns {
|
public static class KeyRingData implements KeyRingsColumns, BaseColumns {
|
||||||
|
@ -57,7 +57,10 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
private static final int API_ACCOUNTS = 304;
|
private static final int API_ACCOUNTS = 304;
|
||||||
private static final int API_ACCOUNTS_BY_ACCOUNT_NAME = 306;
|
private static final int API_ACCOUNTS_BY_ACCOUNT_NAME = 306;
|
||||||
|
|
||||||
// private static final int DATA_STREAM = 401;
|
private static final int KEY_RINGS_FIND_BY_EMAIL = 400;
|
||||||
|
private static final int KEY_RINGS_FIND_BY_SUBKEY = 401;
|
||||||
|
|
||||||
|
// private static final int DATA_STREAM = 501;
|
||||||
|
|
||||||
protected UriMatcher mUriMatcher;
|
protected UriMatcher mUriMatcher;
|
||||||
|
|
||||||
@ -85,6 +88,20 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
+ "/" + KeychainContract.PATH_PUBLIC,
|
+ "/" + KeychainContract.PATH_PUBLIC,
|
||||||
KEY_RINGS_PUBLIC);
|
KEY_RINGS_PUBLIC);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find by criteria other than master key id
|
||||||
|
*
|
||||||
|
* key_rings/find/email/_
|
||||||
|
* key_rings/find/subkey/_
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
|
||||||
|
+ KeychainContract.PATH_FIND + "/" + KeychainContract.PATH_BY_EMAIL + "/*",
|
||||||
|
KEY_RINGS_FIND_BY_EMAIL);
|
||||||
|
matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/"
|
||||||
|
+ KeychainContract.PATH_FIND + "/" + KeychainContract.PATH_BY_SUBKEY + "/*",
|
||||||
|
KEY_RINGS_FIND_BY_SUBKEY);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* list key_ring specifics
|
* list key_ring specifics
|
||||||
*
|
*
|
||||||
@ -200,7 +217,6 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")");
|
Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")");
|
||||||
|
|
||||||
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
||||||
SQLiteDatabase db = mKeychainDatabase.getReadableDatabase();
|
|
||||||
|
|
||||||
int match = mUriMatcher.match(uri);
|
int match = mUriMatcher.match(uri);
|
||||||
|
|
||||||
@ -209,7 +225,9 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
|
|
||||||
switch (match) {
|
switch (match) {
|
||||||
case KEY_RING_UNIFIED:
|
case KEY_RING_UNIFIED:
|
||||||
case KEY_RINGS_UNIFIED: {
|
case KEY_RINGS_UNIFIED:
|
||||||
|
case KEY_RINGS_FIND_BY_EMAIL:
|
||||||
|
case KEY_RINGS_FIND_BY_SUBKEY: {
|
||||||
HashMap<String, String> projectionMap = new HashMap<String, String>();
|
HashMap<String, String> projectionMap = new HashMap<String, String>();
|
||||||
projectionMap.put(KeyRings._ID, Tables.KEYS + ".oid AS _id");
|
projectionMap.put(KeyRings._ID, Tables.KEYS + ".oid AS _id");
|
||||||
projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID);
|
projectionMap.put(KeyRings.MASTER_KEY_ID, Tables.KEYS + "." + Keys.MASTER_KEY_ID);
|
||||||
@ -242,13 +260,64 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
);
|
);
|
||||||
qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0");
|
qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0");
|
||||||
|
|
||||||
if(match == KEY_RING_UNIFIED) {
|
switch(match) {
|
||||||
qb.appendWhere(" AND " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = ");
|
case KEY_RING_UNIFIED: {
|
||||||
qb.appendWhereEscapeString(uri.getPathSegments().get(1));
|
qb.appendWhere(" AND " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = ");
|
||||||
} else if (TextUtils.isEmpty(sortOrder)) {
|
qb.appendWhereEscapeString(uri.getPathSegments().get(1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case KEY_RINGS_FIND_BY_SUBKEY: {
|
||||||
|
try {
|
||||||
|
String subkey = Long.valueOf(uri.getLastPathSegment()).toString();
|
||||||
|
qb.appendWhere(" AND EXISTS ("
|
||||||
|
+ " SELECT 1 FROM " + Tables.KEYS + " AS tmp"
|
||||||
|
+ " WHERE tmp." + UserIds.MASTER_KEY_ID
|
||||||
|
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
|
||||||
|
+ " AND tmp." + Keys.KEY_ID + " = " + subkey + ""
|
||||||
|
+ ")");
|
||||||
|
} catch(NumberFormatException e) {
|
||||||
|
Log.e(Constants.TAG, "Malformed find by subkey query!", e);
|
||||||
|
qb.appendWhere(" AND 0");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case KEY_RINGS_FIND_BY_EMAIL: {
|
||||||
|
String chunks[] = uri.getLastPathSegment().split(" *, *");
|
||||||
|
boolean gotCondition = false;
|
||||||
|
String emailWhere = "";
|
||||||
|
// JAVA ♥
|
||||||
|
for (int i = 0; i < chunks.length; ++i) {
|
||||||
|
if (chunks[i].length() == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (i != 0) {
|
||||||
|
emailWhere += " OR ";
|
||||||
|
}
|
||||||
|
emailWhere += "tmp." + UserIds.USER_ID + " LIKE ";
|
||||||
|
// match '*<email>', so it has to be at the *end* of the user id
|
||||||
|
emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
|
||||||
|
gotCondition = true;
|
||||||
|
}
|
||||||
|
if(gotCondition) {
|
||||||
|
qb.appendWhere(" AND EXISTS ("
|
||||||
|
+ " SELECT 1 FROM " + Tables.USER_IDS + " AS tmp"
|
||||||
|
+ " WHERE tmp." + UserIds.MASTER_KEY_ID
|
||||||
|
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
|
||||||
|
+ " AND (" + emailWhere + ")"
|
||||||
|
+ ")");
|
||||||
|
} else {
|
||||||
|
// TODO better way to do this?
|
||||||
|
Log.e(Constants.TAG, "Malformed find by email query!");
|
||||||
|
qb.appendWhere(" AND 0");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(sortOrder)) {
|
||||||
sortOrder =
|
sortOrder =
|
||||||
Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID + " IS NULL DESC"
|
Tables.KEY_RINGS_SECRET + "." + KeyRings.MASTER_KEY_ID + " IS NULL ASC, "
|
||||||
+ Tables.USER_IDS + "." + UserIds.USER_ID + " ASC";
|
+ Tables.USER_IDS + "." + UserIds.USER_ID + " ASC";
|
||||||
}
|
}
|
||||||
|
|
||||||
// uri to watch is all /key_rings/
|
// uri to watch is all /key_rings/
|
||||||
@ -256,33 +325,6 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/*case SECRET_KEY_RING_BY_EMAILS:
|
|
||||||
case PUBLIC_KEY_RING_BY_EMAILS:
|
|
||||||
qb = buildKeyRingQuery(qb, match);
|
|
||||||
|
|
||||||
String emails = uri.getLastPathSegment();
|
|
||||||
String chunks[] = emails.split(" *, *");
|
|
||||||
boolean gotCondition = false;
|
|
||||||
String emailWhere = "";
|
|
||||||
for (int i = 0; i < chunks.length; ++i) {
|
|
||||||
if (chunks[i].length() == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (i != 0) {
|
|
||||||
emailWhere += " OR ";
|
|
||||||
}
|
|
||||||
emailWhere += "tmp." + UserIds.USER_ID + " LIKE ";
|
|
||||||
// match '*<email>', so it has to be at the *end* of the user id
|
|
||||||
emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
|
|
||||||
gotCondition = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gotCondition) {
|
|
||||||
qb.appendWhere(" AND EXISTS (SELECT tmp." + Base._ID + " FROM "
|
|
||||||
+ Tables.USER_IDS + " AS tmp WHERE tmp." + UserIds.KEY_RING_ROW_ID
|
|
||||||
+ " = " + Tables.KEY_RINGS + "." + Base._ID + " AND (" + emailWhere
|
|
||||||
+ "))");
|
|
||||||
}*/
|
|
||||||
|
|
||||||
case KEY_RING_KEYS: {
|
case KEY_RING_KEYS: {
|
||||||
HashMap<String, String> projectionMap = new HashMap<String, String>();
|
HashMap<String, String> projectionMap = new HashMap<String, String>();
|
||||||
@ -387,7 +429,7 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown URI " + uri);
|
throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,6 +441,7 @@ public class KeychainProvider extends ContentProvider {
|
|||||||
orderBy = sortOrder;
|
orderBy = sortOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SQLiteDatabase db = mKeychainDatabase.getReadableDatabase();
|
||||||
Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, having, 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
|
// Tell the cursor what uri to watch, so it knows when its source data changes
|
||||||
|
@ -61,15 +61,15 @@ public class OpenPgpService extends RemoteService {
|
|||||||
ArrayList<Long> keyIds = new ArrayList<Long>();
|
ArrayList<Long> keyIds = new ArrayList<Long>();
|
||||||
|
|
||||||
boolean missingUserIdsCheck = false;
|
boolean missingUserIdsCheck = false;
|
||||||
boolean dublicateUserIdsCheck = false;
|
boolean duplicateUserIdsCheck = false;
|
||||||
ArrayList<String> missingUserIds = new ArrayList<String>();
|
ArrayList<String> missingUserIds = new ArrayList<String>();
|
||||||
ArrayList<String> dublicateUserIds = new ArrayList<String>();
|
ArrayList<String> duplicateUserIds = new ArrayList<String>();
|
||||||
|
|
||||||
for (String email : encryptionUserIds) {
|
for (String email : encryptionUserIds) {
|
||||||
Uri uri = KeychainContract.KeyRings.buildUnifiedKeyRingsByEmailUri(email);
|
Uri uri = KeyRings.buildUnifiedKeyRingsFindByEmailUri(email);
|
||||||
Cursor cur = getContentResolver().query(uri, null, null, null, null);
|
Cursor cur = getContentResolver().query(uri, null, null, null, null);
|
||||||
if (cur.moveToFirst()) {
|
if (cur.moveToFirst()) {
|
||||||
long id = cur.getLong(cur.getColumnIndex(KeychainContract.KeyRings.MASTER_KEY_ID));
|
long id = cur.getLong(cur.getColumnIndex(KeyRings.MASTER_KEY_ID));
|
||||||
keyIds.add(id);
|
keyIds.add(id);
|
||||||
} else {
|
} else {
|
||||||
missingUserIdsCheck = true;
|
missingUserIdsCheck = true;
|
||||||
@ -77,8 +77,8 @@ public class OpenPgpService extends RemoteService {
|
|||||||
Log.d(Constants.TAG, "user id missing");
|
Log.d(Constants.TAG, "user id missing");
|
||||||
}
|
}
|
||||||
if (cur.moveToNext()) {
|
if (cur.moveToNext()) {
|
||||||
dublicateUserIdsCheck = true;
|
duplicateUserIdsCheck = true;
|
||||||
dublicateUserIds.add(email);
|
duplicateUserIds.add(email);
|
||||||
Log.d(Constants.TAG, "more than one user id with the same email");
|
Log.d(Constants.TAG, "more than one user id with the same email");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,13 +90,13 @@ public class OpenPgpService extends RemoteService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// allow the user to verify pub key selection
|
// allow the user to verify pub key selection
|
||||||
if (missingUserIdsCheck || dublicateUserIdsCheck) {
|
if (missingUserIdsCheck || duplicateUserIdsCheck) {
|
||||||
// build PendingIntent
|
// build PendingIntent
|
||||||
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class);
|
||||||
intent.setAction(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS);
|
intent.setAction(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS);
|
||||||
intent.putExtra(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
|
intent.putExtra(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray);
|
||||||
intent.putExtra(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
|
intent.putExtra(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds);
|
||||||
intent.putExtra(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS, dublicateUserIds);
|
intent.putExtra(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS, duplicateUserIds);
|
||||||
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data);
|
||||||
|
|
||||||
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
|
PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0,
|
||||||
|
@ -46,6 +46,7 @@ import org.sufficientlysecure.keychain.Constants;
|
|||||||
import org.sufficientlysecure.keychain.Id;
|
import org.sufficientlysecure.keychain.Id;
|
||||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
import org.sufficientlysecure.keychain.helper.Preferences;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -170,15 +171,11 @@ public class PassphraseCacheService extends Service {
|
|||||||
// try to get master key id which is used as an identifier for cached passphrases
|
// try to get master key id which is used as an identifier for cached passphrases
|
||||||
long masterKeyId = keyId;
|
long masterKeyId = keyId;
|
||||||
if (masterKeyId != Id.key.symmetric) {
|
if (masterKeyId != Id.key.symmetric) {
|
||||||
PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingWithKeyId(this, keyId);
|
masterKeyId = ProviderHelper.getMasterKeyId(this,
|
||||||
if (keyRing == null) {
|
KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId)));
|
||||||
|
// Failure
|
||||||
|
if(masterKeyId == 0)
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
PGPSecretKey masterKey = keyRing.getSecretKey();
|
|
||||||
if (masterKey == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
masterKeyId = masterKey.getKeyID();
|
|
||||||
}
|
}
|
||||||
Log.d(TAG, "getCachedPassphraseImpl() for masterKeyId " + masterKeyId);
|
Log.d(TAG, "getCachedPassphraseImpl() for masterKeyId " + masterKeyId);
|
||||||
|
|
||||||
|
@ -160,23 +160,11 @@ public class EncryptAsymmetricFragment extends Fragment {
|
|||||||
if (preselectedEncryptionKeyIds != null) {
|
if (preselectedEncryptionKeyIds != null) {
|
||||||
Vector<Long> goodIds = new Vector<Long>();
|
Vector<Long> goodIds = new Vector<Long>();
|
||||||
for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) {
|
for (int i = 0; i < preselectedEncryptionKeyIds.length; ++i) {
|
||||||
// TODO: don't use bouncy castle objects!
|
long id = ProviderHelper.getMasterKeyId(getActivity(),
|
||||||
|
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(preselectedEncryptionKeyIds[i]))
|
||||||
PGPPublicKeyRing keyRing = ProviderHelper.getPGPPublicKeyRingWithKeyId(getActivity(),
|
);
|
||||||
preselectedEncryptionKeyIds[i]);
|
// TODO check for available encrypt keys... is this even relevant?
|
||||||
PGPPublicKey masterKey;
|
goodIds.add(id);
|
||||||
if (keyRing == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
masterKey = keyRing.getPublicKey();
|
|
||||||
if (masterKey == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Vector<PGPPublicKey> encryptKeys = PgpKeyHelper.getUsableEncryptKeys(keyRing);
|
|
||||||
if (encryptKeys.size() == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
goodIds.add(masterKey.getKeyID());
|
|
||||||
}
|
}
|
||||||
if (goodIds.size() > 0) {
|
if (goodIds.size() > 0) {
|
||||||
long[] keyIds = new long[goodIds.size()];
|
long[] keyIds = new long[goodIds.size()];
|
||||||
|
Loading…
Reference in New Issue
Block a user