make user_ids table typed, with attribute_data support

Conflicts:
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java
This commit is contained in:
Vincent Breitmoser 2015-01-13 23:09:48 +01:00
parent 33ab856214
commit 2b1c5358b7
9 changed files with 119 additions and 87 deletions

View File

@ -51,9 +51,11 @@ public class KeychainContract {
String EXPIRY = "expiry"; String EXPIRY = "expiry";
} }
interface UserIdsColumns { interface UserPacketsColumns {
String MASTER_KEY_ID = "master_key_id"; // foreign key to key_rings._ID String MASTER_KEY_ID = "master_key_id"; // foreign key to key_rings._ID
String TYPE = "type"; // not a database id
String USER_ID = "user_id"; // not a database id String USER_ID = "user_id"; // not a database id
String ATTRIBUTE_DATA = "attribute_data"; // not a database id
String RANK = "rank"; // ONLY used for sorting! no key, no nothing! String RANK = "rank"; // ONLY used for sorting! no key, no nothing!
String IS_PRIMARY = "is_primary"; String IS_PRIMARY = "is_primary";
String IS_REVOKED = "is_revoked"; String IS_REVOKED = "is_revoked";
@ -105,7 +107,7 @@ public class KeychainContract {
public static final String BASE_API_APPS = "api_apps"; public static final String BASE_API_APPS = "api_apps";
public static final String PATH_ACCOUNTS = "accounts"; public static final String PATH_ACCOUNTS = "accounts";
public static class KeyRings implements BaseColumns, KeysColumns, UserIdsColumns { public static class KeyRings implements BaseColumns, KeysColumns, UserPacketsColumns {
public static final String MASTER_KEY_ID = KeysColumns.MASTER_KEY_ID; public static final String MASTER_KEY_ID = KeysColumns.MASTER_KEY_ID;
public static final String IS_REVOKED = KeysColumns.IS_REVOKED; public static final String IS_REVOKED = KeysColumns.IS_REVOKED;
public static final String VERIFIED = CertsColumns.VERIFIED; public static final String VERIFIED = CertsColumns.VERIFIED;
@ -225,7 +227,7 @@ public class KeychainContract {
} }
public static class UserIds implements UserIdsColumns, BaseColumns { public static class UserPackets implements UserPacketsColumns, BaseColumns {
public static final String VERIFIED = "verified"; public static final String VERIFIED = "verified";
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build(); .appendPath(BASE_KEY_RINGS).build();
@ -304,7 +306,7 @@ public class KeychainContract {
} }
public static class Certs implements CertsColumns, BaseColumns { public static class Certs implements CertsColumns, BaseColumns {
public static final String USER_ID = UserIdsColumns.USER_ID; public static final String USER_ID = UserPacketsColumns.USER_ID;
public static final String SIGNER_UID = "signer_user_id"; public static final String SIGNER_UID = "signer_user_id";
public static final int UNVERIFIED = 0; public static final int UNVERIFIED = 0;

View File

@ -33,7 +33,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns;
import org.sufficientlysecure.keychain.ui.ConsolidateDialogActivity; import org.sufficientlysecure.keychain.ui.ConsolidateDialogActivity;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
@ -52,7 +52,7 @@ import java.io.IOException;
*/ */
public class KeychainDatabase extends SQLiteOpenHelper { public class KeychainDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "openkeychain.db"; private static final String DATABASE_NAME = "openkeychain.db";
private static final int DATABASE_VERSION = 6; private static final int DATABASE_VERSION = 7;
static Boolean apgHack = false; static Boolean apgHack = false;
private Context mContext; private Context mContext;
@ -60,7 +60,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
String KEY_RINGS_PUBLIC = "keyrings_public"; String KEY_RINGS_PUBLIC = "keyrings_public";
String KEY_RINGS_SECRET = "keyrings_secret"; String KEY_RINGS_SECRET = "keyrings_secret";
String KEYS = "keys"; String KEYS = "keys";
String USER_IDS = "user_ids"; String USER_PACKETS = "user_ids";
String CERTS = "certs"; String CERTS = "certs";
String API_APPS = "api_apps"; String API_APPS = "api_apps";
String API_ACCOUNTS = "api_accounts"; String API_ACCOUNTS = "api_accounts";
@ -106,18 +106,20 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE" + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE"
+ ")"; + ")";
private static final String CREATE_USER_IDS = private static final String CREATE_USER_PACKETS =
"CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS + "(" "CREATE TABLE IF NOT EXISTS " + Tables.USER_PACKETS + "("
+ UserIdsColumns.MASTER_KEY_ID + " INTEGER, " + UserPacketsColumns.MASTER_KEY_ID + " INTEGER, "
+ UserIdsColumns.USER_ID + " TEXT, " + UserPacketsColumns.TYPE + " INT, "
+ UserPacketsColumns.USER_ID + " TEXT, "
+ UserPacketsColumns.ATTRIBUTE_DATA + " BLOB, "
+ UserIdsColumns.IS_PRIMARY + " INTEGER, " + UserPacketsColumns.IS_PRIMARY + " INTEGER, "
+ UserIdsColumns.IS_REVOKED + " INTEGER, " + UserPacketsColumns.IS_REVOKED + " INTEGER, "
+ UserIdsColumns.RANK+ " INTEGER, " + UserPacketsColumns.RANK+ " INTEGER, "
+ "PRIMARY KEY(" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.USER_ID + "), " + "PRIMARY KEY(" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.USER_ID + "), "
+ "UNIQUE (" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.RANK + "), " + "UNIQUE (" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.RANK + "), "
+ "FOREIGN KEY(" + UserIdsColumns.MASTER_KEY_ID + ") REFERENCES " + "FOREIGN KEY(" + UserPacketsColumns.MASTER_KEY_ID + ") REFERENCES "
+ Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE" + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE"
+ ")"; + ")";
@ -138,7 +140,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ "FOREIGN KEY(" + CertsColumns.MASTER_KEY_ID + ") REFERENCES " + "FOREIGN KEY(" + CertsColumns.MASTER_KEY_ID + ") REFERENCES "
+ Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE," + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE,"
+ "FOREIGN KEY(" + CertsColumns.MASTER_KEY_ID + ", " + CertsColumns.RANK + ") REFERENCES " + "FOREIGN KEY(" + CertsColumns.MASTER_KEY_ID + ", " + CertsColumns.RANK + ") REFERENCES "
+ Tables.USER_IDS + "(" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.RANK + ") ON DELETE CASCADE" + Tables.USER_PACKETS + "(" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.RANK + ") ON DELETE CASCADE"
+ ")"; + ")";
private static final String CREATE_API_APPS = private static final String CREATE_API_APPS =
@ -189,7 +191,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
db.execSQL(CREATE_KEYRINGS_PUBLIC); db.execSQL(CREATE_KEYRINGS_PUBLIC);
db.execSQL(CREATE_KEYRINGS_SECRET); db.execSQL(CREATE_KEYRINGS_SECRET);
db.execSQL(CREATE_KEYS); db.execSQL(CREATE_KEYS);
db.execSQL(CREATE_USER_IDS); db.execSQL(CREATE_USER_PACKETS);
db.execSQL(CREATE_CERTS); db.execSQL(CREATE_CERTS);
db.execSQL(CREATE_API_APPS); db.execSQL(CREATE_API_APPS);
db.execSQL(CREATE_API_APPS_ACCOUNTS); db.execSQL(CREATE_API_APPS_ACCOUNTS);
@ -238,6 +240,9 @@ public class KeychainDatabase extends SQLiteOpenHelper {
case 5: case 5:
// do consolidate for 3.0 beta3 // do consolidate for 3.0 beta3
// fall through // fall through
case 6:
db.execSQL("ALTER TABLE user_ids ADD COLUMN type INTEGER");
db.execSQL("ALTER TABLE user_ids ADD COLUMN attribute_data BLOB");
} }
// always do consolidate after upgrade // always do consolidate after upgrade

View File

@ -37,7 +37,8 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
@ -205,7 +206,7 @@ public class KeychainProvider extends ContentProvider {
return Keys.CONTENT_TYPE; return Keys.CONTENT_TYPE;
case KEY_RING_USER_IDS: case KEY_RING_USER_IDS:
return UserIds.CONTENT_TYPE; return UserPackets.CONTENT_TYPE;
case KEY_RING_SECRET: case KEY_RING_SECRET:
return KeyRings.CONTENT_ITEM_TYPE; return KeyRings.CONTENT_ITEM_TYPE;
@ -262,7 +263,7 @@ public class KeychainProvider extends ContentProvider {
projectionMap.put(KeyRings.EXPIRY, Tables.KEYS + "." + Keys.EXPIRY); projectionMap.put(KeyRings.EXPIRY, Tables.KEYS + "." + Keys.EXPIRY);
projectionMap.put(KeyRings.ALGORITHM, Tables.KEYS + "." + Keys.ALGORITHM); projectionMap.put(KeyRings.ALGORITHM, Tables.KEYS + "." + Keys.ALGORITHM);
projectionMap.put(KeyRings.FINGERPRINT, Tables.KEYS + "." + Keys.FINGERPRINT); projectionMap.put(KeyRings.FINGERPRINT, Tables.KEYS + "." + Keys.FINGERPRINT);
projectionMap.put(KeyRings.USER_ID, UserIds.USER_ID); projectionMap.put(KeyRings.USER_ID, UserPackets.USER_ID);
projectionMap.put(KeyRings.VERIFIED, KeyRings.VERIFIED); projectionMap.put(KeyRings.VERIFIED, KeyRings.VERIFIED);
projectionMap.put(KeyRings.PUBKEY_DATA, projectionMap.put(KeyRings.PUBKEY_DATA,
Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.KEY_RING_DATA Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.KEY_RING_DATA
@ -296,11 +297,12 @@ public class KeychainProvider extends ContentProvider {
qb.setTables( qb.setTables(
Tables.KEYS Tables.KEYS
+ " INNER JOIN " + Tables.USER_IDS + " ON (" + " INNER JOIN " + Tables.USER_PACKETS + " ON ("
+ Tables.KEYS + "." + Keys.MASTER_KEY_ID + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " = " + " = "
+ Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
+ " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = 0" // we KNOW that the rank zero user packet is a user id!
+ " AND " + Tables.USER_PACKETS + "." + UserPackets.RANK + " = 0"
+ ") LEFT JOIN " + Tables.CERTS + " ON (" + ") LEFT JOIN " + Tables.CERTS + " ON ("
+ Tables.KEYS + "." + Keys.MASTER_KEY_ID + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " = " + " = "
@ -376,7 +378,7 @@ public class KeychainProvider extends ContentProvider {
String subkey = Long.valueOf(uri.getLastPathSegment()).toString(); String subkey = Long.valueOf(uri.getLastPathSegment()).toString();
qb.appendWhere(" AND EXISTS (" qb.appendWhere(" AND EXISTS ("
+ " SELECT 1 FROM " + Tables.KEYS + " AS tmp" + " SELECT 1 FROM " + Tables.KEYS + " AS tmp"
+ " WHERE tmp." + UserIds.MASTER_KEY_ID + " WHERE tmp." + UserPackets.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND tmp." + Keys.KEY_ID + " = " + subkey + "" + " AND tmp." + Keys.KEY_ID + " = " + subkey + ""
+ ")"); + ")");
@ -398,15 +400,15 @@ public class KeychainProvider extends ContentProvider {
if (i != 0) { if (i != 0) {
emailWhere += " OR "; emailWhere += " OR ";
} }
emailWhere += "tmp." + UserIds.USER_ID + " LIKE "; emailWhere += "tmp." + UserPackets.USER_ID + " LIKE ";
// match '*<email>', so it has to be at the *end* of the user id // match '*<email>', so it has to be at the *end* of the user id
emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">"); emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">");
gotCondition = true; gotCondition = true;
} }
if(gotCondition) { if(gotCondition) {
qb.appendWhere(" AND EXISTS (" qb.appendWhere(" AND EXISTS ("
+ " SELECT 1 FROM " + Tables.USER_IDS + " AS tmp" + " SELECT 1 FROM " + Tables.USER_PACKETS + " AS tmp"
+ " WHERE tmp." + UserIds.MASTER_KEY_ID + " WHERE tmp." + UserPackets.MASTER_KEY_ID
+ " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " AND (" + emailWhere + ")" + " AND (" + emailWhere + ")"
+ ")"); + ")");
@ -420,7 +422,7 @@ public class KeychainProvider extends ContentProvider {
} }
if (TextUtils.isEmpty(sortOrder)) { if (TextUtils.isEmpty(sortOrder)) {
sortOrder = Tables.USER_IDS + "." + UserIds.USER_ID + " ASC"; sortOrder = Tables.USER_PACKETS + "." + UserPackets.USER_ID + " ASC";
} }
// uri to watch is all /key_rings/ // uri to watch is all /key_rings/
@ -459,36 +461,42 @@ public class KeychainProvider extends ContentProvider {
case KEY_RINGS_USER_IDS: case KEY_RINGS_USER_IDS:
case KEY_RING_USER_IDS: { case KEY_RING_USER_IDS: {
HashMap<String, String> projectionMap = new HashMap<String, String>(); HashMap<String, String> projectionMap = new HashMap<String, String>();
projectionMap.put(UserIds._ID, Tables.USER_IDS + ".oid AS _id"); projectionMap.put(UserPackets._ID, Tables.USER_PACKETS + ".oid AS _id");
projectionMap.put(UserIds.MASTER_KEY_ID, Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID); projectionMap.put(UserPackets.MASTER_KEY_ID, Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID);
projectionMap.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); projectionMap.put(UserPackets.TYPE, Tables.USER_PACKETS + "." + UserPackets.TYPE);
projectionMap.put(UserIds.RANK, Tables.USER_IDS + "." + UserIds.RANK); projectionMap.put(UserPackets.USER_ID, Tables.USER_PACKETS + "." + UserPackets.USER_ID);
projectionMap.put(UserIds.IS_PRIMARY, Tables.USER_IDS + "." + UserIds.IS_PRIMARY); projectionMap.put(UserPackets.ATTRIBUTE_DATA, Tables.USER_PACKETS + "." + UserPackets.ATTRIBUTE_DATA);
projectionMap.put(UserIds.IS_REVOKED, Tables.USER_IDS + "." + UserIds.IS_REVOKED); projectionMap.put(UserPackets.RANK, Tables.USER_PACKETS + "." + UserPackets.RANK);
projectionMap.put(UserPackets.IS_PRIMARY, Tables.USER_PACKETS + "." + UserPackets.IS_PRIMARY);
projectionMap.put(UserPackets.IS_REVOKED, Tables.USER_PACKETS + "." + UserPackets.IS_REVOKED);
// we take the minimum (>0) here, where "1" is "verified by known secret key" // we take the minimum (>0) here, where "1" is "verified by known secret key"
projectionMap.put(UserIds.VERIFIED, "MIN(" + Certs.VERIFIED + ") AS " + UserIds.VERIFIED); projectionMap.put(UserPackets.VERIFIED, "MIN(" + Certs.VERIFIED + ") AS " + UserPackets.VERIFIED);
qb.setProjectionMap(projectionMap); qb.setProjectionMap(projectionMap);
qb.setTables(Tables.USER_IDS qb.setTables(Tables.USER_PACKETS
+ " LEFT JOIN " + Tables.CERTS + " ON (" + " LEFT JOIN " + Tables.CERTS + " ON ("
+ Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " = " + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = "
+ Tables.CERTS + "." + Certs.MASTER_KEY_ID + Tables.CERTS + "." + Certs.MASTER_KEY_ID
+ " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = " + " AND " + Tables.USER_PACKETS + "." + UserPackets.RANK + " = "
+ Tables.CERTS + "." + Certs.RANK + Tables.CERTS + "." + Certs.RANK
+ " AND " + Tables.CERTS + "." + Certs.VERIFIED + " > 0" + " AND " + Tables.CERTS + "." + Certs.VERIFIED + " > 0"
+ ")"); + ")");
groupBy = Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID groupBy = Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
+ ", " + Tables.USER_IDS + "." + UserIds.RANK; + ", " + Tables.USER_PACKETS + "." + UserPackets.RANK;
// for now, we only respect user ids here, so TYPE must be NULL
// TODO expand with KEY_RING_USER_PACKETS query type which lifts this restriction
qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL AND ");
// If we are searching for a particular keyring's ids, add where // If we are searching for a particular keyring's ids, add where
if (match == KEY_RING_USER_IDS) { if (match == KEY_RING_USER_IDS) {
qb.appendWhere(Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " = "); qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = ");
qb.appendWhereEscapeString(uri.getPathSegments().get(1)); qb.appendWhereEscapeString(uri.getPathSegments().get(1));
} }
if (TextUtils.isEmpty(sortOrder)) { if (TextUtils.isEmpty(sortOrder)) {
sortOrder = Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " ASC" sortOrder = Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " ASC"
+ "," + Tables.USER_IDS + "." + UserIds.RANK + " ASC"; + "," + Tables.USER_PACKETS + "." + UserPackets.RANK + " ASC";
} }
break; break;
@ -542,20 +550,24 @@ public class KeychainProvider extends ContentProvider {
projectionMap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION); projectionMap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION);
projectionMap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER); projectionMap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER);
projectionMap.put(Certs.DATA, Tables.CERTS + "." + Certs.DATA); projectionMap.put(Certs.DATA, Tables.CERTS + "." + Certs.DATA);
projectionMap.put(Certs.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); projectionMap.put(Certs.USER_ID, Tables.USER_PACKETS + "." + UserPackets.USER_ID);
projectionMap.put(Certs.SIGNER_UID, "signer." + UserIds.USER_ID + " AS " + Certs.SIGNER_UID); projectionMap.put(Certs.SIGNER_UID, "signer." + UserPackets.USER_ID + " AS " + Certs.SIGNER_UID);
qb.setProjectionMap(projectionMap); qb.setProjectionMap(projectionMap);
qb.setTables(Tables.CERTS qb.setTables(Tables.CERTS
+ " JOIN " + Tables.USER_IDS + " ON (" + " JOIN " + Tables.USER_PACKETS + " ON ("
+ Tables.CERTS + "." + Certs.MASTER_KEY_ID + " = " + Tables.CERTS + "." + Certs.MASTER_KEY_ID + " = "
+ Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
+ " AND " + " AND "
+ Tables.CERTS + "." + Certs.RANK + " = " + Tables.CERTS + "." + Certs.RANK + " = "
+ Tables.USER_IDS + "." + UserIds.RANK + Tables.USER_PACKETS + "." + UserPackets.RANK
+ ") LEFT JOIN " + Tables.USER_IDS + " AS signer ON (" // for now, we only return user ids here, so TYPE must be NULL
// TODO at some point, we should lift this restriction
+ " AND "
+ Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL"
+ ") LEFT JOIN " + Tables.USER_PACKETS + " AS signer ON ("
+ Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = " + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = "
+ "signer." + UserIds.MASTER_KEY_ID + "signer." + UserPackets.MASTER_KEY_ID
+ " AND " + " AND "
+ "signer." + Keys.RANK + " = 0" + "signer." + Keys.RANK + " = 0"
+ ")"); + ")");
@ -662,8 +674,18 @@ public class KeychainProvider extends ContentProvider {
break; break;
case KEY_RING_USER_IDS: case KEY_RING_USER_IDS:
db.insertOrThrow(Tables.USER_IDS, null, values); // iff TYPE is null, user_id MUST be null as well
keyId = values.getAsLong(UserIds.MASTER_KEY_ID); if ( ! (values.get(UserPacketsColumns.TYPE) == null
? (values.get(UserPacketsColumns.USER_ID) != null && values.get(UserPacketsColumns.ATTRIBUTE_DATA) == null)
: (values.get(UserPacketsColumns.ATTRIBUTE_DATA) != null && values.get(UserPacketsColumns.USER_ID) == null)
)) {
throw new AssertionError("Incorrect type for user packet! This is a bug!");
}
if (values.get(UserPacketsColumns.RANK) == 0 && values.get(UserPacketsColumns.USER_ID) == null) {
throw new AssertionError("Rank 0 user packet must be a user id!");
}
db.insertOrThrow(Tables.USER_PACKETS, null, values);
keyId = values.getAsLong(UserPackets.MASTER_KEY_ID);
break; break;
case KEY_RING_CERTS: case KEY_RING_CERTS:

View File

@ -31,6 +31,7 @@ import android.support.v4.util.LongSparseArray;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
@ -53,7 +54,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AccountSettings;
import org.sufficientlysecure.keychain.remote.AppSettings; import org.sufficientlysecure.keychain.remote.AppSettings;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
@ -1236,13 +1236,16 @@ public class ProviderHelper {
private ContentProviderOperation private ContentProviderOperation
buildUserIdOperations(long masterKeyId, UserIdItem item, int rank) { buildUserIdOperations(long masterKeyId, UserIdItem item, int rank) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put(UserIds.MASTER_KEY_ID, masterKeyId); values.put(UserPackets.MASTER_KEY_ID, masterKeyId);
values.put(UserIds.USER_ID, item.userId); values.put(UserPackets.USER_ID, item.userId);
values.put(UserIds.IS_PRIMARY, item.isPrimary); values.put(UserPackets.IS_PRIMARY, item.isPrimary);
values.put(UserIds.IS_REVOKED, item.isRevoked); values.put(UserPackets.IS_REVOKED, item.isRevoked);
values.put(UserIds.RANK, rank); values.put(UserPackets.RANK, rank);
// we explicitly set these to null here, to indicate that this is a user id, not an attribute
values.put(UserPackets.TYPE, (String) null);
values.put(UserPackets.ATTRIBUTE_DATA, (String) null);
Uri uri = UserIds.buildUserIdsUri(masterKeyId); Uri uri = UserPackets.buildUserIdsUri(masterKeyId);
return ContentProviderOperation.newInsert(uri).withValues(values).build(); return ContentProviderOperation.newInsert(uri).withValues(values).build();
} }

View File

@ -49,7 +49,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel; import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
@ -82,11 +82,11 @@ public class CertifyKeyFragment extends LoaderFragment
private long mSignMasterKeyId = Constants.key.none; private long mSignMasterKeyId = Constants.key.none;
public static final String[] USER_IDS_PROJECTION = new String[]{ public static final String[] USER_IDS_PROJECTION = new String[]{
UserIds._ID, UserPackets._ID,
UserIds.MASTER_KEY_ID, UserPackets.MASTER_KEY_ID,
UserIds.USER_ID, UserPackets.USER_ID,
UserIds.IS_PRIMARY, UserPackets.IS_PRIMARY,
UserIds.IS_REVOKED UserPackets.IS_REVOKED
}; };
private static final int INDEX_MASTER_KEY_ID = 1; private static final int INDEX_MASTER_KEY_ID = 1;
private static final int INDEX_USER_ID = 2; private static final int INDEX_USER_ID = 2;
@ -182,7 +182,7 @@ public class CertifyKeyFragment extends LoaderFragment
@Override @Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) { public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri uri = UserIds.buildUserIdsUri(); Uri uri = UserPackets.buildUserIdsUri();
String selection, ids[]; String selection, ids[];
{ {
@ -196,15 +196,15 @@ public class CertifyKeyFragment extends LoaderFragment
} }
} }
// put together selection string // put together selection string
selection = UserIds.IS_REVOKED + " = 0" + " AND " selection = UserPackets.IS_REVOKED + " = 0" + " AND "
+ Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
+ " IN (" + placeholders + ")"; + " IN (" + placeholders + ")";
} }
return new CursorLoader(getActivity(), uri, return new CursorLoader(getActivity(), uri,
USER_IDS_PROJECTION, selection, ids, USER_IDS_PROJECTION, selection, ids,
Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " ASC" Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " ASC"
+ ", " + Tables.USER_IDS + "." + UserIds.USER_ID + " ASC" + ", " + Tables.USER_PACKETS + "." + UserPackets.USER_ID + " ASC"
); );
} }

View File

@ -47,6 +47,7 @@ import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentService;
@ -334,7 +335,7 @@ public class EditKeyFragment extends LoaderFragment implements
switch (id) { switch (id) {
case LOADER_ID_USER_IDS: { case LOADER_ID_USER_IDS: {
Uri baseUri = KeychainContract.UserIds.buildUserIdsUri(mDataUri); Uri baseUri = UserPackets.buildUserIdsUri(mDataUri);
return new CursorLoader(getActivity(), baseUri, return new CursorLoader(getActivity(), baseUri,
UserIdsAdapter.USER_IDS_PROJECTION, null, null, null); UserIdsAdapter.USER_IDS_PROJECTION, null, null, null);
} }

View File

@ -37,10 +37,10 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
@ -199,7 +199,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements
return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null); return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null);
} }
case LOADER_ID_USER_IDS: { case LOADER_ID_USER_IDS: {
Uri baseUri = UserIds.buildUserIdsUri(mDataUri); Uri baseUri = UserPackets.buildUserIdsUri(mDataUri);
return new CursorLoader(getActivity(), baseUri, return new CursorLoader(getActivity(), baseUri,
UserIdsAdapter.USER_IDS_PROJECTION, null, null, null); UserIdsAdapter.USER_IDS_PROJECTION, null, null, null);
} }

View File

@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.ui.adapter;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.PorterDuff;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.support.v4.widget.CursorAdapter; import android.support.v4.widget.CursorAdapter;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -32,10 +31,9 @@ import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
@ -47,12 +45,12 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC
private SaveKeyringParcel mSaveKeyringParcel; private SaveKeyringParcel mSaveKeyringParcel;
public static final String[] USER_IDS_PROJECTION = new String[]{ public static final String[] USER_IDS_PROJECTION = new String[]{
UserIds._ID, UserPackets._ID,
UserIds.USER_ID, UserPackets.USER_ID,
UserIds.RANK, UserPackets.RANK,
UserIds.VERIFIED, UserPackets.VERIFIED,
UserIds.IS_PRIMARY, UserPackets.IS_PRIMARY,
UserIds.IS_REVOKED UserPackets.IS_REVOKED
}; };
private static final int INDEX_ID = 0; private static final int INDEX_ID = 0;
private static final int INDEX_USER_ID = 1; private static final int INDEX_USER_ID = 1;

View File

@ -35,6 +35,7 @@ import android.util.Patterns;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.KeyRing;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract;
@ -57,10 +58,10 @@ public class ContactHelper {
KeychainContract.KeyRings.EXPIRY, KeychainContract.KeyRings.EXPIRY,
KeychainContract.KeyRings.IS_REVOKED}; KeychainContract.KeyRings.IS_REVOKED};
public static final String[] USER_IDS_PROJECTION = new String[]{ public static final String[] USER_IDS_PROJECTION = new String[]{
KeychainContract.UserIds.USER_ID UserPackets.USER_ID
}; };
public static final String NON_REVOKED_SELECTION = KeychainContract.UserIds.IS_REVOKED + "=0"; public static final String NON_REVOKED_SELECTION = UserPackets.IS_REVOKED + "=0";
public static final String[] ID_PROJECTION = new String[]{ContactsContract.RawContacts._ID}; public static final String[] ID_PROJECTION = new String[]{ContactsContract.RawContacts._ID};
public static final String[] SOURCE_ID_PROJECTION = new String[]{ContactsContract.RawContacts.SOURCE_ID}; public static final String[] SOURCE_ID_PROJECTION = new String[]{ContactsContract.RawContacts.SOURCE_ID};
@ -413,7 +414,7 @@ public class ContactHelper {
int rawContactId, long masterKeyId) { int rawContactId, long masterKeyId) {
ops.add(selectByRawContactAndItemType(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI), ops.add(selectByRawContactAndItemType(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI),
rawContactId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE).build()); rawContactId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE).build());
Cursor ids = resolver.query(KeychainContract.UserIds.buildUserIdsUri(masterKeyId), Cursor ids = resolver.query(UserPackets.buildUserIdsUri(masterKeyId),
USER_IDS_PROJECTION, NON_REVOKED_SELECTION, null, null); USER_IDS_PROJECTION, NON_REVOKED_SELECTION, null, null);
if (ids != null) { if (ids != null) {
while (ids.moveToNext()) { while (ids.moveToNext()) {