Fix and optimize Autocrypt 1.0 logic
This commit is contained in:
@@ -19,12 +19,13 @@ package org.sufficientlysecure.keychain.provider;
|
||||
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.net.Uri;
|
||||
import android.text.format.DateUtils;
|
||||
|
||||
@@ -35,6 +36,7 @@ public class AutocryptPeerDataAccessObject {
|
||||
private static final long AUTOCRYPT_DISCOURAGE_THRESHOLD_MILLIS = 35 * DateUtils.DAY_IN_MILLIS;
|
||||
|
||||
private static final String[] PROJECTION_AUTOCRYPT_QUERY = {
|
||||
ApiAutocryptPeer.IDENTIFIER,
|
||||
ApiAutocryptPeer.LAST_SEEN,
|
||||
ApiAutocryptPeer.MASTER_KEY_ID,
|
||||
ApiAutocryptPeer.LAST_SEEN_KEY,
|
||||
@@ -48,18 +50,19 @@ public class AutocryptPeerDataAccessObject {
|
||||
ApiAutocryptPeer.GOSSIP_KEY_IS_EXPIRED,
|
||||
ApiAutocryptPeer.GOSSIP_KEY_IS_VERIFIED,
|
||||
};
|
||||
private static final int INDEX_LAST_SEEN = 0;
|
||||
private static final int INDEX_MASTER_KEY_ID = 1;
|
||||
private static final int INDEX_LAST_SEEN_KEY = 2;
|
||||
private static final int INDEX_STATE = 3;
|
||||
private static final int INDEX_KEY_IS_REVOKED = 4;
|
||||
private static final int INDEX_KEY_IS_EXPIRED = 5;
|
||||
private static final int INDEX_KEY_IS_VERIFIED = 6;
|
||||
private static final int INDEX_GOSSIP_MASTER_KEY_ID = 7;
|
||||
private static final int INDEX_GOSSIP_LAST_SEEN_KEY = 8;
|
||||
private static final int INDEX_GOSSIP_KEY_IS_REVOKED = 9;
|
||||
private static final int INDEX_GOSSIP_KEY_IS_EXPIRED = 10;
|
||||
private static final int INDEX_GOSSIP_KEY_IS_VERIFIED = 11;
|
||||
private static final int INDEX_IDENTIFIER = 0;
|
||||
private static final int INDEX_LAST_SEEN = 1;
|
||||
private static final int INDEX_MASTER_KEY_ID = 2;
|
||||
private static final int INDEX_LAST_SEEN_KEY = 3;
|
||||
private static final int INDEX_STATE = 4;
|
||||
private static final int INDEX_KEY_IS_REVOKED = 5;
|
||||
private static final int INDEX_KEY_IS_EXPIRED = 6;
|
||||
private static final int INDEX_KEY_IS_VERIFIED = 7;
|
||||
private static final int INDEX_GOSSIP_MASTER_KEY_ID = 8;
|
||||
private static final int INDEX_GOSSIP_LAST_SEEN_KEY = 9;
|
||||
private static final int INDEX_GOSSIP_KEY_IS_REVOKED = 10;
|
||||
private static final int INDEX_GOSSIP_KEY_IS_EXPIRED = 11;
|
||||
private static final int INDEX_GOSSIP_KEY_IS_VERIFIED = 12;
|
||||
|
||||
private final SimpleContentResolverInterface queryInterface;
|
||||
private final String packageName;
|
||||
@@ -178,7 +181,7 @@ public class AutocryptPeerDataAccessObject {
|
||||
queryInterface.delete(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId), null, null);
|
||||
}
|
||||
|
||||
public AutocryptPeerStateResult getAutocryptState(String autocryptId) {
|
||||
public Map<String,AutocryptPeerStateResult> getAutocryptState(String... autocryptIds) {
|
||||
/*
|
||||
Determine if encryption is possible
|
||||
If there is no peers[to-addr], then set ui-recommendation to disable, and terminate.
|
||||
@@ -199,47 +202,70 @@ If autocrypt_timestamp is more than 35 days older than last_seen, set preliminar
|
||||
Otherwise, set preliminary-recommendation to available.
|
||||
*/
|
||||
|
||||
Cursor cursor = queryInterface.query(ApiAutocryptPeer.buildByPackageNameAndAutocryptId(packageName, autocryptId),
|
||||
PROJECTION_AUTOCRYPT_QUERY, null, null, null);
|
||||
Map<String,AutocryptPeerStateResult> result = new HashMap<>();
|
||||
StringBuilder selection = new StringBuilder(ApiAutocryptPeer.IDENTIFIER + " IN (?");
|
||||
for (int i = 1; i < autocryptIds.length; i++) {
|
||||
selection.append(",?");
|
||||
}
|
||||
selection.append(")");
|
||||
|
||||
Cursor cursor = queryInterface.query(ApiAutocryptPeer.buildByPackageName(packageName),
|
||||
PROJECTION_AUTOCRYPT_QUERY, selection.toString(), autocryptIds, null);
|
||||
try {
|
||||
if (!cursor.moveToFirst()) {
|
||||
return new AutocryptPeerStateResult(AutocryptState.DISABLE, null, false);
|
||||
}
|
||||
while (cursor.moveToNext()) {
|
||||
String autocryptId = cursor.getString(INDEX_IDENTIFIER);
|
||||
|
||||
boolean hasKey = !cursor.isNull(INDEX_MASTER_KEY_ID);
|
||||
boolean isRevoked = cursor.getInt(INDEX_KEY_IS_REVOKED) != 0;
|
||||
boolean isExpired = cursor.getInt(INDEX_KEY_IS_EXPIRED) != 0;
|
||||
if (hasKey && !isRevoked && !isExpired) {
|
||||
long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
|
||||
long lastSeen = cursor.getLong(INDEX_LAST_SEEN);
|
||||
long lastSeenKey = cursor.getLong(INDEX_LAST_SEEN_KEY);
|
||||
boolean isVerified = cursor.getInt(INDEX_KEY_IS_VERIFIED) != 0;
|
||||
if (lastSeenKey < (lastSeen - AUTOCRYPT_DISCOURAGE_THRESHOLD_MILLIS)) {
|
||||
return new AutocryptPeerStateResult(AutocryptState.DISCOURAGED_OLD, masterKeyId, isVerified);
|
||||
boolean hasKey = !cursor.isNull(INDEX_MASTER_KEY_ID);
|
||||
boolean isRevoked = cursor.getInt(INDEX_KEY_IS_REVOKED) != 0;
|
||||
boolean isExpired = cursor.getInt(INDEX_KEY_IS_EXPIRED) != 0;
|
||||
if (hasKey && !isRevoked && !isExpired) {
|
||||
long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID);
|
||||
long lastSeen = cursor.getLong(INDEX_LAST_SEEN);
|
||||
long lastSeenKey = cursor.getLong(INDEX_LAST_SEEN_KEY);
|
||||
boolean isVerified = cursor.getInt(INDEX_KEY_IS_VERIFIED) != 0;
|
||||
if (lastSeenKey < (lastSeen - AUTOCRYPT_DISCOURAGE_THRESHOLD_MILLIS)) {
|
||||
AutocryptPeerStateResult peerResult = new AutocryptPeerStateResult(
|
||||
AutocryptState.DISCOURAGED_OLD, masterKeyId, isVerified);
|
||||
result.put(autocryptId, peerResult);
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean isMutual = cursor.getInt(INDEX_STATE) != 0;
|
||||
if (isMutual) {
|
||||
AutocryptPeerStateResult peerResult = new AutocryptPeerStateResult(
|
||||
AutocryptState.MUTUAL, masterKeyId, isVerified);
|
||||
result.put(autocryptId, peerResult);
|
||||
continue;
|
||||
} else {
|
||||
AutocryptPeerStateResult peerResult = new AutocryptPeerStateResult(
|
||||
AutocryptState.AVAILABLE, masterKeyId, isVerified);
|
||||
result.put(autocryptId, peerResult);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isMutual = cursor.getInt(INDEX_STATE) != 0;
|
||||
if (isMutual) {
|
||||
return new AutocryptPeerStateResult(AutocryptState.MUTUAL, masterKeyId, isVerified);
|
||||
} else {
|
||||
return new AutocryptPeerStateResult(AutocryptState.AVAILABLE, masterKeyId, isVerified);
|
||||
boolean gossipHasKey = !cursor.isNull(INDEX_GOSSIP_MASTER_KEY_ID);
|
||||
boolean gossipIsRevoked = cursor.getInt(INDEX_GOSSIP_KEY_IS_REVOKED) != 0;
|
||||
boolean gossipIsExpired = cursor.getInt(INDEX_GOSSIP_KEY_IS_EXPIRED) != 0;
|
||||
boolean isVerified = cursor.getInt(INDEX_GOSSIP_KEY_IS_VERIFIED) != 0;
|
||||
if (gossipHasKey && !gossipIsRevoked && !gossipIsExpired) {
|
||||
long masterKeyId = cursor.getLong(INDEX_GOSSIP_MASTER_KEY_ID);
|
||||
AutocryptPeerStateResult peerResult =
|
||||
new AutocryptPeerStateResult(
|
||||
AutocryptState.DISCOURAGED_GOSSIP, masterKeyId, isVerified);
|
||||
result.put(autocryptId, peerResult);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
boolean gossipHasKey = !cursor.isNull(INDEX_GOSSIP_MASTER_KEY_ID);
|
||||
boolean gossipIsRevoked = cursor.getInt(INDEX_GOSSIP_KEY_IS_REVOKED) != 0;
|
||||
boolean gossipIsExpired = cursor.getInt(INDEX_GOSSIP_KEY_IS_EXPIRED) != 0;
|
||||
boolean isVerified = cursor.getInt(INDEX_GOSSIP_KEY_IS_VERIFIED) != 0;
|
||||
if (gossipHasKey && !gossipIsRevoked && !gossipIsExpired) {
|
||||
System.err.println("xx");
|
||||
long masterKeyId = cursor.getLong(INDEX_GOSSIP_MASTER_KEY_ID);
|
||||
return new AutocryptPeerStateResult(AutocryptState.DISCOURAGED_GOSSIP, masterKeyId, isVerified);
|
||||
AutocryptPeerStateResult peerResult = new AutocryptPeerStateResult(
|
||||
AutocryptState.DISABLE, null, false);
|
||||
result.put(autocryptId, peerResult);
|
||||
}
|
||||
|
||||
return new AutocryptPeerStateResult(AutocryptState.DISABLE, null, false);
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static class AutocryptPeerStateResult {
|
||||
|
||||
@@ -386,6 +386,10 @@ public class KeychainContract {
|
||||
return CONTENT_URI.buildUpon().appendPath(PATH_BY_KEY_ID).appendPath(uri.getPathSegments().get(1)).build();
|
||||
}
|
||||
|
||||
public static Uri buildByPackageName(String packageName) {
|
||||
return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName).build();
|
||||
}
|
||||
|
||||
public static Uri buildByPackageNameAndAutocryptId(String packageName, String autocryptPeer) {
|
||||
return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName).appendPath(autocryptPeer).build();
|
||||
}
|
||||
|
||||
@@ -236,6 +236,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
+ UserPacketsColumns.USER_ID + ", " + UserPacketsColumns.MASTER_KEY_ID + ");");
|
||||
db.execSQL("CREATE INDEX verified_certs ON certs ("
|
||||
+ CertsColumns.VERIFIED + ", " + CertsColumns.MASTER_KEY_ID + ");");
|
||||
db.execSQL("CREATE INDEX uids_by_email ON user_packets ("
|
||||
+ UserPacketsColumns.EMAIL + ");");
|
||||
|
||||
Preferences.getPreferences(mContext).setKeySignaturesTableInitialized();
|
||||
}
|
||||
@@ -412,6 +414,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
case 24:
|
||||
db.execSQL("DROP TABLE api_autocrypt_peers");
|
||||
db.execSQL(CREATE_API_AUTOCRYPT_PEERS);
|
||||
db.execSQL("CREATE INDEX uids_by_email ON user_packets (email);");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -717,14 +717,10 @@ public class KeychainProvider extends ContentProvider {
|
||||
case AUTOCRYPT_PEERS_BY_MASTER_KEY_ID:
|
||||
case AUTOCRYPT_PEERS_BY_PACKAGE_NAME:
|
||||
case AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID: {
|
||||
if (selection != null || selectionArgs != null) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
long unixSeconds = new Date().getTime() / 1000;
|
||||
|
||||
HashMap<String, String> projectionMap = new HashMap<>();
|
||||
projectionMap.put(ApiAutocryptPeer._ID, "oid AS " + ApiAutocryptPeer._ID);
|
||||
projectionMap.put(ApiAutocryptPeer._ID, Tables.API_AUTOCRYPT_PEERS + ".oid AS " + ApiAutocryptPeer._ID);
|
||||
projectionMap.put(ApiAutocryptPeer.IDENTIFIER, ApiAutocryptPeer.IDENTIFIER);
|
||||
projectionMap.put(ApiAutocryptPeer.PACKAGE_NAME, ApiAutocryptPeer.PACKAGE_NAME);
|
||||
projectionMap.put(ApiAutocryptPeer.LAST_SEEN, ApiAutocryptPeer.LAST_SEEN);
|
||||
@@ -771,8 +767,8 @@ public class KeychainProvider extends ContentProvider {
|
||||
} else if (match == AUTOCRYPT_PEERS_BY_PACKAGE_NAME) {
|
||||
String packageName = uri.getPathSegments().get(2);
|
||||
|
||||
selection = Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.PACKAGE_NAME + " = ?";
|
||||
selectionArgs = new String[] { packageName };
|
||||
qb.appendWhere(Tables.API_AUTOCRYPT_PEERS + "." + ApiAutocryptPeer.PACKAGE_NAME + " = ");
|
||||
qb.appendWhereEscapeString(packageName);
|
||||
} else { // AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID
|
||||
String packageName = uri.getPathSegments().get(2);
|
||||
String autocryptPeer = uri.getPathSegments().get(3);
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.security.AccessControlException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
@@ -53,6 +54,7 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainExternalContract;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainExternalContract.AutocryptStatus;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainExternalContract.EmailStatus;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainProvider;
|
||||
import org.sufficientlysecure.keychain.provider.SimpleContentResolverInterface;
|
||||
import org.sufficientlysecure.keychain.util.CloseDatabaseCursorFactory;
|
||||
import timber.log.Timber;
|
||||
@@ -67,6 +69,8 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
|
||||
private static final int API_APPS = 301;
|
||||
private static final int API_APPS_BY_PACKAGE_NAME = 302;
|
||||
|
||||
private static final int AUTOCRYPT_PEERS_BY_PACKAGE_NAME = 602;
|
||||
|
||||
public static final String TEMP_TABLE_QUERIED_ADDRESSES = "queried_addresses";
|
||||
public static final String TEMP_TABLE_COLUMN_ADDRES = "address";
|
||||
|
||||
@@ -99,6 +103,9 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
|
||||
// can only query status of calling app - for internal use only!
|
||||
matcher.addURI(KeychainContract.CONTENT_AUTHORITY, KeychainContract.BASE_API_APPS + "/*", API_APPS_BY_PACKAGE_NAME);
|
||||
|
||||
matcher.addURI(KeychainContract.CONTENT_AUTHORITY, KeychainContract.BASE_AUTOCRYPT_PEERS + "/" +
|
||||
KeychainContract.PATH_BY_PACKAGE_NAME + "/*", AUTOCRYPT_PEERS_BY_PACKAGE_NAME);
|
||||
|
||||
return matcher;
|
||||
}
|
||||
|
||||
@@ -138,6 +145,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
|
||||
public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs,
|
||||
String sortOrder) {
|
||||
Timber.v("query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")");
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
||||
|
||||
@@ -239,7 +247,8 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
|
||||
TEMP_TABLE_COLUMN_ADDRES + " TEXT NOT NULL PRIMARY KEY, " +
|
||||
AutocryptStatus.UID_KEY_STATUS + " INT, " +
|
||||
AutocryptStatus.UID_ADDRESS + " TEXT, " +
|
||||
AutocryptStatus.UID_MASTER_KEY_ID + " TEXT, " +
|
||||
AutocryptStatus.UID_MASTER_KEY_ID + " INT, " +
|
||||
AutocryptStatus.UID_CANDIDATES + " INT, " +
|
||||
AutocryptStatus.AUTOCRYPT_KEY_STATUS + " INT, " +
|
||||
AutocryptStatus.AUTOCRYPT_PEER_STATE + " INT, " +
|
||||
AutocryptStatus.AUTOCRYPT_MASTER_KEY_ID + " INT" +
|
||||
@@ -250,48 +259,26 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
|
||||
db.insert(TEMP_TABLE_QUERIED_ADDRESSES, null, cv);
|
||||
}
|
||||
|
||||
long unixSeconds = System.currentTimeMillis() / 1000;
|
||||
db.execSQL("REPLACE INTO " + TEMP_TABLE_QUERIED_ADDRESSES +
|
||||
"(" + TEMP_TABLE_COLUMN_ADDRES + ", " + AutocryptStatus.UID_KEY_STATUS + ", " + AutocryptStatus.UID_ADDRESS + ")" +
|
||||
" SELECT " + TEMP_TABLE_COLUMN_ADDRES + ", " +
|
||||
"CASE ( MIN (certs_user_id." + Certs.VERIFIED + " ) ) "
|
||||
// remap to keep this provider contract independent from our internal representation
|
||||
+ " WHEN " + Certs.VERIFIED_SELF + " THEN " + KeychainExternalContract.KEY_STATUS_UNVERIFIED
|
||||
+ " WHEN " + Certs.VERIFIED_SECRET + " THEN " + KeychainExternalContract.KEY_STATUS_VERIFIED
|
||||
+ " WHEN NULL THEN NULL"
|
||||
+ " END AS " + AutocryptStatus.UID_KEY_STATUS +
|
||||
", " + Tables.USER_PACKETS + "." + UserPackets.USER_ID +
|
||||
" FROM " + TEMP_TABLE_QUERIED_ADDRESSES
|
||||
+ " JOIN " + Tables.USER_PACKETS + " ON ("
|
||||
+ Tables.USER_PACKETS + "." + UserPackets.USER_ID + " IS NOT NULL"
|
||||
+ " AND " + Tables.USER_PACKETS + "." + UserPackets.EMAIL + " LIKE " + TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES
|
||||
+ ")"
|
||||
+ " JOIN " + Tables.CERTS + " AS certs_user_id ON ("
|
||||
+ Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = certs_user_id." + Certs.MASTER_KEY_ID
|
||||
+ " AND " + Tables.USER_PACKETS + "." + UserPackets.RANK + " = certs_user_id." + Certs.RANK
|
||||
+ ")"
|
||||
+ " WHERE (EXISTS (SELECT * FROM " + Tables.KEYS + " WHERE "
|
||||
+ Tables.KEYS + "." + Keys.KEY_ID + " = " + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
|
||||
+ " AND " + Tables.KEYS + "." + Keys.IS_REVOKED + " = 0"
|
||||
+ " AND NOT " + "(" + Tables.KEYS + "." + Keys.EXPIRY + " IS NOT NULL AND " + Tables.KEYS + "." + Keys.EXPIRY
|
||||
+ " < " + unixSeconds + ")"
|
||||
+ ")) OR " + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " IS NULL"
|
||||
+ " GROUP BY " + TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES
|
||||
);
|
||||
boolean isWildcardSelector = selectionArgs.length == 1 && selectionArgs[0].contains("%");
|
||||
|
||||
AutocryptPeerDataAccessObject autocryptPeerDao =
|
||||
new AutocryptPeerDataAccessObject(getContext(), callingPackageName);
|
||||
for (String autocryptId : selectionArgs) {
|
||||
AutocryptPeerStateResult autocryptState = autocryptPeerDao.getAutocryptState(autocryptId);
|
||||
cv.put(AutocryptStatus.AUTOCRYPT_PEER_STATE, getPeerStateValue(autocryptState.autocryptState));
|
||||
if (autocryptState.masterKeyId != null) {
|
||||
cv.put(AutocryptStatus.AUTOCRYPT_MASTER_KEY_ID, autocryptState.masterKeyId);
|
||||
cv.put(AutocryptStatus.AUTOCRYPT_KEY_STATUS, autocryptState.isVerified ?
|
||||
KeychainExternalContract.KEY_STATUS_VERIFIED : KeychainExternalContract.KEY_STATUS_UNVERIFIED);
|
||||
}
|
||||
System.err.println(cv.toString());
|
||||
db.update(TEMP_TABLE_QUERIED_ADDRESSES, cv,
|
||||
TEMP_TABLE_COLUMN_ADDRES + "=?", new String [] { autocryptId });
|
||||
List<String> plist = Arrays.asList(projection);
|
||||
|
||||
boolean queriesUidResult = plist.contains(AutocryptStatus.UID_KEY_STATUS) ||
|
||||
plist.contains(AutocryptStatus.UID_ADDRESS) ||
|
||||
plist.contains(AutocryptStatus.UID_MASTER_KEY_ID) ||
|
||||
plist.contains(AutocryptStatus.UID_CANDIDATES);
|
||||
if (queriesUidResult) {
|
||||
fillTempTableWithUidResult(db, isWildcardSelector);
|
||||
}
|
||||
|
||||
boolean queriesAutocryptResult = plist.contains(AutocryptStatus.AUTOCRYPT_PEER_STATE) ||
|
||||
plist.contains(AutocryptStatus.AUTOCRYPT_MASTER_KEY_ID) ||
|
||||
plist.contains(AutocryptStatus.AUTOCRYPT_KEY_STATUS);
|
||||
if (isWildcardSelector && queriesAutocryptResult) {
|
||||
throw new UnsupportedOperationException("Cannot wildcard-query autocrypt results!");
|
||||
}
|
||||
if (!isWildcardSelector && queriesAutocryptResult) {
|
||||
fillTempTableWithAutocryptResult(selectionArgs, db, callingPackageName, cv);
|
||||
}
|
||||
|
||||
qb.setTables(TEMP_TABLE_QUERIED_ADDRESSES);
|
||||
@@ -316,6 +303,11 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
|
||||
break;
|
||||
}
|
||||
|
||||
case AUTOCRYPT_PEERS_BY_PACKAGE_NAME:
|
||||
KeychainProvider keychainProvider = new KeychainProvider();
|
||||
keychainProvider.attachInfo(getContext(), null);
|
||||
return keychainProvider.query(uri, projection, selection, selectionArgs, sortOrder);
|
||||
|
||||
default: {
|
||||
throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")");
|
||||
}
|
||||
@@ -341,10 +333,102 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
|
||||
}
|
||||
|
||||
Timber.d("Query: " + qb.buildQuery(projection, selection, groupBy, null, orderBy, null));
|
||||
Timber.d(Constants.TAG, "Query took %s ms", (System.currentTimeMillis() - startTime));
|
||||
|
||||
return cursor;
|
||||
}
|
||||
|
||||
private void fillTempTableWithAutocryptResult(String[] selectionArgs, SQLiteDatabase db, String callingPackageName,
|
||||
ContentValues cv) {
|
||||
AutocryptPeerDataAccessObject autocryptPeerDao =
|
||||
new AutocryptPeerDataAccessObject(this, callingPackageName);
|
||||
Map<String,AutocryptPeerStateResult> autocryptStates = autocryptPeerDao.getAutocryptState(selectionArgs);
|
||||
|
||||
for (String autocryptId : selectionArgs) {
|
||||
cv.clear();
|
||||
|
||||
AutocryptPeerStateResult peerResult = autocryptStates.get(autocryptId);
|
||||
if (peerResult == null) {
|
||||
cv.put(AutocryptStatus.AUTOCRYPT_PEER_STATE, AutocryptStatus.AUTOCRYPT_PEER_DISABLED);
|
||||
} else {
|
||||
cv.put(AutocryptStatus.AUTOCRYPT_PEER_STATE, getPeerStateValue(peerResult.autocryptState));
|
||||
if (peerResult.masterKeyId != null) {
|
||||
cv.put(AutocryptStatus.AUTOCRYPT_MASTER_KEY_ID, peerResult.masterKeyId);
|
||||
cv.put(AutocryptStatus.AUTOCRYPT_KEY_STATUS, peerResult.isVerified ?
|
||||
KeychainExternalContract.KEY_STATUS_VERIFIED :
|
||||
KeychainExternalContract.KEY_STATUS_UNVERIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
db.update(TEMP_TABLE_QUERIED_ADDRESSES, cv,TEMP_TABLE_COLUMN_ADDRES + "=?",
|
||||
new String[] { autocryptId });
|
||||
}
|
||||
}
|
||||
|
||||
private void fillTempTableWithUidResult(SQLiteDatabase db, boolean isWildcardSelector) {
|
||||
String cmpOperator = isWildcardSelector ? " LIKE " : " = ";
|
||||
long unixSeconds = System.currentTimeMillis() / 1000;
|
||||
db.execSQL("REPLACE INTO " + TEMP_TABLE_QUERIED_ADDRESSES +
|
||||
"(" + TEMP_TABLE_COLUMN_ADDRES + ", " + AutocryptStatus.UID_KEY_STATUS + ", " +
|
||||
AutocryptStatus.UID_ADDRESS + ", " + AutocryptStatus.UID_MASTER_KEY_ID
|
||||
+ ", " + AutocryptStatus.UID_CANDIDATES + ")" +
|
||||
" SELECT " + TEMP_TABLE_COLUMN_ADDRES + ", " +
|
||||
"CASE ( MIN (" + Tables.CERTS + "." + Certs.VERIFIED + " ) ) "
|
||||
// remap to keep this provider contract independent from our internal representation
|
||||
+ " WHEN " + Certs.VERIFIED_SELF + " THEN " + KeychainExternalContract.KEY_STATUS_UNVERIFIED
|
||||
+ " WHEN " + Certs.VERIFIED_SECRET + " THEN " + KeychainExternalContract.KEY_STATUS_VERIFIED
|
||||
+ " END AS " + AutocryptStatus.UID_KEY_STATUS
|
||||
+ ", " + Tables.USER_PACKETS + "." + UserPackets.USER_ID
|
||||
+ ", " + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID
|
||||
+ ", COUNT(DISTINCT " + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + ")"
|
||||
+ " FROM " + TEMP_TABLE_QUERIED_ADDRESSES
|
||||
+ " LEFT JOIN " + Tables.USER_PACKETS + " ON ("
|
||||
+ Tables.USER_PACKETS + "." + UserPackets.EMAIL + cmpOperator +
|
||||
TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES
|
||||
+ ")"
|
||||
+ " LEFT JOIN " + Tables.CERTS + " ON ("
|
||||
+ Tables.CERTS + "." + Certs.MASTER_KEY_ID + " = " + Tables.USER_PACKETS + "." +
|
||||
UserPackets.MASTER_KEY_ID
|
||||
+ " AND " + Tables.CERTS + "." + Certs.RANK + " = " + Tables.USER_PACKETS + "." +
|
||||
UserPackets.RANK
|
||||
+ " AND " + Tables.CERTS + "." + Certs.VERIFIED + " > 0"
|
||||
+ ")"
|
||||
+ " WHERE (EXISTS (SELECT 1 FROM " + Tables.KEYS + " WHERE "
|
||||
+ Tables.KEYS + "." + Keys.KEY_ID + " = " + Tables.USER_PACKETS + "." +
|
||||
UserPackets.MASTER_KEY_ID
|
||||
+ " AND " + Tables.KEYS + "." + Keys.RANK + " = 0"
|
||||
+ " AND " + Tables.KEYS + "." + Keys.IS_REVOKED + " = 0"
|
||||
+ " AND NOT " + "(" + Tables.KEYS + "." + Keys.EXPIRY + " IS NOT NULL AND " + Tables.KEYS +
|
||||
"." + Keys.EXPIRY
|
||||
+ " < " + unixSeconds + ")"
|
||||
+ ")) OR " + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " IS NULL"
|
||||
+ " GROUP BY " + TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES);
|
||||
}
|
||||
|
||||
private void explainQuery(SQLiteDatabase db, String sql) {
|
||||
Cursor explainCursor = db.rawQuery("EXPLAIN QUERY PLAN " + sql, new String[0]);
|
||||
|
||||
// this is a debugging feature, we can be a little careless
|
||||
explainCursor.moveToFirst();
|
||||
|
||||
StringBuilder line = new StringBuilder();
|
||||
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
|
||||
line.append(explainCursor.getColumnName(i)).append(", ");
|
||||
}
|
||||
Timber.d(Constants.TAG, line.toString());
|
||||
|
||||
while (!explainCursor.isAfterLast()) {
|
||||
line = new StringBuilder();
|
||||
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
|
||||
line.append(explainCursor.getString(i)).append(", ");
|
||||
}
|
||||
Timber.d(Constants.TAG, line.toString());
|
||||
explainCursor.moveToNext();
|
||||
}
|
||||
|
||||
explainCursor.close();
|
||||
}
|
||||
|
||||
private int getPeerStateValue(AutocryptState autocryptState) {
|
||||
switch (autocryptState) {
|
||||
case DISABLE: return AutocryptStatus.AUTOCRYPT_PEER_DISABLED;
|
||||
|
||||
Reference in New Issue
Block a user