From bae90f1b2350037804cac7400f18948135ea5c6e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 18 Jun 2018 11:40:39 +0200 Subject: [PATCH] extract UpdatedKeys access from KeychainProvider into KeyMetadataDao --- .../keychain/keysync/KeyserverSyncWorker.java | 8 +- .../keychain/model/CustomColumnAdapters.java | 28 +++++ .../keychain/model/KeyMetadata.java | 24 ++++ .../keychain/operations/CertifyOperation.java | 12 +- .../keychain/operations/EditKeyOperation.java | 8 +- .../keychain/operations/ImportOperation.java | 12 +- .../provider/ApiDataAccessObject.java | 1 - .../provider/DatabaseNotifyManager.java | 2 +- .../keychain/provider/KeyMetadataDao.java | 69 +++++++++++ .../keychain/provider/KeyRepository.java | 28 ----- .../provider/KeyWritableRepository.java | 40 +----- .../keychain/provider/KeychainContract.java | 10 -- .../keychain/provider/KeychainDatabase.java | 18 ++- .../keychain/provider/KeychainProvider.java | 66 ---------- .../provider/LastUpdateInteractor.java | 105 ---------------- .../ui/SettingsKeyserverFragment.java | 8 +- .../keychain/ui/keyview/ViewKeyFragment.java | 6 +- .../ui/keyview/loader/KeyserverStatusDao.java | 114 ------------------ .../ui/keyview/loader/ViewKeyLiveData.java | 13 +- .../presenter/KeyserverStatusPresenter.java | 10 +- .../keychain/KeyMetadata.sq | 28 +++++ .../keychain/KeyRingsPublic.sq | 4 + .../org/sufficientlysecure/keychain/Keys.sq | 21 ++++ .../keychain/provider/InteropTest.java | 24 ++-- 24 files changed, 245 insertions(+), 414 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/CustomColumnAdapters.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/KeyMetadata.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyMetadataDao.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/LastUpdateInteractor.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusDao.java create mode 100644 OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/KeyMetadata.sq create mode 100644 OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/KeyRingsPublic.sq create mode 100644 OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Keys.sq diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keysync/KeyserverSyncWorker.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keysync/KeyserverSyncWorker.java index 4429c254a..bbcac9f08 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keysync/KeyserverSyncWorker.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keysync/KeyserverSyncWorker.java @@ -26,8 +26,8 @@ import org.sufficientlysecure.keychain.operations.ImportOperation; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.Progressable; +import org.sufficientlysecure.keychain.provider.KeyMetadataDao; import org.sufficientlysecure.keychain.provider.KeyWritableRepository; -import org.sufficientlysecure.keychain.provider.LastUpdateInteractor; import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.OrbotRequiredDialogActivity; @@ -49,14 +49,14 @@ public class KeyserverSyncWorker extends Worker { Constants.DEBUG_KEYSERVER_SYNC ? 2 : (int) TimeUnit.MINUTES.toSeconds(10); private AtomicBoolean cancellationSignal = new AtomicBoolean(false); - private LastUpdateInteractor lastUpdateInteractor; + private KeyMetadataDao keyMetadataDao; private KeyWritableRepository keyWritableRepository; private Preferences preferences; @NonNull @Override public WorkerResult doWork() { - lastUpdateInteractor = LastUpdateInteractor.create(getApplicationContext()); + keyMetadataDao = KeyMetadataDao.create(getApplicationContext()); keyWritableRepository = KeyWritableRepository.create(getApplicationContext()); preferences = Preferences.getPreferences(getApplicationContext()); @@ -73,7 +73,7 @@ public class KeyserverSyncWorker extends Worker { Progressable notificationProgressable) { long staleKeyThreshold = System.currentTimeMillis() - (isForceUpdate ? 0 : KEY_STALE_THRESHOLD_MILLIS); List staleKeyFingerprints = - lastUpdateInteractor.getFingerprintsForKeysOlderThan(staleKeyThreshold, TimeUnit.MILLISECONDS); + keyMetadataDao.getFingerprintsForKeysOlderThan(staleKeyThreshold, TimeUnit.MILLISECONDS); List staleKeyParcelableKeyRings = fingerprintListToParcelableKeyRings(staleKeyFingerprints); if (isStopped()) { // if we've already been cancelled diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/CustomColumnAdapters.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/CustomColumnAdapters.java new file mode 100644 index 000000000..2bc69aa1a --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/CustomColumnAdapters.java @@ -0,0 +1,28 @@ +package org.sufficientlysecure.keychain.model; + + +import java.util.Date; + +import android.support.annotation.NonNull; + +import com.squareup.sqldelight.ColumnAdapter; + + +public final class CustomColumnAdapters { + + private CustomColumnAdapters() { } + + static final ColumnAdapter DATE_ADAPTER = new ColumnAdapter() { + @NonNull + @Override + public Date decode(Long databaseValue) { + // Both SQLite and OpenPGP prefer a second granularity for timestamps - so we'll translate here + return new Date(databaseValue * 1000); + } + + @Override + public Long encode(@NonNull Date value) { + return value.getTime() / 1000; + } + }; +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/KeyMetadata.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/KeyMetadata.java new file mode 100644 index 000000000..c1df957b8 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/model/KeyMetadata.java @@ -0,0 +1,24 @@ +package org.sufficientlysecure.keychain.model; + + +import com.google.auto.value.AutoValue; +import org.sufficientlysecure.keychain.KeyMetadataModel; + + +@AutoValue +public abstract class KeyMetadata implements KeyMetadataModel { + public static final Factory FACTORY = new Factory<>( + AutoValue_KeyMetadata::new, CustomColumnAdapters.DATE_ADAPTER); + + public boolean hasBeenUpdated() { + return last_updated() != null; + } + + public boolean isPublished() { + if (last_updated() == null) { + throw new IllegalStateException("Cannot get publication state if key has never been updated!"); + } + Boolean seenOnKeyservers = seen_on_keyservers(); + return seenOnKeyservers != null && seenOnKeyservers; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java index e2372fdd8..92bea7d39 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java @@ -41,7 +41,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeyRepository.NotFoundException; import org.sufficientlysecure.keychain.provider.KeyWritableRepository; -import org.sufficientlysecure.keychain.provider.LastUpdateInteractor; +import org.sufficientlysecure.keychain.provider.KeyMetadataDao; import org.sufficientlysecure.keychain.service.CertifyActionsParcel; import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; import org.sufficientlysecure.keychain.service.ContactSyncAdapterService; @@ -62,13 +62,13 @@ import org.sufficientlysecure.keychain.util.Passphrase; * @see CertifyActionsParcel */ public class CertifyOperation extends BaseReadWriteOperation { - private final LastUpdateInteractor lastUpdateInteractor; + private final KeyMetadataDao keyMetadataDao; - public CertifyOperation(Context context, KeyWritableRepository databaseInteractor, Progressable progressable, AtomicBoolean + public CertifyOperation(Context context, KeyWritableRepository keyWritableRepository, Progressable progressable, AtomicBoolean cancelled) { - super(context, databaseInteractor, progressable, cancelled); + super(context, keyWritableRepository, progressable, cancelled); - this.lastUpdateInteractor = LastUpdateInteractor.create(context); + this.keyMetadataDao = KeyMetadataDao.create(context); } @NonNull @@ -234,7 +234,7 @@ public class CertifyOperation extends BaseReadWriteOperation { - private final LastUpdateInteractor lastUpdateInteractor; + private final KeyMetadataDao keyMetadataDao; public EditKeyOperation(Context context, KeyWritableRepository databaseInteractor, Progressable progressable, AtomicBoolean cancelled) { super(context, databaseInteractor, progressable, cancelled); - this.lastUpdateInteractor = LastUpdateInteractor.create(context); + this.keyMetadataDao = KeyMetadataDao.create(context); } /** @@ -172,7 +172,7 @@ public class EditKeyOperation extends BaseReadWriteOperation log.add(saveResult, 1); if (isNewKey) { - lastUpdateInteractor.renewKeyLastUpdatedTime(ring.getMasterKeyId(), saveParcel.isShouldUpload()); + keyMetadataDao.renewKeyLastUpdatedTime(ring.getMasterKeyId(), saveParcel.isShouldUpload()); } // If the save operation didn't succeed, exit here diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java index 1bee1c825..4ec08b046 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java @@ -55,7 +55,7 @@ import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeyWritableRepository; -import org.sufficientlysecure.keychain.provider.LastUpdateInteractor; +import org.sufficientlysecure.keychain.provider.KeyMetadataDao; import org.sufficientlysecure.keychain.service.ContactSyncAdapterService; import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; @@ -90,7 +90,7 @@ public class ImportOperation extends BaseReadWriteOperation public static final String CACHE_FILE_NAME = "key_import.pcl"; - private final LastUpdateInteractor lastUpdateInteractor; + private final KeyMetadataDao keyMetadataDao; private FacebookKeyserverClient facebookServer; private KeybaseKeyserverClient keybaseServer; @@ -98,14 +98,14 @@ public class ImportOperation extends BaseReadWriteOperation public ImportOperation(Context context, KeyWritableRepository databaseInteractor, Progressable progressable) { super(context, databaseInteractor, progressable); - this.lastUpdateInteractor = LastUpdateInteractor.create(context); + this.keyMetadataDao = KeyMetadataDao.create(context); } public ImportOperation(Context context, KeyWritableRepository databaseInteractor, Progressable progressable, AtomicBoolean cancelled) { super(context, databaseInteractor, progressable, cancelled); - this.lastUpdateInteractor = LastUpdateInteractor.create(context); + this.keyMetadataDao = KeyMetadataDao.create(context); } // Overloaded functions for using progressable supplied in constructor during import @@ -200,7 +200,7 @@ public class ImportOperation extends BaseReadWriteOperation byte[] fingerprintHex = entry.getExpectedFingerprint(); if (fingerprintHex != null) { - lastUpdateInteractor.renewKeyLastUpdatedTime( + keyMetadataDao.renewKeyLastUpdatedTime( KeyFormattingUtils.getKeyIdFromFingerprint(fingerprintHex), false); } continue; @@ -250,7 +250,7 @@ public class ImportOperation extends BaseReadWriteOperation } if (!skipSave) { - lastUpdateInteractor.renewKeyLastUpdatedTime(key.getMasterKeyId(), keyWasDownloaded); + keyMetadataDao.renewKeyLastUpdatedTime(key.getMasterKeyId(), keyWasDownloaded); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ApiDataAccessObject.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ApiDataAccessObject.java index 16082fb56..936519cf8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ApiDataAccessObject.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ApiDataAccessObject.java @@ -37,7 +37,6 @@ import org.sufficientlysecure.keychain.model.ApiApp; public class ApiDataAccessObject { - private final SupportSQLiteDatabase db; public ApiDataAccessObject(Context context) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/DatabaseNotifyManager.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/DatabaseNotifyManager.java index b283dd576..1bfb0fe65 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/DatabaseNotifyManager.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/DatabaseNotifyManager.java @@ -35,7 +35,7 @@ public class DatabaseNotifyManager { contentResolver.notifyChange(uri, null); } - public void notifyKeyserverStatusChange(long masterKeyId) { + public void notifyKeyMetadataChange(long masterKeyId) { Uri uri = KeyRings.buildGenericKeyRingUri(masterKeyId); contentResolver.notifyChange(uri, null); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyMetadataDao.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyMetadataDao.java new file mode 100644 index 000000000..f98258c8b --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyMetadataDao.java @@ -0,0 +1,69 @@ +package org.sufficientlysecure.keychain.provider; + + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import android.arch.persistence.db.SupportSQLiteDatabase; +import android.content.Context; +import android.database.Cursor; + +import com.squareup.sqldelight.SqlDelightQuery; +import org.sufficientlysecure.keychain.KeyMetadataModel.ReplaceKeyMetadata; +import org.sufficientlysecure.keychain.model.KeyMetadata; + + +public class KeyMetadataDao { + private final SupportSQLiteDatabase db; + private DatabaseNotifyManager databaseNotifyManager; + + + public static KeyMetadataDao create(Context context) { + SupportSQLiteDatabase supportSQLiteDatabase = new KeychainDatabase(context).getWritableDatabase(); + DatabaseNotifyManager databaseNotifyManager = DatabaseNotifyManager.create(context); + + return new KeyMetadataDao(supportSQLiteDatabase, databaseNotifyManager); + } + + private KeyMetadataDao(SupportSQLiteDatabase supportSQLiteDatabase, DatabaseNotifyManager databaseNotifyManager) { + this.db = supportSQLiteDatabase; + this.databaseNotifyManager = databaseNotifyManager; + } + + public KeyMetadata getKeyMetadata(long masterKeyId) { + SqlDelightQuery query = KeyMetadata.FACTORY.selectByMasterKeyId(masterKeyId); + try (Cursor cursor = db.query(query)) { + if (cursor.moveToFirst()) { + return KeyMetadata.FACTORY.selectByMasterKeyIdMapper().map(cursor); + } + } + return null; + } + + public void resetAllLastUpdatedTimes() { + new KeyMetadata.DeleteAllLastUpdatedTimes(db).execute(); + } + + public void renewKeyLastUpdatedTime(long masterKeyId, boolean seenOnKeyservers) { + ReplaceKeyMetadata replaceStatement = new ReplaceKeyMetadata(db, KeyMetadata.FACTORY); + replaceStatement.bind(masterKeyId, new Date(), seenOnKeyservers); + replaceStatement.executeInsert(); + + databaseNotifyManager.notifyKeyMetadataChange(masterKeyId); + } + + public List getFingerprintsForKeysOlderThan(long olderThan, TimeUnit timeUnit) { + SqlDelightQuery query = KeyMetadata.FACTORY.selectFingerprintsForKeysOlderThan(new Date(timeUnit.toMillis(olderThan))); + + List fingerprintList = new ArrayList<>(); + try (Cursor cursor = db.query(query)) { + while (cursor.moveToNext()) { + byte[] fingerprint = KeyMetadata.FACTORY.selectFingerprintsForKeysOlderThanMapper().map(cursor); + fingerprintList.add(fingerprint); + } + } + return fingerprintList; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java index f0afe66ee..5b5f6e82a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyRepository.java @@ -27,7 +27,6 @@ import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.net.Uri; -import android.support.annotation.Nullable; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; @@ -39,9 +38,7 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; -import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import timber.log.Timber; @@ -284,31 +281,6 @@ public class KeyRepository { return contentResolver; } - @Nullable - Long getLastUpdateTime(long masterKeyId) { - Cursor lastUpdatedCursor = contentResolver.query( - UpdatedKeys.CONTENT_URI, - new String[] { UpdatedKeys.LAST_UPDATED }, - Tables.UPDATED_KEYS + "." + UpdatedKeys.MASTER_KEY_ID + " = ?", - new String[] { "" + masterKeyId }, - null - ); - if (lastUpdatedCursor == null) { - return null; - } - - Long lastUpdateTime; - try { - if (!lastUpdatedCursor.moveToNext()) { - return null; - } - lastUpdateTime = lastUpdatedCursor.getLong(0); - } finally { - lastUpdatedCursor.close(); - } - return lastUpdateTime; - } - public final byte[] loadPublicKeyRingData(long masterKeyId) throws NotFoundException { byte[] data = (byte[]) getGenericDataOrNull(KeyRingData.buildPublicKeyRingUri(masterKeyId), KeyRingData.KEY_RING_DATA, FIELD_TYPE_BLOB); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java index 4602112b1..d2e1af3e4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeyWritableRepository.java @@ -61,7 +61,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeySignatures; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; -import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.IterableIterator; @@ -84,36 +83,30 @@ public class KeyWritableRepository extends KeyRepository { private static final int MAX_CACHED_KEY_SIZE = 1024 * 50; private final Context context; - private final LastUpdateInteractor lastUpdateInteractor; private final DatabaseNotifyManager databaseNotifyManager; public static KeyWritableRepository create(Context context) { LocalPublicKeyStorage localPublicKeyStorage = LocalPublicKeyStorage.getInstance(context); LocalSecretKeyStorage localSecretKeyStorage = LocalSecretKeyStorage.getInstance(context); DatabaseNotifyManager databaseNotifyManager = DatabaseNotifyManager.create(context); - LastUpdateInteractor lastUpdateInteractor = LastUpdateInteractor.create(context); - return new KeyWritableRepository(context, localPublicKeyStorage, localSecretKeyStorage, databaseNotifyManager, - lastUpdateInteractor); + return new KeyWritableRepository(context, localPublicKeyStorage, localSecretKeyStorage, databaseNotifyManager); } @VisibleForTesting KeyWritableRepository(Context context, LocalPublicKeyStorage localPublicKeyStorage, LocalSecretKeyStorage localSecretKeyStorage, - DatabaseNotifyManager databaseNotifyManager, - LastUpdateInteractor lastUpdateInteractor) { - this(context, localPublicKeyStorage, localSecretKeyStorage, databaseNotifyManager, lastUpdateInteractor, - new OperationLog(), 0); + DatabaseNotifyManager databaseNotifyManager) { + this(context, localPublicKeyStorage, localSecretKeyStorage, databaseNotifyManager, new OperationLog(), 0); } private KeyWritableRepository(Context context, LocalPublicKeyStorage localPublicKeyStorage, LocalSecretKeyStorage localSecretKeyStorage, DatabaseNotifyManager databaseNotifyManager, - LastUpdateInteractor lastUpdateInteractor, OperationLog log, int indent) { + OperationLog log, int indent) { super(context.getContentResolver(), localPublicKeyStorage, localSecretKeyStorage, log, indent); this.context = context; this.databaseNotifyManager = databaseNotifyManager; - this.lastUpdateInteractor = lastUpdateInteractor; } private LongSparseArray getTrustedMasterKeys() { @@ -532,11 +525,6 @@ public class KeyWritableRepository extends KeyRepository { mIndent -= 1; } - ContentProviderOperation lastUpdateReinsertOp = getLastUpdatedReinsertOperationByMasterKeyId(masterKeyId); - if (lastUpdateReinsertOp != null) { - operations.add(lastUpdateReinsertOp); - } - try { // delete old version of this keyRing (from database only!), which also deletes all keys and userIds on cascade int deleted = contentResolver.delete( @@ -567,26 +555,6 @@ public class KeyWritableRepository extends KeyRepository { } - private ContentProviderOperation getLastUpdatedReinsertOperationByMasterKeyId(long masterKeyId) { - Long lastUpdateTime = getLastUpdateTime(masterKeyId); - if (lastUpdateTime == null) { - return null; - } - - Boolean seenOnKeyservers = lastUpdateInteractor.getSeenOnKeyservers(masterKeyId); - - ContentValues lastUpdatedEntry = new ContentValues(2); - lastUpdatedEntry.put(UpdatedKeys.MASTER_KEY_ID, masterKeyId); - lastUpdatedEntry.put(UpdatedKeys.LAST_UPDATED, lastUpdateTime); - if (seenOnKeyservers != null){ - lastUpdatedEntry.put(UpdatedKeys.SEEN_ON_KEYSERVERS, seenOnKeyservers); - } - return ContentProviderOperation - .newInsert(UpdatedKeys.CONTENT_URI) - .withValues(lastUpdatedEntry) - .build(); - } - private void writePublicKeyRing(CanonicalizedPublicKeyRing keyRing, long masterKeyId, ArrayList operations) throws IOException { byte[] encodedKey = keyRing.getEncoded(); 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 a47583170..a44da1370 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -263,16 +263,6 @@ public class KeychainContract { } - public static class UpdatedKeys implements UpdatedKeysColumns, BaseColumns { - public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() - .appendPath(BASE_UPDATED_KEYS).build(); - - public static final String CONTENT_TYPE - = "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.updated_keys"; - public static final String CONTENT_ITEM_TYPE - = "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.provider.updated_keys"; - } - public static class KeySignatures implements KeySignaturesColumns, BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_KEY_SIGNATURES).build(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index e35b6a85d..81861e9e8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -34,6 +34,9 @@ import android.database.sqlite.SQLiteException; import android.provider.BaseColumns; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.KeyMetadataModel; +import org.sufficientlysecure.keychain.KeyRingsPublicModel; +import org.sufficientlysecure.keychain.model.ApiApp; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAllowedKeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAutocryptPeerColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns; @@ -57,7 +60,7 @@ import timber.log.Timber; */ public class KeychainDatabase { private static final String DATABASE_NAME = "openkeychain.db"; - private static final int DATABASE_VERSION = 26; + private static final int DATABASE_VERSION = 27; private final SupportSQLiteOpenHelper supportSQLiteOpenHelper; private Context context; @@ -226,15 +229,16 @@ public class KeychainDatabase { private void onCreate(SupportSQLiteDatabase db) { Timber.w("Creating database..."); - db.execSQL(CREATE_KEYRINGS_PUBLIC); + db.execSQL(KeyRingsPublicModel.CREATE_TABLE); db.execSQL(CREATE_KEYS); db.execSQL(CREATE_USER_PACKETS); db.execSQL(CREATE_CERTS); - db.execSQL(CREATE_UPDATE_KEYS); + db.execSQL(KeyMetadataModel.CREATE_TABLE); db.execSQL(CREATE_KEY_SIGNATURES); db.execSQL(CREATE_API_APPS_ALLOWED_KEYS); db.execSQL(CREATE_OVERRIDDEN_WARNINGS); db.execSQL(CREATE_API_AUTOCRYPT_PEERS); + db.execSQL(ApiApp.CREATE_TABLE); db.execSQL("CREATE INDEX keys_by_rank ON keys (" + KeysColumns.RANK + ", " + KeysColumns.MASTER_KEY_ID + ");"); db.execSQL("CREATE INDEX uids_by_rank ON user_packets (" + UserPacketsColumns.RANK + ", " @@ -459,6 +463,9 @@ public class KeychainDatabase { } } + case 26: { + migrateUpdatedKeysToKeyMetadataTable(db); + } } } @@ -476,6 +483,11 @@ public class KeychainDatabase { // db.execSQL("DROP TABLE keyrings_secret"); } + private void migrateUpdatedKeysToKeyMetadataTable(SupportSQLiteDatabase db) { + db.execSQL("ALTER TABLE updated_keys RENAME TO key_metadata;"); + db.execSQL("UPDATE key_metadata SET last_updated = last_updated * 1000;"); + } + public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) { // Downgrade is ok for the debug version, makes it easier to work with branches if (Constants.DEBUG) { 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 1f64a9a49..805dd8036 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -45,7 +45,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeySignatures; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; -import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; @@ -75,9 +74,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe private static final int KEY_RINGS_FIND_BY_USER_ID = 402; private static final int KEY_RINGS_FILTER_BY_SIGNER = 403; - private static final int UPDATED_KEYS = 500; - private static final int UPDATED_KEYS_SPECIFIC = 501; - private static final int AUTOCRYPT_PEERS_BY_MASTER_KEY_ID = 601; private static final int AUTOCRYPT_PEERS_BY_PACKAGE_NAME = 602; private static final int AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID = 603; @@ -192,14 +188,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe matcher.addURI(authority, KeychainContract.BASE_AUTOCRYPT_PEERS + "/" + KeychainContract.PATH_BY_PACKAGE_NAME + "/*/*", AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID); - - /* - * to access table containing last updated dates of keys - */ - matcher.addURI(authority, KeychainContract.BASE_UPDATED_KEYS, UPDATED_KEYS); - matcher.addURI(authority, KeychainContract.BASE_UPDATED_KEYS + "/*", UPDATED_KEYS_SPECIFIC); - - matcher.addURI(authority, KeychainContract.BASE_KEY_SIGNATURES, KEY_SIGNATURES); @@ -239,12 +227,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe case KEY_RING_USER_IDS: return UserPackets.CONTENT_TYPE; - case UPDATED_KEYS: - return UpdatedKeys.CONTENT_TYPE; - - case UPDATED_KEYS_SPECIFIC: - return UpdatedKeys.CONTENT_ITEM_TYPE; - case KEY_SIGNATURES: return KeySignatures.CONTENT_TYPE; @@ -727,30 +709,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe break; } - case UPDATED_KEYS: - case UPDATED_KEYS_SPECIFIC: { - HashMap projectionMap = new HashMap<>(); - projectionMap.put(UpdatedKeys.MASTER_KEY_ID, - Tables.UPDATED_KEYS + "." + UpdatedKeys.MASTER_KEY_ID + " AS " + UpdatedKeys.MASTER_KEY_ID); - projectionMap.put(UpdatedKeys.LAST_UPDATED, - Tables.UPDATED_KEYS + "." + UpdatedKeys.LAST_UPDATED + " AS " + UpdatedKeys.LAST_UPDATED); - projectionMap.put(UpdatedKeys.SEEN_ON_KEYSERVERS, - Tables.UPDATED_KEYS + "." + UpdatedKeys.SEEN_ON_KEYSERVERS + " AS " + UpdatedKeys.SEEN_ON_KEYSERVERS); - projectionMap.put(UpdatedKeys.FINGERPRINT, - Tables.KEYS + "." + Keys.FINGERPRINT + " AS " + UpdatedKeys.FINGERPRINT); - qb.setProjectionMap(projectionMap); - - qb.setTables(Tables.UPDATED_KEYS + - " LEFT JOIN " + Tables.KEYS + - " ON (" + Tables.KEYS + "." + Keys.KEY_ID + " = " + Tables.UPDATED_KEYS + "." + UpdatedKeys.MASTER_KEY_ID + ")" - ); - - if (match == UPDATED_KEYS_SPECIFIC) { - qb.appendWhere(Tables.UPDATED_KEYS + "." + UpdatedKeys.MASTER_KEY_ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(1)); - } - break; - } default: { throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")"); } @@ -835,17 +793,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe keyId = values.getAsLong(Certs.MASTER_KEY_ID); break; } - case UPDATED_KEYS: { - keyId = values.getAsLong(UpdatedKeys.MASTER_KEY_ID); - try { - db.insert(Tables.UPDATED_KEYS, SQLiteDatabase.CONFLICT_FAIL, values); - } catch (SQLiteConstraintException e) { - db.update(Tables.UPDATED_KEYS, SQLiteDatabase.CONFLICT_IGNORE, values, - UpdatedKeys.MASTER_KEY_ID + " = ?", new String[] { Long.toString(keyId) }); - } - rowUri = UpdatedKeys.CONTENT_URI; - break; - } case KEY_SIGNATURES: { db.insert(Tables.KEY_SIGNATURES, SQLiteDatabase.CONFLICT_FAIL, values); rowUri = KeySignatures.CONTENT_URI; @@ -953,19 +900,6 @@ public class KeychainProvider extends ContentProvider implements SimpleContentRe count = db.update(Tables.KEYS, SQLiteDatabase.CONFLICT_FAIL, values, actualSelection, selectionArgs); break; } - case UPDATED_KEYS: { - if (values.size() != 2 || - !values.containsKey(UpdatedKeys.SEEN_ON_KEYSERVERS) || - !values.containsKey(UpdatedKeys.LAST_UPDATED) || - values.get(UpdatedKeys.LAST_UPDATED) != null || - values.get(UpdatedKeys.SEEN_ON_KEYSERVERS) != null || - selection != null || selectionArgs != null) { - throw new UnsupportedOperationException("can only reset all keys"); - } - - db.update(Tables.UPDATED_KEYS, SQLiteDatabase.CONFLICT_FAIL, values, null, null); - break; - } case AUTOCRYPT_PEERS_BY_PACKAGE_NAME_AND_TRUST_ID: { String packageName = uri.getPathSegments().get(2); String identifier = uri.getLastPathSegment(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/LastUpdateInteractor.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/LastUpdateInteractor.java deleted file mode 100644 index 268df323d..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/LastUpdateInteractor.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.sufficientlysecure.keychain.provider; - - -import java.util.ArrayList; -import java.util.GregorianCalendar; -import java.util.List; -import java.util.concurrent.TimeUnit; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.support.annotation.Nullable; - -import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys; -import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; - - -public class LastUpdateInteractor { - private final ContentResolver contentResolver; - private final DatabaseNotifyManager databaseNotifyManager; - - - public static LastUpdateInteractor create(Context context) { - return new LastUpdateInteractor(context.getContentResolver(), DatabaseNotifyManager.create(context)); - } - - private LastUpdateInteractor(ContentResolver contentResolver, DatabaseNotifyManager databaseNotifyManager) { - this.contentResolver = contentResolver; - this.databaseNotifyManager = databaseNotifyManager; - } - - @Nullable - public Boolean getSeenOnKeyservers(long masterKeyId) { - Cursor cursor = contentResolver.query( - UpdatedKeys.CONTENT_URI, - new String[] { UpdatedKeys.SEEN_ON_KEYSERVERS }, - Tables.UPDATED_KEYS + "." + UpdatedKeys.MASTER_KEY_ID + " = ?", - new String[] { "" + masterKeyId }, - null - ); - if (cursor == null) { - return null; - } - - Boolean seenOnKeyservers; - try { - if (!cursor.moveToNext()) { - return null; - } - seenOnKeyservers = cursor.isNull(0) ? null : cursor.getInt(0) != 0; - } finally { - cursor.close(); - } - return seenOnKeyservers; - } - - public void resetAllLastUpdatedTimes() { - ContentValues values = new ContentValues(); - values.putNull(UpdatedKeys.LAST_UPDATED); - values.putNull(UpdatedKeys.SEEN_ON_KEYSERVERS); - contentResolver.update(UpdatedKeys.CONTENT_URI, values, null, null); - } - - public Uri renewKeyLastUpdatedTime(long masterKeyId, boolean seenOnKeyservers) { - boolean isFirstKeyserverStatusCheck = getSeenOnKeyservers(masterKeyId) == null; - - ContentValues values = new ContentValues(); - values.put(UpdatedKeys.MASTER_KEY_ID, masterKeyId); - values.put(UpdatedKeys.LAST_UPDATED, GregorianCalendar.getInstance().getTimeInMillis() / 1000); - if (seenOnKeyservers || isFirstKeyserverStatusCheck) { - values.put(UpdatedKeys.SEEN_ON_KEYSERVERS, seenOnKeyservers); - } - - // this will actually update/replace, doing the right thing™ for seenOnKeyservers value - // see `KeychainProvider.insert()` - Uri insert = contentResolver.insert(UpdatedKeys.CONTENT_URI, values); - databaseNotifyManager.notifyKeyserverStatusChange(masterKeyId); - return insert; - } - - public List getFingerprintsForKeysOlderThan(long olderThan, TimeUnit timeUnit) { - Cursor outdatedKeysCursor = contentResolver.query( - KeychainContract.UpdatedKeys.CONTENT_URI, - new String[] { KeychainContract.UpdatedKeys.FINGERPRINT, }, - KeychainContract.UpdatedKeys.LAST_UPDATED + " < ?", - new String[] { Long.toString(timeUnit.toSeconds(olderThan)) }, - null - ); - - List fingerprintList = new ArrayList<>(); - if (outdatedKeysCursor == null) { - return fingerprintList; - } - - while (outdatedKeysCursor.moveToNext()) { - byte[] fingerprint = outdatedKeysCursor.getBlob(0); - fingerprintList.add(fingerprint); - } - outdatedKeysCursor.close(); - - return fingerprintList; - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java index 5b20987fc..d60734651 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java @@ -43,7 +43,7 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.HkpKeyserverAddress; -import org.sufficientlysecure.keychain.provider.LastUpdateInteractor; +import org.sufficientlysecure.keychain.provider.KeyMetadataDao; import org.sufficientlysecure.keychain.ui.dialog.AddEditKeyserverDialogFragment; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; @@ -63,7 +63,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC private List mKeyservers; private KeyserverListAdapter mAdapter; - private LastUpdateInteractor lastUpdateInteractor; + private KeyMetadataDao keyMetadataDao; public static SettingsKeyserverFragment newInstance(ArrayList keyservers) { Bundle args = new Bundle(); @@ -78,7 +78,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - lastUpdateInteractor = LastUpdateInteractor.create(getContext()); + keyMetadataDao = KeyMetadataDao.create(getContext()); return inflater.inflate(R.layout.settings_keyserver_fragment, null); } @@ -230,7 +230,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC Preferences.getPreferences(getActivity()).setKeyServers(mKeyserversMutable); mKeyservers = Collections.unmodifiableList(new ArrayList<>(mKeyserversMutable)); - lastUpdateInteractor.resetAllLastUpdatedTimes(); + keyMetadataDao.resetAllLastUpdatedTimes(); } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java index b1e675583..07e59d289 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java @@ -39,10 +39,10 @@ import android.view.ViewGroup; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; +import org.sufficientlysecure.keychain.model.KeyMetadata; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.ui.base.LoaderFragment; import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo; -import org.sufficientlysecure.keychain.ui.keyview.loader.KeyserverStatusDao.KeyserverStatus; import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeySubkeyStatus; import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactDao.SystemContactInfo; import org.sufficientlysecure.keychain.ui.keyview.presenter.IdentitiesPresenter; @@ -108,7 +108,7 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView, O private LiveData> identityInfo; private LiveData subkeyStatus; private LiveData systemContactInfo; - private LiveData keyserverStatus; + private LiveData keyserverStatus; LiveData> getIdentityInfo(IdentitiesPresenter identitiesPresenter) { if (identityInfo == null) { @@ -131,7 +131,7 @@ public class ViewKeyFragment extends LoaderFragment implements ViewKeyMvpView, O return systemContactInfo; } - LiveData getKeyserverStatus(KeyserverStatusPresenter keyserverStatusPresenter) { + LiveData getKeyserverStatus(KeyserverStatusPresenter keyserverStatusPresenter) { if (keyserverStatus == null) { keyserverStatus = keyserverStatusPresenter.getLiveDataInstance(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusDao.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusDao.java deleted file mode 100644 index 7fdc0c6d5..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/KeyserverStatusDao.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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.ui.keyview.loader; - - -import java.util.Date; - -import android.content.ContentResolver; -import android.content.Context; -import android.database.Cursor; - -import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeys; -import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; -import timber.log.Timber; - - -public class KeyserverStatusDao { - public static final String[] PROJECTION = new String[] { - UpdatedKeys.LAST_UPDATED, - UpdatedKeys.SEEN_ON_KEYSERVERS - }; - private static final int INDEX_LAST_UPDATED = 0; - private static final int INDEX_SEEN_ON_KEYSERVERS = 1; - - - private final ContentResolver contentResolver; - - public static KeyserverStatusDao getInstance(Context context) { - ContentResolver contentResolver = context.getContentResolver(); - return new KeyserverStatusDao(contentResolver); - } - - private KeyserverStatusDao(ContentResolver contentResolver) { - this.contentResolver = contentResolver; - } - - public KeyserverStatus getKeyserverStatus(long masterKeyId) { - Cursor cursor = contentResolver.query(UpdatedKeys.CONTENT_URI, PROJECTION, - Tables.UPDATED_KEYS + "." + UpdatedKeys.MASTER_KEY_ID + " = ?", new String[] { Long.toString(masterKeyId) }, null); - if (cursor == null) { - Timber.e("Error loading key items!"); - return null; - } - - try { - if (cursor.moveToFirst()) { - if (cursor.isNull(INDEX_SEEN_ON_KEYSERVERS) || cursor.isNull(INDEX_LAST_UPDATED)) { - return new KeyserverStatus(masterKeyId); - } - - boolean isPublished = cursor.getInt(INDEX_SEEN_ON_KEYSERVERS) != 0; - Date lastUpdated = new Date(cursor.getLong(INDEX_LAST_UPDATED) * 1000); - - return new KeyserverStatus(masterKeyId, isPublished, lastUpdated); - } - - return new KeyserverStatus(masterKeyId); - } finally { - cursor.close(); - } - } - - public static class KeyserverStatus { - private final long masterKeyId; - private final boolean isPublished; - private final Date lastUpdated; - - KeyserverStatus(long masterKeyId, boolean isPublished, Date lastUpdated) { - this.masterKeyId = masterKeyId; - this.isPublished = isPublished; - this.lastUpdated = lastUpdated; - } - - KeyserverStatus(long masterKeyId) { - this.masterKeyId = masterKeyId; - this.isPublished = false; - this.lastUpdated = null; - } - - long getMasterKeyId() { - return masterKeyId; - } - - public boolean hasBeenUpdated() { - return lastUpdated != null; - } - - public boolean isPublished() { - if (lastUpdated == null) { - throw new IllegalStateException("Cannot get publication state if key has never been updated!"); - } - return isPublished; - } - - public Date getLastUpdated() { - return lastUpdated; - } - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/ViewKeyLiveData.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/ViewKeyLiveData.java index c9bb64dd9..8d09591ea 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/ViewKeyLiveData.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/ViewKeyLiveData.java @@ -6,9 +6,10 @@ import java.util.List; import android.content.Context; +import org.sufficientlysecure.keychain.model.KeyMetadata; +import org.sufficientlysecure.keychain.provider.KeyMetadataDao; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo; -import org.sufficientlysecure.keychain.ui.keyview.loader.KeyserverStatusDao.KeyserverStatus; import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeySubkeyStatus; import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.SubKeyItem; import org.sufficientlysecure.keychain.ui.keyview.loader.SystemContactDao.SystemContactInfo; @@ -78,21 +79,21 @@ public class ViewKeyLiveData { } } - public static class KeyserverStatusLiveData extends AsyncTaskLiveData { - private final KeyserverStatusDao keyserverStatusDao; + public static class KeyserverStatusLiveData extends AsyncTaskLiveData { + private final KeyMetadataDao keyMetadataDao; private final long masterKeyId; public KeyserverStatusLiveData(Context context, long masterKeyId) { super(context, KeyRings.buildGenericKeyRingUri(masterKeyId)); - this.keyserverStatusDao = KeyserverStatusDao.getInstance(context); + this.keyMetadataDao = KeyMetadataDao.create(context); this.masterKeyId = masterKeyId; } @Override - public KeyserverStatus asyncLoadData() { - return keyserverStatusDao.getKeyserverStatus(masterKeyId); + public KeyMetadata asyncLoadData() { + return keyMetadataDao.getKeyMetadata(masterKeyId); } } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyserverStatusPresenter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyserverStatusPresenter.java index 76e63f15e..c0548848f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyserverStatusPresenter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/presenter/KeyserverStatusPresenter.java @@ -25,11 +25,11 @@ import android.arch.lifecycle.Observer; import android.content.Context; import android.support.annotation.Nullable; -import org.sufficientlysecure.keychain.ui.keyview.loader.KeyserverStatusDao.KeyserverStatus; +import org.sufficientlysecure.keychain.model.KeyMetadata; import org.sufficientlysecure.keychain.ui.keyview.loader.ViewKeyLiveData.KeyserverStatusLiveData; -public class KeyserverStatusPresenter implements Observer { +public class KeyserverStatusPresenter implements Observer { private final Context context; private final KeyserverStatusMvpView view; @@ -46,12 +46,12 @@ public class KeyserverStatusPresenter implements Observer { this.isSecret = isSecret; } - public LiveData getLiveDataInstance() { + public LiveData getLiveDataInstance() { return new KeyserverStatusLiveData(context, masterKeyId); } @Override - public void onChanged(@Nullable KeyserverStatus keyserverStatus) { + public void onChanged(@Nullable KeyMetadata keyserverStatus) { if (keyserverStatus == null) { view.setDisplayStatusUnknown(); return; @@ -63,7 +63,7 @@ public class KeyserverStatusPresenter implements Observer { } else { view.setDisplayStatusNotPublished(); } - view.setLastUpdated(keyserverStatus.getLastUpdated()); + view.setLastUpdated(keyserverStatus.last_updated()); } else { view.setDisplayStatusUnknown(); } diff --git a/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/KeyMetadata.sq b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/KeyMetadata.sq new file mode 100644 index 000000000..0f41a6c2a --- /dev/null +++ b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/KeyMetadata.sq @@ -0,0 +1,28 @@ +import java.lang.Boolean; +import java.util.Date; + +CREATE TABLE IF NOT EXISTS key_metadata ( + master_key_id INTEGER PRIMARY KEY, + last_updated INTEGER AS Date, + seen_on_keyservers INTEGER AS Boolean, + FOREIGN KEY(master_key_id) REFERENCES keyrings_public(master_key_id) ON DELETE CASCADE +); + +selectByMasterKeyId: +SELECT * + FROM key_metadata + WHERE master_key_id = ?; + +deleteAllLastUpdatedTimes: +UPDATE key_metadata + SET last_updated = null, seen_on_keyservers = null; + +replaceKeyMetadata: +REPLACE INTO key_metadata + (master_key_id, last_updated, seen_on_keyservers) VALUES (?, ?, ?); + +selectFingerprintsForKeysOlderThan: +SELECT fingerprint + FROM key_metadata + LEFT JOIN keys ON (key_metadata.master_key_id = keys.master_key_id AND keys.rank = 0) + WHERE last_updated < ?; \ No newline at end of file diff --git a/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/KeyRingsPublic.sq b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/KeyRingsPublic.sq new file mode 100644 index 000000000..be8435d7b --- /dev/null +++ b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/KeyRingsPublic.sq @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS keyrings_public ( + master_key_id INTEGER PRIMARY KEY, + key_ring_data BLOB +); \ No newline at end of file diff --git a/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Keys.sq b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Keys.sq new file mode 100644 index 000000000..69bbd4b91 --- /dev/null +++ b/OpenKeychain/src/main/sqldelight/org/sufficientlysecure/keychain/Keys.sq @@ -0,0 +1,21 @@ +CREATE TABLE IF NOT EXISTS keys ( + master_key_id INTEGER, + rank INTEGER, + key_id INTEGER, + key_size INTEGER, + key_curve_oid TEXT, + algorithm INTEGER, + fingerprint BLOB, + can_certify INTEGER, + can_sign INTEGER, + can_encrypt INTEGER, + can_authenticate INTEGER, + is_revoked INTEGER, + has_secret INTEGER, + is_secure INTEGER, + creation INTEGER, + expiry INTEGER, + PRIMARY KEY(master_key_id, rank), + FOREIGN KEY(master_key_id) REFERENCES + keyrings_public(master_key_id) ON DELETE CASCADE +); \ No newline at end of file diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/InteropTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/InteropTest.java index 357960e94..14288f7bb 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/InteropTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/provider/InteropTest.java @@ -16,6 +16,17 @@ package org.sufficientlysecure.keychain.provider; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.File; +import java.io.FileInputStream; +import java.net.URL; +import java.security.Security; +import java.util.ArrayList; + import android.net.Uri; import org.bouncycastle.jce.provider.BouncyCastleProvider; @@ -47,16 +58,6 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Passphrase; -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.File; -import java.io.FileInputStream; -import java.net.URL; -import java.security.Security; -import java.util.ArrayList; - @RunWith(KeychainTestRunner.class) public class InteropTest { @@ -245,8 +246,7 @@ public class InteropTest { KeyWritableRepository helper = new KeyWritableRepository(RuntimeEnvironment.application, LocalPublicKeyStorage.getInstance(RuntimeEnvironment.application), LocalSecretKeyStorage.getInstance(RuntimeEnvironment.application), - DatabaseNotifyManager.create(RuntimeEnvironment.application), - LastUpdateInteractor.create(RuntimeEnvironment.application)) { + DatabaseNotifyManager.create(RuntimeEnvironment.application)) { @Override public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws PgpKeyNotFoundException {