From f64e1467ad0549702ef90801fc020298a32e2566 Mon Sep 17 00:00:00 2001 From: Adithya Abraham Philip Date: Thu, 25 Jun 2015 06:43:14 +0530 Subject: [PATCH] split import and export ops --- .../keychain/operations/CertifyOperation.java | 8 +- .../keychain/operations/ExportOperation.java | 343 ++++++++++++++++ ...ortOperation.java => ImportOperation.java} | 372 ++---------------- .../keychain/provider/ProviderHelper.java | 6 +- .../keychain/service/ExportKeyringParcel.java | 2 +- .../keychain/service/ImportExportParcel.java | 38 -- .../keychain/service/ImportKeyringParcel.java | 2 +- .../keychain/service/KeychainService.java | 13 +- .../ui/adapter/ImportKeysAdapter.java | 5 +- .../keychain/operations/ExportTest.java | 3 +- 10 files changed, 396 insertions(+), 396 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java rename OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/{ImportExportOperation.java => ImportOperation.java} (62%) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportExportParcel.java 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 cb76c8ba3..0806e6a16 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java @@ -188,10 +188,10 @@ public class CertifyOperation extends BaseOperation { } HkpKeyserver keyServer = null; - ImportExportOperation importExportOperation = null; + ExportOperation exportOperation = null; if (parcel.keyServerUri != null) { keyServer = new HkpKeyserver(parcel.keyServerUri); - importExportOperation = new ImportExportOperation(mContext, mProviderHelper, mProgressable); + exportOperation = new ExportOperation(mContext, mProviderHelper, mProgressable); } // Write all certified keys into the database @@ -209,10 +209,10 @@ public class CertifyOperation extends BaseOperation { mProviderHelper.clearLog(); SaveKeyringResult result = mProviderHelper.savePublicKeyRing(certifiedKey); - if (importExportOperation != null) { + if (exportOperation != null) { // TODO use subresult, get rid of try/catch! try { - importExportOperation.uploadKeyRingToServer(keyServer, certifiedKey); + exportOperation.uploadKeyRingToServer(keyServer, certifiedKey); uploadOk += 1; } catch (AddKeyException e) { Log.e(Constants.TAG, "error uploading key", e); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java new file mode 100644 index 000000000..01a45bc79 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2012-2014 Dominik Schürmann + * Copyright (C) 2010-2014 Thialfihar + * + * 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.operations; + +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; + +import org.spongycastle.bcpg.ArmoredOutputStream; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; +import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException; +import org.sufficientlysecure.keychain.operations.results.ExportResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing; +import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; +import org.sufficientlysecure.keychain.pgp.Progressable; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; +import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.ExportKeyringParcel; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.FileHelper; +import org.sufficientlysecure.keychain.util.Log; + +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * An operation class which implements high level export + * operations. + * This class receives a source and/or destination of keys as input and performs + * all steps for this export. + * + * @see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries() + * For the export operation, the input consists of a set of key ids and + * either the name of a file or an output uri to write to. + * TODO rework uploadKeyRingToServer + */ +public class ExportOperation extends BaseOperation { + + public ExportOperation(Context context, ProviderHelper providerHelper, Progressable + progressable) { + super(context, providerHelper, progressable); + } + + public ExportOperation(Context context, ProviderHelper providerHelper, + Progressable progressable, AtomicBoolean cancelled) { + super(context, providerHelper, progressable, cancelled); + } + + public void uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring) + throws AddKeyException { + uploadKeyRingToServer(server, keyring.getUncachedKeyRing()); + } + + public void uploadKeyRingToServer(HkpKeyserver server, UncachedKeyRing keyring) throws + AddKeyException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ArmoredOutputStream aos = null; + try { + aos = new ArmoredOutputStream(bos); + keyring.encode(aos); + aos.close(); + + String armoredKey = bos.toString("UTF-8"); + server.add(armoredKey); + } catch (IOException e) { + Log.e(Constants.TAG, "IOException", e); + throw new AddKeyException(); + } finally { + try { + if (aos != null) { + aos.close(); + } + bos.close(); + } catch (IOException e) { + // this is just a finally thing, no matter if it doesn't work out. + } + } + } + + public ExportResult exportToFile(long[] masterKeyIds, boolean exportSecret, String outputFile) { + + OperationLog log = new OperationLog(); + if (masterKeyIds != null) { + log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length); + } else { + log.add(LogType.MSG_EXPORT_ALL, 0); + } + + // do we have a file name? + if (outputFile == null) { + log.add(LogType.MSG_EXPORT_ERROR_NO_FILE, 1); + return new ExportResult(ExportResult.RESULT_ERROR, log); + } + + // check if storage is ready + if (!FileHelper.isStorageMounted(outputFile)) { + log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1); + return new ExportResult(ExportResult.RESULT_ERROR, log); + } + + try { + OutputStream outStream = new FileOutputStream(outputFile); + try { + ExportResult result = exportKeyRings(log, masterKeyIds, exportSecret, outStream); + if (result.cancelled()) { + //noinspection ResultOfMethodCallIgnored + new File(outputFile).delete(); + } + return result; + } finally { + outStream.close(); + } + } catch (IOException e) { + log.add(LogType.MSG_EXPORT_ERROR_FOPEN, 1); + return new ExportResult(ExportResult.RESULT_ERROR, log); + } + + } + + public ExportResult exportToUri(long[] masterKeyIds, boolean exportSecret, Uri outputUri) { + + OperationLog log = new OperationLog(); + if (masterKeyIds != null) { + log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length); + } else { + log.add(LogType.MSG_EXPORT_ALL, 0); + } + + // do we have a file name? + if (outputUri == null) { + log.add(LogType.MSG_EXPORT_ERROR_NO_URI, 1); + return new ExportResult(ExportResult.RESULT_ERROR, log); + } + + try { + OutputStream outStream = mProviderHelper.getContentResolver().openOutputStream + (outputUri); + return exportKeyRings(log, masterKeyIds, exportSecret, outStream); + } catch (FileNotFoundException e) { + log.add(LogType.MSG_EXPORT_ERROR_URI_OPEN, 1); + return new ExportResult(ExportResult.RESULT_ERROR, log); + } + + } + + ExportResult exportKeyRings(OperationLog log, long[] masterKeyIds, boolean exportSecret, + OutputStream outStream) { + + /* TODO isn't this checked above, with the isStorageMounted call? + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1); + return new ExportResult(ExportResult.RESULT_ERROR, log); + } + */ + + if (!BufferedOutputStream.class.isInstance(outStream)) { + outStream = new BufferedOutputStream(outStream); + } + + int okSecret = 0, okPublic = 0, progress = 0; + + Cursor cursor = null; + try { + + String selection = null, ids[] = null; + + if (masterKeyIds != null) { + // generate placeholders and string selection args + ids = new String[masterKeyIds.length]; + StringBuilder placeholders = new StringBuilder("?"); + for (int i = 0; i < masterKeyIds.length; i++) { + ids[i] = Long.toString(masterKeyIds[i]); + if (i != 0) { + placeholders.append(",?"); + } + } + + // put together selection string + selection = Tables.KEY_RINGS_PUBLIC + "." + KeyRings.MASTER_KEY_ID + + " IN (" + placeholders + ")"; + } + + cursor = mProviderHelper.getContentResolver().query( + KeyRings.buildUnifiedKeyRingsUri(), new String[]{ + KeyRings.MASTER_KEY_ID, KeyRings.PUBKEY_DATA, + KeyRings.PRIVKEY_DATA, KeyRings.HAS_ANY_SECRET + }, selection, ids, Tables.KEYS + "." + KeyRings.MASTER_KEY_ID + ); + + if (cursor == null || !cursor.moveToFirst()) { + log.add(LogType.MSG_EXPORT_ERROR_DB, 1); + return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret); + } + + int numKeys = cursor.getCount(); + + updateProgress( + mContext.getResources().getQuantityString(R.plurals.progress_exporting_key, + numKeys), 0, numKeys); + + // For each public masterKey id + while (!cursor.isAfterLast()) { + + long keyId = cursor.getLong(0); + ArmoredOutputStream arOutStream = null; + + // Create an output stream + try { + arOutStream = new ArmoredOutputStream(outStream); + + log.add(LogType.MSG_EXPORT_PUBLIC, 1, KeyFormattingUtils.beautifyKeyId(keyId)); + + byte[] data = cursor.getBlob(1); + CanonicalizedKeyRing ring = + UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true); + ring.encode(arOutStream); + + okPublic += 1; + } catch (PgpGeneralException e) { + log.add(LogType.MSG_EXPORT_ERROR_KEY, 2); + updateProgress(progress++, numKeys); + continue; + } finally { + // make sure this is closed + if (arOutStream != null) { + arOutStream.close(); + } + arOutStream = null; + } + + if (exportSecret && cursor.getInt(3) > 0) { + try { + arOutStream = new ArmoredOutputStream(outStream); + + // export secret key part + log.add(LogType.MSG_EXPORT_SECRET, 2, KeyFormattingUtils.beautifyKeyId + (keyId)); + byte[] data = cursor.getBlob(2); + CanonicalizedKeyRing ring = + UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true); + ring.encode(arOutStream); + + okSecret += 1; + } catch (PgpGeneralException e) { + log.add(LogType.MSG_EXPORT_ERROR_KEY, 2); + updateProgress(progress++, numKeys); + continue; + } finally { + // make sure this is closed + if (arOutStream != null) { + arOutStream.close(); + } + } + } + + updateProgress(progress++, numKeys); + + cursor.moveToNext(); + } + + updateProgress(R.string.progress_done, numKeys, numKeys); + + } catch (IOException e) { + log.add(LogType.MSG_EXPORT_ERROR_IO, 1); + return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret); + } finally { + // Make sure the stream is closed + if (outStream != null) try { + outStream.close(); + } catch (Exception e) { + Log.e(Constants.TAG, "error closing stream", e); + } + if (cursor != null) { + cursor.close(); + } + } + + + log.add(LogType.MSG_EXPORT_SUCCESS, 1); + return new ExportResult(ExportResult.RESULT_OK, log, okPublic, okSecret); + + } + + public ExportResult execute(ExportKeyringParcel exportInput, CryptoInputParcel cryptoInput) { + switch (exportInput.mExportType) { + case UPLOAD_KEYSERVER: { + HkpKeyserver hkpKeyserver = new HkpKeyserver(exportInput.mKeyserver); + try { + CanonicalizedPublicKeyRing keyring + = mProviderHelper.getCanonicalizedPublicKeyRing( + exportInput.mCanonicalizedPublicKeyringUri); + uploadKeyRingToServer(hkpKeyserver, keyring); + // TODO: replace with proper log + return new ExportResult(ExportResult.RESULT_OK, new OperationLog()); + } catch (Exception e) { + return new ExportResult(ExportResult.RESULT_ERROR, new OperationLog()); + // TODO: Implement better exception handling, replace with log + } + } + case EXPORT_FILE: { + return exportToFile(exportInput.mMasterKeyIds, exportInput.mExportSecret, + exportInput.mOutputFile); + } + case EXPORT_URI: { + return exportToUri(exportInput.mMasterKeyIds, exportInput.mExportSecret, + exportInput.mOutputUri); + } + default: { // can't happen + return null; + } + } + } +} \ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java similarity index 62% rename from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java rename to OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java index 3b9390866..ace059dac 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java @@ -19,53 +19,32 @@ package org.sufficientlysecure.keychain.operations; import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.os.Parcelable; -import org.spongycastle.bcpg.ArmoredOutputStream; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver; import org.sufficientlysecure.keychain.keyimport.Keyserver; -import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.operations.results.ConsolidateResult; -import org.sufficientlysecure.keychain.operations.results.ExportResult; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult; -import org.sufficientlysecure.keychain.pgp.CanonicalizedKeyRing; -import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.ContactSyncAdapterService; -import org.sufficientlysecure.keychain.service.ExportKeyringParcel; -import org.sufficientlysecure.keychain.service.ImportExportParcel; import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ParcelableFileCache; import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; import org.sufficientlysecure.keychain.util.ProgressScaler; -import java.io.BufferedOutputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStream; -import java.net.Proxy; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; @@ -79,12 +58,11 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -/** An operation class which implements high level import and export +/** + * An operation class which implements high level import * operations. - * * This class receives a source and/or destination of keys as input and performs - * all steps for this import or export. - * + * all steps for this import. * For the import operation, the only valid source is an Iterator of * ParcelableKeyRing, each of which must contain either a single * keyring encoded as bytes, or a unique reference to a keyring @@ -93,55 +71,22 @@ import java.util.concurrent.atomic.AtomicBoolean; * secret keys, because some implementations (notably Symantec PGP Desktop) do * not include self certificates for user ids in the secret keyring. The import * method here will generally import keyrings in the order given by the - * iterator. so this should be ensured beforehand. + * iterator, so this should be ensured beforehand. + * * @see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries() - * - * For the export operation, the input consists of a set of key ids and - * either the name of a file or an output uri to write to. - * - * TODO rework uploadKeyRingToServer - * */ -public class ImportExportOperation extends BaseOperation { +public class ImportOperation extends BaseOperation { - public ImportExportOperation(Context context, ProviderHelper providerHelper, Progressable progressable) { + public ImportOperation(Context context, ProviderHelper providerHelper, Progressable + progressable) { super(context, providerHelper, progressable); } - public ImportExportOperation(Context context, ProviderHelper providerHelper, - Progressable progressable, AtomicBoolean cancelled) { + public ImportOperation(Context context, ProviderHelper providerHelper, + Progressable progressable, AtomicBoolean cancelled) { super(context, providerHelper, progressable, cancelled); } - public void uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring) throws AddKeyException { - uploadKeyRingToServer(server, keyring.getUncachedKeyRing()); - } - - public void uploadKeyRingToServer(HkpKeyserver server, UncachedKeyRing keyring) throws AddKeyException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ArmoredOutputStream aos = null; - try { - aos = new ArmoredOutputStream(bos); - keyring.encode(aos); - aos.close(); - - String armoredKey = bos.toString("UTF-8"); - server.add(armoredKey); - } catch (IOException e) { - Log.e(Constants.TAG, "IOException", e); - throw new AddKeyException(); - } finally { - try { - if (aos != null) { - aos.close(); - } - bos.close(); - } catch (IOException e) { - // this is just a finally thing, no matter if it doesn't work out. - } - } - } - // Overloaded functions for using progressable supplied in constructor during import public ImportKeyResult serialKeyRingImport(Iterator entries, int num, String keyServerUri) { @@ -244,7 +189,8 @@ public class ImportExportOperation extends BaseOperation { else { // We fetch from keyservers first, because we tend to get more certificates - // from there, so the number of certificates which are merged in later is smaller. + // from there, so the number of certificates which are merged in later is + // smaller. // If we have a keyServerUri and a fingerprint or at least a keyId, // download from HKP @@ -339,12 +285,12 @@ public class ImportExportOperation extends BaseOperation { mProviderHelper.clearLog(); if (key.isSecret()) { result = mProviderHelper.saveSecretKeyRing(key, - new ProgressScaler(progressable, (int)(position*progSteps), - (int)((position+1)*progSteps), 100)); + new ProgressScaler(progressable, (int) (position * progSteps), + (int) ((position + 1) * progSteps), 100)); } else { result = mProviderHelper.savePublicKeyRing(key, - new ProgressScaler(progressable, (int)(position*progSteps), - (int)((position+1)*progSteps), 100)); + new ProgressScaler(progressable, (int) (position * progSteps), + (int) ((position + 1) * progSteps), 100)); } if (!result.success()) { badKeys += 1; @@ -415,7 +361,7 @@ public class ImportExportOperation extends BaseOperation { } // Final log entry, it's easier to do this individually - if ( (newKeys > 0 || updatedKeys > 0) && badKeys > 0) { + if ((newKeys > 0 || updatedKeys > 0) && badKeys > 0) { log.add(LogType.MSG_IMPORT_PARTIAL, 1); } else if (newKeys > 0 || updatedKeys > 0) { log.add(LogType.MSG_IMPORT_SUCCESS, 1); @@ -427,247 +373,9 @@ public class ImportExportOperation extends BaseOperation { importedMasterKeyIdsArray); } - public ExportResult exportToFile(long[] masterKeyIds, boolean exportSecret, String outputFile) { - - OperationLog log = new OperationLog(); - if (masterKeyIds != null) { - log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length); - } else { - log.add(LogType.MSG_EXPORT_ALL, 0); - } - - // do we have a file name? - if (outputFile == null) { - log.add(LogType.MSG_EXPORT_ERROR_NO_FILE, 1); - return new ExportResult(ExportResult.RESULT_ERROR, log); - } - - // check if storage is ready - if (!FileHelper.isStorageMounted(outputFile)) { - log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1); - return new ExportResult(ExportResult.RESULT_ERROR, log); - } - - try { - OutputStream outStream = new FileOutputStream(outputFile); - try { - ExportResult result = exportKeyRings(log, masterKeyIds, exportSecret, outStream); - if (result.cancelled()) { - //noinspection ResultOfMethodCallIgnored - new File(outputFile).delete(); - } - return result; - } finally { - outStream.close(); - } - } catch (IOException e) { - log.add(LogType.MSG_EXPORT_ERROR_FOPEN, 1); - return new ExportResult(ExportResult.RESULT_ERROR, log); - } - - } - - public ExportResult exportToUri(long[] masterKeyIds, boolean exportSecret, Uri outputUri) { - - OperationLog log = new OperationLog(); - if (masterKeyIds != null) { - log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length); - } else { - log.add(LogType.MSG_EXPORT_ALL, 0); - } - - // do we have a file name? - if (outputUri == null) { - log.add(LogType.MSG_EXPORT_ERROR_NO_URI, 1); - return new ExportResult(ExportResult.RESULT_ERROR, log); - } - - try { - OutputStream outStream = mProviderHelper.getContentResolver().openOutputStream(outputUri); - return exportKeyRings(log, masterKeyIds, exportSecret, outStream); - } catch (FileNotFoundException e) { - log.add(LogType.MSG_EXPORT_ERROR_URI_OPEN, 1); - return new ExportResult(ExportResult.RESULT_ERROR, log); - } - - } - - ExportResult exportKeyRings(OperationLog log, long[] masterKeyIds, boolean exportSecret, - OutputStream outStream) { - - /* TODO isn't this checked above, with the isStorageMounted call? - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1); - return new ExportResult(ExportResult.RESULT_ERROR, log); - } - */ - - if ( ! BufferedOutputStream.class.isInstance(outStream)) { - outStream = new BufferedOutputStream(outStream); - } - - int okSecret = 0, okPublic = 0, progress = 0; - - Cursor cursor = null; - try { - - String selection = null, ids[] = null; - - if (masterKeyIds != null) { - // generate placeholders and string selection args - ids = new String[masterKeyIds.length]; - StringBuilder placeholders = new StringBuilder("?"); - for (int i = 0; i < masterKeyIds.length; i++) { - ids[i] = Long.toString(masterKeyIds[i]); - if (i != 0) { - placeholders.append(",?"); - } - } - - // put together selection string - selection = Tables.KEY_RINGS_PUBLIC + "." + KeyRings.MASTER_KEY_ID - + " IN (" + placeholders + ")"; - } - - cursor = mProviderHelper.getContentResolver().query( - KeyRings.buildUnifiedKeyRingsUri(), new String[]{ - KeyRings.MASTER_KEY_ID, KeyRings.PUBKEY_DATA, - KeyRings.PRIVKEY_DATA, KeyRings.HAS_ANY_SECRET - }, selection, ids, Tables.KEYS + "." + KeyRings.MASTER_KEY_ID - ); - - if (cursor == null || !cursor.moveToFirst()) { - log.add(LogType.MSG_EXPORT_ERROR_DB, 1); - return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret); - } - - int numKeys = cursor.getCount(); - - updateProgress( - mContext.getResources().getQuantityString(R.plurals.progress_exporting_key, - numKeys), 0, numKeys); - - // For each public masterKey id - while (!cursor.isAfterLast()) { - - long keyId = cursor.getLong(0); - ArmoredOutputStream arOutStream = null; - - // Create an output stream - try { - arOutStream = new ArmoredOutputStream(outStream); - - log.add(LogType.MSG_EXPORT_PUBLIC, 1, KeyFormattingUtils.beautifyKeyId(keyId)); - - byte[] data = cursor.getBlob(1); - CanonicalizedKeyRing ring = - UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true); - ring.encode(arOutStream); - - okPublic += 1; - } catch (PgpGeneralException e) { - log.add(LogType.MSG_EXPORT_ERROR_KEY, 2); - updateProgress(progress++, numKeys); - continue; - } finally { - // make sure this is closed - if (arOutStream != null) { - arOutStream.close(); - } - arOutStream = null; - } - - if (exportSecret && cursor.getInt(3) > 0) { - try { - arOutStream = new ArmoredOutputStream(outStream); - - // export secret key part - log.add(LogType.MSG_EXPORT_SECRET, 2, KeyFormattingUtils.beautifyKeyId(keyId)); - byte[] data = cursor.getBlob(2); - CanonicalizedKeyRing ring = - UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true); - ring.encode(arOutStream); - - okSecret += 1; - } catch (PgpGeneralException e) { - log.add(LogType.MSG_EXPORT_ERROR_KEY, 2); - updateProgress(progress++, numKeys); - continue; - } finally { - // make sure this is closed - if (arOutStream != null) { - arOutStream.close(); - } - } - } - - updateProgress(progress++, numKeys); - - cursor.moveToNext(); - } - - updateProgress(R.string.progress_done, numKeys, numKeys); - - } catch (IOException e) { - log.add(LogType.MSG_EXPORT_ERROR_IO, 1); - return new ExportResult(ExportResult.RESULT_ERROR, log, okPublic, okSecret); - } finally { - // Make sure the stream is closed - if (outStream != null) try { - outStream.close(); - } catch (Exception e) { - Log.e(Constants.TAG, "error closing stream", e); - } - if (cursor != null) { - cursor.close(); - } - } - - - log.add(LogType.MSG_EXPORT_SUCCESS, 1); - return new ExportResult(ExportResult.RESULT_OK, log, okPublic, okSecret); - - } - @Override - public OperationResult execute(ImportExportParcel input, CryptoInputParcel cryptoInput) { - if (input instanceof ExportKeyringParcel) { - ExportKeyringParcel exportInput = (ExportKeyringParcel) input; - switch (exportInput.mExportType) { - case UPLOAD_KEYSERVER: { - HkpKeyserver hkpKeyserver = new HkpKeyserver(exportInput.mKeyserver); - try { - CanonicalizedPublicKeyRing keyring - = mProviderHelper.getCanonicalizedPublicKeyRing( - exportInput.mCanonicalizedPublicKeyringUri); - uploadKeyRingToServer(hkpKeyserver, keyring); - // TODO: replace with proper log - return new ExportResult(ExportResult.RESULT_OK, new OperationLog()); - } catch (Exception e) { - // TODO: Implement better exception handling, replace with log - } - break; - } - case EXPORT_FILE: { - return exportToFile(exportInput.mMasterKeyIds, exportInput.mExportSecret, - exportInput.mOutputFile); - } - case EXPORT_URI: { - return exportToUri(exportInput.mMasterKeyIds, exportInput.mExportSecret, - exportInput.mOutputUri); - } - default: { - return null; - } - } - } - else if (input instanceof ImportKeyringParcel) { - ImportKeyringParcel importInput = (ImportKeyringParcel) input; - return importKeys(importInput.mKeyList, importInput.mKeyserver); - } else { - throw new RuntimeException("Invalid input parcel at ImportExportOperation"); - } - return null; + public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) { + return importKeys(importInput.mKeyList, importInput.mKeyserver); } public ImportKeyResult importKeys(ArrayList keyList, String keyServer) { @@ -678,9 +386,10 @@ public class ImportExportOperation extends BaseOperation { ParcelableFileCache cache = new ParcelableFileCache<>(mContext, "key_import.pcl"); - result = serialKeyRingImport(cache, keyServer); + result = serialKeyRingImport(cache, keyServer); } else { - // if there is more than one key with the same fingerprint, we do a serial import to prevent + // if there is more than one key with the same fingerprint, we do a serial import to + // prevent // https://github.com/open-keychain/open-keychain/issues/1221 HashSet keyFingerprintSet = new HashSet<>(); for (int i = 0; i < keyList.size(); i++) { @@ -688,7 +397,7 @@ public class ImportExportOperation extends BaseOperation { } if (keyFingerprintSet.size() == keyList.size()) { // all keys have unique fingerprints - result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer); + result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer); } else { result = serialKeyRingImport(keyList, keyServer); } @@ -704,28 +413,7 @@ public class ImportExportOperation extends BaseOperation { if (keyListIterator != null) { KeyImportAccumulator accumulator = new KeyImportAccumulator(totKeys, mProgressable); - final Progressable ignoreProgressable = new Progressable() { - @Override - public void setProgress(String message, int current, int total) { - - } - - @Override - public void setProgress(int resourceId, int current, int total) { - - } - - @Override - public void setProgress(int current, int total) { - - } - - @Override - public void setPreventCancel() { - - } - }; - + final ProgressScaler ignoreProgressable = new ProgressScaler(); final int maxThreads = 200; ExecutorService importExecutor = new ThreadPoolExecutor(0, maxThreads, @@ -739,7 +427,8 @@ public class ImportExportOperation extends BaseOperation { final ParcelableKeyRing pkRing = keyListIterator.next(); - Callable importOperationCallable = new Callable() { + Callable importOperationCallable = new Callable + () { @Override public ImportKeyResult call() { @@ -758,10 +447,12 @@ public class ImportExportOperation extends BaseOperation { try { accumulator.accumulateKeyImport(importCompletionService.take().get()); } catch (InterruptedException | ExecutionException e) { - Log.e(Constants.TAG, "A key could not be imported during multi-threaded import", e); + Log.e(Constants.TAG, "A key could not be imported during multi-threaded " + + "import", e); // do nothing? if (e instanceof ExecutionException) { - // Since serialKeyRingImport does not throw any exceptions, this is what would have happened if + // Since serialKeyRingImport does not throw any exceptions, this is what + // would have happened if // we were importing the key on this thread throw new RuntimeException(); } @@ -792,7 +483,8 @@ public class ImportExportOperation extends BaseOperation { * Also sets the progress to 0 on instantiation. * * @param totalKeys total number of keys to be imported - * @param externalProgressable the external progressable to be updated every time a key is imported + * @param externalProgressable the external progressable to be updated every time a key + * is imported */ public KeyImportAccumulator(int totalKeys, Progressable externalProgressable) { mTotalKeys = totalKeys; 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 5fbc7f6c7..590c58f97 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -32,7 +32,7 @@ import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; -import org.sufficientlysecure.keychain.operations.ImportExportOperation; +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; @@ -1248,7 +1248,7 @@ public class ProviderHelper { // 3. Re-Import secret keyrings from cache if (numSecrets > 0) { - ImportKeyResult result = new ImportExportOperation(mContext, this, + ImportKeyResult result = new ImportOperation(mContext, this, new ProgressFixedScaler(progress, 10, 25, 100, R.string.progress_con_reimport)) .serialKeyRingImport(itSecrets, numSecrets, null); log.add(result, indent); @@ -1276,7 +1276,7 @@ public class ProviderHelper { // 4. Re-Import public keyrings from cache if (numPublics > 0) { - ImportKeyResult result = new ImportExportOperation(mContext, this, + ImportKeyResult result = new ImportOperation(mContext, this, new ProgressFixedScaler(progress, 25, 99, 100, R.string.progress_con_reimport)) .serialKeyRingImport(itPublics, numPublics, null); log.add(result, indent); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java index 236f7f0e3..ef5b48df3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java @@ -23,7 +23,7 @@ import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; -public class ExportKeyringParcel extends ImportExportParcel implements Parcelable { +public class ExportKeyringParcel implements Parcelable { public String mKeyserver; public Uri mCanonicalizedPublicKeyringUri; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportExportParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportExportParcel.java deleted file mode 100644 index 2e14cc6b6..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportExportParcel.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2015 Dominik Schürmann - * Copyright (C) 2015 Vincent Breitmoser - * Copyright (C) 2015 Adithya Abraham Philip - * - * 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.service; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Empty class, simply serves as a base class for ImportKeyringParcel and ExportKeyringParcel - */ -public class ImportExportParcel implements Parcelable { - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java index 0b7149c75..a41dd71cb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java @@ -24,7 +24,7 @@ import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import java.util.ArrayList; -public class ImportKeyringParcel extends ImportExportParcel { +public class ImportKeyringParcel implements Parcelable { // if null, keys are expected to be read from a cache file in ImportExportOperations public ArrayList mKeyList; public String mKeyserver; // must be set if keys are to be imported from a keyserver diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java index 6a74520db..1cd76b462 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java @@ -33,7 +33,8 @@ import org.sufficientlysecure.keychain.operations.CertifyOperation; import org.sufficientlysecure.keychain.operations.ConsolidateOperation; import org.sufficientlysecure.keychain.operations.DeleteOperation; import org.sufficientlysecure.keychain.operations.EditKeyOperation; -import org.sufficientlysecure.keychain.operations.ImportExportOperation; +import org.sufficientlysecure.keychain.operations.ExportOperation; +import org.sufficientlysecure.keychain.operations.ImportOperation; import org.sufficientlysecure.keychain.operations.KeybaseVerificationOperation; import org.sufficientlysecure.keychain.operations.PromoteKeyOperation; import org.sufficientlysecure.keychain.operations.SignEncryptOperation; @@ -121,10 +122,12 @@ public class KeychainService extends Service implements Progressable { } else if (inputParcel instanceof PromoteKeyringParcel) { op = new PromoteKeyOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled); - } else if (inputParcel instanceof ImportKeyringParcel - || inputParcel instanceof ExportKeyringParcel) { - op = new ImportExportOperation(outerThis, new ProviderHelper(outerThis), - outerThis, mActionCanceled); + } else if (inputParcel instanceof ImportKeyringParcel) { + op = new ImportOperation(outerThis, new ProviderHelper(outerThis), outerThis, + mActionCanceled); + } else if (inputParcel instanceof ExportKeyringParcel) { + op = new ExportOperation(outerThis, new ProviderHelper(outerThis), outerThis, + mActionCanceled); } else if (inputParcel instanceof ConsolidateInputParcel) { op = new ConsolidateOperation(outerThis, new ProviderHelper(outerThis), outerThis); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index db88de676..93c6593ab 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -33,6 +33,7 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; +import org.sufficientlysecure.keychain.operations.ImportOperation; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.Highlighter; @@ -92,8 +93,8 @@ public class ImportKeysAdapter extends ArrayAdapter { } /** This method returns a list of all selected entries, with public keys sorted - * before secret keys, see ImportExportOperation for specifics. - * @see org.sufficientlysecure.keychain.operations.ImportExportOperation + * before secret keys, see ImportOperation for specifics. + * @see ImportOperation */ public ArrayList getSelectedEntries() { ArrayList result = new ArrayList<>(); diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java index fa1288e08..a659dc7da 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java @@ -30,7 +30,6 @@ import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowLog; import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.jce.provider.BouncyCastleProvider; -import org.sufficientlysecure.keychain.BuildConfig; import org.sufficientlysecure.keychain.WorkaroundBuildConfig; import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult; import org.sufficientlysecure.keychain.operations.results.ExportResult; @@ -127,7 +126,7 @@ public class ExportTest { @Test public void testExportAll() throws Exception { - ImportExportOperation op = new ImportExportOperation(RuntimeEnvironment.application, + ExportOperation op = new ExportOperation(RuntimeEnvironment.application, new ProviderHelper(RuntimeEnvironment.application), null); // make sure there is a local cert (so the later checks that there are none are meaningful)