UID_STATUS_MAPPER =
+ FACTORY.selectUserIdStatusByEmailMapper(AutoValue_UserPacket_UidStatus::new);
+
+ public static UserPacket create(long masterKeyId, int rank, Long type, String userId, String name, String email,
+ String comment, byte[] attribute_data, boolean isPrimary, boolean isRevoked) {
+ return new AutoValue_UserPacket(masterKeyId, rank, type,
+ userId, name, email, comment, attribute_data, isPrimary, isRevoked);
+ }
+
+ public static InsertUserPacket createInsertStatement(SupportSQLiteDatabase db) {
+ return new InsertUserPacket(db);
+ }
+
+ public void bindTo(InsertUserPacket statement) {
+ statement.bind(master_key_id(), rank(), type(), user_id(), name(), email(), comment(), attribute_data(),
+ is_primary(), is_revoked());
+ }
@AutoValue
public static abstract class UserId implements SelectUserIdsByMasterKeyIdModel {
@@ -40,4 +57,11 @@ public abstract class UserPacket implements UserPacketsModel {
return CustomColumnAdapters.VERIFICATON_STATUS_ADAPTER.decode(verified_int());
}
}
+
+ @AutoValue
+ public static abstract class UidStatus implements UidStatusModel {
+ public VerificationStatus keyStatus() {
+ return CustomColumnAdapters.VERIFICATON_STATUS_ADAPTER.decode(key_status_int());
+ }
+ }
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
index f6426bd8d..c0e0eef32 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java
@@ -339,7 +339,6 @@ public abstract class OperationResult implements Parcelable {
MSG_IP_ENCODE_FAIL (LogLevel.DEBUG, R.string.msg_ip_encode_fail),
MSG_IP_ERROR_IO_EXC (LogLevel.ERROR, R.string.msg_ip_error_io_exc),
MSG_IP_ERROR_OP_EXC (LogLevel.ERROR, R.string.msg_ip_error_op_exc),
- MSG_IP_ERROR_REMOTE_EX (LogLevel.ERROR, R.string.msg_ip_error_remote_ex),
MSG_IP_FINGERPRINT_ERROR (LogLevel.ERROR, R.string.msg_ip_fingerprint_error),
MSG_IP_FINGERPRINT_OK (LogLevel.INFO, R.string.msg_ip_fingerprint_ok),
MSG_IP_INSERT_KEYRING (LogLevel.DEBUG, R.string.msg_ip_insert_keyring),
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
index 0602d0b70..c93f574f2 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java
@@ -17,18 +17,11 @@
package org.sufficientlysecure.keychain.provider;
-import android.net.Uri;
+
import android.provider.BaseColumns;
-import org.sufficientlysecure.keychain.Constants;
-
public class KeychainContract {
- public interface KeyRingsColumns {
- String MASTER_KEY_ID = "master_key_id"; // not a database id
- String KEY_RING_DATA = "key_ring_data"; // PGPPublicKeyRing / PGPSecretKeyRing blob
- }
-
public interface KeysColumns {
String MASTER_KEY_ID = "master_key_id"; // not a database id
String RANK = "rank";
@@ -51,11 +44,6 @@ public class KeychainContract {
String EXPIRY = "expiry";
}
- public interface KeySignaturesColumns {
- String MASTER_KEY_ID = "master_key_id"; // not a database id
- String SIGNER_KEY_ID = "signer_key_id";
- }
-
public interface UserPacketsColumns {
String MASTER_KEY_ID = "master_key_id"; // foreign key to key_rings._ID
String TYPE = "type"; // not a database id
@@ -79,78 +67,15 @@ public class KeychainContract {
String DATA = "data";
}
- public interface ApiAppsAllowedKeysColumns {
- String KEY_ID = "key_id"; // not a database id
- String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name
- }
-
- public interface OverriddenWarnings {
- String IDENTIFIER = "identifier";
- }
-
- public static final String CONTENT_AUTHORITY = Constants.PROVIDER_AUTHORITY;
-
- private static final Uri BASE_CONTENT_URI_INTERNAL = Uri
- .parse("content://" + CONTENT_AUTHORITY);
-
- public static final String BASE_KEY_RINGS = "key_rings";
-
- public static final String BASE_KEY_SIGNATURES = "key_signatures";
-
- public static final String PATH_PUBLIC = "public";
- public static final String PATH_USER_IDS = "user_ids";
- public static final String PATH_KEYS = "keys";
- public static final String PATH_CERTS = "certs";
-
- public static class KeyRings implements BaseColumns, KeysColumns, UserPacketsColumns {
- public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
- .appendPath(BASE_KEY_RINGS).build();
- }
-
- public static class KeyRingData implements KeyRingsColumns, BaseColumns {
- public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
- .appendPath(BASE_KEY_RINGS).build();
-
- public static Uri buildPublicKeyRingUri(long masterKeyId) {
- return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).appendPath(PATH_PUBLIC).build();
- }
- }
-
public static class Keys implements KeysColumns, BaseColumns {
- public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
- .appendPath(BASE_KEY_RINGS).build();
-
- public static Uri buildKeysUri(long masterKeyId) {
- return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).appendPath(PATH_KEYS).build();
- }
-
- }
-
- public static class KeySignatures implements KeySignaturesColumns, BaseColumns {
- public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
- .appendPath(BASE_KEY_SIGNATURES).build();
}
public static class UserPackets implements UserPacketsColumns, BaseColumns {
- public static final String VERIFIED = "verified";
- public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
- .appendPath(BASE_KEY_RINGS).build();
-
- public static Uri buildUserIdsUri(long masterKeyId) {
- return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).appendPath(PATH_USER_IDS).build();
- }
}
public static class Certs implements CertsColumns, BaseColumns {
public static final int VERIFIED_SECRET = 1;
public static final int VERIFIED_SELF = 2;
-
- public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
- .appendPath(BASE_KEY_RINGS).build();
-
- public static Uri buildCertsUri(long masterKeyId) {
- return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).appendPath(PATH_CERTS).build();
- }
}
private KeychainContract() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainExternalContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainExternalContract.java
index 4802b667e..d0a3058a5 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainExternalContract.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainExternalContract.java
@@ -38,19 +38,6 @@ public class KeychainExternalContract {
public static final int KEY_STATUS_UNVERIFIED = 1;
public static final int KEY_STATUS_VERIFIED = 2;
- public static class EmailStatus implements BaseColumns {
- public static final String EMAIL_ADDRESS = "email_address";
- public static final String USER_ID = "user_id";
- public static final String USER_ID_STATUS = "email_status";
- public static final String MASTER_KEY_ID = "master_key_id";
-
- public static final Uri CONTENT_URI = BASE_CONTENT_URI_EXTERNAL.buildUpon()
- .appendPath(BASE_EMAIL_STATUS).build();
-
- public static final String CONTENT_TYPE
- = "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.email_status";
- }
-
public static class AutocryptStatus implements BaseColumns {
public static final String ADDRESS = "address";
@@ -72,9 +59,6 @@ public class KeychainExternalContract {
public static final Uri CONTENT_URI = BASE_CONTENT_URI_EXTERNAL.buildUpon()
.appendPath(BASE_AUTOCRYPT_STATUS).build();
-
- public static final String CONTENT_TYPE =
- "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.email_status";
}
private KeychainExternalContract() {
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
index 6efac8a72..dde5ef6ab 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java
@@ -1,225 +1,48 @@
-/*
- * Copyright (C) 2017 Schürmann & Breitmoser GbR
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-
package org.sufficientlysecure.keychain.provider;
-import android.arch.persistence.db.SupportSQLiteDatabase;
import android.content.ContentProvider;
import android.content.ContentValues;
-import android.content.UriMatcher;
import android.database.Cursor;
-import android.database.sqlite.SQLiteConstraintException;
-import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.support.annotation.NonNull;
-import android.text.TextUtils;
-
-import org.sufficientlysecure.keychain.KeychainDatabase;
-import org.sufficientlysecure.keychain.daos.DatabaseNotifyManager;
-import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeySignatures;
-import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns;
-import org.sufficientlysecure.keychain.KeychainDatabase.Tables;
-import timber.log.Timber;
+import android.support.annotation.Nullable;
public class KeychainProvider extends ContentProvider {
- private static final int KEY_RING_KEYS = 201;
- private static final int KEY_RING_USER_IDS = 202;
- private static final int KEY_RING_PUBLIC = 203;
- private static final int KEY_RING_CERTS = 205;
-
- private static final int KEY_SIGNATURES = 700;
-
- protected UriMatcher mUriMatcher;
-
- /**
- * Build and return a {@link UriMatcher} that catches all {@link Uri} variations supported by
- * this {@link ContentProvider}.
- */
- protected UriMatcher buildUriMatcher() {
- final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
-
- String authority = KeychainContract.CONTENT_AUTHORITY;
-
- /*
- * list key_ring specifics
- *
- *
- * key_rings/_/keys
- * key_rings/_/user_ids
- * key_rings/_/public
- * key_rings/_/certs
- *
- */
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
- + KeychainContract.PATH_KEYS,
- KEY_RING_KEYS);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
- + KeychainContract.PATH_USER_IDS,
- KEY_RING_USER_IDS);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
- + KeychainContract.PATH_PUBLIC,
- KEY_RING_PUBLIC);
- matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/"
- + KeychainContract.PATH_CERTS,
- KEY_RING_CERTS);
-
- matcher.addURI(authority, KeychainContract.BASE_KEY_SIGNATURES, KEY_SIGNATURES);
-
-
- return matcher;
- }
-
- private KeychainDatabase mKeychainDatabase;
@Override
public boolean onCreate() {
- mUriMatcher = buildUriMatcher();
- return true;
+ return false;
}
- public KeychainDatabase getDb() {
- if(mKeychainDatabase == null) {
- mKeychainDatabase = KeychainDatabase.getInstance(getContext());
- }
- return mKeychainDatabase;
+ @Nullable
+ @Override
+ public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
+ @Nullable String[] selectionArgs, @Nullable String sortOrder) {
+ throw new UnsupportedOperationException();
}
+ @Nullable
@Override
public String getType(@NonNull Uri uri) {
throw new UnsupportedOperationException();
}
+ @Nullable
@Override
- public Cursor query(
- @NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Uri insert(@NonNull Uri uri, ContentValues values) {
- Timber.d("insert(uri=" + uri + ", values=" + values.toString() + ")");
-
- final SupportSQLiteDatabase db = getDb().getWritableDatabase();
-
- Uri rowUri = null;
- Long keyId = null;
- try {
- final int match = mUriMatcher.match(uri);
-
- switch (match) {
- case KEY_RING_PUBLIC: {
- db.insert(Tables.KEY_RINGS_PUBLIC, SQLiteDatabase.CONFLICT_FAIL, values);
- keyId = values.getAsLong(Keys.MASTER_KEY_ID);
- break;
- }
- case KEY_RING_KEYS: {
- db.insert(Tables.KEYS, SQLiteDatabase.CONFLICT_FAIL, values);
- keyId = values.getAsLong(Keys.MASTER_KEY_ID);
- break;
- }
- case KEY_RING_USER_IDS: {
- // iff TYPE is null, user_id MUST be null as well
- 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 (((Number) values.get(UserPacketsColumns.RANK)).intValue() == 0 && values.get(UserPacketsColumns.USER_ID) == null) {
- throw new AssertionError("Rank 0 user packet must be a user id!");
- }
- db.insert(Tables.USER_PACKETS, SQLiteDatabase.CONFLICT_FAIL, values);
- keyId = values.getAsLong(UserPackets.MASTER_KEY_ID);
- break;
- }
- case KEY_RING_CERTS: {
- // we replace here, keeping only the latest signature
- // TODO this would be better handled in savePublicKeyRing directly!
- db.insert(Tables.CERTS, SQLiteDatabase.CONFLICT_FAIL, values);
- keyId = values.getAsLong(Certs.MASTER_KEY_ID);
- break;
- }
- case KEY_SIGNATURES: {
- db.insert(Tables.KEY_SIGNATURES, SQLiteDatabase.CONFLICT_FAIL, values);
- rowUri = KeySignatures.CONTENT_URI;
- break;
- }
- default: {
- throw new UnsupportedOperationException("Unknown uri: " + uri);
- }
- }
-
- if (keyId != null) {
- uri = DatabaseNotifyManager.getNotifyUriMasterKeyId(keyId);
- rowUri = uri;
- }
-
- } catch (SQLiteConstraintException e) {
- Timber.d(e, "Constraint exception on insert! Entry already existing?");
- }
-
- return rowUri;
- }
-
- @Override
- public int delete(@NonNull Uri uri, String additionalSelection, String[] selectionArgs) {
+ public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
throw new UnsupportedOperationException();
}
@Override
- public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- Timber.v("update(uri=" + uri + ", values=" + values.toString() + ")");
+ public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
- final SupportSQLiteDatabase db = getDb().getWritableDatabase();
-
- int count = 0;
- try {
- final int match = mUriMatcher.match(uri);
- switch (match) {
- case KEY_RING_KEYS: {
- if (values.size() != 1 || !values.containsKey(Keys.HAS_SECRET)) {
- throw new UnsupportedOperationException(
- "Only has_secret column may be updated!");
- }
- // make sure we get a long value here
- Long mkid = Long.parseLong(uri.getPathSegments().get(1));
- String actualSelection = Keys.MASTER_KEY_ID + " = " + Long.toString(mkid);
- if (!TextUtils.isEmpty(selection)) {
- actualSelection += " AND (" + selection + ")";
- }
- count = db.update(Tables.KEYS, SQLiteDatabase.CONFLICT_FAIL, values, actualSelection, selectionArgs);
- break;
- }
- default: {
- throw new UnsupportedOperationException("Unknown uri: " + uri);
- }
- }
- } catch (SQLiteConstraintException e) {
- Timber.d(e, "Constraint exception on update! Entry already existing?");
- }
-
- return count;
+ @Override
+ public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
+ @Nullable String[] selectionArgs) {
+ throw new UnsupportedOperationException();
}
}
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AutocryptInteractor.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AutocryptInteractor.java
index 6dfa1e143..db6aaa94c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AutocryptInteractor.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/AutocryptInteractor.java
@@ -3,8 +3,11 @@ package org.sufficientlysecure.keychain.remote;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import android.content.Context;
import android.support.annotation.Nullable;
@@ -146,15 +149,15 @@ public class AutocryptInteractor {
return uncachedKeyRing;
}
- public List determineAutocryptRecommendations(String... autocryptIds) {
- List result = new ArrayList<>(autocryptIds.length);
+ public Map determineAutocryptRecommendations(String... autocryptIds) {
+ Map result = new HashMap<>(autocryptIds.length);
for (AutocryptKeyStatus autocryptKeyStatus : autocryptPeerDao.getAutocryptKeyStatus(packageName, autocryptIds)) {
AutocryptRecommendationResult peerResult = determineAutocryptRecommendation(autocryptKeyStatus);
- result.add(peerResult);
+ result.put(peerResult.peerId, peerResult);
}
- return result;
+ return Collections.unmodifiableMap(result);
}
/** Determines Autocrypt "ui-recommendation", according to spec.
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java
index 753f198a2..f3a6fca2e 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/KeychainExternalProvider.java
@@ -20,34 +20,28 @@ package org.sufficientlysecure.keychain.remote;
import java.security.AccessControlException;
import java.util.Arrays;
-import java.util.HashMap;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
-import android.arch.persistence.db.SupportSQLiteDatabase;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteQueryBuilder;
+import android.database.MatrixCursor;
import android.net.Uri;
import android.support.annotation.NonNull;
-import android.text.TextUtils;
+import android.widget.Toast;
import org.sufficientlysecure.keychain.BuildConfig;
-import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.daos.ApiAppDao;
-import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
-import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
-import org.sufficientlysecure.keychain.KeychainDatabase;
-import org.sufficientlysecure.keychain.KeychainDatabase.Tables;
+import org.sufficientlysecure.keychain.daos.DatabaseNotifyManager;
+import org.sufficientlysecure.keychain.daos.UserIdDao;
+import org.sufficientlysecure.keychain.model.UserPacket.UidStatus;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing.VerificationStatus;
import org.sufficientlysecure.keychain.provider.KeychainExternalContract;
import org.sufficientlysecure.keychain.provider.KeychainExternalContract.AutocryptStatus;
-import org.sufficientlysecure.keychain.provider.KeychainExternalContract.EmailStatus;
import org.sufficientlysecure.keychain.remote.AutocryptInteractor.AutocryptRecommendationResult;
import org.sufficientlysecure.keychain.remote.AutocryptInteractor.AutocryptState;
import timber.log.Timber;
@@ -59,39 +53,23 @@ public class KeychainExternalProvider extends ContentProvider {
private static final int AUTOCRYPT_STATUS = 201;
private static final int AUTOCRYPT_STATUS_INTERNAL = 202;
- public static final String TEMP_TABLE_QUERIED_ADDRESSES = "queried_addresses";
- public static final String TEMP_TABLE_COLUMN_ADDRES = "address";
-
private UriMatcher uriMatcher;
private ApiPermissionHelper apiPermissionHelper;
- /**
- * Build and return a {@link UriMatcher} that catches all {@link Uri} variations supported by
- * this {@link ContentProvider}.
- */
protected UriMatcher buildUriMatcher() {
final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
String authority = KeychainExternalContract.CONTENT_AUTHORITY_EXTERNAL;
- /*
- * list email_status
- *
- *
- * email_status/
- *
- */
matcher.addURI(authority, KeychainExternalContract.BASE_EMAIL_STATUS, EMAIL_STATUS);
-
matcher.addURI(authority, KeychainExternalContract.BASE_AUTOCRYPT_STATUS, AUTOCRYPT_STATUS);
matcher.addURI(authority, KeychainExternalContract.BASE_AUTOCRYPT_STATUS + "/*", AUTOCRYPT_STATUS_INTERNAL);
return matcher;
}
- /** {@inheritDoc} */
@Override
public boolean onCreate() {
uriMatcher = buildUriMatcher();
@@ -105,104 +83,22 @@ public class KeychainExternalProvider extends ContentProvider {
return true;
}
- /**
- * {@inheritDoc}
- */
- @Override
- public String getType(@NonNull Uri uri) {
- final int match = uriMatcher.match(uri);
- switch (match) {
- case EMAIL_STATUS:
- return EmailStatus.CONTENT_TYPE;
- default:
- throw new UnsupportedOperationException("Unknown uri: " + uri);
- }
- }
-
- /**
- * {@inheritDoc}
- */
@Override
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();
-
- int match = uriMatcher.match(uri);
-
- String groupBy = null;
-
- SupportSQLiteDatabase db = KeychainDatabase.getTemporaryInstance(getContext()).getReadableDatabase();
+ Context context = getContext();
+ if (context == null) {
+ throw new IllegalStateException();
+ }
String callingPackageName = apiPermissionHelper.getCurrentCallingPackage();
+ int match = uriMatcher.match(uri);
switch (match) {
case EMAIL_STATUS: {
- boolean callerIsAllowed = apiPermissionHelper.isAllowedIgnoreErrors();
- if (!callerIsAllowed) {
- throw new AccessControlException("An application must register before use of KeychainExternalProvider!");
- }
-
- db.execSQL("CREATE TEMPORARY TABLE " + TEMP_TABLE_QUERIED_ADDRESSES + " (" + TEMP_TABLE_COLUMN_ADDRES + " TEXT);");
- ContentValues cv = new ContentValues();
- for (String address : selectionArgs) {
- cv.put(TEMP_TABLE_COLUMN_ADDRES, address);
- db.insert(TEMP_TABLE_QUERIED_ADDRESSES, SQLiteDatabase.CONFLICT_FAIL, cv);
- }
-
- HashMap projectionMap = new HashMap<>();
- projectionMap.put(EmailStatus._ID, "email AS _id");
- projectionMap.put(EmailStatus.EMAIL_ADDRESS, // this is actually the queried address
- TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES + " AS " + EmailStatus.EMAIL_ADDRESS);
- projectionMap.put(EmailStatus.USER_ID,
- Tables.USER_PACKETS + "." + UserPackets.USER_ID + " AS " + EmailStatus.USER_ID);
- // we take the minimum (>0) here, where "1" is "verified by known secret key", "2" is "self-certified"
- projectionMap.put(EmailStatus.USER_ID_STATUS, "CASE ( MIN (" + 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 " + KeychainExternalContract.KEY_STATUS_UNVERIFIED
- + " END AS " + EmailStatus.USER_ID_STATUS);
- projectionMap.put(EmailStatus.MASTER_KEY_ID,
- Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " AS " + EmailStatus.MASTER_KEY_ID);
- qb.setProjectionMap(projectionMap);
-
- if (projection == null) {
- throw new IllegalArgumentException("Please provide a projection!");
- }
-
- qb.setTables(
- TEMP_TABLE_QUERIED_ADDRESSES
- + " LEFT 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
- + ")"
- + " LEFT JOIN " + Tables.CERTS + " ON ("
- + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = " + Tables.CERTS + "." + Certs.MASTER_KEY_ID
- + " AND " + Tables.USER_PACKETS + "." + UserPackets.RANK + " = " + Tables.CERTS + "." + Certs.RANK
- + ")"
- );
- // in case there are multiple verifying certificates
- groupBy = TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES;
- List plist = Arrays.asList(projection);
- if (plist.contains(EmailStatus.USER_ID)) {
- groupBy += ", " + Tables.USER_PACKETS + "." + UserPackets.USER_ID;
- }
-
- // verified == 0 has no self-cert, which is basically an error case. never return that!
- // verified == null is fine, because it means there was no join partner
- qb.appendWhere(Tables.CERTS + "." + Certs.VERIFIED + " IS NULL OR " + Tables.CERTS + "." + Certs.VERIFIED + " > 0");
-
- if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = EmailStatus.EMAIL_ADDRESS;
- }
-
- // uri to watch is all /key_rings/
- uri = KeyRings.CONTENT_URI;
-
- break;
+ Toast.makeText(context, "This API is no longer supported by OpenKeychain!", Toast.LENGTH_SHORT).show();
+ return new MatrixCursor(projection);
}
case AUTOCRYPT_STATUS_INTERNAL:
@@ -223,164 +119,127 @@ public class KeychainExternalProvider extends ContentProvider {
throw new IllegalArgumentException("Please provide a projection!");
}
- db.execSQL("CREATE TEMPORARY TABLE " + TEMP_TABLE_QUERIED_ADDRESSES + " (" +
- TEMP_TABLE_COLUMN_ADDRES + " TEXT NOT NULL PRIMARY KEY, " +
- AutocryptStatus.UID_KEY_STATUS + " INT, " +
- AutocryptStatus.UID_ADDRESS + " TEXT, " +
- AutocryptStatus.UID_MASTER_KEY_ID + " INT, " +
- AutocryptStatus.UID_CANDIDATES + " INT, " +
- AutocryptStatus.AUTOCRYPT_PEER_STATE + " INT DEFAULT " + AutocryptStatus.AUTOCRYPT_PEER_DISABLED + ", " +
- AutocryptStatus.AUTOCRYPT_KEY_STATUS + " INT, " +
- AutocryptStatus.AUTOCRYPT_MASTER_KEY_ID + " INT" +
- ");");
- ContentValues cv = new ContentValues();
- for (String address : selectionArgs) {
- cv.put(TEMP_TABLE_COLUMN_ADDRES, address);
- db.insert(TEMP_TABLE_QUERIED_ADDRESSES, SQLiteDatabase.CONFLICT_FAIL, cv);
- }
-
- boolean isWildcardSelector = selectionArgs.length == 1 && selectionArgs[0].contains("%");
-
List plist = Arrays.asList(projection);
-
+ boolean isWildcardSelector = selectionArgs.length == 1 && selectionArgs[0].contains("%");
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) {
- AutocryptInteractor autocryptInteractor =
- AutocryptInteractor.getInstance(getContext(), callingPackageName);
- fillTempTableWithAutocryptRecommendations(db, autocryptInteractor, selectionArgs);
- }
- HashMap projectionMap = new HashMap<>();
- projectionMap.put(AutocryptStatus._ID, AutocryptStatus._ID);
- projectionMap.put(AutocryptStatus.ADDRESS, AutocryptStatus.ADDRESS);
- projectionMap.put(AutocryptStatus.UID_KEY_STATUS, AutocryptStatus.UID_KEY_STATUS);
- projectionMap.put(AutocryptStatus.UID_ADDRESS, AutocryptStatus.UID_ADDRESS);
- projectionMap.put(AutocryptStatus.UID_MASTER_KEY_ID, AutocryptStatus.UID_MASTER_KEY_ID);
- projectionMap.put(AutocryptStatus.UID_CANDIDATES, AutocryptStatus.UID_CANDIDATES);
- projectionMap.put(AutocryptStatus.AUTOCRYPT_PEER_STATE, AutocryptStatus.AUTOCRYPT_PEER_STATE);
- projectionMap.put(AutocryptStatus.AUTOCRYPT_KEY_STATUS, AutocryptStatus.AUTOCRYPT_KEY_STATUS);
- projectionMap.put(AutocryptStatus.AUTOCRYPT_MASTER_KEY_ID, AutocryptStatus.AUTOCRYPT_MASTER_KEY_ID);
- qb.setProjectionMap(projectionMap);
- qb.setTables(TEMP_TABLE_QUERIED_ADDRESSES);
+ Map uidStatuses = queriesUidResult ?
+ loadUidStatusMap(selectionArgs, isWildcardSelector) : Collections.emptyMap();
+ Map autocryptStates = queriesAutocryptResult ?
+ loadAutocryptRecommendationMap(selectionArgs, callingPackageName) : Collections.emptyMap();
- if (TextUtils.isEmpty(sortOrder)) {
- sortOrder = AutocryptStatus.ADDRESS;
- }
+ MatrixCursor cursor =
+ mapResultsToProjectedMatrixCursor(projection, selectionArgs, uidStatuses, autocryptStates);
- // uri to watch is all /key_rings/
- uri = KeyRings.CONTENT_URI;
- break;
+ uri = DatabaseNotifyManager.getNotifyUriAllKeys();
+ cursor.setNotificationUri(context.getContentResolver(), uri);
+
+ return cursor;
}
default: {
throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")");
}
-
}
+ }
- // If no sort order is specified use the default
- String orderBy;
- if (TextUtils.isEmpty(sortOrder)) {
- orderBy = null;
- } else {
- orderBy = sortOrder;
- }
+ @NonNull
+ private MatrixCursor mapResultsToProjectedMatrixCursor(String[] projection, String[] selectionArgs,
+ Map uidStatuses, Map autocryptStates) {
+ MatrixCursor cursor = new MatrixCursor(projection);
+ for (String selectionArg : selectionArgs) {
+ AutocryptRecommendationResult autocryptResult = autocryptStates.get(selectionArg);
+ UidStatus uidStatus = uidStatuses.get(selectionArg);
- qb.setStrict(true);
- String query = qb.buildQuery(projection, null, groupBy, null, orderBy, null);
- Cursor cursor = db.query(query);
- if (cursor != null) {
- // Tell the cursor what uri to watch, so it knows when its source data changes
- cursor.setNotificationUri(getContext().getContentResolver(), uri);
- if (Constants.DEBUG_LOG_DB_QUERIES) {
- DatabaseUtils.dumpCursor(cursor);
+ Object[] row = new Object[projection.length];
+ for (int i = 0; i < projection.length; i++) {
+ if (AutocryptStatus.ADDRESS.equals(projection[i]) || AutocryptStatus._ID.equals(projection[i])) {
+ row[i] = selectionArg;
+ } else {
+ row[i] = columnNameToRowContent(projection[i], autocryptResult, uidStatus);
+ }
}
+ cursor.addRow(row);
}
-
- 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 fillTempTableWithAutocryptRecommendations(SupportSQLiteDatabase db,
- AutocryptInteractor autocryptInteractor, String[] peerIds) {
- List autocryptStates =
- autocryptInteractor.determineAutocryptRecommendations(peerIds);
-
- fillTempTableWithAutocryptRecommendations(db, autocryptStates);
- }
-
- private void fillTempTableWithAutocryptRecommendations(SupportSQLiteDatabase db,
- List autocryptRecommendations) {
- ContentValues cv = new ContentValues();
- for (AutocryptRecommendationResult peerResult : autocryptRecommendations) {
- cv.clear();
-
- 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 ?
+ private Object columnNameToRowContent(
+ String columnName, AutocryptRecommendationResult autocryptResult, UidStatus uidStatus) {
+ switch (columnName) {
+ case AutocryptStatus.UID_KEY_STATUS: {
+ if (uidStatus == null) {
+ return null;
+ }
+ return uidStatus.keyStatus() == VerificationStatus.VERIFIED_SECRET ?
KeychainExternalContract.KEY_STATUS_VERIFIED :
- KeychainExternalContract.KEY_STATUS_UNVERIFIED);
+ KeychainExternalContract.KEY_STATUS_UNVERIFIED;
}
+ case AutocryptStatus.UID_ADDRESS:
+ if (uidStatus == null) {
+ return null;
+ }
+ return uidStatus.user_id();
- db.update(TEMP_TABLE_QUERIED_ADDRESSES, SQLiteDatabase.CONFLICT_IGNORE, cv,TEMP_TABLE_COLUMN_ADDRES + "=?",
- new String[] { peerResult.peerId });
+ case AutocryptStatus.UID_MASTER_KEY_ID:
+ if (uidStatus == null) {
+ return null;
+ }
+ return uidStatus.master_key_id();
+
+ case AutocryptStatus.UID_CANDIDATES:
+ if (uidStatus == null) {
+ return null;
+ }
+ return uidStatus.candidates();
+
+ case AutocryptStatus.AUTOCRYPT_PEER_STATE:
+ if (autocryptResult == null) {
+ return null;
+ }
+ return getPeerStateValue(autocryptResult.autocryptState);
+
+ case AutocryptStatus.AUTOCRYPT_KEY_STATUS:
+ if (autocryptResult == null) {
+ return null;
+ }
+ return autocryptResult.isVerified ?
+ KeychainExternalContract.KEY_STATUS_VERIFIED : KeychainExternalContract.KEY_STATUS_UNVERIFIED;
+
+ case AutocryptStatus.AUTOCRYPT_MASTER_KEY_ID:
+ if (autocryptResult == null) {
+ return null;
+ }
+ return autocryptResult.masterKeyId;
+
+ default:
+ throw new IllegalArgumentException("Unhandled case " + columnName);
}
}
- private void fillTempTableWithUidResult(SupportSQLiteDatabase 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 Map loadUidStatusMap(String[] selectionArgs, boolean isWildcardSelector) {
+ UserIdDao userIdDao = UserIdDao.getInstance(getContext());
+ if (isWildcardSelector) {
+ UidStatus uidStatus = userIdDao.getUidStatusByEmailLike(selectionArgs[0]);
+ return Collections.singletonMap(selectionArgs[0], uidStatus);
+ } else {
+ return userIdDao.getUidStatusByEmail(selectionArgs);
+ }
+ }
+
+ private Map loadAutocryptRecommendationMap(
+ String[] selectionArgs, String callingPackageName) {
+ AutocryptInteractor autocryptInteractor = AutocryptInteractor.getInstance(getContext(), callingPackageName);
+ return autocryptInteractor.determineAutocryptRecommendations(selectionArgs);
}
private int getPeerStateValue(AutocryptState autocryptState) {
@@ -394,6 +253,11 @@ public class KeychainExternalProvider extends ContentProvider {
throw new IllegalStateException("Unhandled case!");
}
+ @Override
+ public String getType(@NonNull Uri uri) {
+ throw new UnsupportedOperationException("Unknown uri: " + uri);
+ }
+
@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
throw new UnsupportedOperationException();
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
index d40cbfe12..5e7519a7c 100644
--- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java
@@ -52,8 +52,11 @@ import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemClickListener;
import eu.davidea.flexibleadapter.FlexibleAdapter.OnItemLongClickListener;
import eu.davidea.flexibleadapter.SelectableAdapter.Mode;
import org.sufficientlysecure.keychain.Constants;
+import org.sufficientlysecure.keychain.KeychainDatabase;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
+import org.sufficientlysecure.keychain.daos.DatabaseNotifyManager;
+import org.sufficientlysecure.keychain.daos.KeyRepository;
import org.sufficientlysecure.keychain.keysync.KeyserverSyncManager;
import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo;
import org.sufficientlysecure.keychain.operations.KeySyncParcel;
@@ -61,9 +64,6 @@ import org.sufficientlysecure.keychain.operations.results.BenchmarkResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
-import org.sufficientlysecure.keychain.daos.KeyRepository;
-import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
-import org.sufficientlysecure.keychain.KeychainDatabase;
import org.sufficientlysecure.keychain.service.BenchmarkInputParcel;
import org.sufficientlysecure.keychain.ui.adapter.FlexibleKeyDetailsItem;
import org.sufficientlysecure.keychain.ui.adapter.FlexibleKeyDummyItem;
@@ -492,7 +492,7 @@ public class KeyListFragment extends RecyclerFragment= strftime('%s', 'now'));
+
unifiedKeyView:
CREATE VIEW unifiedKeyView AS
SELECT keys.master_key_id, keys.fingerprint, MIN(user_packets.rank), user_packets.user_id, user_packets.name, user_packets.email, user_packets.comment, keys.creation, keys.expiry, keys.is_revoked, keys.is_secure, keys.can_certify, certs.verified,
@@ -86,11 +105,11 @@ SELECT fingerprint
selectEffectiveSignKeyIdByMasterKeyId:
SELECT key_id
FROM keys
- WHERE is_revoked = 0 AND is_secure = 1 AND has_secret > 1 AND ( expiry IS NULL OR expiry >= date('now') )
+ WHERE is_revoked = 0 AND is_secure = 1 AND has_secret > 1 AND ( expiry IS NULL OR expiry >= strftime('%s', 'now') )
AND can_sign = 1 AND master_key_id = ?;
selectEffectiveAuthKeyIdByMasterKeyId:
SELECT key_id
FROM keys
- WHERE is_revoked = 0 AND is_secure = 1 AND has_secret > 1 AND ( expiry IS NULL OR expiry >= date('now') )
+ WHERE is_revoked = 0 AND is_secure = 1 AND has_secret > 1 AND ( expiry IS NULL OR expiry >= strftime('%s', 'now') )
AND can_authenticate = 1 AND master_key_id = ?;
diff --git a/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/UserPackets.sq b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/UserPackets.sq
index 81b5d94e9..e152d7fed 100644
--- a/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/UserPackets.sq
+++ b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/UserPackets.sq
@@ -16,6 +16,9 @@ CREATE TABLE IF NOT EXISTS user_packets(
keyrings_public(master_key_id) ON DELETE CASCADE
);
+insertUserPacket:
+INSERT INTO user_packets VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
+
selectUserIdsByMasterKeyId:
SELECT user_packets.master_key_id, user_packets.rank, user_id, name, email, comment, is_primary, is_revoked, MIN(certs.verified) AS verified_int
FROM user_packets
@@ -46,3 +49,23 @@ SELECT user_packets.master_key_id, user_packets.rank, attribute_data, is_primary
LEFT JOIN certs ON ( user_packets.master_key_id = certs.master_key_id AND user_packets.rank = certs.rank AND certs.verified > 0 )
WHERE user_packets.type = ? AND user_packets.master_key_id = ? AND user_packets.rank = ?
GROUP BY user_packets.master_key_id, user_packets.rank;
+
+
+uidStatus:
+CREATE VIEW uidStatus AS
+ SELECT user_packets.email, MIN(certs.verified) AS key_status_int, user_packets.user_id, user_packets.master_key_id, COUNT(DISTINCT user_packets.master_key_id) AS candidates
+ FROM user_packets
+ JOIN validMasterKeys USING (master_key_id)
+ LEFT JOIN certs ON (certs.master_key_id = user_packets.master_key_id AND certs.rank = user_packets.rank AND certs.verified > 0)
+ WHERE user_packets.email IS NOT NULL
+ GROUP BY user_packets.email;
+
+selectUserIdStatusByEmail:
+SELECT *
+FROM uidStatus
+ WHERE email IN ?;
+
+selectUserIdStatusByEmailLike:
+SELECT *
+FROM uidStatus
+ WHERE email LIKE ?;
diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/KeychainExternalProviderTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/KeychainExternalProviderTest.java
index 27ffb3ffd..327145607 100644
--- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/KeychainExternalProviderTest.java
+++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/remote/KeychainExternalProviderTest.java
@@ -18,19 +18,18 @@ import org.robolectric.shadows.ShadowBinder;
import org.robolectric.shadows.ShadowLog;
import org.robolectric.shadows.ShadowPackageManager;
import org.sufficientlysecure.keychain.KeychainTestRunner;
+import org.sufficientlysecure.keychain.daos.ApiAppDao;
+import org.sufficientlysecure.keychain.daos.AutocryptPeerDao;
+import org.sufficientlysecure.keychain.daos.KeyWritableRepository;
import org.sufficientlysecure.keychain.model.ApiApp;
import org.sufficientlysecure.keychain.model.AutocryptPeer.GossipOrigin;
import org.sufficientlysecure.keychain.operations.CertifyOperation;
import org.sufficientlysecure.keychain.operations.results.CertifyResult;
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
-import org.sufficientlysecure.keychain.daos.ApiAppDao;
-import org.sufficientlysecure.keychain.daos.AutocryptPeerDao;
import org.sufficientlysecure.keychain.provider.KeyRepositorySaveTest;
-import org.sufficientlysecure.keychain.daos.KeyWritableRepository;
import org.sufficientlysecure.keychain.provider.KeychainExternalContract;
import org.sufficientlysecure.keychain.provider.KeychainExternalContract.AutocryptStatus;
-import org.sufficientlysecure.keychain.provider.KeychainExternalContract.EmailStatus;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
@@ -49,10 +48,7 @@ public class KeychainExternalProviderTest {
static final String PACKAGE_NAME = "test.package";
static final byte[] PACKAGE_SIGNATURE = new byte[] { 1, 2, 3 };
static final String MAIL_ADDRESS_1 = "twi@openkeychain.org";
- static final String MAIL_ADDRESS_2 = "pink@openkeychain.org";
- static final String MAIL_ADDRESS_SEC_1 = "twi-sec@openkeychain.org";
static final String USER_ID_1 = "twi ";
- static final String USER_ID_SEC_1 = "twi ";
static final long KEY_ID_SECRET = 0x5D4DA4423C39122FL;
static final long KEY_ID_PUBLIC = 0x9A282CE2AB44A382L;
public static final String AUTOCRYPT_PEER = "tid";
@@ -92,8 +88,8 @@ public class KeychainExternalProviderTest {
apiAppDao.deleteApiApp(PACKAGE_NAME);
contentResolver.query(
- EmailStatus.CONTENT_URI,
- new String[] { EmailStatus.EMAIL_ADDRESS, EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID },
+ AutocryptStatus.CONTENT_URI,
+ new String[] { AutocryptStatus.ADDRESS },
null, new String [] { }, null
);
}
@@ -104,106 +100,12 @@ public class KeychainExternalProviderTest {
apiAppDao.insertApiApp(ApiApp.create(PACKAGE_NAME, new byte[] { 1, 2, 4 }));
contentResolver.query(
- EmailStatus.CONTENT_URI,
- new String[] { EmailStatus.EMAIL_ADDRESS, EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID },
+ AutocryptStatus.CONTENT_URI,
+ new String[] { AutocryptStatus.ADDRESS },
null, new String [] { }, null
);
}
- @Test
- public void testEmailStatus_withNonExistentAddress() throws Exception {
- Cursor cursor = contentResolver.query(
- EmailStatus.CONTENT_URI, new String[] {
- EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID_STATUS, EmailStatus.USER_ID },
- null, new String [] { MAIL_ADDRESS_1 }, null
- );
-
- assertNotNull(cursor);
- assertTrue(cursor.moveToFirst());
- assertEquals(MAIL_ADDRESS_1, cursor.getString(0));
- assertEquals(KeychainExternalContract.KEY_STATUS_UNAVAILABLE, cursor.getInt(1));
- assertTrue(cursor.isNull(2));
- }
-
- @Test
- public void testEmailStatus() throws Exception {
- insertPublicKeyringFrom("/test-keys/testring.pub");
-
- Cursor cursor = contentResolver.query(
- EmailStatus.CONTENT_URI, new String[] {
- EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID_STATUS, EmailStatus.USER_ID },
- null, new String [] { MAIL_ADDRESS_1 }, null
- );
-
- assertNotNull(cursor);
- assertTrue(cursor.moveToFirst());
- assertEquals(MAIL_ADDRESS_1, cursor.getString(0));
- assertEquals(KeychainExternalContract.KEY_STATUS_UNVERIFIED, cursor.getInt(1));
- assertEquals("twi ", cursor.getString(2));
- assertFalse(cursor.moveToNext());
- }
-
- @Test
- public void testEmailStatus_multiple() throws Exception {
- insertPublicKeyringFrom("/test-keys/testring.pub");
-
- Cursor cursor = contentResolver.query(
- EmailStatus.CONTENT_URI, new String[] {
- EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID_STATUS, EmailStatus.USER_ID },
- null, new String [] { MAIL_ADDRESS_1, MAIL_ADDRESS_2 }, null
- );
-
- assertNotNull(cursor);
- assertTrue(cursor.moveToNext());
- assertEquals(MAIL_ADDRESS_2, cursor.getString(0));
- assertEquals(KeychainExternalContract.KEY_STATUS_UNAVAILABLE, cursor.getInt(1));
- assertTrue(cursor.isNull(2));
- assertTrue(cursor.moveToNext());
- assertEquals(MAIL_ADDRESS_1, cursor.getString(0));
- assertEquals(KeychainExternalContract.KEY_STATUS_UNVERIFIED, cursor.getInt(1));
- assertEquals("twi ", cursor.getString(2));
- assertFalse(cursor.moveToNext());
- }
-
- @Test
- public void testEmailStatus_withSecretKey() throws Exception {
- insertSecretKeyringFrom("/test-keys/testring.sec");
-
- Cursor cursor = contentResolver.query(
- EmailStatus.CONTENT_URI, new String[] {
- EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID_STATUS, EmailStatus.USER_ID },
- null, new String [] { MAIL_ADDRESS_SEC_1 }, null
- );
-
- assertNotNull(cursor);
- assertTrue(cursor.moveToFirst());
- assertEquals(MAIL_ADDRESS_SEC_1, cursor.getString(0));
- assertEquals(USER_ID_SEC_1, cursor.getString(2));
- assertEquals(2, cursor.getInt(1));
- assertFalse(cursor.moveToNext());
- }
-
- @Test
- public void testEmailStatus_withConfirmedKey() throws Exception {
- insertSecretKeyringFrom("/test-keys/testring.sec");
- insertPublicKeyringFrom("/test-keys/testring.pub");
-
- certifyKey(KEY_ID_SECRET, KEY_ID_PUBLIC, USER_ID_1);
-
- Cursor cursor = contentResolver.query(
- EmailStatus.CONTENT_URI, new String[] {
- EmailStatus.EMAIL_ADDRESS, EmailStatus.USER_ID, EmailStatus.USER_ID_STATUS},
- null, new String [] { MAIL_ADDRESS_1 }, null
- );
-
- assertNotNull(cursor);
- assertTrue(cursor.moveToFirst());
- assertEquals(MAIL_ADDRESS_1, cursor.getString(0));
- assertEquals(USER_ID_1, cursor.getString(1));
- assertEquals(KeychainExternalContract.KEY_STATUS_VERIFIED, cursor.getInt(2));
- assertFalse(cursor.moveToNext());
- }
-
@Test
public void testAutocryptStatus_autocryptPeer_withUnconfirmedKey() throws Exception {
insertSecretKeyringFrom("/test-keys/testring.sec");