get rid of consolidate operation

This commit is contained in:
Vincent Breitmoser
2017-09-22 04:26:12 +02:00
parent f413d77360
commit b814753b3b
13 changed files with 18 additions and 690 deletions

View File

@@ -25,7 +25,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import android.content.ContentProviderOperation;
@@ -41,11 +40,6 @@ import android.support.v4.util.LongSparseArray;
import org.openintents.openpgp.util.OpenPgpUtils;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.ImportOperation;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
@@ -73,11 +67,7 @@ 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;
import org.sufficientlysecure.keychain.util.IteratorWithSize;
import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.ProgressFixedScaler;
import org.sufficientlysecure.keychain.util.Utf8Util;
/**
@@ -1070,335 +1060,6 @@ public class KeyWritableRepository extends KeyRepository {
}
}
@NonNull
public ConsolidateResult consolidateDatabaseStep1(Progressable progress) {
OperationLog log = new OperationLog();
int indent = 0;
// 1a. fetch all secret keyrings into a cache file
log.add(LogType.MSG_CON, indent);
indent += 1;
if (mConsolidateCritical) {
log.add(LogType.MSG_CON_RECURSIVE, indent);
return new ConsolidateResult(ConsolidateResult.RESULT_OK, log);
}
progress.setProgress(R.string.progress_con_saving, 0, 100);
// The consolidate operation can never be cancelled!
progress.setPreventCancel();
try {
log.add(LogType.MSG_CON_SAVE_SECRET, indent);
indent += 1;
final Cursor cursor = mContentResolver.query(KeyRingData.buildSecretKeyRingUri(),
new String[]{KeyRingData.KEY_RING_DATA}, null, null, null);
if (cursor == null) {
log.add(LogType.MSG_CON_ERROR_DB, indent);
return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, log);
}
// No keys existing might be a legitimate option, we write an empty file in that case
cursor.moveToFirst();
ParcelableFileCache<ParcelableKeyRing> cache =
new ParcelableFileCache<>(mContext, "consolidate_secret.pcl");
cache.writeCache(cursor.getCount(), new Iterator<ParcelableKeyRing>() {
ParcelableKeyRing ring;
@Override
public boolean hasNext() {
if (ring != null) {
return true;
}
if (cursor.isAfterLast()) {
return false;
}
ring = ParcelableKeyRing.createFromEncodedBytes(cursor.getBlob(0));
cursor.moveToNext();
return true;
}
@Override
public ParcelableKeyRing next() {
try {
return ring;
} finally {
ring = null;
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
});
cursor.close();
} catch (IOException e) {
Log.e(Constants.TAG, "error saving secret", e);
log.add(LogType.MSG_CON_ERROR_IO_SECRET, indent);
return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, log);
} finally {
indent -= 1;
}
progress.setProgress(R.string.progress_con_saving, 3, 100);
// 1b. fetch all public keyrings into a cache file
try {
log.add(LogType.MSG_CON_SAVE_PUBLIC, indent);
indent += 1;
final Cursor cursor = mContentResolver.query(
KeyRingData.buildPublicKeyRingUri(),
new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA}, null, null, null);
if (cursor == null) {
log.add(LogType.MSG_CON_ERROR_DB, indent);
return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, log);
}
// No keys existing might be a legitimate option, we write an empty file in that case
cursor.moveToFirst();
ParcelableFileCache<ParcelableKeyRing> cache =
new ParcelableFileCache<>(mContext, "consolidate_public.pcl");
cache.writeCache(cursor.getCount(), new Iterator<ParcelableKeyRing>() {
ParcelableKeyRing ring;
@Override
public boolean hasNext() {
if (ring != null) {
return true;
}
if (cursor.isAfterLast()) {
return false;
}
long masterKeyId = cursor.getLong(0);
byte[] keyBytes = cursor.getBlob(1);
if (keyBytes == null) {
try {
keyBytes = mLocalPublicKeyStorage.readPublicKey(masterKeyId);
} catch (IOException e) {
Log.e(Constants.TAG, "Failed reading key data!", e);
}
}
if (keyBytes == null) {
throw new IllegalStateException("Lost a key! This should never happen!");
}
ring = ParcelableKeyRing.createFromEncodedBytes(keyBytes);
cursor.moveToNext();
return true;
}
@Override
public ParcelableKeyRing next() {
try {
return ring;
} finally {
ring = null;
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
});
cursor.close();
} catch (IOException e) {
Log.e(Constants.TAG, "error saving public", e);
log.add(LogType.MSG_CON_ERROR_IO_PUBLIC, indent);
return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, log);
} finally {
indent -= 1;
}
log.add(LogType.MSG_CON_CRITICAL_IN, indent);
Preferences.getPreferences(mContext).setCachedConsolidate(true);
return consolidateDatabaseStep2(log, indent, progress, false);
}
@NonNull
public ConsolidateResult consolidateDatabaseStep2(Progressable progress) {
return consolidateDatabaseStep2(new OperationLog(), 0, progress, true);
}
private static boolean mConsolidateCritical = false;
@NonNull
private ConsolidateResult consolidateDatabaseStep2(
OperationLog log, int indent, Progressable progress, boolean recovery) {
synchronized (KeyWritableRepository.class) {
if (mConsolidateCritical) {
log.add(LogType.MSG_CON_ERROR_CONCURRENT, indent);
return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, log);
}
mConsolidateCritical = true;
}
try {
Preferences prefs = Preferences.getPreferences(mContext);
if (recovery) {
log.add(LogType.MSG_CON_RECOVER, indent);
indent += 1;
}
if (!prefs.getCachedConsolidate()) {
log.add(LogType.MSG_CON_ERROR_BAD_STATE, indent);
return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, log);
}
// 2. wipe database (IT'S DANGEROUS)
// first, backup our list of updated key times
ArrayList<ContentValues> updatedKeysValues = new ArrayList<>();
final int INDEX_MASTER_KEY_ID = 0;
final int INDEX_LAST_UPDATED = 1;
final int INDEX_SEEN_ON_KEYSERVERS = 2;
Cursor lastUpdatedCursor = mContentResolver.query(
UpdatedKeys.CONTENT_URI,
new String[]{
UpdatedKeys.MASTER_KEY_ID,
UpdatedKeys.LAST_UPDATED,
UpdatedKeys.SEEN_ON_KEYSERVERS
},
null, null, null);
while (lastUpdatedCursor.moveToNext()) {
ContentValues values = new ContentValues();
values.put(UpdatedKeys.MASTER_KEY_ID,
lastUpdatedCursor.getLong(INDEX_MASTER_KEY_ID));
if (!lastUpdatedCursor.isNull(INDEX_LAST_UPDATED)) {
values.put(UpdatedKeys.LAST_UPDATED, lastUpdatedCursor.getLong(INDEX_LAST_UPDATED));
} else {
values.putNull(UpdatedKeys.LAST_UPDATED);
}
if (!lastUpdatedCursor.isNull(INDEX_SEEN_ON_KEYSERVERS)) {
values.put(UpdatedKeys.SEEN_ON_KEYSERVERS, lastUpdatedCursor.getInt(INDEX_SEEN_ON_KEYSERVERS));
} else {
values.putNull(UpdatedKeys.SEEN_ON_KEYSERVERS);
}
updatedKeysValues.add(values);
}
lastUpdatedCursor.close();
log.add(LogType.MSG_CON_DB_CLEAR, indent);
mContentResolver.delete(KeyRings.buildUnifiedKeyRingsUri(), null, null);
ParcelableFileCache<ParcelableKeyRing> cacheSecret, cachePublic;
// Set flag that we have a cached consolidation here
try {
cacheSecret = new ParcelableFileCache<>(mContext, "consolidate_secret.pcl");
IteratorWithSize<ParcelableKeyRing> itSecrets = cacheSecret.readCache(false);
int numSecrets = itSecrets.getSize();
log.add(LogType.MSG_CON_REIMPORT_SECRET, indent, numSecrets);
indent += 1;
// 3. Re-Import secret keyrings from cache
if (numSecrets > 0) {
ImportKeyResult result = new ImportOperation(mContext, this,
new ProgressFixedScaler(progress, 10, 25, 100, R.string.progress_con_reimport))
.serialKeyRingImport(itSecrets, numSecrets, null, null, false);
log.add(result, indent);
} else {
log.add(LogType.MSG_CON_REIMPORT_SECRET_SKIP, indent);
}
} catch (IOException e) {
Log.e(Constants.TAG, "error importing secret", e);
log.add(LogType.MSG_CON_ERROR_SECRET, indent);
return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, log);
} finally {
indent -= 1;
}
try {
cachePublic = new ParcelableFileCache<>(mContext, "consolidate_public.pcl");
IteratorWithSize<ParcelableKeyRing> itPublics = cachePublic.readCache();
int numPublics = itPublics.getSize();
log.add(LogType.MSG_CON_REIMPORT_PUBLIC, indent, numPublics);
indent += 1;
// 4. Re-Import public keyrings from cache
if (numPublics > 0) {
ImportKeyResult result = new ImportOperation(mContext, this,
new ProgressFixedScaler(progress, 25, 99, 100, R.string.progress_con_reimport))
.serialKeyRingImport(itPublics, numPublics, null, null, false);
log.add(result, indent);
// re-insert our backed up list of updated key times
// TODO: can this cause issues in case a public key re-import failed?
mContentResolver.bulkInsert(UpdatedKeys.CONTENT_URI,
updatedKeysValues.toArray(new ContentValues[updatedKeysValues.size()]));
} else {
log.add(LogType.MSG_CON_REIMPORT_PUBLIC_SKIP, indent);
}
} catch (IOException e) {
Log.e(Constants.TAG, "error importing public", e);
log.add(LogType.MSG_CON_ERROR_PUBLIC, indent);
return new ConsolidateResult(ConsolidateResult.RESULT_ERROR, log);
} finally {
indent -= 1;
}
log.add(LogType.MSG_CON_CRITICAL_OUT, indent);
Preferences.getPreferences(mContext).setCachedConsolidate(false);
// 5. Delete caches
try {
log.add(LogType.MSG_CON_DELETE_SECRET, indent);
indent += 1;
cacheSecret.delete();
} catch (IOException e) {
// doesn't /really/ matter
Log.e(Constants.TAG, "IOException during delete of secret cache", e);
log.add(LogType.MSG_CON_WARN_DELETE_SECRET, indent);
} finally {
indent -= 1;
}
try {
log.add(LogType.MSG_CON_DELETE_PUBLIC, indent);
indent += 1;
cachePublic.delete();
} catch (IOException e) {
// doesn't /really/ matter
Log.e(Constants.TAG, "IOException during deletion of public cache", e);
log.add(LogType.MSG_CON_WARN_DELETE_PUBLIC, indent);
} finally {
indent -= 1;
}
progress.setProgress(100, 100);
log.add(LogType.MSG_CON_SUCCESS, indent);
return new ConsolidateResult(ConsolidateResult.RESULT_OK, log);
} finally {
mConsolidateCritical = false;
}
}
/**
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
*/

View File

@@ -25,7 +25,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
@@ -42,7 +41,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.OverriddenWarnings;
import org.sufficientlysecure.keychain.provider.KeychainContract.UpdatedKeysColumns;
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns;
import org.sufficientlysecure.keychain.ui.ConsolidateDialogActivity;
import org.sufficientlysecure.keychain.util.Log;
/**
@@ -406,19 +404,7 @@ public class KeychainDatabase extends SQLiteOpenHelper {
+ "PRIMARY KEY(master_key_id, signer_key_id), "
+ "FOREIGN KEY(master_key_id) REFERENCES keyrings_public(master_key_id) ON DELETE CASCADE"
+ ")");
if (oldVersion == 18 || oldVersion == 19 || oldVersion == 20 || oldVersion == 21 || oldVersion == 22 ||
oldVersion == 23) {
return;
}
}
// TODO: don't depend on consolidate! make migrations inline!
// consolidate after upgrade
Intent consolidateIntent = new Intent(mContext.getApplicationContext(), ConsolidateDialogActivity.class);
consolidateIntent.putExtra(ConsolidateDialogActivity.EXTRA_CONSOLIDATE_RECOVERY, false);
consolidateIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.getApplicationContext().startActivity(consolidateIntent);
}
@Override