aboutsummaryrefslogtreecommitdiffstats
path: root/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/ImportExportOperation.java
diff options
context:
space:
mode:
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/ImportExportOperation.java')
-rw-r--r--OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/ImportExportOperation.java279
1 files changed, 279 insertions, 0 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/ImportExportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/ImportExportOperation.java
new file mode 100644
index 000000000..32151c4d9
--- /dev/null
+++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/ops/ImportExportOperation.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
+ * Copyright (C) 2010-2014 Thialfihar <thi@thialfihar.org>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package org.sufficientlysecure.keychain.pgp.ops;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Environment;
+
+import org.spongycastle.bcpg.ArmoredOutputStream;
+import org.spongycastle.openpgp.PGPException;
+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.keyimport.ParcelableKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
+import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKeyRing;
+import org.sufficientlysecure.keychain.pgp.PgpHelper;
+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;
+import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.service.KeychainIntentService;
+import org.sufficientlysecure.keychain.service.results.OperationResult.LogType;
+import org.sufficientlysecure.keychain.service.results.OperationResult.OperationLog;
+import org.sufficientlysecure.keychain.service.results.ImportKeyResult;
+import org.sufficientlysecure.keychain.service.results.SaveKeyringResult;
+import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
+import org.sufficientlysecure.keychain.util.Log;
+import org.sufficientlysecure.keychain.util.ProgressScaler;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class ImportExportOperation extends BaseOperation {
+
+ public ImportExportOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
+ super(context, providerHelper, progressable);
+ }
+
+ public ImportExportOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) {
+ super(context, providerHelper, progressable, cancelled);
+ }
+
+ public void uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing 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 ImportKeyResult importKeyRings(Iterator<ParcelableKeyRing> entries, int num) {
+ updateProgress(R.string.progress_importing, 0, 100);
+
+ OperationLog log = new OperationLog();
+
+ // If there aren't even any keys, do nothing here.
+ if (entries == null || !entries.hasNext()) {
+ return new ImportKeyResult(
+ ImportKeyResult.RESULT_FAIL_NOTHING, log, 0, 0, 0, 0,
+ new long[]{});
+ }
+
+ int newKeys = 0, oldKeys = 0, badKeys = 0, secret = 0;
+ ArrayList<Long> importedMasterKeyIds = new ArrayList<Long>();
+
+ int position = 0;
+ double progSteps = 100.0 / num;
+
+ // iterate over all entries
+ while (entries.hasNext()) {
+ ParcelableKeyRing entry = entries.next();
+
+ // Has this action been cancelled? If so, don't proceed any further
+ if (mCancelled != null && mCancelled.get()) {
+ break;
+ }
+
+ try {
+ UncachedKeyRing key = UncachedKeyRing.decodeFromData(entry.getBytes());
+
+ String expectedFp = entry.getExpectedFingerprint();
+ if(expectedFp != null) {
+ if(!KeyFormattingUtils.convertFingerprintToHex(key.getFingerprint()).equals(expectedFp)) {
+ Log.d(Constants.TAG, "fingerprint: " + KeyFormattingUtils.convertFingerprintToHex(key.getFingerprint()));
+ Log.d(Constants.TAG, "expected fingerprint: " + expectedFp);
+ Log.e(Constants.TAG, "Actual key fingerprint is not the same as expected!");
+ badKeys += 1;
+ continue;
+ } else {
+ Log.d(Constants.TAG, "Actual key fingerprint matches expected one.");
+ }
+ }
+
+ SaveKeyringResult result;
+ mProviderHelper.clearLog();
+ if (key.isSecret()) {
+ result = mProviderHelper.saveSecretKeyRing(key,
+ new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100));
+ } else {
+ result = mProviderHelper.savePublicKeyRing(key,
+ new ProgressScaler(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100));
+ }
+ if (!result.success()) {
+ badKeys += 1;
+ } else if (result.updated()) {
+ oldKeys += 1;
+ importedMasterKeyIds.add(key.getMasterKeyId());
+ } else {
+ newKeys += 1;
+ if (key.isSecret()) {
+ secret += 1;
+ }
+ importedMasterKeyIds.add(key.getMasterKeyId());
+ }
+
+ log.add(result, 1);
+
+ } catch (IOException e) {
+ Log.e(Constants.TAG, "Encountered bad key on import!", e);
+ ++badKeys;
+ } catch (PgpGeneralException e) {
+ Log.e(Constants.TAG, "Encountered bad key on import!", e);
+ ++badKeys;
+ }
+ // update progress
+ position++;
+ }
+
+ int resultType = 0;
+ // special return case: no new keys at all
+ if (badKeys == 0 && newKeys == 0 && oldKeys == 0) {
+ resultType = ImportKeyResult.RESULT_FAIL_NOTHING;
+ } else {
+ if (newKeys > 0) {
+ resultType |= ImportKeyResult.RESULT_OK_NEWKEYS;
+ }
+ if (oldKeys > 0) {
+ resultType |= ImportKeyResult.RESULT_OK_UPDATED;
+ }
+ if (badKeys > 0) {
+ resultType |= ImportKeyResult.RESULT_WITH_ERRORS;
+ if (newKeys == 0 && oldKeys == 0) {
+ resultType |= ImportKeyResult.RESULT_ERROR;
+ }
+ }
+ if (log.containsWarnings()) {
+ resultType |= ImportKeyResult.RESULT_WARNINGS;
+ }
+ }
+ if (mCancelled != null && mCancelled.get()) {
+ log.add(LogType.MSG_OPERATION_CANCELLED, 0);
+ resultType |= ImportKeyResult.RESULT_CANCELLED;
+ }
+
+ // convert to long array
+ long[] importedMasterKeyIdsArray = new long[importedMasterKeyIds.size()];
+ for (int i = 0; i < importedMasterKeyIds.size(); ++i) {
+ importedMasterKeyIdsArray[i] = importedMasterKeyIds.get(i);
+ }
+
+ return new ImportKeyResult(resultType, log, newKeys, oldKeys, badKeys, secret,
+ importedMasterKeyIdsArray);
+ }
+
+ public Bundle exportKeyRings(ArrayList<Long> publicKeyRingMasterIds,
+ ArrayList<Long> secretKeyRingMasterIds,
+ OutputStream outStream) throws PgpGeneralException,
+ PGPException, IOException {
+ Bundle returnData = new Bundle();
+
+ int masterKeyIdsSize = publicKeyRingMasterIds.size() + secretKeyRingMasterIds.size();
+ int progress = 0;
+
+ updateProgress(
+ mContext.getResources().getQuantityString(R.plurals.progress_exporting_key,
+ masterKeyIdsSize), 0, 100);
+
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ throw new PgpGeneralException(
+ mContext.getString(R.string.error_external_storage_not_ready));
+ }
+ // For each public masterKey id
+ for (long pubKeyMasterId : publicKeyRingMasterIds) {
+ progress++;
+ // Create an output stream
+ ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
+ String version = PgpHelper.getVersionForHeader(mContext);
+ if (version != null) {
+ arOutStream.setHeader("Version", version);
+ }
+
+ updateProgress(progress * 100 / masterKeyIdsSize, 100);
+
+ try {
+ CanonicalizedPublicKeyRing ring = mProviderHelper.getCanonicalizedPublicKeyRing(
+ KeychainContract.KeyRings.buildUnifiedKeyRingUri(pubKeyMasterId)
+ );
+
+ ring.encode(arOutStream);
+ } catch (ProviderHelper.NotFoundException e) {
+ Log.e(Constants.TAG, "key not found!", e);
+ // TODO: inform user?
+ }
+
+ arOutStream.close();
+ }
+
+ // For each secret masterKey id
+ for (long secretKeyMasterId : secretKeyRingMasterIds) {
+ progress++;
+ // Create an output stream
+ ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream);
+ String version = PgpHelper.getVersionForHeader(mContext);
+ if (version != null) {
+ arOutStream.setHeader("Version", version);
+ }
+
+ updateProgress(progress * 100 / masterKeyIdsSize, 100);
+
+ try {
+ CanonicalizedSecretKeyRing secretKeyRing =
+ mProviderHelper.getCanonicalizedSecretKeyRing(secretKeyMasterId);
+ secretKeyRing.encode(arOutStream);
+ } catch (ProviderHelper.NotFoundException e) {
+ Log.e(Constants.TAG, "key not found!", e);
+ // TODO: inform user?
+ }
+
+ arOutStream.close();
+ }
+
+ returnData.putInt(KeychainIntentService.RESULT_EXPORT, masterKeyIdsSize);
+
+ updateProgress(R.string.progress_done, 100, 100);
+
+ return returnData;
+ }
+
+}