From c725239a69544213229e2d1d0b69b4dca0bcc5d9 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 19 Aug 2014 15:45:42 +0200 Subject: [PATCH] consolidate: split into two steps, can pick up at second step if anything fails --- .../keychain/Constants.java | 3 + .../keychain/helper/Preferences.java | 31 +++++++++ .../keychain/provider/ProviderHelper.java | 65 ++++++++++++++----- .../service/KeychainIntentService.java | 13 +--- .../service/OperationResultParcel.java | 1 + .../keychain/util/FileImportCache.java | 22 ++++++- OpenKeychain/src/main/res/values/strings.xml | 3 + 7 files changed, 112 insertions(+), 26 deletions(-) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index bce093427..78b66464b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -68,6 +68,9 @@ public final class Constants { public static final String KEY_SERVERS_DEFAULT_VERSION = "keyServersDefaultVersion"; public static final String WRITE_VERSION_HEADER = "writeVersionHeader"; public static final String FIRST_TIME = "firstTime"; + public static final String CACHED_CONSOLIDATE = "cachedConsolidate"; + public static final String CACHED_CONSOLIDATE_SECRETS = "cachedConsolidateSecrets"; + public static final String CACHED_CONSOLIDATE_PUBLICS = "cachedConsolidatePublics"; } public static final class Defaults { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java index 14ae46840..9a10148f2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/helper/Preferences.java @@ -25,6 +25,7 @@ import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.spongycastle.bcpg.HashAlgorithmTags; import org.spongycastle.openpgp.PGPEncryptedData; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Constants.Pref; import java.util.ArrayList; import java.util.Arrays; @@ -134,6 +135,36 @@ public class Preferences { editor.commit(); } + public boolean getCachedConsolidate() { + return mSharedPreferences.getBoolean(Pref.CACHED_CONSOLIDATE, false); + } + + public void setCachedConsolidate(boolean value) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putBoolean(Pref.CACHED_CONSOLIDATE, value); + editor.commit(); + } + + public int getCachedConsolidateNumPublics() { + return mSharedPreferences.getInt(Pref.CACHED_CONSOLIDATE_PUBLICS, 100); + } + + public void setCachedConsolidateNumPublics(int value) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putInt(Pref.CACHED_CONSOLIDATE_PUBLICS, value); + editor.commit(); + } + + public int getCachedConsolidateNumSecrets() { + return mSharedPreferences.getInt(Pref.CACHED_CONSOLIDATE_SECRETS, 100); + } + + public void setCachedConsolidateNumSecrets(int value) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putInt(Pref.CACHED_CONSOLIDATE_SECRETS, value); + editor.commit(); + } + public boolean isFirstTime() { return mSharedPreferences.getBoolean(Constants.Pref.FIRST_TIME, true); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 3594ded51..5f55eedd5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -28,6 +28,8 @@ import android.os.RemoteException; import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey; import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; @@ -58,6 +60,7 @@ import org.sufficientlysecure.keychain.service.OperationResults.SaveKeyringResul import org.sufficientlysecure.keychain.util.FileImportCache; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.ProgressFixedScaler; import org.sufficientlysecure.keychain.util.ProgressScaler; import java.io.ByteArrayOutputStream; @@ -823,11 +826,9 @@ public class ProviderHelper { } - public ConsolidateResult consolidateDatabase(Progressable progress) { + public ConsolidateResult consolidateDatabaseStep1(Progressable progress) { // 1a. fetch all secret keyrings into a cache file - int numSecrets, numPublics; - log(LogLevel.START, LogType.MSG_CON, mIndent); mIndent += 1; @@ -836,7 +837,7 @@ public class ProviderHelper { log(LogLevel.DEBUG, LogType.MSG_CON_SAVE_SECRET, mIndent); mIndent += 1; - final Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] { + final Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[]{ KeyRings.PRIVKEY_DATA, KeyRings.FINGERPRINT, KeyRings.HAS_ANY_SECRET }, KeyRings.HAS_ANY_SECRET + " = 1", null, null); @@ -844,7 +845,7 @@ public class ProviderHelper { return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog); } - numSecrets = cursor.getCount(); + Preferences.getPreferences(mContext).setCachedConsolidateNumSecrets(cursor.getCount()); FileImportCache cache = new FileImportCache(mContext, "consolidate_secret.pcl"); @@ -894,7 +895,7 @@ public class ProviderHelper { log(LogLevel.DEBUG, LogType.MSG_CON_SAVE_PUBLIC, mIndent); mIndent += 1; - final Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[] { + final Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[]{ KeyRings.PUBKEY_DATA, KeyRings.FINGERPRINT }, null, null, null); @@ -902,7 +903,7 @@ public class ProviderHelper { return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog); } - numPublics = cursor.getCount(); + Preferences.getPreferences(mContext).setCachedConsolidateNumSecrets(cursor.getCount()); FileImportCache cache = new FileImportCache(mContext, "consolidate_public.pcl"); @@ -946,19 +947,39 @@ public class ProviderHelper { mIndent -= 1; } + Preferences.getPreferences(mContext).setCachedConsolidate(true); + + return consolidateDatabaseStep2(progress); + } + + public ConsolidateResult consolidateDatabaseStep2(Progressable progress) { + + Preferences prefs = Preferences.getPreferences(mContext); + if ( ! prefs.getCachedConsolidate()) { + log(LogLevel.ERROR, LogType.MSG_CON_ERROR_BAD_STATE); + return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog); + } + + // Set flag that we have a cached consolidation here + int numSecrets = prefs.getCachedConsolidateNumSecrets(); + int numPublics = prefs.getCachedConsolidateNumPublics(); + // 2. wipe database (IT'S DANGEROUS) log(LogLevel.DEBUG, LogType.MSG_CON_DB_CLEAR, mIndent); new KeychainDatabase(mContext).clearDatabase(); + FileImportCache cacheSecret = + new FileImportCache(mContext, "consolidate_secret.pcl"); + FileImportCache cachePublic = + new FileImportCache(mContext, "consolidate_public.pcl"); + // 3. Re-Import secret keyrings from cache try { log(LogLevel.DEBUG, LogType.MSG_CON_REIMPORT_SECRET, mIndent, numSecrets); mIndent += 1; - FileImportCache cache = - new FileImportCache(mContext, "consolidate_secret.pcl"); - new PgpImportExport(mContext, this, new ProgressScaler(progress, 10, 25, 100)) - .importKeyRings(cache.readCache(), numSecrets); + new PgpImportExport(mContext, this, new ProgressFixedScaler(progress, 10, 25, 100, R.string.progress_con_reimport)) + .importKeyRings(cacheSecret.readCache(false), numSecrets); } catch (IOException e) { Log.e(Constants.TAG, "error importing secret"); return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog); @@ -966,15 +987,13 @@ public class ProviderHelper { mIndent -= 1; } - // 3. Re-Import public keyrings from cache + // 4. Re-Import public keyrings from cache try { log(LogLevel.DEBUG, LogType.MSG_CON_REIMPORT_PUBLIC, mIndent, numPublics); mIndent += 1; - FileImportCache cache = - new FileImportCache(mContext, "consolidate_public.pcl"); new PgpImportExport(mContext, this, new ProgressScaler(progress, 25, 99, 100)) - .importKeyRings(cache.readCache(), numPublics); + .importKeyRings(cachePublic.readCache(false), numPublics); } catch (IOException e) { Log.e(Constants.TAG, "error importing public"); return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, mLog); @@ -982,6 +1001,22 @@ public class ProviderHelper { mIndent -= 1; } + Preferences.getPreferences(mContext).setCachedConsolidate(false); + + // 5. Delete caches + try { + cacheSecret.delete(); + } catch (IOException e) { + // doesn't really matter + Log.e(Constants.TAG, "IOException during delete of secret cache", e); + } + try { + cachePublic.delete(); + } catch (IOException e) { + // doesn't really matter + Log.e(Constants.TAG, "IOException during deletion of public cache", e); + } + progress.setProgress(100, 100); log(LogLevel.OK, LogType.MSG_CON_SUCCESS, mIndent); mIndent -= 1; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 9f5650df6..2c1bc8463 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -490,10 +490,7 @@ public class KeychainIntentService extends IntentService PgpImportExport pgpImportExport = new PgpImportExport(this, this); ImportKeyResult result = pgpImportExport.importKeyRings(entries); - Bundle resultData = new Bundle(); - resultData.putParcelable(RESULT_IMPORT, result); - - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); } catch (Exception e) { sendErrorToHandler(e); } @@ -670,12 +667,8 @@ public class KeychainIntentService extends IntentService } } else if (ACTION_CONSOLIDATE.equals(action)) { - ConsolidateResult result = new ProviderHelper(this).consolidateDatabase(this); - - Bundle resultData = new Bundle(); - resultData.putParcelable(RESULT_CONSOLIDATE, result); - - sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); + ConsolidateResult result = new ProviderHelper(this).consolidateDatabaseStep1(this); + sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, result); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java index c601ec57e..3f478cfed 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/OperationResultParcel.java @@ -391,6 +391,7 @@ public class OperationResultParcel implements Parcelable { // consolidate MSG_CON (R.string.msg_con), + MSG_CON_ERROR_BAD_STATE (R.string.msg_con_error_bad_state), MSG_CON_SAVE_SECRET (R.string.msg_con_save_secret), MSG_CON_SAVE_PUBLIC (R.string.msg_con_save_public), MSG_CON_DB_CLEAR (R.string.msg_con_db_clear), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java index 35833adc6..09275fc95 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileImportCache.java @@ -92,6 +92,10 @@ public class FileImportCache { } public Iterator readCache() throws IOException { + return readCache(true); + } + + public Iterator readCache(final boolean deleteAfterRead) throws IOException { File cacheDir = mContext.getCacheDir(); if (cacheDir == null) { @@ -166,7 +170,10 @@ public class FileImportCache { if (!closed) { try { ois.close(); - tempFile.delete(); + if (deleteAfterRead) { + //noinspection ResultOfMethodCallIgnored + tempFile.delete(); + } } catch (IOException e) { // nvm } @@ -177,4 +184,17 @@ public class FileImportCache { }; } + + public boolean delete() throws IOException { + + File cacheDir = mContext.getCacheDir(); + if (cacheDir == null) { + // https://groups.google.com/forum/#!topic/android-developers/-694j87eXVU + throw new IOException("cache dir is null!"); + } + + final File tempFile = new File(cacheDir, mFilename); + return tempFile.delete(); + } + } diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 8d57f6f2e..e506bf488 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -302,6 +302,8 @@ verifying integrity… deleting \'%s\' securely… + reimporting database… + Name/Email/Key ID… Name/Email/Proof/Key… @@ -671,6 +673,7 @@ Consolidating database + Consolidation started while no database was cached! This is probably a programming error, please file a bug report. Saving secret keyrings Saving public keyrings Clearing database