Merge pull request #1362 from open-keychain/operation-migration
Migrated all operations to KeychainNewService
This commit is contained in:
@@ -709,9 +709,6 @@
|
||||
android:name=".remote.CryptoInputParcelCacheService"
|
||||
android:exported="false"
|
||||
android:process=":remote_api" />
|
||||
<service
|
||||
android:name=".service.KeychainNewService"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name=".service.KeychainService"
|
||||
android:exported="false" />
|
||||
|
||||
@@ -58,12 +58,13 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
* @see CertifyActionsParcel
|
||||
*
|
||||
*/
|
||||
public class CertifyOperation extends BaseOperation {
|
||||
public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
|
||||
|
||||
public CertifyOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) {
|
||||
super(context, providerHelper, progressable, cancelled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CertifyResult execute(CertifyActionsParcel parcel, CryptoInputParcel cryptoInput) {
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
@@ -86,8 +87,10 @@ public class CertifyOperation extends BaseOperation {
|
||||
case PATTERN:
|
||||
case PASSPHRASE:
|
||||
if (!cryptoInput.hasPassphrase()) {
|
||||
return new CertifyResult(log, RequiredInputParcel.createRequiredSignPassphrase(
|
||||
certificationKey.getKeyId(), certificationKey.getKeyId(), null));
|
||||
return new CertifyResult(log,
|
||||
RequiredInputParcel.createRequiredSignPassphrase(
|
||||
certificationKey.getKeyId(), certificationKey.getKeyId(), null)
|
||||
);
|
||||
}
|
||||
// certification is always with the master key id, so use that one
|
||||
passphrase = cryptoInput.getPassphrase();
|
||||
@@ -185,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
|
||||
@@ -206,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);
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
|
||||
*
|
||||
* 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.operations;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
|
||||
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.ConsolidateInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
|
||||
public class ConsolidateOperation extends BaseOperation<ConsolidateInputParcel> {
|
||||
|
||||
public ConsolidateOperation(Context context, ProviderHelper providerHelper, Progressable
|
||||
progressable) {
|
||||
super(context, providerHelper, progressable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsolidateResult execute(ConsolidateInputParcel consolidateInputParcel,
|
||||
CryptoInputParcel cryptoInputParcel) {
|
||||
if (consolidateInputParcel.mConsolidateRecovery) {
|
||||
return mProviderHelper.consolidateDatabaseStep2(mProgressable);
|
||||
} else {
|
||||
return mProviderHelper.consolidateDatabaseStep1(mProgressable);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,8 @@ import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
|
||||
import org.sufficientlysecure.keychain.service.DeleteKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
|
||||
/** An operation which implements a high level keyring delete operation.
|
||||
@@ -37,13 +39,18 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
* a list.
|
||||
*
|
||||
*/
|
||||
public class DeleteOperation extends BaseOperation {
|
||||
public class DeleteOperation extends BaseOperation<DeleteKeyringParcel> {
|
||||
|
||||
public DeleteOperation(Context context, ProviderHelper providerHelper, Progressable progressable) {
|
||||
super(context, providerHelper, progressable);
|
||||
}
|
||||
|
||||
public DeleteResult execute(long[] masterKeyIds, boolean isSecret) {
|
||||
@Override
|
||||
public DeleteResult execute(DeleteKeyringParcel deleteKeyringParcel,
|
||||
CryptoInputParcel cryptoInputParcel) {
|
||||
|
||||
long[] masterKeyIds = deleteKeyringParcel.mMasterKeyIds;
|
||||
boolean isSecret = deleteKeyringParcel.mIsSecret;
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* 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.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<ExportKeyringParcel> {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,57 +19,50 @@
|
||||
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.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.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.FileHelper;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
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.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
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
|
||||
@@ -78,72 +71,57 @@ 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<ImportKeyringParcel> {
|
||||
|
||||
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());
|
||||
// Overloaded functions for using progressable supplied in constructor during import
|
||||
public ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
|
||||
String keyServerUri) {
|
||||
return serialKeyRingImport(entries, num, keyServerUri, mProgressable);
|
||||
}
|
||||
|
||||
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 ImportKeyResult importKeyRings(List<ParcelableKeyRing> entries, String keyServerUri) {
|
||||
public ImportKeyResult serialKeyRingImport(List<ParcelableKeyRing> entries,
|
||||
String keyServerUri) {
|
||||
|
||||
Iterator<ParcelableKeyRing> it = entries.iterator();
|
||||
int numEntries = entries.size();
|
||||
|
||||
return importKeyRings(it, numEntries, keyServerUri);
|
||||
return serialKeyRingImport(it, numEntries, keyServerUri, mProgressable);
|
||||
|
||||
}
|
||||
|
||||
public ImportKeyResult importKeyRings(ParcelableFileCache<ParcelableKeyRing> cache, String keyServerUri) {
|
||||
public ImportKeyResult serialKeyRingImport(List<ParcelableKeyRing> entries, String keyServerUri,
|
||||
Progressable progressable) {
|
||||
|
||||
Iterator<ParcelableKeyRing> it = entries.iterator();
|
||||
int numEntries = entries.size();
|
||||
|
||||
return serialKeyRingImport(it, numEntries, keyServerUri, progressable);
|
||||
|
||||
}
|
||||
|
||||
public ImportKeyResult serialKeyRingImport(ParcelableFileCache<ParcelableKeyRing> cache,
|
||||
String keyServerUri) {
|
||||
|
||||
// get entries from cached file
|
||||
try {
|
||||
IteratorWithSize<ParcelableKeyRing> it = cache.readCache();
|
||||
int numEntries = it.getSize();
|
||||
|
||||
return importKeyRings(it, numEntries, keyServerUri);
|
||||
return serialKeyRingImport(it, numEntries, keyServerUri, mProgressable);
|
||||
} catch (IOException e) {
|
||||
|
||||
// Special treatment here, we need a lot
|
||||
@@ -157,15 +135,18 @@ public class ImportExportOperation extends BaseOperation {
|
||||
}
|
||||
|
||||
/**
|
||||
* Since the introduction of multithreaded import, we expect calling functions to handle the key sync i,e
|
||||
* ContactSyncAdapterService.requestSync()
|
||||
* Since the introduction of multithreaded import, we expect calling functions to handle the
|
||||
* key sync i,eContactSyncAdapterService.requestSync()
|
||||
*
|
||||
* @param entries keys to import
|
||||
* @param num number of keys to import
|
||||
* @param entries keys to import
|
||||
* @param num number of keys to import
|
||||
* @param keyServerUri contains uri of keyserver to import from, if it is an import from cloud
|
||||
* @param progressable Allows multi-threaded import to supply a progressable that ignores the
|
||||
* progress of a single key being imported
|
||||
* @return
|
||||
*/
|
||||
public ImportKeyResult importKeyRings(Iterator<ParcelableKeyRing> entries, int num, String keyServerUri) {
|
||||
public ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
|
||||
String keyServerUri, Progressable progressable) {
|
||||
updateProgress(R.string.progress_importing, 0, 100);
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
@@ -208,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
|
||||
@@ -224,7 +206,8 @@ public class ImportExportOperation extends BaseOperation {
|
||||
byte[] data;
|
||||
// Download by fingerprint, or keyId - whichever is available
|
||||
if (entry.mExpectedFingerprint != null) {
|
||||
log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, "0x" + entry.mExpectedFingerprint.substring(24));
|
||||
log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, "0x" +
|
||||
entry.mExpectedFingerprint.substring(24));
|
||||
data = keyServer.get("0x" + entry.mExpectedFingerprint).getBytes();
|
||||
} else {
|
||||
log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, entry.mKeyIdHex);
|
||||
@@ -302,10 +285,12 @@ public class ImportExportOperation extends BaseOperation {
|
||||
mProviderHelper.clearLog();
|
||||
if (key.isSecret()) {
|
||||
result = mProviderHelper.saveSecretKeyRing(key,
|
||||
new ProgressScaler(mProgressable, (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(mProgressable, (int)(position*progSteps), (int)((position+1)*progSteps), 100));
|
||||
new ProgressScaler(progressable, (int) (position * progSteps),
|
||||
(int) ((position + 1) * progSteps), 100));
|
||||
}
|
||||
if (!result.success()) {
|
||||
badKeys += 1;
|
||||
@@ -333,7 +318,7 @@ public class ImportExportOperation extends BaseOperation {
|
||||
// Special: consolidate on secret key import (cannot be cancelled!)
|
||||
if (secret > 0) {
|
||||
setPreventCancel();
|
||||
ConsolidateResult result = mProviderHelper.consolidateDatabaseStep1(mProgressable);
|
||||
ConsolidateResult result = mProviderHelper.consolidateDatabaseStep1(progressable);
|
||||
log.add(result, 1);
|
||||
}
|
||||
|
||||
@@ -376,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);
|
||||
@@ -388,206 +373,193 @@ public class ImportExportOperation extends BaseOperation {
|
||||
importedMasterKeyIdsArray);
|
||||
}
|
||||
|
||||
public ExportResult exportToFile(long[] masterKeyIds, boolean exportSecret, String outputFile) {
|
||||
@Override
|
||||
public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) {
|
||||
return importKeys(importInput.mKeyList, importInput.mKeyserver);
|
||||
}
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
if (masterKeyIds != null) {
|
||||
log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length);
|
||||
public ImportKeyResult importKeys(ArrayList<ParcelableKeyRing> keyList, String keyServer) {
|
||||
|
||||
ImportKeyResult result;
|
||||
|
||||
if (keyList == null) {// import from file, do serially
|
||||
ParcelableFileCache<ParcelableKeyRing> cache = new ParcelableFileCache<>(mContext,
|
||||
"key_import.pcl");
|
||||
|
||||
result = serialKeyRingImport(cache, keyServer);
|
||||
} 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();
|
||||
// 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<String> keyFingerprintSet = new HashSet<>();
|
||||
for (int i = 0; i < keyList.size(); i++) {
|
||||
keyFingerprintSet.add(keyList.get(i).mExpectedFingerprint);
|
||||
}
|
||||
if (keyFingerprintSet.size() == keyList.size()) {
|
||||
// all keys have unique fingerprints
|
||||
result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer);
|
||||
} else {
|
||||
result = serialKeyRingImport(keyList, keyServer);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.add(LogType.MSG_EXPORT_ERROR_FOPEN, 1);
|
||||
return new ExportResult(ExportResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
ContactSyncAdapterService.requestSync();
|
||||
return result;
|
||||
}
|
||||
|
||||
public ExportResult exportToUri(long[] masterKeyIds, boolean exportSecret, Uri outputUri) {
|
||||
private ImportKeyResult multiThreadedKeyImport(Iterator<ParcelableKeyRing> keyListIterator,
|
||||
int totKeys, final String keyServer) {
|
||||
Log.d(Constants.TAG, "Multi-threaded key import starting");
|
||||
if (keyListIterator != null) {
|
||||
KeyImportAccumulator accumulator = new KeyImportAccumulator(totKeys, mProgressable);
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
if (masterKeyIds != null) {
|
||||
log.add(LogType.MSG_EXPORT, 0, masterKeyIds.length);
|
||||
} else {
|
||||
log.add(LogType.MSG_EXPORT_ALL, 0);
|
||||
}
|
||||
final ProgressScaler ignoreProgressable = new ProgressScaler();
|
||||
|
||||
// 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);
|
||||
}
|
||||
final int maxThreads = 200;
|
||||
ExecutorService importExecutor = new ThreadPoolExecutor(0, maxThreads,
|
||||
30L, TimeUnit.SECONDS,
|
||||
new SynchronousQueue<Runnable>());
|
||||
|
||||
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);
|
||||
}
|
||||
ExecutorCompletionService<ImportKeyResult> importCompletionService =
|
||||
new ExecutorCompletionService(importExecutor);
|
||||
|
||||
}
|
||||
while (keyListIterator.hasNext()) { // submit all key rings to be imported
|
||||
|
||||
ExportResult exportKeyRings(OperationLog log, long[] masterKeyIds, boolean exportSecret,
|
||||
OutputStream outStream) {
|
||||
final ParcelableKeyRing pkRing = keyListIterator.next();
|
||||
|
||||
/* 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);
|
||||
}
|
||||
*/
|
||||
Callable<ImportKeyResult> importOperationCallable = new Callable<ImportKeyResult>
|
||||
() {
|
||||
|
||||
if ( ! BufferedOutputStream.class.isInstance(outStream)) {
|
||||
outStream = new BufferedOutputStream(outStream);
|
||||
}
|
||||
@Override
|
||||
public ImportKeyResult call() {
|
||||
|
||||
int okSecret = 0, okPublic = 0, progress = 0;
|
||||
ArrayList<ParcelableKeyRing> list = new ArrayList<>();
|
||||
list.add(pkRing);
|
||||
|
||||
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(",?");
|
||||
return serialKeyRingImport(list, keyServer, ignoreProgressable);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// put together selection string
|
||||
selection = Tables.KEY_RINGS_PUBLIC + "." + KeyRings.MASTER_KEY_ID
|
||||
+ " IN (" + placeholders + ")";
|
||||
importCompletionService.submit(importOperationCallable);
|
||||
}
|
||||
|
||||
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
|
||||
while (!accumulator.isImportFinished()) { // accumulate the results of each import
|
||||
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();
|
||||
}
|
||||
accumulator.accumulateKeyImport(importCompletionService.take().get());
|
||||
} catch (InterruptedException | ExecutionException 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
|
||||
// we were importing the key on this thread
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
return accumulator.getConsolidatedResult();
|
||||
}
|
||||
|
||||
|
||||
log.add(LogType.MSG_EXPORT_SUCCESS, 1);
|
||||
return new ExportResult(ExportResult.RESULT_OK, log, okPublic, okSecret);
|
||||
|
||||
return null; // TODO: Decide if we should just crash instead of returning null
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Used to accumulate the results of individual key imports
|
||||
*/
|
||||
private class KeyImportAccumulator {
|
||||
private OperationResult.OperationLog mImportLog = new OperationResult.OperationLog();
|
||||
Progressable mProgressable;
|
||||
private int mTotalKeys;
|
||||
private int mImportedKeys = 0;
|
||||
ArrayList<Long> mImportedMasterKeyIds = new ArrayList<Long>();
|
||||
private int mBadKeys = 0;
|
||||
private int mNewKeys = 0;
|
||||
private int mUpdatedKeys = 0;
|
||||
private int mSecret = 0;
|
||||
private int mResultType = 0;
|
||||
|
||||
/**
|
||||
* Accumulates keyring imports and updates the progressable whenever a new key is imported.
|
||||
* 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
|
||||
*/
|
||||
public KeyImportAccumulator(int totalKeys, Progressable externalProgressable) {
|
||||
mTotalKeys = totalKeys;
|
||||
mProgressable = externalProgressable;
|
||||
mProgressable.setProgress(0, totalKeys);
|
||||
}
|
||||
|
||||
public int getTotalKeys() {
|
||||
return mTotalKeys;
|
||||
}
|
||||
|
||||
public int getImportedKeys() {
|
||||
return mImportedKeys;
|
||||
}
|
||||
|
||||
public synchronized void accumulateKeyImport(ImportKeyResult result) {
|
||||
mImportedKeys++;
|
||||
|
||||
mProgressable.setProgress(mImportedKeys, mTotalKeys);
|
||||
|
||||
mImportLog.addAll(result.getLog().toList());//accumulates log
|
||||
mBadKeys += result.mBadKeys;
|
||||
mNewKeys += result.mNewKeys;
|
||||
mUpdatedKeys += result.mUpdatedKeys;
|
||||
mSecret += result.mSecret;
|
||||
|
||||
long[] masterKeyIds = result.getImportedMasterKeyIds();
|
||||
for (long masterKeyId : masterKeyIds) {
|
||||
mImportedMasterKeyIds.add(masterKeyId);
|
||||
}
|
||||
|
||||
// if any key import has been cancelled, set result type to cancelled
|
||||
// resultType is added to in getConsolidatedKayImport to account for remaining factors
|
||||
mResultType |= result.getResult() & ImportKeyResult.RESULT_CANCELLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns accumulated result of all imports so far
|
||||
*/
|
||||
public ImportKeyResult getConsolidatedResult() {
|
||||
|
||||
// adding required information to mResultType
|
||||
// special case,no keys requested for import
|
||||
if (mBadKeys == 0 && mNewKeys == 0 && mUpdatedKeys == 0) {
|
||||
mResultType = ImportKeyResult.RESULT_FAIL_NOTHING;
|
||||
} else {
|
||||
if (mNewKeys > 0) {
|
||||
mResultType |= ImportKeyResult.RESULT_OK_NEWKEYS;
|
||||
}
|
||||
if (mUpdatedKeys > 0) {
|
||||
mResultType |= ImportKeyResult.RESULT_OK_UPDATED;
|
||||
}
|
||||
if (mBadKeys > 0) {
|
||||
mResultType |= ImportKeyResult.RESULT_WITH_ERRORS;
|
||||
if (mNewKeys == 0 && mUpdatedKeys == 0) {
|
||||
mResultType |= ImportKeyResult.RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
if (mImportLog.containsWarnings()) {
|
||||
mResultType |= ImportKeyResult.RESULT_WARNINGS;
|
||||
}
|
||||
}
|
||||
|
||||
long masterKeyIds[] = new long[mImportedMasterKeyIds.size()];
|
||||
for (int i = 0; i < masterKeyIds.length; i++) {
|
||||
masterKeyIds[i] = mImportedMasterKeyIds.get(i);
|
||||
}
|
||||
|
||||
return new ImportKeyResult(mResultType, mImportLog, mNewKeys, mUpdatedKeys, mBadKeys,
|
||||
mSecret, masterKeyIds);
|
||||
}
|
||||
|
||||
public boolean isImportFinished() {
|
||||
return mTotalKeys == mImportedKeys;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
|
||||
*
|
||||
* 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.operations;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.textuality.keybase.lib.Proof;
|
||||
import com.textuality.keybase.lib.prover.Prover;
|
||||
import de.measite.minidns.Client;
|
||||
import de.measite.minidns.DNSMessage;
|
||||
import de.measite.minidns.Question;
|
||||
import de.measite.minidns.Record;
|
||||
import de.measite.minidns.record.Data;
|
||||
import de.measite.minidns.record.TXT;
|
||||
import org.json.JSONObject;
|
||||
import org.spongycastle.openpgp.PGPUtil;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.KeybaseVerificationResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
|
||||
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificationParcel> {
|
||||
|
||||
public KeybaseVerificationOperation(Context context, ProviderHelper providerHelper,
|
||||
Progressable progressable) {
|
||||
super(context, providerHelper, progressable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeybaseVerificationResult execute(KeybaseVerificationParcel keybaseInput,
|
||||
CryptoInputParcel cryptoInput) {
|
||||
|
||||
String requiredFingerprint = keybaseInput.mRequiredFingerprint;
|
||||
|
||||
OperationResult.OperationLog log = new OperationResult.OperationLog();
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_VERIFICATION, 0, requiredFingerprint);
|
||||
|
||||
try {
|
||||
String keybaseProof = keybaseInput.mKeybaseProof;
|
||||
Proof proof = new Proof(new JSONObject(keybaseProof));
|
||||
mProgressable.setProgress(R.string.keybase_message_fetching_data, 0, 100);
|
||||
|
||||
Prover prover = Prover.findProverFor(proof);
|
||||
|
||||
if (prover == null) {
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_NO_PROVER, 1,
|
||||
proof.getPrettyName());
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
if (!prover.fetchProofData()) {
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_FETCH_PROOF, 1);
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
if (!prover.checkFingerprint(requiredFingerprint)) {
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_FINGERPRINT_MISMATCH, 1);
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
String domain = prover.dnsTxtCheckRequired();
|
||||
if (domain != null) {
|
||||
DNSMessage dnsQuery = new Client().query(new Question(domain, Record.TYPE.TXT));
|
||||
if (dnsQuery == null) {
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_DNS_FAIL, 1);
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 2,
|
||||
getFlattenedProverLog(prover));
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
Record[] records = dnsQuery.getAnswers();
|
||||
List<List<byte[]>> extents = new ArrayList<List<byte[]>>();
|
||||
for (Record r : records) {
|
||||
Data d = r.getPayload();
|
||||
if (d instanceof TXT) {
|
||||
extents.add(((TXT) d).getExtents());
|
||||
}
|
||||
}
|
||||
if (!prover.checkDnsTxt(extents)) {
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 1,
|
||||
getFlattenedProverLog(prover));
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
}
|
||||
|
||||
byte[] messageBytes = prover.getPgpMessage().getBytes();
|
||||
if (prover.rawMessageCheckRequired()) {
|
||||
InputStream messageByteStream = PGPUtil.getDecoderStream(new
|
||||
ByteArrayInputStream
|
||||
(messageBytes));
|
||||
if (!prover.checkRawMessageBytes(messageByteStream)) {
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 1,
|
||||
getFlattenedProverLog(prover));
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
}
|
||||
|
||||
PgpDecryptVerify op = new PgpDecryptVerify(mContext, mProviderHelper, mProgressable);
|
||||
|
||||
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(messageBytes)
|
||||
.setSignedLiteralData(true)
|
||||
.setRequiredSignerFingerprint(requiredFingerprint);
|
||||
|
||||
DecryptVerifyResult decryptVerifyResult = op.execute(input, new CryptoInputParcel());
|
||||
|
||||
if (!decryptVerifyResult.success()) {
|
||||
log.add(decryptVerifyResult, 1);
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
if (!prover.validate(new String(decryptVerifyResult.getOutputBytes()))) {
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_PAYLOAD_MISMATCH, 1);
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_OK, log, prover);
|
||||
} catch (Exception e) {
|
||||
// just adds the passed parameter, in this case e.getMessage()
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_SPECIFIC, 1, e.getMessage());
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
}
|
||||
|
||||
private String getFlattenedProverLog(Prover prover) {
|
||||
String log = "";
|
||||
for (String line : prover.getLog()) {
|
||||
log += line + "\n";
|
||||
}
|
||||
return log;
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,8 @@ import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
|
||||
import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
|
||||
@@ -45,13 +47,24 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
* without secret key material, using a GNU_DUMMY s2k type.
|
||||
*
|
||||
*/
|
||||
public class PromoteKeyOperation extends BaseOperation {
|
||||
public class PromoteKeyOperation extends BaseOperation<PromoteKeyringParcel> {
|
||||
|
||||
public PromoteKeyOperation(Context context, ProviderHelper providerHelper,
|
||||
Progressable progressable, AtomicBoolean cancelled) {
|
||||
super(context, providerHelper, progressable, cancelled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PromoteKeyResult execute(PromoteKeyringParcel promoteKeyringParcel,
|
||||
CryptoInputParcel cryptoInputParcel) {
|
||||
// Input
|
||||
long masterKeyId = promoteKeyringParcel.mKeyRingId;
|
||||
byte[] cardAid = promoteKeyringParcel.mCardAid;
|
||||
long[] subKeyIds = promoteKeyringParcel.mSubKeyIds;
|
||||
|
||||
return execute(masterKeyId, cardAid, subKeyIds);
|
||||
}
|
||||
|
||||
public PromoteKeyResult execute(long masterKeyId, byte[] cardAid, long[] subKeyIds) {
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
|
||||
*
|
||||
* 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.operations.results;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import com.textuality.keybase.lib.KeybaseException;
|
||||
import com.textuality.keybase.lib.prover.Prover;
|
||||
|
||||
public class KeybaseVerificationResult extends OperationResult implements Parcelable {
|
||||
public final String mProofUrl;
|
||||
public final String mPresenceUrl;
|
||||
public final String mPresenceLabel;
|
||||
|
||||
public KeybaseVerificationResult(int result, OperationLog log) {
|
||||
super(result, log);
|
||||
mProofUrl = null;
|
||||
mPresenceLabel = null;
|
||||
mPresenceUrl = null;
|
||||
}
|
||||
|
||||
public KeybaseVerificationResult(int result, OperationLog log, Prover prover)
|
||||
throws KeybaseException {
|
||||
super(result, log);
|
||||
mProofUrl = prover.getProofUrl();
|
||||
mPresenceUrl = prover.getPresenceUrl();
|
||||
mPresenceLabel = prover.getPresenceLabel();
|
||||
}
|
||||
|
||||
protected KeybaseVerificationResult(Parcel in) {
|
||||
super(in);
|
||||
mProofUrl = in.readString();
|
||||
mPresenceUrl = in.readString();
|
||||
mPresenceLabel = in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeString(mProofUrl);
|
||||
dest.writeString(mPresenceUrl);
|
||||
dest.writeString(mPresenceLabel);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<KeybaseVerificationResult> CREATOR = new Parcelable.Creator<KeybaseVerificationResult>() {
|
||||
@Override
|
||||
public KeybaseVerificationResult createFromParcel(Parcel in) {
|
||||
return new KeybaseVerificationResult(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeybaseVerificationResult[] newArray(int size) {
|
||||
return new KeybaseVerificationResult[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -750,7 +750,19 @@ public abstract class OperationResult implements Parcelable {
|
||||
MSG_DEL_OK (LogLevel.OK, R.plurals.msg_del_ok),
|
||||
MSG_DEL_FAIL (LogLevel.WARN, R.plurals.msg_del_fail),
|
||||
|
||||
//export log
|
||||
// keybase verification
|
||||
MSG_KEYBASE_VERIFICATION(LogLevel.START, R.string.msg_keybase_verification),
|
||||
|
||||
MSG_KEYBASE_ERROR_NO_PROVER(LogLevel.ERROR, R.string.msg_keybase_error_no_prover),
|
||||
MSG_KEYBASE_ERROR_FETCH_PROOF(LogLevel.ERROR, R.string.msg_keybase_error_fetching_evidence),
|
||||
MSG_KEYBASE_ERROR_FINGERPRINT_MISMATCH(LogLevel.ERROR,
|
||||
R.string.msg_keybase_error_key_mismatch),
|
||||
MSG_KEYBASE_ERROR_DNS_FAIL(LogLevel.ERROR, R.string.msg_keybase_error_dns_fail),
|
||||
MSG_KEYBASE_ERROR_SPECIFIC(LogLevel.ERROR, R.string.msg_keybase_error_specific),
|
||||
MSG_KEYBASE_ERROR_PAYLOAD_MISMATCH(LogLevel.ERROR,
|
||||
R.string.msg_keybase_error_msg_payload_mismatch),
|
||||
|
||||
// export log
|
||||
MSG_EXPORT_LOG(LogLevel.START,R.string.msg_export_log_start),
|
||||
MSG_EXPORT_LOG_EXPORT_ERROR_NO_FILE(LogLevel.ERROR,R.string.msg_export_log_error_no_file),
|
||||
MSG_EXPORT_LOG_EXPORT_ERROR_FOPEN(LogLevel.ERROR,R.string.msg_export_log_error_fopen),
|
||||
|
||||
@@ -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,9 +1248,9 @@ 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))
|
||||
.importKeyRings(itSecrets, numSecrets, null);
|
||||
.serialKeyRingImport(itSecrets, numSecrets, null);
|
||||
log.add(result, indent);
|
||||
} else {
|
||||
log.add(LogType.MSG_CON_REIMPORT_SECRET_SKIP, indent);
|
||||
@@ -1276,9 +1276,9 @@ 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))
|
||||
.importKeyRings(itPublics, numPublics, null);
|
||||
.serialKeyRingImport(itPublics, numPublics, null);
|
||||
log.add(result, indent);
|
||||
} else {
|
||||
log.add(LogType.MSG_CON_REIMPORT_PUBLIC_SKIP, indent);
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
|
||||
*
|
||||
* 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.service;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class ConsolidateInputParcel implements Parcelable {
|
||||
|
||||
public boolean mConsolidateRecovery;
|
||||
|
||||
public ConsolidateInputParcel(boolean consolidateRecovery) {
|
||||
mConsolidateRecovery = consolidateRecovery;
|
||||
}
|
||||
|
||||
protected ConsolidateInputParcel(Parcel in) {
|
||||
mConsolidateRecovery = in.readByte() != 0x00;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeByte((byte) (mConsolidateRecovery ? 0x01 : 0x00));
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<ConsolidateInputParcel> CREATOR = new Parcelable.Creator<ConsolidateInputParcel>() {
|
||||
@Override
|
||||
public ConsolidateInputParcel createFromParcel(Parcel in) {
|
||||
return new ConsolidateInputParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsolidateInputParcel[] newArray(int size) {
|
||||
return new ConsolidateInputParcel[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
|
||||
*
|
||||
* 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.service;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class DeleteKeyringParcel implements Parcelable {
|
||||
|
||||
public long[] mMasterKeyIds;
|
||||
public boolean mIsSecret;
|
||||
|
||||
public DeleteKeyringParcel(long[] masterKeyIds, boolean isSecret) {
|
||||
mMasterKeyIds = masterKeyIds;
|
||||
mIsSecret = isSecret;
|
||||
}
|
||||
|
||||
protected DeleteKeyringParcel(Parcel in) {
|
||||
mIsSecret = in.readByte() != 0x00;
|
||||
mMasterKeyIds = in.createLongArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeByte((byte) (mIsSecret ? 0x01 : 0x00));
|
||||
dest.writeLongArray(mMasterKeyIds);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<DeleteKeyringParcel> CREATOR = new Parcelable.Creator<DeleteKeyringParcel>() {
|
||||
@Override
|
||||
public DeleteKeyringParcel createFromParcel(Parcel in) {
|
||||
return new DeleteKeyringParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteKeyringParcel[] newArray(int size) {
|
||||
return new DeleteKeyringParcel[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
|
||||
*
|
||||
* 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.service;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class ExportKeyringParcel implements Parcelable {
|
||||
public String mKeyserver;
|
||||
public Uri mCanonicalizedPublicKeyringUri;
|
||||
|
||||
public boolean mExportSecret;
|
||||
public long mMasterKeyIds[];
|
||||
public String mOutputFile;
|
||||
public Uri mOutputUri;
|
||||
public ExportType mExportType;
|
||||
|
||||
public enum ExportType {
|
||||
UPLOAD_KEYSERVER,
|
||||
EXPORT_FILE,
|
||||
EXPORT_URI
|
||||
}
|
||||
|
||||
public ExportKeyringParcel(String keyserver, Uri keyringUri) {
|
||||
mExportType = ExportType.UPLOAD_KEYSERVER;
|
||||
mKeyserver = keyserver;
|
||||
mCanonicalizedPublicKeyringUri = keyringUri;
|
||||
}
|
||||
|
||||
public ExportKeyringParcel(long[] masterKeyIds, boolean exportSecret, String outputFile) {
|
||||
mExportType = ExportType.EXPORT_FILE;
|
||||
mMasterKeyIds = masterKeyIds;
|
||||
mExportSecret = exportSecret;
|
||||
mOutputFile = outputFile;
|
||||
}
|
||||
|
||||
public ExportKeyringParcel(long[] masterKeyIds, boolean exportSecret, Uri outputUri) {
|
||||
mExportType = ExportType.EXPORT_URI;
|
||||
mMasterKeyIds = masterKeyIds;
|
||||
mExportSecret = exportSecret;
|
||||
mOutputUri = outputUri;
|
||||
}
|
||||
|
||||
protected ExportKeyringParcel(Parcel in) {
|
||||
mKeyserver = in.readString();
|
||||
mCanonicalizedPublicKeyringUri = (Uri) in.readValue(Uri.class.getClassLoader());
|
||||
mExportSecret = in.readByte() != 0x00;
|
||||
mOutputFile = in.readString();
|
||||
mOutputUri = (Uri) in.readValue(Uri.class.getClassLoader());
|
||||
mExportType = (ExportType) in.readValue(ExportType.class.getClassLoader());
|
||||
mMasterKeyIds = in.createLongArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(mKeyserver);
|
||||
dest.writeValue(mCanonicalizedPublicKeyringUri);
|
||||
dest.writeByte((byte) (mExportSecret ? 0x01 : 0x00));
|
||||
dest.writeString(mOutputFile);
|
||||
dest.writeValue(mOutputUri);
|
||||
dest.writeValue(mExportType);
|
||||
dest.writeLongArray(mMasterKeyIds);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<ExportKeyringParcel> CREATOR = new Parcelable.Creator<ExportKeyringParcel>() {
|
||||
@Override
|
||||
public ExportKeyringParcel createFromParcel(Parcel in) {
|
||||
return new ExportKeyringParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExportKeyringParcel[] newArray(int size) {
|
||||
return new ExportKeyringParcel[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
|
||||
*
|
||||
* 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.service;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ImportKeyringParcel implements Parcelable {
|
||||
// if null, keys are expected to be read from a cache file in ImportExportOperations
|
||||
public ArrayList<ParcelableKeyRing> mKeyList;
|
||||
public String mKeyserver; // must be set if keys are to be imported from a keyserver
|
||||
|
||||
public ImportKeyringParcel (ArrayList<ParcelableKeyRing> keyList, String keyserver) {
|
||||
mKeyList = keyList;
|
||||
mKeyserver = keyserver;
|
||||
}
|
||||
|
||||
protected ImportKeyringParcel(Parcel in) {
|
||||
if (in.readByte() == 0x01) {
|
||||
mKeyList = new ArrayList<>();
|
||||
in.readList(mKeyList, ParcelableKeyRing.class.getClassLoader());
|
||||
} else {
|
||||
mKeyList = null;
|
||||
}
|
||||
mKeyserver = in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
if (mKeyList == null) {
|
||||
dest.writeByte((byte) (0x00));
|
||||
} else {
|
||||
dest.writeByte((byte) (0x01));
|
||||
dest.writeList(mKeyList);
|
||||
}
|
||||
dest.writeString(mKeyserver);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<ImportKeyringParcel> CREATOR = new Parcelable.Creator<ImportKeyringParcel>() {
|
||||
@Override
|
||||
public ImportKeyringParcel createFromParcel(Parcel in) {
|
||||
return new ImportKeyringParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportKeyringParcel[] newArray(int size) {
|
||||
return new ImportKeyringParcel[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
|
||||
*
|
||||
* 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.service;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class KeybaseVerificationParcel implements Parcelable {
|
||||
|
||||
public String mKeybaseProof;
|
||||
public String mRequiredFingerprint;
|
||||
|
||||
public KeybaseVerificationParcel(String keybaseProof, String requiredFingerprint) {
|
||||
mKeybaseProof = keybaseProof;
|
||||
mRequiredFingerprint = requiredFingerprint;
|
||||
}
|
||||
|
||||
protected KeybaseVerificationParcel(Parcel in) {
|
||||
mKeybaseProof = in.readString();
|
||||
mRequiredFingerprint = in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(mKeybaseProof);
|
||||
dest.writeString(mRequiredFingerprint);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<KeybaseVerificationParcel> CREATOR = new Parcelable.Creator<KeybaseVerificationParcel>() {
|
||||
@Override
|
||||
public KeybaseVerificationParcel createFromParcel(Parcel in) {
|
||||
return new KeybaseVerificationParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeybaseVerificationParcel[] newArray(int size) {
|
||||
return new KeybaseVerificationParcel[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
*
|
||||
* 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.service;
|
||||
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.operations.BaseOperation;
|
||||
import org.sufficientlysecure.keychain.operations.CertifyOperation;
|
||||
import org.sufficientlysecure.keychain.operations.EditKeyOperation;
|
||||
import org.sufficientlysecure.keychain.operations.SignEncryptOperation;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
|
||||
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
/**
|
||||
* This Service contains all important long lasting operations for OpenKeychain. It receives Intents with
|
||||
* data from the activities or other apps, executes them, and stops itself after doing them.
|
||||
*/
|
||||
public class KeychainNewService extends Service implements Progressable {
|
||||
|
||||
// messenger for communication (hack)
|
||||
public static final String EXTRA_MESSENGER = "messenger";
|
||||
|
||||
// extras for operation
|
||||
public static final String EXTRA_OPERATION_INPUT = "op_input";
|
||||
public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
|
||||
|
||||
// this attribute can possibly merged with the one above? not sure...
|
||||
private AtomicBoolean mActionCanceled = new AtomicBoolean(false);
|
||||
|
||||
ThreadLocal<Messenger> mMessenger = new ThreadLocal<>();
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is run on the main thread, we need to spawn a runnable which runs on another thread for the actual operation
|
||||
*/
|
||||
@Override
|
||||
public int onStartCommand(final Intent intent, int flags, int startId) {
|
||||
|
||||
Runnable actionRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// We have not been cancelled! (yet)
|
||||
mActionCanceled.set(false);
|
||||
|
||||
Bundle extras = intent.getExtras();
|
||||
|
||||
// Set messenger for communication (for this particular thread)
|
||||
mMessenger.set(extras.<Messenger>getParcelable(EXTRA_MESSENGER));
|
||||
|
||||
// Input
|
||||
Parcelable inputParcel = extras.getParcelable(EXTRA_OPERATION_INPUT);
|
||||
CryptoInputParcel cryptoInput = extras.getParcelable(EXTRA_CRYPTO_INPUT);
|
||||
|
||||
// Operation
|
||||
BaseOperation op;
|
||||
|
||||
// just for brevity
|
||||
KeychainNewService outerThis = KeychainNewService.this;
|
||||
if (inputParcel instanceof SignEncryptParcel) {
|
||||
op = new SignEncryptOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled);
|
||||
} else if (inputParcel instanceof PgpDecryptVerifyInputParcel) {
|
||||
op = new PgpDecryptVerify(outerThis, new ProviderHelper(outerThis), outerThis);
|
||||
} else if (inputParcel instanceof SaveKeyringParcel) {
|
||||
op = new EditKeyOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled);
|
||||
} else if (inputParcel instanceof CertifyAction) {
|
||||
op = new CertifyOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // this is unchecked, we make sure it's the correct op above!
|
||||
OperationResult result = op.execute(inputParcel, cryptoInput);
|
||||
|
||||
sendMessageToHandler(MessageStatus.OKAY, result);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
Thread actionThread = new Thread(actionRunnable);
|
||||
actionThread.start();
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
private void sendMessageToHandler(MessageStatus status, Integer arg2, Bundle data) {
|
||||
|
||||
Message msg = Message.obtain();
|
||||
assert msg != null;
|
||||
msg.arg1 = status.ordinal();
|
||||
if (arg2 != null) {
|
||||
msg.arg2 = arg2;
|
||||
}
|
||||
if (data != null) {
|
||||
msg.setData(data);
|
||||
}
|
||||
|
||||
try {
|
||||
mMessenger.get().send(msg);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
|
||||
} catch (NullPointerException e) {
|
||||
Log.w(Constants.TAG, "Messenger is null!", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessageToHandler(MessageStatus status, OperationResult data) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(OperationResult.EXTRA_RESULT, data);
|
||||
sendMessageToHandler(status, null, bundle);
|
||||
}
|
||||
|
||||
private void sendMessageToHandler(MessageStatus status) {
|
||||
sendMessageToHandler(status, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set progress of ProgressDialog by sending message to handler on UI thread
|
||||
*/
|
||||
@Override
|
||||
public void setProgress(String message, int progress, int max) {
|
||||
Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max="
|
||||
+ max);
|
||||
|
||||
Bundle data = new Bundle();
|
||||
if (message != null) {
|
||||
data.putString(ServiceProgressHandler.DATA_MESSAGE, message);
|
||||
}
|
||||
data.putInt(ServiceProgressHandler.DATA_PROGRESS, progress);
|
||||
data.putInt(ServiceProgressHandler.DATA_PROGRESS_MAX, max);
|
||||
|
||||
sendMessageToHandler(MessageStatus.UPDATE_PROGRESS, null, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgress(int resourceId, int progress, int max) {
|
||||
setProgress(getString(resourceId), progress, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgress(int progress, int max) {
|
||||
setProgress(null, progress, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreventCancel() {
|
||||
sendMessageToHandler(MessageStatus.PREVENT_CANCEL);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,146 +20,55 @@ package org.sufficientlysecure.keychain.service;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import com.textuality.keybase.lib.Proof;
|
||||
import com.textuality.keybase.lib.prover.Prover;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.spongycastle.openpgp.PGPUtil;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
|
||||
import org.sufficientlysecure.keychain.keyimport.Keyserver;
|
||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||
import org.sufficientlysecure.keychain.operations.BaseOperation;
|
||||
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;
|
||||
import org.sufficientlysecure.keychain.operations.results.CertifyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.DeleteResult;
|
||||
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.OperationLog;
|
||||
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
|
||||
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||
import org.sufficientlysecure.keychain.pgp.SignEncryptParcel;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import de.measite.minidns.Client;
|
||||
import de.measite.minidns.DNSMessage;
|
||||
import de.measite.minidns.Question;
|
||||
import de.measite.minidns.Record;
|
||||
import de.measite.minidns.record.Data;
|
||||
import de.measite.minidns.record.TXT;
|
||||
|
||||
/**
|
||||
* This Service contains all important long lasting operations for OpenKeychain. It receives Intents with
|
||||
* data from the activities or other apps, executes them, and stops itself after doing them.
|
||||
*/
|
||||
public class KeychainService extends Service implements Progressable {
|
||||
|
||||
/* extras that can be given by intent */
|
||||
// messenger for communication (hack)
|
||||
public static final String EXTRA_MESSENGER = "messenger";
|
||||
public static final String EXTRA_DATA = "data";
|
||||
|
||||
/* possible actions */
|
||||
|
||||
public static final String ACTION_VERIFY_KEYBASE_PROOF = Constants.INTENT_PREFIX + "VERIFY_KEYBASE_PROOF";
|
||||
|
||||
public static final String ACTION_EDIT_KEYRING = Constants.INTENT_PREFIX + "EDIT_KEYRING";
|
||||
|
||||
public static final String ACTION_PROMOTE_KEYRING = Constants.INTENT_PREFIX + "PROMOTE_KEYRING";
|
||||
|
||||
public static final String ACTION_IMPORT_KEYRING = Constants.INTENT_PREFIX + "IMPORT_KEYRING";
|
||||
public static final String ACTION_EXPORT_KEYRING = Constants.INTENT_PREFIX + "EXPORT_KEYRING";
|
||||
|
||||
public static final String ACTION_UPLOAD_KEYRING = Constants.INTENT_PREFIX + "UPLOAD_KEYRING";
|
||||
|
||||
public static final String ACTION_DELETE = Constants.INTENT_PREFIX + "DELETE";
|
||||
|
||||
public static final String ACTION_CONSOLIDATE = Constants.INTENT_PREFIX + "CONSOLIDATE";
|
||||
|
||||
public static final String ACTION_CANCEL = Constants.INTENT_PREFIX + "CANCEL";
|
||||
|
||||
/* keys for data bundle */
|
||||
|
||||
// keybase proof
|
||||
public static final String KEYBASE_REQUIRED_FINGERPRINT = "keybase_required_fingerprint";
|
||||
public static final String KEYBASE_PROOF = "keybase_proof";
|
||||
|
||||
// save keyring
|
||||
public static final String EDIT_KEYRING_PARCEL = "save_parcel";
|
||||
public static final String EDIT_KEYRING_PASSPHRASE = "passphrase";
|
||||
// extras for operation
|
||||
public static final String EXTRA_OPERATION_INPUT = "op_input";
|
||||
public static final String EXTRA_CRYPTO_INPUT = "crypto_input";
|
||||
|
||||
// delete keyring(s)
|
||||
public static final String DELETE_KEY_LIST = "delete_list";
|
||||
public static final String DELETE_IS_SECRET = "delete_is_secret";
|
||||
|
||||
// import key
|
||||
public static final String IMPORT_KEY_LIST = "import_key_list";
|
||||
public static final String IMPORT_KEY_SERVER = "import_key_server";
|
||||
|
||||
// export key
|
||||
public static final String EXPORT_FILENAME = "export_filename";
|
||||
public static final String EXPORT_URI = "export_uri";
|
||||
public static final String EXPORT_SECRET = "export_secret";
|
||||
public static final String EXPORT_ALL = "export_all";
|
||||
public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "export_key_ring_id";
|
||||
|
||||
// upload key
|
||||
public static final String UPLOAD_KEY_SERVER = "upload_key_server";
|
||||
|
||||
// promote key
|
||||
public static final String PROMOTE_MASTER_KEY_ID = "promote_master_key_id";
|
||||
public static final String PROMOTE_CARD_AID = "promote_card_aid";
|
||||
public static final String PROMOTE_SUBKEY_IDS = "promote_fingerprints";
|
||||
|
||||
// consolidate
|
||||
public static final String CONSOLIDATE_RECOVERY = "consolidate_recovery";
|
||||
|
||||
Messenger mMessenger;
|
||||
public static final String ACTION_CANCEL = "action_cancel";
|
||||
|
||||
// this attribute can possibly merged with the one above? not sure...
|
||||
private AtomicBoolean mActionCanceled = new AtomicBoolean(false);
|
||||
|
||||
|
||||
private KeyImportAccumulator mKeyImportAccumulator;
|
||||
|
||||
private KeychainService mKeychainService;
|
||||
ThreadLocal<Messenger> mMessenger = new ThreadLocal<>();
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
@@ -171,9 +80,8 @@ public class KeychainService extends Service implements Progressable {
|
||||
*/
|
||||
@Override
|
||||
public int onStartCommand(final Intent intent, int flags, int startId) {
|
||||
mKeychainService = this;
|
||||
|
||||
if (ACTION_CANCEL.equals(intent.getAction())) {
|
||||
if (intent.getAction() != null && intent.getAction().equals(ACTION_CANCEL)) {
|
||||
mActionCanceled.set(true);
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
@@ -185,317 +93,64 @@ public class KeychainService extends Service implements Progressable {
|
||||
mActionCanceled.set(false);
|
||||
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras == null) {
|
||||
Log.e(Constants.TAG, "Extras bundle is null!");
|
||||
|
||||
// Set messenger for communication (for this particular thread)
|
||||
mMessenger.set(extras.<Messenger>getParcelable(EXTRA_MESSENGER));
|
||||
|
||||
// Input
|
||||
Parcelable inputParcel = extras.getParcelable(EXTRA_OPERATION_INPUT);
|
||||
CryptoInputParcel cryptoInput = extras.getParcelable(EXTRA_CRYPTO_INPUT);
|
||||
|
||||
// Operation
|
||||
BaseOperation op;
|
||||
|
||||
// just for brevity
|
||||
KeychainService outerThis = KeychainService.this;
|
||||
if (inputParcel instanceof SignEncryptParcel) {
|
||||
op = new SignEncryptOperation(outerThis, new ProviderHelper(outerThis),
|
||||
outerThis, mActionCanceled);
|
||||
} else if (inputParcel instanceof PgpDecryptVerifyInputParcel) {
|
||||
op = new PgpDecryptVerify(outerThis, new ProviderHelper(outerThis), outerThis);
|
||||
} else if (inputParcel instanceof SaveKeyringParcel) {
|
||||
op = new EditKeyOperation(outerThis, new ProviderHelper(outerThis), outerThis,
|
||||
mActionCanceled);
|
||||
} else if (inputParcel instanceof CertifyActionsParcel) {
|
||||
op = new CertifyOperation(outerThis, new ProviderHelper(outerThis), outerThis,
|
||||
mActionCanceled);
|
||||
} else if (inputParcel instanceof DeleteKeyringParcel) {
|
||||
op = new DeleteOperation(outerThis, new ProviderHelper(outerThis), outerThis);
|
||||
} else if (inputParcel instanceof PromoteKeyringParcel) {
|
||||
op = new PromoteKeyOperation(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);
|
||||
} else if (inputParcel instanceof KeybaseVerificationParcel) {
|
||||
op = new KeybaseVerificationOperation(outerThis, new ProviderHelper(outerThis),
|
||||
outerThis);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(extras.containsKey(EXTRA_MESSENGER) || extras.containsKey(EXTRA_DATA) || (intent
|
||||
.getAction() == null))) {
|
||||
Log.e(Constants.TAG,
|
||||
"Extra bundle must contain a messenger, a data bundle, and an action!");
|
||||
return;
|
||||
}
|
||||
@SuppressWarnings("unchecked") // this is unchecked, we make sure it's the correct op above!
|
||||
OperationResult result = op.execute(inputParcel, cryptoInput);
|
||||
sendMessageToHandler(MessageStatus.OKAY, result);
|
||||
|
||||
Uri dataUri = intent.getData();
|
||||
|
||||
mMessenger = (Messenger) extras.get(EXTRA_MESSENGER);
|
||||
Bundle data = extras.getBundle(EXTRA_DATA);
|
||||
if (data == null) {
|
||||
Log.e(Constants.TAG, "data extra is null!");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.logDebugBundle(data, "EXTRA_DATA");
|
||||
|
||||
ProviderHelper providerHelper = new ProviderHelper(mKeychainService);
|
||||
|
||||
String action = intent.getAction();
|
||||
|
||||
// executeServiceMethod action from extra bundle
|
||||
switch (action) {
|
||||
case ACTION_CONSOLIDATE: {
|
||||
|
||||
// Operation
|
||||
ConsolidateResult result;
|
||||
if (data.containsKey(CONSOLIDATE_RECOVERY) && data.getBoolean(CONSOLIDATE_RECOVERY)) {
|
||||
result = providerHelper.consolidateDatabaseStep2(mKeychainService);
|
||||
} else {
|
||||
result = providerHelper.consolidateDatabaseStep1(mKeychainService);
|
||||
}
|
||||
|
||||
// Result
|
||||
sendMessageToHandler(MessageStatus.OKAY, result);
|
||||
|
||||
break;
|
||||
}
|
||||
case ACTION_VERIFY_KEYBASE_PROOF: {
|
||||
|
||||
try {
|
||||
Proof proof = new Proof(new JSONObject(data.getString(KEYBASE_PROOF)));
|
||||
setProgress(R.string.keybase_message_fetching_data, 0, 100);
|
||||
|
||||
Prover prover = Prover.findProverFor(proof);
|
||||
|
||||
if (prover == null) {
|
||||
sendProofError(getString(R.string.keybase_no_prover_found) + ": " + proof
|
||||
.getPrettyName());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!prover.fetchProofData()) {
|
||||
sendProofError(prover.getLog(), getString(R.string.keybase_problem_fetching_evidence));
|
||||
return;
|
||||
}
|
||||
String requiredFingerprint = data.getString(KEYBASE_REQUIRED_FINGERPRINT);
|
||||
if (!prover.checkFingerprint(requiredFingerprint)) {
|
||||
sendProofError(getString(R.string.keybase_key_mismatch));
|
||||
return;
|
||||
}
|
||||
|
||||
String domain = prover.dnsTxtCheckRequired();
|
||||
if (domain != null) {
|
||||
DNSMessage dnsQuery = new Client().query(new Question(domain, Record.TYPE.TXT));
|
||||
if (dnsQuery == null) {
|
||||
sendProofError(prover.getLog(), getString(R.string.keybase_dns_query_failure));
|
||||
return;
|
||||
}
|
||||
Record[] records = dnsQuery.getAnswers();
|
||||
List<List<byte[]>> extents = new ArrayList<List<byte[]>>();
|
||||
for (Record r : records) {
|
||||
Data d = r.getPayload();
|
||||
if (d instanceof TXT) {
|
||||
extents.add(((TXT) d).getExtents());
|
||||
}
|
||||
}
|
||||
if (!prover.checkDnsTxt(extents)) {
|
||||
sendProofError(prover.getLog(), null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] messageBytes = prover.getPgpMessage().getBytes();
|
||||
if (prover.rawMessageCheckRequired()) {
|
||||
InputStream messageByteStream = PGPUtil.getDecoderStream(new ByteArrayInputStream
|
||||
(messageBytes));
|
||||
if (!prover.checkRawMessageBytes(messageByteStream)) {
|
||||
sendProofError(prover.getLog(), null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
PgpDecryptVerify op = new PgpDecryptVerify(mKeychainService, providerHelper,
|
||||
mKeychainService);
|
||||
|
||||
PgpDecryptVerifyInputParcel input = new PgpDecryptVerifyInputParcel(messageBytes)
|
||||
.setSignedLiteralData(true)
|
||||
.setRequiredSignerFingerprint(requiredFingerprint);
|
||||
|
||||
DecryptVerifyResult decryptVerifyResult = op.execute(input, new CryptoInputParcel());
|
||||
|
||||
if (!decryptVerifyResult.success()) {
|
||||
OperationLog log = decryptVerifyResult.getLog();
|
||||
OperationResult.LogEntryParcel lastEntry = null;
|
||||
for (OperationResult.LogEntryParcel entry : log) {
|
||||
lastEntry = entry;
|
||||
}
|
||||
sendProofError(getString(lastEntry.mType.getMsgId()));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!prover.validate(new String(decryptVerifyResult.getOutputBytes()))) {
|
||||
sendProofError(getString(R.string.keybase_message_payload_mismatch));
|
||||
return;
|
||||
}
|
||||
|
||||
Bundle resultData = new Bundle();
|
||||
resultData.putString(ServiceProgressHandler.DATA_MESSAGE, "OK");
|
||||
|
||||
// these help the handler construct a useful human-readable message
|
||||
resultData.putString(ServiceProgressHandler.KEYBASE_PROOF_URL, prover.getProofUrl());
|
||||
resultData.putString(ServiceProgressHandler.KEYBASE_PRESENCE_URL, prover.getPresenceUrl());
|
||||
resultData.putString(ServiceProgressHandler.KEYBASE_PRESENCE_LABEL, prover
|
||||
.getPresenceLabel());
|
||||
sendMessageToHandler(MessageStatus.OKAY, resultData);
|
||||
} catch (Exception e) {
|
||||
sendErrorToHandler(e);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ACTION_DELETE: {
|
||||
|
||||
// Input
|
||||
long[] masterKeyIds = data.getLongArray(DELETE_KEY_LIST);
|
||||
boolean isSecret = data.getBoolean(DELETE_IS_SECRET);
|
||||
|
||||
// Operation
|
||||
DeleteOperation op = new DeleteOperation(mKeychainService, providerHelper, mKeychainService);
|
||||
DeleteResult result = op.execute(masterKeyIds, isSecret);
|
||||
|
||||
// Result
|
||||
sendMessageToHandler(MessageStatus.OKAY, result);
|
||||
|
||||
break;
|
||||
}
|
||||
case ACTION_EDIT_KEYRING: {
|
||||
|
||||
// Input
|
||||
SaveKeyringParcel saveParcel = data.getParcelable(EDIT_KEYRING_PARCEL);
|
||||
CryptoInputParcel cryptoInput = data.getParcelable(EXTRA_CRYPTO_INPUT);
|
||||
|
||||
// Operation
|
||||
EditKeyOperation op = new EditKeyOperation(mKeychainService, providerHelper,
|
||||
mKeychainService, mActionCanceled);
|
||||
OperationResult result = op.execute(saveParcel, cryptoInput);
|
||||
|
||||
// Result
|
||||
sendMessageToHandler(MessageStatus.OKAY, result);
|
||||
|
||||
break;
|
||||
}
|
||||
case ACTION_PROMOTE_KEYRING: {
|
||||
|
||||
// Input
|
||||
long keyRingId = data.getLong(PROMOTE_MASTER_KEY_ID);
|
||||
byte[] cardAid = data.getByteArray(PROMOTE_CARD_AID);
|
||||
long[] subKeyIds = data.getLongArray(PROMOTE_SUBKEY_IDS);
|
||||
|
||||
// Operation
|
||||
PromoteKeyOperation op = new PromoteKeyOperation(
|
||||
mKeychainService, providerHelper, mKeychainService,
|
||||
mActionCanceled);
|
||||
PromoteKeyResult result = op.execute(keyRingId, cardAid, subKeyIds);
|
||||
|
||||
// Result
|
||||
sendMessageToHandler(MessageStatus.OKAY, result);
|
||||
|
||||
break;
|
||||
}
|
||||
case ACTION_EXPORT_KEYRING: {
|
||||
|
||||
// Input
|
||||
boolean exportSecret = data.getBoolean(EXPORT_SECRET, false);
|
||||
String outputFile = data.getString(EXPORT_FILENAME);
|
||||
Uri outputUri = data.getParcelable(EXPORT_URI);
|
||||
|
||||
boolean exportAll = data.getBoolean(EXPORT_ALL);
|
||||
long[] masterKeyIds = exportAll ? null : data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID);
|
||||
|
||||
// Operation
|
||||
ImportExportOperation importExportOperation = new ImportExportOperation(
|
||||
mKeychainService, providerHelper, mKeychainService);
|
||||
ExportResult result;
|
||||
if (outputFile != null) {
|
||||
result = importExportOperation.exportToFile(masterKeyIds, exportSecret, outputFile);
|
||||
} else {
|
||||
result = importExportOperation.exportToUri(masterKeyIds, exportSecret, outputUri);
|
||||
}
|
||||
|
||||
// Result
|
||||
sendMessageToHandler(MessageStatus.OKAY, result);
|
||||
|
||||
break;
|
||||
}
|
||||
case ACTION_IMPORT_KEYRING: {
|
||||
|
||||
// Input
|
||||
String keyServer = data.getString(IMPORT_KEY_SERVER);
|
||||
ArrayList<ParcelableKeyRing> keyList = data.getParcelableArrayList(IMPORT_KEY_LIST);
|
||||
|
||||
// either keyList or cache must be null, no guarantees otherwise
|
||||
if (keyList == null) {// import from file, do serially
|
||||
serialKeyImport(null, keyServer, providerHelper);
|
||||
} else {
|
||||
// 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<String> keyFingerprintSet = new HashSet<>();
|
||||
for (int i = 0; i < keyList.size(); i++) {
|
||||
keyFingerprintSet.add(keyList.get(i).mExpectedFingerprint);
|
||||
}
|
||||
if (keyFingerprintSet.size() == keyList.size()) {
|
||||
// all keys have unique fingerprints
|
||||
multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer);
|
||||
} else {
|
||||
serialKeyImport(keyList, keyServer, providerHelper);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ACTION_UPLOAD_KEYRING: {
|
||||
try {
|
||||
|
||||
// Input
|
||||
String keyServer = data.getString(UPLOAD_KEY_SERVER);
|
||||
// and dataUri!
|
||||
|
||||
// Operation
|
||||
HkpKeyserver server = new HkpKeyserver(keyServer);
|
||||
|
||||
CanonicalizedPublicKeyRing keyring = providerHelper.getCanonicalizedPublicKeyRing(dataUri);
|
||||
ImportExportOperation importExportOperation = new ImportExportOperation(mKeychainService,
|
||||
providerHelper, mKeychainService);
|
||||
|
||||
try {
|
||||
importExportOperation.uploadKeyRingToServer(server, keyring);
|
||||
} catch (Keyserver.AddKeyException e) {
|
||||
throw new PgpGeneralException("Unable to export key to selected server");
|
||||
}
|
||||
|
||||
sendMessageToHandler(MessageStatus.OKAY);
|
||||
} catch (Exception e) {
|
||||
sendErrorToHandler(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!intent.getAction().equals(ACTION_IMPORT_KEYRING)) {
|
||||
// import keyring handles stopping service on its own
|
||||
stopSelf();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Thread actionThread = new Thread(actionRunnable);
|
||||
actionThread.start();
|
||||
|
||||
|
||||
return START_NOT_STICKY;
|
||||
}
|
||||
|
||||
private void sendProofError(List<String> log, String label) {
|
||||
String msg = null;
|
||||
label = (label == null) ? "" : label + ": ";
|
||||
for (String m : log) {
|
||||
Log.e(Constants.TAG, label + m);
|
||||
msg = m;
|
||||
}
|
||||
sendProofError(label + msg);
|
||||
}
|
||||
|
||||
private void sendProofError(String msg) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(ServiceProgressHandler.DATA_ERROR, msg);
|
||||
sendMessageToHandler(MessageStatus.OKAY, bundle);
|
||||
}
|
||||
|
||||
private void sendErrorToHandler(Exception e) {
|
||||
// TODO: Implement a better exception handling here
|
||||
// contextualize the exception, if necessary
|
||||
String message;
|
||||
if (e instanceof PgpGeneralMsgIdException) {
|
||||
e = ((PgpGeneralMsgIdException) e).getContextualized(mKeychainService);
|
||||
message = e.getMessage();
|
||||
} else {
|
||||
message = e.getMessage();
|
||||
}
|
||||
Log.d(Constants.TAG, "KeychainService Exception: ", e);
|
||||
|
||||
Bundle data = new Bundle();
|
||||
data.putString(ServiceProgressHandler.DATA_ERROR, message);
|
||||
sendMessageToHandler(MessageStatus.EXCEPTION, null, data);
|
||||
}
|
||||
|
||||
private void sendMessageToHandler(MessageStatus status, Integer arg2, Bundle data) {
|
||||
|
||||
Message msg = Message.obtain();
|
||||
@@ -509,7 +164,7 @@ public class KeychainService extends Service implements Progressable {
|
||||
}
|
||||
|
||||
try {
|
||||
mMessenger.send(msg);
|
||||
mMessenger.get().send(msg);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
|
||||
} catch (NullPointerException e) {
|
||||
@@ -523,10 +178,6 @@ public class KeychainService extends Service implements Progressable {
|
||||
sendMessageToHandler(status, null, bundle);
|
||||
}
|
||||
|
||||
private void sendMessageToHandler(MessageStatus status, Bundle data) {
|
||||
sendMessageToHandler(status, null, data);
|
||||
}
|
||||
|
||||
private void sendMessageToHandler(MessageStatus status) {
|
||||
sendMessageToHandler(status, null, null);
|
||||
}
|
||||
@@ -564,214 +215,4 @@ public class KeychainService extends Service implements Progressable {
|
||||
sendMessageToHandler(MessageStatus.PREVENT_CANCEL);
|
||||
}
|
||||
|
||||
public void serialKeyImport(ArrayList<ParcelableKeyRing> keyList, final String keyServer,
|
||||
ProviderHelper providerHelper) {
|
||||
Log.d(Constants.TAG, "serial key import starting");
|
||||
ParcelableFileCache<ParcelableKeyRing> cache =
|
||||
new ParcelableFileCache<>(mKeychainService, "key_import.pcl");
|
||||
|
||||
// Operation
|
||||
ImportExportOperation importExportOperation = new ImportExportOperation(
|
||||
mKeychainService, providerHelper, mKeychainService,
|
||||
mActionCanceled);
|
||||
// Either list or cache must be null, no guarantees otherwise.
|
||||
ImportKeyResult result = keyList != null
|
||||
? importExportOperation.importKeyRings(keyList, keyServer)
|
||||
: importExportOperation.importKeyRings(cache, keyServer);
|
||||
|
||||
ContactSyncAdapterService.requestSync();
|
||||
// Result
|
||||
sendMessageToHandler(MessageStatus.OKAY, result);
|
||||
|
||||
stopSelf();
|
||||
}
|
||||
|
||||
public void multiThreadedKeyImport(Iterator<ParcelableKeyRing> keyListIterator, int totKeys, final String
|
||||
keyServer) {
|
||||
Log.d(Constants.TAG, "Multi-threaded key import starting");
|
||||
if (keyListIterator != null) {
|
||||
mKeyImportAccumulator = new KeyImportAccumulator(totKeys, mKeychainService);
|
||||
setProgress(0, totKeys);
|
||||
|
||||
final int maxThreads = 200;
|
||||
ExecutorService importExecutor = new ThreadPoolExecutor(0, maxThreads,
|
||||
30L, TimeUnit.SECONDS,
|
||||
new SynchronousQueue<Runnable>());
|
||||
|
||||
while (keyListIterator.hasNext()) {
|
||||
|
||||
final ParcelableKeyRing pkRing = keyListIterator.next();
|
||||
|
||||
Runnable importOperationRunnable = new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ImportKeyResult result = null;
|
||||
try {
|
||||
ImportExportOperation importExportOperation = new ImportExportOperation(
|
||||
mKeychainService,
|
||||
new ProviderHelper(mKeychainService),
|
||||
mKeyImportAccumulator.getImportProgressable(),
|
||||
mActionCanceled);
|
||||
|
||||
ArrayList<ParcelableKeyRing> list = new ArrayList<>();
|
||||
list.add(pkRing);
|
||||
|
||||
result = importExportOperation.importKeyRings(list,
|
||||
keyServer);
|
||||
} finally {
|
||||
// in the off-chance that importKeyRings does something to crash the
|
||||
// thread before it can call singleKeyRingImportCompleted, our imported
|
||||
// key count will go wrong. This will cause the service to never die,
|
||||
// and the progress dialog to stay displayed. The finally block was
|
||||
// originally meant to ensure singleKeyRingImportCompleted was called,
|
||||
// and checks for null were to be introduced, but in such a scenario,
|
||||
// knowing an uncaught error exists in importKeyRings is more important.
|
||||
|
||||
// if a null gets passed, something wrong is happening. We want a crash.
|
||||
|
||||
mKeyImportAccumulator.singleKeyRingImportCompleted(result);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
importExecutor.execute(importOperationRunnable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to accumulate the results of individual key imports
|
||||
*/
|
||||
private class KeyImportAccumulator {
|
||||
private OperationResult.OperationLog mImportLog = new OperationResult.OperationLog();
|
||||
private int mTotalKeys;
|
||||
private int mImportedKeys = 0;
|
||||
private Progressable mInternalProgressable;
|
||||
ArrayList<Long> mImportedMasterKeyIds = new ArrayList<Long>();
|
||||
private int mBadKeys = 0;
|
||||
private int mNewKeys = 0;
|
||||
private int mUpdatedKeys = 0;
|
||||
private int mSecret = 0;
|
||||
private int mResultType = 0;
|
||||
|
||||
/**
|
||||
* meant to be used with a service due to stopSelf() in singleKeyRingImportCompleted. Remove this if
|
||||
* generalising.
|
||||
*
|
||||
* @param totalKeys total number of keys to be imported
|
||||
* @param externalProgressable the external progressable to be updated every time a key is imported
|
||||
*/
|
||||
public KeyImportAccumulator(int totalKeys, Progressable externalProgressable) {
|
||||
mTotalKeys = totalKeys;
|
||||
// ignore updates from ImportExportOperation for now
|
||||
mInternalProgressable = 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() {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private synchronized void singleKeyRingImportCompleted(ImportKeyResult result) {
|
||||
// increase imported key count and accumulate log and bad, new etc. key counts from result
|
||||
mKeyImportAccumulator.accumulateKeyImport(result);
|
||||
|
||||
setProgress(mKeyImportAccumulator.getImportedKeys(), mKeyImportAccumulator.getTotalKeys());
|
||||
|
||||
if (mKeyImportAccumulator.isImportFinished()) {
|
||||
ContactSyncAdapterService.requestSync();
|
||||
|
||||
sendMessageToHandler(ServiceProgressHandler.MessageStatus.OKAY,
|
||||
mKeyImportAccumulator.getConsolidatedImportKeyResult());
|
||||
|
||||
stopSelf();//we're done here
|
||||
}
|
||||
}
|
||||
|
||||
public Progressable getImportProgressable() {
|
||||
return mInternalProgressable;
|
||||
}
|
||||
|
||||
public int getTotalKeys() {
|
||||
return mTotalKeys;
|
||||
}
|
||||
|
||||
public int getImportedKeys() {
|
||||
return mImportedKeys;
|
||||
}
|
||||
|
||||
public synchronized void accumulateKeyImport(ImportKeyResult result) {
|
||||
mImportedKeys++;
|
||||
mImportLog.addAll(result.getLog().toList());//accumulates log
|
||||
mBadKeys += result.mBadKeys;
|
||||
mNewKeys += result.mNewKeys;
|
||||
mUpdatedKeys += result.mUpdatedKeys;
|
||||
mSecret += result.mSecret;
|
||||
|
||||
long[] masterKeyIds = result.getImportedMasterKeyIds();
|
||||
for (long masterKeyId : masterKeyIds) {
|
||||
mImportedMasterKeyIds.add(masterKeyId);
|
||||
}
|
||||
|
||||
// if any key import has been cancelled, set result type to cancelled
|
||||
// resultType is added to in getConsolidatedKayImport to account for remaining factors
|
||||
mResultType |= result.getResult() & ImportKeyResult.RESULT_CANCELLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns accumulated result of all imports so far
|
||||
*/
|
||||
public ImportKeyResult getConsolidatedImportKeyResult() {
|
||||
|
||||
// adding required information to mResultType
|
||||
// special case,no keys requested for import
|
||||
if (mBadKeys == 0 && mNewKeys == 0 && mUpdatedKeys == 0) {
|
||||
mResultType = ImportKeyResult.RESULT_FAIL_NOTHING;
|
||||
} else {
|
||||
if (mNewKeys > 0) {
|
||||
mResultType |= ImportKeyResult.RESULT_OK_NEWKEYS;
|
||||
}
|
||||
if (mUpdatedKeys > 0) {
|
||||
mResultType |= ImportKeyResult.RESULT_OK_UPDATED;
|
||||
}
|
||||
if (mBadKeys > 0) {
|
||||
mResultType |= ImportKeyResult.RESULT_WITH_ERRORS;
|
||||
if (mNewKeys == 0 && mUpdatedKeys == 0) {
|
||||
mResultType |= ImportKeyResult.RESULT_ERROR;
|
||||
}
|
||||
}
|
||||
if (mImportLog.containsWarnings()) {
|
||||
mResultType |= ImportKeyResult.RESULT_WARNINGS;
|
||||
}
|
||||
}
|
||||
|
||||
long masterKeyIds[] = new long[mImportedMasterKeyIds.size()];
|
||||
for (int i = 0; i < masterKeyIds.length; i++) {
|
||||
masterKeyIds[i] = mImportedMasterKeyIds.get(i);
|
||||
}
|
||||
|
||||
return new ImportKeyResult(mResultType, mImportLog, mNewKeys, mUpdatedKeys, mBadKeys,
|
||||
mSecret, masterKeyIds);
|
||||
}
|
||||
|
||||
public boolean isImportFinished() {
|
||||
return mTotalKeys == mImportedKeys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
|
||||
*
|
||||
* 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.service;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
public class PromoteKeyringParcel implements Parcelable {
|
||||
|
||||
public long mKeyRingId;
|
||||
public byte[] mCardAid;
|
||||
public long[] mSubKeyIds;
|
||||
|
||||
public PromoteKeyringParcel(long keyRingId, byte[] cardAid, long[] subKeyIds) {
|
||||
mKeyRingId = keyRingId;
|
||||
mCardAid = cardAid;
|
||||
mSubKeyIds = subKeyIds;
|
||||
}
|
||||
|
||||
protected PromoteKeyringParcel(Parcel in) {
|
||||
mKeyRingId = in.readLong();
|
||||
mCardAid = in.createByteArray();
|
||||
mSubKeyIds = in.createLongArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeLong(mKeyRingId);
|
||||
dest.writeByteArray(mCardAid);
|
||||
dest.writeLongArray(mSubKeyIds);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<PromoteKeyringParcel> CREATOR = new Parcelable.Creator<PromoteKeyringParcel>() {
|
||||
@Override
|
||||
public PromoteKeyringParcel createFromParcel(Parcel in) {
|
||||
return new PromoteKeyringParcel(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PromoteKeyringParcel[] newArray(int size) {
|
||||
return new PromoteKeyringParcel[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -64,6 +64,8 @@ public class ServiceProgressHandler extends Handler {
|
||||
public static final String KEYBASE_PRESENCE_URL = "keybase_presence_url";
|
||||
public static final String KEYBASE_PRESENCE_LABEL = "keybase_presence_label";
|
||||
|
||||
public static final String TAG_PROGRESS_DIALOG = "progressDialog";
|
||||
|
||||
FragmentActivity mActivity;
|
||||
|
||||
public ServiceProgressHandler(FragmentActivity activity) {
|
||||
@@ -88,7 +90,7 @@ public class ServiceProgressHandler extends Handler {
|
||||
Handler handler = new Handler();
|
||||
handler.post(new Runnable() {
|
||||
public void run() {
|
||||
frag.show(manager, "progressDialog");
|
||||
frag.show(manager, TAG_PROGRESS_DIALOG);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -19,15 +19,12 @@
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.Parcel;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
@@ -52,15 +49,11 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.CertifyActionsParcel;
|
||||
import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.MultiUserIdsAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -302,7 +295,7 @@ public class CertifyKeyFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CertifyActionsParcel createOperationInput() {
|
||||
public CertifyActionsParcel createOperationInput() {
|
||||
|
||||
// Bail out if there is not at least one user id selected
|
||||
ArrayList<CertifyAction> certifyActions = mUserIdsAdapter.getSelectedCertifyActions();
|
||||
@@ -325,7 +318,7 @@ public class CertifyKeyFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCryptoOperationSuccess(CertifyResult result) {
|
||||
public void onCryptoOperationSuccess(CertifyResult result) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(CertifyResult.EXTRA_RESULT, result);
|
||||
getActivity().setResult(Activity.RESULT_OK, intent);
|
||||
@@ -333,7 +326,7 @@ public class CertifyKeyFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCryptoOperationCancelled() {
|
||||
public void onCryptoOperationCancelled() {
|
||||
super.onCryptoOperationCancelled();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,25 +17,27 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
|
||||
import org.sufficientlysecure.keychain.service.ConsolidateInputParcel;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
|
||||
/**
|
||||
* We can not directly create a dialog on the application context.
|
||||
* This activity encapsulates a DialogFragment to emulate a dialog.
|
||||
*/
|
||||
public class ConsolidateDialogActivity extends FragmentActivity {
|
||||
public class ConsolidateDialogActivity extends FragmentActivity
|
||||
implements CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult> {
|
||||
|
||||
public static final String EXTRA_CONSOLIDATE_RECOVERY = "consolidate_recovery";
|
||||
|
||||
private CryptoOperationHelper<ConsolidateInputParcel, ConsolidateResult> mConsolidateOpHelper;
|
||||
private boolean mRecovery;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -48,55 +50,45 @@ public class ConsolidateDialogActivity extends FragmentActivity {
|
||||
}
|
||||
|
||||
private void consolidateRecovery(boolean recovery) {
|
||||
// Message is received after importing is done in KeychainService
|
||||
ServiceProgressHandler saveHandler = new ServiceProgressHandler(this) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
/* don't care about the results (for now?)
|
||||
mRecovery = recovery;
|
||||
|
||||
// get returned data bundle
|
||||
Bundle returnData = message.getInputData();
|
||||
if (returnData == null) {
|
||||
return;
|
||||
}
|
||||
final ConsolidateResult result =
|
||||
returnData.getParcelable(KeychainService.RESULT_CONSOLIDATE);
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
result.createNotify(ConsolidateDialogActivity.this).show();
|
||||
*/
|
||||
|
||||
ConsolidateDialogActivity.this.finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Send all information needed to service to import key in other thread
|
||||
Intent intent = new Intent(this, KeychainService.class);
|
||||
intent.setAction(KeychainService.ACTION_CONSOLIDATE);
|
||||
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
data.putBoolean(KeychainService.CONSOLIDATE_RECOVERY, recovery);
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(saveHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
// show progress dialog
|
||||
saveHandler.showProgressDialog(
|
||||
getString(R.string.progress_importing),
|
||||
ProgressDialog.STYLE_HORIZONTAL, false
|
||||
);
|
||||
|
||||
// start service with intent
|
||||
startService(intent);
|
||||
mConsolidateOpHelper = new CryptoOperationHelper<>(this, this, R.string.progress_importing);
|
||||
mConsolidateOpHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (mConsolidateOpHelper != null) {
|
||||
mConsolidateOpHelper.handleActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsolidateInputParcel createOperationInput() {
|
||||
return new ConsolidateInputParcel(mRecovery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(ConsolidateResult result) {
|
||||
// don't care about result (for now?)
|
||||
ConsolidateDialogActivity.this.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(ConsolidateResult result) {
|
||||
// don't care about result (for now?)
|
||||
ConsolidateDialogActivity.this.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,12 +35,14 @@ import org.spongycastle.bcpg.sig.KeyFlags;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.ExportResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm;
|
||||
@@ -49,6 +51,7 @@ import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
@@ -68,6 +71,9 @@ public class CreateKeyFinalFragment extends CryptoOperationFragment<SaveKeyringP
|
||||
|
||||
SaveKeyringParcel mSaveKeyringParcel;
|
||||
|
||||
private CryptoOperationHelper<ExportKeyringParcel, ExportResult> mUploadOpHelper;
|
||||
private CryptoOperationHelper<SaveKeyringParcel, EditKeyResult> mCreateOpHelper;
|
||||
|
||||
public static CreateKeyFinalFragment newInstance() {
|
||||
CreateKeyFinalFragment frag = new CreateKeyFinalFragment();
|
||||
|
||||
@@ -206,66 +212,61 @@ public class CreateKeyFinalFragment extends CryptoOperationFragment<SaveKeyringP
|
||||
private void createKey() {
|
||||
final CreateKeyActivity createKeyActivity = (CreateKeyActivity) getActivity();
|
||||
|
||||
Intent intent = new Intent(getActivity(), KeychainService.class);
|
||||
intent.setAction(KeychainService.ACTION_EDIT_KEYRING);
|
||||
|
||||
ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
|
||||
CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult> createKeyCallback
|
||||
= new CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult>() {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
public SaveKeyringParcel createOperationInput() {
|
||||
return mSaveKeyringParcel;
|
||||
}
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
// get returned data bundle
|
||||
Bundle returnData = message.getData();
|
||||
if (returnData == null) {
|
||||
return;
|
||||
}
|
||||
final EditKeyResult result =
|
||||
returnData.getParcelable(OperationResult.EXTRA_RESULT);
|
||||
if (result == null) {
|
||||
Log.e(Constants.TAG, "result == null");
|
||||
return;
|
||||
}
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(EditKeyResult result) {
|
||||
|
||||
if (createKeyActivity.mUseSmartCardSettings) {
|
||||
// save key id in between
|
||||
mSaveKeyringParcel.mMasterKeyId = result.mMasterKeyId;
|
||||
cryptoOperation(new CryptoInputParcel());
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.mMasterKeyId != null && mUploadCheckbox.isChecked()) {
|
||||
// result will be displayed after upload
|
||||
uploadKey(result);
|
||||
return;
|
||||
}
|
||||
|
||||
Intent data = new Intent();
|
||||
data.putExtra(OperationResult.EXTRA_RESULT, result);
|
||||
getActivity().setResult(Activity.RESULT_OK, data);
|
||||
getActivity().finish();
|
||||
if (createKeyActivity.mUseSmartCardSettings) {
|
||||
// save key id in between
|
||||
mSaveKeyringParcel.mMasterKeyId = result.mMasterKeyId;
|
||||
// calls cryptoOperation corresponding to moveToCard
|
||||
cryptoOperation(new CryptoInputParcel());
|
||||
return;
|
||||
}
|
||||
|
||||
if (result.mMasterKeyId != null && mUploadCheckbox.isChecked()) {
|
||||
// result will be displayed after upload
|
||||
uploadKey(result);
|
||||
return;
|
||||
}
|
||||
|
||||
Intent data = new Intent();
|
||||
data.putExtra(OperationResult.EXTRA_RESULT, result);
|
||||
getActivity().setResult(Activity.RESULT_OK, data);
|
||||
getActivity().finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(EditKeyResult result) {
|
||||
result.createNotify(getActivity()).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
Bundle data = new Bundle();
|
||||
data.putParcelable(KeychainService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel);
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
mCreateOpHelper = new CryptoOperationHelper<>(this, createKeyCallback,
|
||||
R.string.progress_building_key);
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(saveHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
saveHandler.showProgressDialog(getString(R.string.progress_building_key),
|
||||
ProgressDialog.STYLE_HORIZONTAL, false);
|
||||
|
||||
getActivity().startService(intent);
|
||||
mCreateOpHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
// currently only used for moveToCard
|
||||
@Override
|
||||
protected SaveKeyringParcel createOperationInput() {
|
||||
public SaveKeyringParcel createOperationInput() {
|
||||
CachedPublicKeyRing key = (new ProviderHelper(getActivity()))
|
||||
.getCachedPublicKeyRing(mSaveKeyringParcel.mMasterKeyId);
|
||||
|
||||
@@ -297,7 +298,7 @@ public class CreateKeyFinalFragment extends CryptoOperationFragment<SaveKeyringP
|
||||
|
||||
// currently only used for moveToCard
|
||||
@Override
|
||||
protected void onCryptoOperationSuccess(OperationResult result) {
|
||||
public void onCryptoOperationSuccess(OperationResult result) {
|
||||
EditKeyResult editResult = (EditKeyResult) result;
|
||||
|
||||
if (editResult.mMasterKeyId != null && mUploadCheckbox.isChecked()) {
|
||||
@@ -314,56 +315,57 @@ public class CreateKeyFinalFragment extends CryptoOperationFragment<SaveKeyringP
|
||||
|
||||
// TODO move into EditKeyOperation
|
||||
private void uploadKey(final EditKeyResult saveKeyResult) {
|
||||
// Send all information needed to service to upload key in other thread
|
||||
final Intent intent = new Intent(getActivity(), KeychainService.class);
|
||||
|
||||
intent.setAction(KeychainService.ACTION_UPLOAD_KEYRING);
|
||||
|
||||
// set data uri as path to keyring
|
||||
Uri blobUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(
|
||||
final Uri blobUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(
|
||||
saveKeyResult.mMasterKeyId);
|
||||
intent.setData(blobUri);
|
||||
|
||||
Bundle data = new Bundle();
|
||||
|
||||
// upload to favorite keyserver
|
||||
String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
|
||||
data.putString(KeychainService.UPLOAD_KEY_SERVER, keyserver);
|
||||
final String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();
|
||||
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
CryptoOperationHelper.Callback<ExportKeyringParcel, ExportResult> callback
|
||||
= new CryptoOperationHelper.Callback<ExportKeyringParcel, ExportResult>() {
|
||||
|
||||
ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
public ExportKeyringParcel createOperationInput() {
|
||||
return new ExportKeyringParcel(keyserver, blobUri);
|
||||
}
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
// TODO: upload operation needs a result!
|
||||
// TODO: then combine these results
|
||||
//if (result.getResult() == OperationResultParcel.RESULT_OK) {
|
||||
//Notify.create(getActivity(), R.string.key_send_success,
|
||||
//Notify.Style.OK).show();
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(ExportResult result) {
|
||||
handleResult(result);
|
||||
}
|
||||
|
||||
Intent data = new Intent();
|
||||
data.putExtra(OperationResult.EXTRA_RESULT, saveKeyResult);
|
||||
getActivity().setResult(Activity.RESULT_OK, data);
|
||||
getActivity().finish();
|
||||
}
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(ExportResult result) {
|
||||
handleResult(result);
|
||||
}
|
||||
|
||||
public void handleResult(ExportResult result) {
|
||||
// TODO: upload operation needs a result! "result" is not currenlty used
|
||||
// TODO: then combine these results (saveKeyResult and update op result)
|
||||
//if (result.getResult() == OperationResultParcel.RESULT_OK) {
|
||||
//Notify.create(getActivity(), R.string.key_send_success,
|
||||
//Notify.Style.OK).show();
|
||||
|
||||
Intent data = new Intent();
|
||||
data.putExtra(OperationResult.EXTRA_RESULT, saveKeyResult);
|
||||
getActivity().setResult(Activity.RESULT_OK, data);
|
||||
getActivity().finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(saveHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
// show progress dialog
|
||||
saveHandler.showProgressDialog(
|
||||
getString(R.string.progress_uploading),
|
||||
ProgressDialog.STYLE_HORIZONTAL, false);
|
||||
|
||||
// start service with intent
|
||||
getActivity().startService(intent);
|
||||
mUploadOpHelper = new CryptoOperationHelper<>(this, callback, R.string.progress_uploading);
|
||||
mUploadOpHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,11 +22,8 @@ import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -37,18 +34,19 @@ import android.widget.TextView;
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
|
||||
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.NfcListenerFragment;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
|
||||
public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListenerFragment {
|
||||
public class CreateKeyYubiKeyImportFragment
|
||||
extends CryptoOperationFragment<ImportKeyringParcel, ImportKeyResult>
|
||||
implements NfcListenerFragment {
|
||||
|
||||
private static final String ARG_FINGERPRINT = "fingerprint";
|
||||
public static final String ARG_AID = "aid";
|
||||
@@ -64,6 +62,10 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
|
||||
private TextView vSerNo;
|
||||
private TextView vUserId;
|
||||
|
||||
// for CryptoOperationFragment key import
|
||||
private String mKeyserver;
|
||||
private ArrayList<ParcelableKeyRing> mKeyList;
|
||||
|
||||
public static Fragment newInstance(byte[] scannedFingerprints, byte[] nfcAid, String userId) {
|
||||
|
||||
CreateKeyYubiKeyImportFragment frag = new CreateKeyYubiKeyImportFragment();
|
||||
@@ -175,77 +177,20 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
|
||||
|
||||
public void importKey() {
|
||||
|
||||
// Message is received after decrypting is done in KeychainService
|
||||
ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
// get returned data bundle
|
||||
Bundle returnData = message.getData();
|
||||
|
||||
ImportKeyResult result =
|
||||
returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
|
||||
|
||||
long[] masterKeyIds = result.getImportedMasterKeyIds();
|
||||
|
||||
// TODO handle masterKeyIds.length != 1...? sorta outlandish scenario
|
||||
|
||||
if (!result.success() || masterKeyIds.length == 0) {
|
||||
result.createNotify(getActivity()).show();
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(getActivity(), ViewKeyActivity.class);
|
||||
// use the imported masterKeyId, not the one from the yubikey, because
|
||||
// that one might* just have been a subkey of the imported key
|
||||
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyIds[0]));
|
||||
intent.putExtra(ViewKeyActivity.EXTRA_DISPLAY_RESULT, result);
|
||||
intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid);
|
||||
intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
|
||||
intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints);
|
||||
startActivity(intent);
|
||||
getActivity().finish();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// Send all information needed to service to decrypt in other thread
|
||||
Intent intent = new Intent(getActivity(), KeychainService.class);
|
||||
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
|
||||
intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
|
||||
|
||||
ArrayList<ParcelableKeyRing> keyList = new ArrayList<>();
|
||||
keyList.add(new ParcelableKeyRing(mNfcFingerprint, null, null));
|
||||
data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, keyList);
|
||||
mKeyList = keyList;
|
||||
|
||||
{
|
||||
Preferences prefs = Preferences.getPreferences(getActivity());
|
||||
Preferences.CloudSearchPrefs cloudPrefs =
|
||||
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
|
||||
data.putString(KeychainService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
|
||||
mKeyserver = cloudPrefs.keyserver;
|
||||
}
|
||||
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
super.setProgressMessageResource(R.string.progress_importing);
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(saveHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
saveHandler.showProgressDialog(
|
||||
getString(R.string.progress_importing),
|
||||
ProgressDialog.STYLE_HORIZONTAL, false
|
||||
);
|
||||
|
||||
// start service with intent
|
||||
getActivity().startService(intent);
|
||||
super.cryptoOperation();
|
||||
|
||||
}
|
||||
|
||||
@@ -264,4 +209,29 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe
|
||||
refreshSearch();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImportKeyringParcel createOperationInput() {
|
||||
return new ImportKeyringParcel(mKeyList, mKeyserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(ImportKeyResult result) {
|
||||
long[] masterKeyIds = result.getImportedMasterKeyIds();
|
||||
if (masterKeyIds.length == 0) {
|
||||
super.onCryptoOperationError(result);
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(getActivity(), ViewKeyActivity.class);
|
||||
// use the imported masterKeyId, not the one from the yubikey, because
|
||||
// that one might* just have been a subkey of the imported key
|
||||
intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyIds[0]));
|
||||
intent.putExtra(ViewKeyActivity.EXTRA_DISPLAY_RESULT, result);
|
||||
intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid);
|
||||
intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId);
|
||||
intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, mNfcFingerprints);
|
||||
startActivity(intent);
|
||||
getActivity().finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
@@ -45,14 +46,14 @@ import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
@@ -78,6 +79,8 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
|
||||
private DecryptVerifyResult mDecryptVerifyResult;
|
||||
private ViewAnimator mOverlayAnimator;
|
||||
|
||||
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;
|
||||
|
||||
@Override
|
||||
public void onViewCreated(View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
@@ -135,43 +138,15 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
|
||||
|
||||
private void lookupUnknownKey(long unknownKeyId) {
|
||||
|
||||
// Message is received after importing is done in KeychainService
|
||||
ServiceProgressHandler serviceHandler = new ServiceProgressHandler(getActivity()) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
// get returned data bundle
|
||||
Bundle returnData = message.getData();
|
||||
|
||||
if (returnData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ImportKeyResult result =
|
||||
returnData.getParcelable(OperationResult.EXTRA_RESULT);
|
||||
|
||||
Activity activity = getActivity();
|
||||
if (result != null && activity != null) {
|
||||
result.createNotify(activity).show();
|
||||
}
|
||||
|
||||
getLoaderManager().restartLoader(LOADER_ID_UNIFIED, null, DecryptFragment.this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
final ArrayList<ParcelableKeyRing> keyList;
|
||||
final String keyserver;
|
||||
|
||||
// search config
|
||||
{
|
||||
Preferences prefs = Preferences.getPreferences(getActivity());
|
||||
Preferences.CloudSearchPrefs cloudPrefs =
|
||||
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
|
||||
data.putString(KeychainService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
|
||||
keyserver = cloudPrefs.keyserver;
|
||||
}
|
||||
|
||||
{
|
||||
@@ -180,19 +155,43 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
|
||||
ArrayList<ParcelableKeyRing> selectedEntries = new ArrayList<>();
|
||||
selectedEntries.add(keyEntry);
|
||||
|
||||
data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, selectedEntries);
|
||||
keyList = selectedEntries;
|
||||
}
|
||||
|
||||
// Send all information needed to service to query keys in other thread
|
||||
Intent intent = new Intent(getActivity(), KeychainService.class);
|
||||
intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> callback
|
||||
= new CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult>() {
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(serviceHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
@Override
|
||||
public ImportKeyringParcel createOperationInput() {
|
||||
return new ImportKeyringParcel(keyList, keyserver);
|
||||
}
|
||||
|
||||
getActivity().startService(intent);
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(ImportKeyResult result) {
|
||||
result.createNotify(getActivity()).show();
|
||||
|
||||
getLoaderManager().restartLoader(LOADER_ID_UNIFIED, null, DecryptFragment.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(ImportKeyResult result) {
|
||||
result.createNotify(getActivity()).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
mImportOpHelper = new CryptoOperationHelper<>(this, callback, R.string.progress_importing);
|
||||
|
||||
mImportOpHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
private void showKey(long keyId) {
|
||||
@@ -457,4 +456,11 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
|
||||
|
||||
protected abstract void onVerifyLoaded(boolean hideErrorOverlay);
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (mImportOpHelper != null) {
|
||||
mImportOpHelper.handleActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,18 +254,12 @@ public class DecryptListFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
mAdapter.setProgress(mCurrentInputUri, progress, max, msg);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dismissProgress() {
|
||||
// progress shown inline, so never mind
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCryptoOperationError(DecryptVerifyResult result) {
|
||||
public void onCryptoOperationError(DecryptVerifyResult result) {
|
||||
final Uri uri = mCurrentInputUri;
|
||||
mCurrentInputUri = null;
|
||||
|
||||
@@ -275,7 +269,7 @@ public class DecryptListFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCryptoOperationSuccess(DecryptVerifyResult result) {
|
||||
public void onCryptoOperationSuccess(DecryptVerifyResult result) {
|
||||
Uri uri = mCurrentInputUri;
|
||||
mCurrentInputUri = null;
|
||||
|
||||
@@ -439,7 +433,7 @@ public class DecryptListFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PgpDecryptVerifyInputParcel createOperationInput() {
|
||||
public PgpDecryptVerifyInputParcel createOperationInput() {
|
||||
|
||||
if (mCurrentInputUri == null) {
|
||||
if (mPendingInputUris.isEmpty()) {
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
@@ -26,7 +25,6 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
@@ -40,9 +38,6 @@ import android.widget.ListView;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
|
||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||
import org.sufficientlysecure.keychain.operations.results.SingletonResult;
|
||||
@@ -54,8 +49,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.ChangeUnlockParcel;
|
||||
import org.sufficientlysecure.keychain.service.SaveKeyringParcel.SubkeyChange;
|
||||
@@ -65,7 +58,12 @@ import org.sufficientlysecure.keychain.ui.adapter.SubkeysAddedAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.UserIdsAddedAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.*;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.AddSubkeyDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.AddUserIdDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.EditSubkeyExpiryDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
@@ -609,12 +607,12 @@ public class EditKeyFragment extends CryptoOperationFragment<SaveKeyringParcel,
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SaveKeyringParcel createOperationInput() {
|
||||
public SaveKeyringParcel createOperationInput() {
|
||||
return mSaveKeyringParcel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCryptoOperationSuccess(OperationResult result) {
|
||||
public void onCryptoOperationSuccess(OperationResult result) {
|
||||
|
||||
// if good -> finish, return result to showkey and display there!
|
||||
Intent intent = new Intent();
|
||||
|
||||
@@ -386,7 +386,7 @@ public class EncryptFilesFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCryptoOperationSuccess(final SignEncryptResult result) {
|
||||
public void onCryptoOperationSuccess(final SignEncryptResult result) {
|
||||
|
||||
if (mDeleteAfterEncrypt) {
|
||||
// TODO make behavior coherent here
|
||||
@@ -485,7 +485,7 @@ public class EncryptFilesFragment
|
||||
|
||||
}
|
||||
|
||||
protected SignEncryptParcel createOperationInput() {
|
||||
public SignEncryptParcel createOperationInput() {
|
||||
|
||||
SignEncryptParcel actionsParcel = getCachedActionsParcel();
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ public class EncryptTextFragment
|
||||
|
||||
}
|
||||
|
||||
protected SignEncryptParcel createOperationInput() {
|
||||
public SignEncryptParcel createOperationInput() {
|
||||
|
||||
if (mMessage == null || mMessage.isEmpty()) {
|
||||
Notify.create(getActivity(), R.string.error_empty_text, Notify.Style.ERROR)
|
||||
@@ -316,7 +316,7 @@ public class EncryptTextFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCryptoOperationSuccess(SignEncryptResult result) {
|
||||
public void onCryptoOperationSuccess(SignEncryptResult result) {
|
||||
|
||||
if (mShareAfterEncrypt) {
|
||||
// Share encrypted message/file
|
||||
|
||||
@@ -17,12 +17,10 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
@@ -35,9 +33,10 @@ import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
@@ -47,7 +46,8 @@ import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ImportKeysActivity extends BaseNfcActivity {
|
||||
public class ImportKeysActivity extends BaseNfcActivity
|
||||
implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
||||
|
||||
public static final String ACTION_IMPORT_KEY = OpenKeychainIntents.IMPORT_KEY;
|
||||
public static final String ACTION_IMPORT_KEY_FROM_KEYSERVER = OpenKeychainIntents.IMPORT_KEY_FROM_KEYSERVER;
|
||||
@@ -82,6 +82,12 @@ public class ImportKeysActivity extends BaseNfcActivity {
|
||||
private Fragment mTopFragment;
|
||||
private View mImportButton;
|
||||
|
||||
// for CryptoOperationHelper.Callback
|
||||
private String mKeyserver;
|
||||
private ArrayList<ParcelableKeyRing> mKeyList;
|
||||
|
||||
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -388,23 +394,9 @@ public class ImportKeysActivity extends BaseNfcActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
ServiceProgressHandler serviceHandler = new ServiceProgressHandler(this) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
|
||||
ImportKeysActivity.this.handleMessage(message);
|
||||
}
|
||||
};
|
||||
|
||||
// Send all information needed to service to import key in other thread
|
||||
Intent intent = new Intent(this, KeychainService.class);
|
||||
|
||||
intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
|
||||
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
mOperationHelper = new CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult>(
|
||||
this, this, R.string.progress_importing
|
||||
);
|
||||
|
||||
ImportKeysListFragment.LoaderState ls = mListFragment.getLoaderState();
|
||||
if (ls instanceof ImportKeysListFragment.BytesLoaderState) {
|
||||
@@ -423,30 +415,18 @@ public class ImportKeysActivity extends BaseNfcActivity {
|
||||
new ParcelableFileCache<>(this, "key_import.pcl");
|
||||
cache.writeCache(selectedEntries);
|
||||
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
mKeyList = null;
|
||||
mKeyserver = null;
|
||||
mOperationHelper.cryptoOperation();
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(serviceHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
// show progress dialog
|
||||
serviceHandler.showProgressDialog(
|
||||
getString(R.string.progress_importing),
|
||||
ProgressDialog.STYLE_HORIZONTAL,
|
||||
true
|
||||
);
|
||||
|
||||
// start service with intent
|
||||
startService(intent);
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "Problem writing cache file", e);
|
||||
Notify.create(this, "Problem writing cache file!", Notify.Style.ERROR)
|
||||
.show((ViewGroup) findViewById(R.id.import_snackbar));
|
||||
}
|
||||
} else if (ls instanceof ImportKeysListFragment.CloudLoaderState) {
|
||||
ImportKeysListFragment.CloudLoaderState sls = (ImportKeysListFragment.CloudLoaderState) ls;
|
||||
|
||||
data.putString(KeychainService.IMPORT_KEY_SERVER, sls.mCloudPrefs.keyserver);
|
||||
ImportKeysListFragment.CloudLoaderState sls =
|
||||
(ImportKeysListFragment.CloudLoaderState) ls;
|
||||
|
||||
// get selected key entries
|
||||
ArrayList<ParcelableKeyRing> keys = new ArrayList<>();
|
||||
@@ -459,22 +439,11 @@ public class ImportKeysActivity extends BaseNfcActivity {
|
||||
);
|
||||
}
|
||||
}
|
||||
data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, keys);
|
||||
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
mKeyList = keys;
|
||||
mKeyserver = sls.mCloudPrefs.keyserver;
|
||||
mOperationHelper.cryptoOperation();
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(serviceHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
// show progress dialog
|
||||
serviceHandler.showProgressDialog(
|
||||
getString(R.string.progress_importing),
|
||||
ProgressDialog.STYLE_HORIZONTAL, true
|
||||
);
|
||||
|
||||
// start service with intent
|
||||
startService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,4 +454,57 @@ public class ImportKeysActivity extends BaseNfcActivity {
|
||||
// either way, finish afterwards
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (mOperationHelper == null ||
|
||||
!mOperationHelper.handleActivityResult(requestCode, resultCode, data)) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleResult (ImportKeyResult result) {
|
||||
if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_RESULT.equals(getIntent().getAction())
|
||||
|| ACTION_IMPORT_KEY_FROM_FILE_AND_RETURN.equals(getIntent().getAction())) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(ImportKeyResult.EXTRA_RESULT, result);
|
||||
ImportKeysActivity.this.setResult(RESULT_OK, intent);
|
||||
ImportKeysActivity.this.finish();
|
||||
return;
|
||||
}
|
||||
if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN_TO_SERVICE.equals(getIntent().getAction())) {
|
||||
ImportKeysActivity.this.setResult(RESULT_OK, mPendingIntentData);
|
||||
ImportKeysActivity.this.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
result.createNotify(ImportKeysActivity.this)
|
||||
.show((ViewGroup) findViewById(R.id.import_snackbar));
|
||||
}
|
||||
// methods from CryptoOperationHelper.Callback
|
||||
|
||||
@Override
|
||||
public ImportKeyringParcel createOperationInput() {
|
||||
return new ImportKeyringParcel(mKeyList, mKeyserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(ImportKeyResult result) {
|
||||
handleResult(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(ImportKeyResult result) {
|
||||
handleResult(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.net.Uri;
|
||||
@@ -26,8 +25,6 @@ import android.nfc.NdefMessage;
|
||||
import android.nfc.NfcAdapter;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.widget.Toast;
|
||||
@@ -43,8 +40,8 @@ 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.SingletonResult;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
@@ -55,7 +52,8 @@ import java.util.Locale;
|
||||
/**
|
||||
* Proxy activity (just a transparent content view) to scan QR Codes using the Barcode Scanner app
|
||||
*/
|
||||
public class ImportKeysProxyActivity extends FragmentActivity {
|
||||
public class ImportKeysProxyActivity extends FragmentActivity
|
||||
implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
||||
|
||||
public static final String ACTION_QR_CODE_API = OpenKeychainIntents.IMPORT_KEY_FROM_QR_CODE;
|
||||
// implies activity returns scanned fingerprint as extra and does not import
|
||||
@@ -64,6 +62,11 @@ public class ImportKeysProxyActivity extends FragmentActivity {
|
||||
|
||||
public static final String EXTRA_FINGERPRINT = "fingerprint";
|
||||
|
||||
// for CryptoOperationHelper
|
||||
private String mKeyserver;
|
||||
private ArrayList<ParcelableKeyRing> mKeyList;
|
||||
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -106,6 +109,10 @@ public class ImportKeysProxyActivity extends FragmentActivity {
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (mImportOpHelper != null) {
|
||||
mImportOpHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
if (requestCode == IntentIntegratorSupportV4.REQUEST_CODE) {
|
||||
IntentResult scanResult = IntentIntegratorSupportV4.parseActivityResult(requestCode,
|
||||
resultCode, data);
|
||||
@@ -205,75 +212,56 @@ public class ImportKeysProxyActivity extends FragmentActivity {
|
||||
|
||||
private void startImportService(ArrayList<ParcelableKeyRing> keyRings) {
|
||||
|
||||
// Message is received after importing is done in KeychainService
|
||||
ServiceProgressHandler serviceHandler = new ServiceProgressHandler(this) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
// get returned data bundle
|
||||
Bundle returnData = message.getData();
|
||||
if (returnData == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
final ImportKeyResult result =
|
||||
returnData.getParcelable(OperationResult.EXTRA_RESULT);
|
||||
if (result == null) {
|
||||
Log.e(Constants.TAG, "result == null");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success()) {
|
||||
// only return if no success...
|
||||
Intent data = new Intent();
|
||||
data.putExtras(returnData);
|
||||
returnResult(data);
|
||||
return;
|
||||
}
|
||||
|
||||
Intent certifyIntent = new Intent(ImportKeysProxyActivity.this,
|
||||
CertifyKeyActivity.class);
|
||||
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
|
||||
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS,
|
||||
result.getImportedMasterKeyIds());
|
||||
startActivityForResult(certifyIntent, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
|
||||
// search config
|
||||
{
|
||||
Preferences prefs = Preferences.getPreferences(this);
|
||||
Preferences.CloudSearchPrefs cloudPrefs =
|
||||
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
|
||||
data.putString(KeychainService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
|
||||
mKeyserver = cloudPrefs.keyserver;
|
||||
}
|
||||
|
||||
data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, keyRings);
|
||||
mKeyList = keyRings;
|
||||
|
||||
// Send all information needed to service to query keys in other thread
|
||||
Intent intent = new Intent(this, KeychainService.class);
|
||||
intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
mImportOpHelper = new CryptoOperationHelper<>(this, this, R.string.progress_importing);
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(serviceHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
mImportOpHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
// show progress dialog
|
||||
serviceHandler.showProgressDialog(
|
||||
getString(R.string.progress_importing),
|
||||
ProgressDialog.STYLE_HORIZONTAL, true);
|
||||
|
||||
// start service with intent
|
||||
startService(intent);
|
||||
// CryptoOperationHelper.Callback methods
|
||||
|
||||
@Override
|
||||
public ImportKeyringParcel createOperationInput() {
|
||||
return new ImportKeyringParcel(mKeyList, mKeyserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(ImportKeyResult result) {
|
||||
Intent certifyIntent = new Intent(this, CertifyKeyActivity.class);
|
||||
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
|
||||
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS,
|
||||
result.getImportedMasterKeyIds());
|
||||
startActivityForResult(certifyIntent, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(ImportKeyResult result) {
|
||||
Bundle returnData = new Bundle();
|
||||
returnData.putParcelable(OperationResult.EXTRA_RESULT, result);
|
||||
Intent data = new Intent();
|
||||
data.putExtras(returnData);
|
||||
returnResult(data);
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,6 @@ package org.sufficientlysecure.keychain.ui;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
@@ -64,9 +63,10 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.service.ConsolidateInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
|
||||
@@ -90,7 +90,8 @@ import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
||||
*/
|
||||
public class KeyListFragment extends LoaderFragment
|
||||
implements SearchView.OnQueryTextListener, AdapterView.OnItemClickListener,
|
||||
LoaderManager.LoaderCallbacks<Cursor>, FabContainer {
|
||||
LoaderManager.LoaderCallbacks<Cursor>, FabContainer,
|
||||
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
||||
|
||||
static final int REQUEST_REPEAT_PASSPHRASE = 1;
|
||||
static final int REQUEST_ACTION = 2;
|
||||
@@ -107,6 +108,14 @@ public class KeyListFragment extends LoaderFragment
|
||||
|
||||
private FloatingActionsMenu mFab;
|
||||
|
||||
// for CryptoOperationHelper import
|
||||
private ArrayList<ParcelableKeyRing> mKeyList;
|
||||
private String mKeyserver;
|
||||
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;
|
||||
|
||||
// for ConsolidateOperation
|
||||
private CryptoOperationHelper<ConsolidateInputParcel, ConsolidateResult> mConsolidateOpHelper;
|
||||
|
||||
// This ids for multiple key export.
|
||||
private ArrayList<Long> mIdsForRepeatAskPassphrase;
|
||||
// This index for remembering the number of master key.
|
||||
@@ -580,112 +589,59 @@ public class KeyListFragment extends LoaderFragment
|
||||
ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
|
||||
keyList.add(keyEntry);
|
||||
}
|
||||
mKeyList = keyList;
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
ServiceProgressHandler serviceHandler = new ServiceProgressHandler(getActivity()) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
// get returned data bundle
|
||||
Bundle returnData = message.getData();
|
||||
if (returnData == null) {
|
||||
return;
|
||||
}
|
||||
final ImportKeyResult result =
|
||||
returnData.getParcelable(OperationResult.EXTRA_RESULT);
|
||||
if (result == null) {
|
||||
Log.e(Constants.TAG, "result == null");
|
||||
return;
|
||||
}
|
||||
|
||||
result.createNotify(getActivity()).show();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Send all information needed to service to query keys in other thread
|
||||
Intent intent = new Intent(getActivity(), KeychainService.class);
|
||||
intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
|
||||
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
|
||||
// search config
|
||||
{
|
||||
Preferences prefs = Preferences.getPreferences(getActivity());
|
||||
Preferences.CloudSearchPrefs cloudPrefs =
|
||||
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
|
||||
data.putString(KeychainService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
|
||||
mKeyserver = cloudPrefs.keyserver;
|
||||
}
|
||||
|
||||
data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, keyList);
|
||||
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(serviceHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
// show progress dialog
|
||||
serviceHandler.showProgressDialog(
|
||||
getString(R.string.progress_updating),
|
||||
ProgressDialog.STYLE_HORIZONTAL, true);
|
||||
|
||||
// start service with intent
|
||||
getActivity().startService(intent);
|
||||
mImportOpHelper = new CryptoOperationHelper<>(this,
|
||||
this, R.string.progress_updating);
|
||||
mImportOpHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
private void consolidate() {
|
||||
// Message is received after importing is done in KeychainService
|
||||
ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
|
||||
|
||||
CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult> callback
|
||||
= new CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult>() {
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
public ConsolidateInputParcel createOperationInput() {
|
||||
return new ConsolidateInputParcel(false); // we want to perform a full consolidate
|
||||
}
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
// get returned data bundle
|
||||
Bundle returnData = message.getData();
|
||||
if (returnData == null) {
|
||||
return;
|
||||
}
|
||||
final ConsolidateResult result =
|
||||
returnData.getParcelable(OperationResult.EXTRA_RESULT);
|
||||
if (result == null) {
|
||||
return;
|
||||
}
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(ConsolidateResult result) {
|
||||
result.createNotify(getActivity()).show();
|
||||
}
|
||||
|
||||
result.createNotify(getActivity()).show();
|
||||
}
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(ConsolidateResult result) {
|
||||
result.createNotify(getActivity()).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Send all information needed to service to import key in other thread
|
||||
Intent intent = new Intent(getActivity(), KeychainService.class);
|
||||
mConsolidateOpHelper =
|
||||
new CryptoOperationHelper<>(this, callback, R.string.progress_importing);
|
||||
|
||||
intent.setAction(KeychainService.ACTION_CONSOLIDATE);
|
||||
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(saveHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
// show progress dialog
|
||||
saveHandler.showProgressDialog(
|
||||
getString(R.string.progress_importing),
|
||||
ProgressDialog.STYLE_HORIZONTAL, false);
|
||||
|
||||
// start service with intent
|
||||
getActivity().startService(intent);
|
||||
mConsolidateOpHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
private void showMultiExportDialog(long[] masterKeyIds) {
|
||||
@@ -724,6 +680,14 @@ public class KeyListFragment extends LoaderFragment
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (mImportOpHelper != null) {
|
||||
mImportOpHelper.handleActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
if (mConsolidateOpHelper != null) {
|
||||
mConsolidateOpHelper.handleActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
if (requestCode == REQUEST_REPEAT_PASSPHRASE) {
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
return;
|
||||
@@ -769,6 +733,32 @@ public class KeyListFragment extends LoaderFragment
|
||||
anim.start();
|
||||
}
|
||||
|
||||
// CryptoOperationHelper.Callback methods
|
||||
@Override
|
||||
public ImportKeyringParcel createOperationInput() {
|
||||
return new ImportKeyringParcel(mKeyList, mKeyserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(ImportKeyResult result) {
|
||||
result.createNotify(getActivity()).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(ImportKeyResult result) {
|
||||
result.createNotify(getActivity()).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public class KeyListAdapter extends KeyAdapter implements StickyListHeadersAdapter {
|
||||
|
||||
private HashMap<Integer, Boolean> mSelection = new HashMap<>();
|
||||
|
||||
@@ -18,15 +18,11 @@
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.NumberPicker;
|
||||
@@ -38,9 +34,9 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
||||
@@ -52,7 +48,8 @@ import edu.cmu.cylab.starslinger.exchange.ExchangeActivity;
|
||||
import edu.cmu.cylab.starslinger.exchange.ExchangeConfig;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public class SafeSlingerActivity extends BaseActivity {
|
||||
public class SafeSlingerActivity extends BaseActivity
|
||||
implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
||||
|
||||
private static final int REQUEST_CODE_SAFE_SLINGER = 211;
|
||||
|
||||
@@ -61,6 +58,12 @@ public class SafeSlingerActivity extends BaseActivity {
|
||||
private long mMasterKeyId;
|
||||
private int mSelectedNumber = 2;
|
||||
|
||||
// for CryptoOperationHelper
|
||||
private ArrayList<ParcelableKeyRing> mKeyList;
|
||||
private String mKeyserver;
|
||||
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
|
||||
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -116,65 +119,17 @@ public class SafeSlingerActivity extends BaseActivity {
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (mOperationHelper != null) {
|
||||
mOperationHelper.handleActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
if (requestCode == REQUEST_CODE_SAFE_SLINGER) {
|
||||
if (resultCode == ExchangeActivity.RESULT_EXCHANGE_CANCELED) {
|
||||
return;
|
||||
}
|
||||
|
||||
final FragmentActivity activity = SafeSlingerActivity.this;
|
||||
|
||||
// Message is received after importing is done in KeychainService
|
||||
ServiceProgressHandler saveHandler = new ServiceProgressHandler(activity) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
// get returned data bundle
|
||||
Bundle returnData = message.getData();
|
||||
if (returnData == null) {
|
||||
return;
|
||||
}
|
||||
final ImportKeyResult result =
|
||||
returnData.getParcelable(OperationResult.EXTRA_RESULT);
|
||||
if (result == null) {
|
||||
Log.e(Constants.TAG, "result == null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!result.success()) {
|
||||
// result.createNotify(activity).show();
|
||||
// only return if no success...
|
||||
Intent data = new Intent();
|
||||
data.putExtras(returnData);
|
||||
setResult(RESULT_OK, data);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
// if (mExchangeMasterKeyId == null) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
Intent certifyIntent = new Intent(activity, CertifyKeyActivity.class);
|
||||
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
|
||||
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, result.getImportedMasterKeyIds());
|
||||
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, mMasterKeyId);
|
||||
startActivityForResult(certifyIntent, 0);
|
||||
|
||||
// mExchangeMasterKeyId = null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Log.d(Constants.TAG, "importKeys started");
|
||||
|
||||
// Send all information needed to service to import key in other thread
|
||||
Intent intent = new Intent(activity, KeychainService.class);
|
||||
|
||||
intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
|
||||
|
||||
// instead of giving the entries by Intent extra, cache them into a
|
||||
// file to prevent Java Binder problems on heavy imports
|
||||
// read FileImportCache for more info.
|
||||
@@ -185,28 +140,18 @@ public class SafeSlingerActivity extends BaseActivity {
|
||||
// We parcel this iteratively into a file - anything we can
|
||||
// display here, we should be able to import.
|
||||
ParcelableFileCache<ParcelableKeyRing> cache =
|
||||
new ParcelableFileCache<>(activity, "key_import.pcl");
|
||||
new ParcelableFileCache<>(this, "key_import.pcl");
|
||||
cache.writeCache(it.size(), it.iterator());
|
||||
|
||||
// fill values for this action
|
||||
Bundle bundle = new Bundle();
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, bundle);
|
||||
mOperationHelper =
|
||||
new CryptoOperationHelper(this, this, R.string.progress_importing);
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(saveHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
// show progress dialog
|
||||
saveHandler.showProgressDialog(
|
||||
getString(R.string.progress_importing),
|
||||
ProgressDialog.STYLE_HORIZONTAL, true
|
||||
);
|
||||
|
||||
// start service with intent
|
||||
activity.startService(intent);
|
||||
mKeyList = null;
|
||||
mKeyserver = null;
|
||||
mOperationHelper.cryptoOperation();
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "Problem writing cache file", e);
|
||||
Notify.create(activity, "Problem writing cache file!", Notify.Style.ERROR).show();
|
||||
Notify.create(this, "Problem writing cache file!", Notify.Style.ERROR).show();
|
||||
}
|
||||
} else {
|
||||
// give everything else down to KeyListActivity!
|
||||
@@ -233,4 +178,39 @@ public class SafeSlingerActivity extends BaseActivity {
|
||||
return list;
|
||||
}
|
||||
|
||||
// CryptoOperationHelper.Callback functions
|
||||
|
||||
@Override
|
||||
public ImportKeyringParcel createOperationInput() {
|
||||
return new ImportKeyringParcel(mKeyList, mKeyserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(ImportKeyResult result) {
|
||||
Intent certifyIntent = new Intent(this, CertifyKeyActivity.class);
|
||||
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_RESULT, result);
|
||||
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, result.getImportedMasterKeyIds());
|
||||
certifyIntent.putExtra(CertifyKeyActivity.EXTRA_CERTIFY_KEY_ID, mMasterKeyId);
|
||||
startActivityForResult(certifyIntent, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(ImportKeyResult result) {
|
||||
Bundle returnData = new Bundle();
|
||||
returnData.putParcelable(OperationResult.EXTRA_RESULT, result);
|
||||
Intent data = new Intent();
|
||||
data.putExtras(returnData);
|
||||
setResult(RESULT_OK, data);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,9 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.NavUtils;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
@@ -33,23 +30,30 @@ import android.widget.Toast;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.ExportResult;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
/**
|
||||
* Sends the selected public key to a keyserver
|
||||
*/
|
||||
public class UploadKeyActivity extends BaseActivity {
|
||||
public class UploadKeyActivity extends BaseActivity
|
||||
implements CryptoOperationHelper.Callback<ExportKeyringParcel, ExportResult> {
|
||||
private View mUploadButton;
|
||||
private Spinner mKeyServerSpinner;
|
||||
|
||||
private Uri mDataUri;
|
||||
|
||||
// CryptoOperationHelper.Callback vars
|
||||
private String mKeyserver;
|
||||
private Uri mUnifiedKeyringUri;
|
||||
private CryptoOperationHelper<ExportKeyringParcel, ExportResult> mUploadOpHelper;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -89,51 +93,23 @@ public class UploadKeyActivity extends BaseActivity {
|
||||
setContentView(R.layout.upload_key_activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (mUploadOpHelper != null) {
|
||||
mUploadOpHelper.handleActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
private void uploadKey() {
|
||||
// Send all information needed to service to upload key in other thread
|
||||
Intent intent = new Intent(this, KeychainService.class);
|
||||
|
||||
intent.setAction(KeychainService.ACTION_UPLOAD_KEYRING);
|
||||
|
||||
// set data uri as path to keyring
|
||||
Uri blobUri = KeyRings.buildUnifiedKeyRingUri(mDataUri);
|
||||
intent.setData(blobUri);
|
||||
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
mUnifiedKeyringUri = blobUri;
|
||||
|
||||
String server = (String) mKeyServerSpinner.getSelectedItem();
|
||||
data.putString(KeychainService.UPLOAD_KEY_SERVER, server);
|
||||
mKeyserver = server;
|
||||
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
|
||||
// Message is received after uploading is done in KeychainService
|
||||
ServiceProgressHandler saveHandler = new ServiceProgressHandler(this) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
|
||||
Toast.makeText(UploadKeyActivity.this, R.string.msg_crt_upload_success,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(saveHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
// show progress dialog
|
||||
saveHandler.showProgressDialog(
|
||||
getString(R.string.progress_uploading),
|
||||
ProgressDialog.STYLE_HORIZONTAL, false);
|
||||
|
||||
// start service with intent
|
||||
startService(intent);
|
||||
mUploadOpHelper = new CryptoOperationHelper(this, this, R.string.progress_uploading);
|
||||
mUploadOpHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -148,4 +124,30 @@ public class UploadKeyActivity extends BaseActivity {
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExportKeyringParcel createOperationInput() {
|
||||
return new ExportKeyringParcel(mKeyserver, mUnifiedKeyringUri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(ExportResult result) {
|
||||
Toast.makeText(UploadKeyActivity.this, R.string.msg_crt_upload_success,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(ExportResult result) {
|
||||
// TODO: Implement proper log for key upload then show error
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,11 +65,13 @@ import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler.MessageStatus;
|
||||
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
|
||||
import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
@@ -89,7 +91,8 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class ViewKeyActivity extends BaseNfcActivity implements
|
||||
LoaderManager.LoaderCallbacks<Cursor> {
|
||||
LoaderManager.LoaderCallbacks<Cursor>,
|
||||
CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
||||
|
||||
public static final String EXTRA_NFC_USER_ID = "nfc_user_id";
|
||||
public static final String EXTRA_NFC_AID = "nfc_aid";
|
||||
@@ -105,6 +108,11 @@ public class ViewKeyActivity extends BaseNfcActivity implements
|
||||
|
||||
protected Uri mDataUri;
|
||||
|
||||
// For CryptoOperationHelper.Callback
|
||||
private String mKeyserver;
|
||||
private ArrayList<ParcelableKeyRing> mKeyList;
|
||||
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
|
||||
|
||||
private TextView mName;
|
||||
private TextView mStatusText;
|
||||
private ImageView mStatusImage;
|
||||
@@ -396,7 +404,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
|
||||
|
||||
private void certifyImmediate() {
|
||||
Intent intent = new Intent(this, CertifyKeyActivity.class);
|
||||
intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[] {mMasterKeyId});
|
||||
intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{mMasterKeyId});
|
||||
|
||||
startCertifyIntent(intent);
|
||||
}
|
||||
@@ -486,6 +494,10 @@ public class ViewKeyActivity extends BaseNfcActivity implements
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (mOperationHelper != null) {
|
||||
mOperationHelper.handleActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
if (requestCode == REQUEST_QR_FINGERPRINT && resultCode == Activity.RESULT_OK) {
|
||||
|
||||
// If there is an EXTRA_RESULT, that's an error. Just show it.
|
||||
@@ -651,56 +663,20 @@ public class ViewKeyActivity extends BaseNfcActivity implements
|
||||
ParcelableKeyRing keyEntry = new ParcelableKeyRing(fingerprint, null, null);
|
||||
ArrayList<ParcelableKeyRing> entries = new ArrayList<>();
|
||||
entries.add(keyEntry);
|
||||
|
||||
// Message is received after importing is done in KeychainService
|
||||
ServiceProgressHandler serviceHandler = new ServiceProgressHandler(this) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
// get returned data bundle
|
||||
Bundle returnData = message.getData();
|
||||
|
||||
mIsRefreshing = false;
|
||||
|
||||
if (returnData == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
final ImportKeyResult result =
|
||||
returnData.getParcelable(OperationResult.EXTRA_RESULT);
|
||||
result.createNotify(ViewKeyActivity.this).show();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
mKeyList = entries;
|
||||
|
||||
// search config
|
||||
{
|
||||
Preferences prefs = Preferences.getPreferences(this);
|
||||
Preferences.CloudSearchPrefs cloudPrefs =
|
||||
new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver());
|
||||
data.putString(KeychainService.IMPORT_KEY_SERVER, cloudPrefs.keyserver);
|
||||
mKeyserver = cloudPrefs.keyserver;
|
||||
}
|
||||
|
||||
data.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, entries);
|
||||
|
||||
// Send all information needed to service to query keys in other thread
|
||||
Intent intent = new Intent(this, KeychainService.class);
|
||||
intent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(serviceHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
// start service with intent
|
||||
startService(intent);
|
||||
mOperationHelper = new CryptoOperationHelper<>(
|
||||
this, this, R.string.progress_importing);
|
||||
|
||||
mOperationHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
private void editKey(Uri dataUri) {
|
||||
@@ -986,4 +962,33 @@ public class ViewKeyActivity extends BaseNfcActivity implements
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
|
||||
}
|
||||
|
||||
// CryptoOperationHelper.Callback functions
|
||||
|
||||
@Override
|
||||
public ImportKeyringParcel createOperationInput() {
|
||||
return new ImportKeyringParcel(mKeyList, mKeyserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(ImportKeyResult result) {
|
||||
mIsRefreshing = false;
|
||||
result.createNotify(this).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
mIsRefreshing = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(ImportKeyResult result) {
|
||||
mIsRefreshing = false;
|
||||
result.createNotify(this).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -17,15 +17,12 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
@@ -48,9 +45,10 @@ import com.textuality.keybase.lib.User;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.KeybaseVerificationResult;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
@@ -59,7 +57,8 @@ import java.util.Hashtable;
|
||||
import java.util.List;
|
||||
|
||||
public class ViewKeyTrustFragment extends LoaderFragment implements
|
||||
LoaderManager.LoaderCallbacks<Cursor> {
|
||||
LoaderManager.LoaderCallbacks<Cursor>,
|
||||
CryptoOperationHelper.Callback<KeybaseVerificationParcel, KeybaseVerificationResult> {
|
||||
|
||||
public static final String ARG_DATA_URI = "uri";
|
||||
|
||||
@@ -76,6 +75,14 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
|
||||
// for retrieving the key we’re working on
|
||||
private Uri mDataUri;
|
||||
|
||||
private Proof mProof;
|
||||
|
||||
// for CryptoOperationHelper,Callback
|
||||
private String mKeybaseProof;
|
||||
private String mKeybaseFingerprint;
|
||||
private CryptoOperationHelper<KeybaseVerificationParcel, KeybaseVerificationResult>
|
||||
mKeybaseOpHelper;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) {
|
||||
View root = super.onCreateView(inflater, superContainer, savedInstanceState);
|
||||
@@ -349,112 +356,130 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
|
||||
}
|
||||
|
||||
private void verify(final Proof proof, final String fingerprint) {
|
||||
Intent intent = new Intent(getActivity(), KeychainService.class);
|
||||
Bundle data = new Bundle();
|
||||
intent.setAction(KeychainService.ACTION_VERIFY_KEYBASE_PROOF);
|
||||
|
||||
data.putString(KeychainService.KEYBASE_PROOF, proof.toString());
|
||||
data.putString(KeychainService.KEYBASE_REQUIRED_FINGERPRINT, fingerprint);
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
mProof = proof;
|
||||
mKeybaseProof = proof.toString();
|
||||
mKeybaseFingerprint = fingerprint;
|
||||
|
||||
mProofVerifyDetail.setVisibility(View.GONE);
|
||||
|
||||
// Create a new Messenger for the communication back after proof work is done
|
||||
ServiceProgressHandler handler = new ServiceProgressHandler(getActivity()) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
mKeybaseOpHelper = new CryptoOperationHelper<>(this, this,
|
||||
R.string.progress_verifying_signature);
|
||||
mKeybaseOpHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
Bundle returnData = message.getData();
|
||||
String msg = returnData.getString(ServiceProgressHandler.DATA_MESSAGE);
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder();
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (mKeybaseOpHelper != null) {
|
||||
mKeybaseOpHelper.handleActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
|
||||
if ((msg != null) && msg.equals("OK")) {
|
||||
// CryptoOperationHelper.Callback methods
|
||||
@Override
|
||||
public KeybaseVerificationParcel createOperationInput() {
|
||||
return new KeybaseVerificationParcel(mKeybaseProof, mKeybaseFingerprint);
|
||||
}
|
||||
|
||||
//yay
|
||||
String proofUrl = returnData.getString(ServiceProgressHandler.KEYBASE_PROOF_URL);
|
||||
String presenceUrl = returnData.getString(ServiceProgressHandler.KEYBASE_PRESENCE_URL);
|
||||
String presenceLabel = returnData.getString(ServiceProgressHandler.KEYBASE_PRESENCE_LABEL);
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(KeybaseVerificationResult result) {
|
||||
|
||||
String proofLabel;
|
||||
switch (proof.getType()) {
|
||||
case Proof.PROOF_TYPE_TWITTER:
|
||||
proofLabel = getString(R.string.keybase_twitter_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_DNS:
|
||||
proofLabel = getString(R.string.keybase_dns_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_WEB_SITE:
|
||||
proofLabel = getString(R.string.keybase_web_site_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_GITHUB:
|
||||
proofLabel = getString(R.string.keybase_github_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_REDDIT:
|
||||
proofLabel = getString(R.string.keybase_reddit_proof);
|
||||
break;
|
||||
default:
|
||||
proofLabel = getString(R.string.keybase_a_post);
|
||||
break;
|
||||
}
|
||||
result.createNotify(getActivity()).show();
|
||||
|
||||
ssb.append(getString(R.string.keybase_proof_succeeded));
|
||||
StyleSpan bold = new StyleSpan(Typeface.BOLD);
|
||||
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
ssb.append("\n\n");
|
||||
int length = ssb.length();
|
||||
ssb.append(proofLabel);
|
||||
if (proofUrl != null) {
|
||||
URLSpan postLink = new URLSpan(proofUrl);
|
||||
ssb.setSpan(postLink, length, length + proofLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
if (Proof.PROOF_TYPE_DNS == proof.getType()) {
|
||||
ssb.append(" ").append(getString(R.string.keybase_for_the_domain)).append(" ");
|
||||
} else {
|
||||
ssb.append(" ").append(getString(R.string.keybase_fetched_from)).append(" ");
|
||||
}
|
||||
length = ssb.length();
|
||||
URLSpan presenceLink = new URLSpan(presenceUrl);
|
||||
ssb.append(presenceLabel);
|
||||
ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
if (Proof.PROOF_TYPE_REDDIT == proof.getType()) {
|
||||
ssb.append(", ").
|
||||
append(getString(R.string.keybase_reddit_attribution)).
|
||||
append(" “").append(proof.getHandle()).append("”, ");
|
||||
}
|
||||
ssb.append(" ").append(getString(R.string.keybase_contained_signature));
|
||||
} else {
|
||||
// verification failed!
|
||||
msg = returnData.getString(ServiceProgressHandler.DATA_ERROR);
|
||||
ssb.append(getString(R.string.keybase_proof_failure));
|
||||
if (msg == null) {
|
||||
msg = getString(R.string.keybase_unknown_proof_failure);
|
||||
}
|
||||
StyleSpan bold = new StyleSpan(Typeface.BOLD);
|
||||
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
ssb.append("\n\n").append(msg);
|
||||
}
|
||||
mProofVerifyHeader.setVisibility(View.VISIBLE);
|
||||
mProofVerifyDetail.setVisibility(View.VISIBLE);
|
||||
mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
mProofVerifyDetail.setText(ssb);
|
||||
}
|
||||
}
|
||||
};
|
||||
String proofUrl = result.mProofUrl;
|
||||
String presenceUrl = result.mPresenceUrl;
|
||||
String presenceLabel = result.mPresenceLabel;
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(handler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
Proof proof = mProof; // TODO: should ideally be contained in result
|
||||
|
||||
// show progress dialog
|
||||
handler.showProgressDialog(
|
||||
getString(R.string.progress_verifying_signature),
|
||||
ProgressDialog.STYLE_HORIZONTAL, false
|
||||
);
|
||||
String proofLabel;
|
||||
switch (proof.getType()) {
|
||||
case Proof.PROOF_TYPE_TWITTER:
|
||||
proofLabel = getString(R.string.keybase_twitter_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_DNS:
|
||||
proofLabel = getString(R.string.keybase_dns_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_WEB_SITE:
|
||||
proofLabel = getString(R.string.keybase_web_site_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_GITHUB:
|
||||
proofLabel = getString(R.string.keybase_github_proof);
|
||||
break;
|
||||
case Proof.PROOF_TYPE_REDDIT:
|
||||
proofLabel = getString(R.string.keybase_reddit_proof);
|
||||
break;
|
||||
default:
|
||||
proofLabel = getString(R.string.keybase_a_post);
|
||||
break;
|
||||
}
|
||||
|
||||
// start service with intent
|
||||
getActivity().startService(intent);
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder();
|
||||
|
||||
ssb.append(getString(R.string.keybase_proof_succeeded));
|
||||
StyleSpan bold = new StyleSpan(Typeface.BOLD);
|
||||
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
ssb.append("\n\n");
|
||||
int length = ssb.length();
|
||||
ssb.append(proofLabel);
|
||||
if (proofUrl != null) {
|
||||
URLSpan postLink = new URLSpan(proofUrl);
|
||||
ssb.setSpan(postLink, length, length + proofLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
if (Proof.PROOF_TYPE_DNS == proof.getType()) {
|
||||
ssb.append(" ").append(getString(R.string.keybase_for_the_domain)).append(" ");
|
||||
} else {
|
||||
ssb.append(" ").append(getString(R.string.keybase_fetched_from)).append(" ");
|
||||
}
|
||||
length = ssb.length();
|
||||
URLSpan presenceLink = new URLSpan(presenceUrl);
|
||||
ssb.append(presenceLabel);
|
||||
ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned
|
||||
.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
if (Proof.PROOF_TYPE_REDDIT == proof.getType()) {
|
||||
ssb.append(", ").
|
||||
append(getString(R.string.keybase_reddit_attribution)).
|
||||
append(" “").append(proof.getHandle()).append("”, ");
|
||||
}
|
||||
ssb.append(" ").append(getString(R.string.keybase_contained_signature));
|
||||
|
||||
displaySpannableResult(ssb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(KeybaseVerificationResult result) {
|
||||
|
||||
result.createNotify(getActivity()).show();
|
||||
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder();
|
||||
|
||||
ssb.append(getString(R.string.keybase_proof_failure));
|
||||
String msg = getString(result.getLog().getLast().mType.mMsgId);
|
||||
if (msg == null) {
|
||||
msg = getString(R.string.keybase_unknown_proof_failure);
|
||||
}
|
||||
StyleSpan bold = new StyleSpan(Typeface.BOLD);
|
||||
ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
ssb.append("\n\n").append(msg);
|
||||
|
||||
displaySpannableResult(ssb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void displaySpannableResult(SpannableStringBuilder ssb) {
|
||||
mProofVerifyHeader.setVisibility(View.VISIBLE);
|
||||
mProofVerifyDetail.setVisibility(View.VISIBLE);
|
||||
mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
mProofVerifyDetail.setText(ssb);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,12 +21,8 @@ package org.sufficientlysecure.keychain.ui;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.LoaderManager.LoaderCallbacks;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
@@ -39,16 +35,16 @@ import android.widget.TextView;
|
||||
|
||||
import org.spongycastle.util.encoders.Hex;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.PromoteKeyResult;
|
||||
import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.service.PromoteKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
|
||||
|
||||
public class ViewKeyYubiKeyFragment extends Fragment
|
||||
public class ViewKeyYubiKeyFragment
|
||||
extends CryptoOperationFragment<PromoteKeyringParcel, PromoteKeyResult>
|
||||
implements LoaderCallbacks<Cursor> {
|
||||
|
||||
public static final String ARG_MASTER_KEY_ID = "master_key_id";
|
||||
@@ -60,6 +56,8 @@ public class ViewKeyYubiKeyFragment extends Fragment
|
||||
private String mUserId;
|
||||
private byte[] mCardAid;
|
||||
private long mMasterKeyId;
|
||||
private long[] mSubKeyIds;
|
||||
|
||||
private Button vButton;
|
||||
private TextView vStatus;
|
||||
|
||||
@@ -127,50 +125,15 @@ public class ViewKeyYubiKeyFragment extends Fragment
|
||||
}
|
||||
|
||||
public void promoteToSecretKey() {
|
||||
|
||||
ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
// get returned data bundle
|
||||
Bundle returnData = message.getData();
|
||||
|
||||
PromoteKeyResult result =
|
||||
returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT);
|
||||
|
||||
result.createNotify(getActivity()).show();
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// Send all information needed to service to decrypt in other thread
|
||||
Intent intent = new Intent(getActivity(), KeychainService.class);
|
||||
|
||||
// fill values for this action
|
||||
|
||||
intent.setAction(KeychainService.ACTION_PROMOTE_KEYRING);
|
||||
|
||||
Bundle data = new Bundle();
|
||||
data.putLong(KeychainService.PROMOTE_MASTER_KEY_ID, mMasterKeyId);
|
||||
data.putByteArray(KeychainService.PROMOTE_CARD_AID, mCardAid);
|
||||
long[] subKeyIds = new long[mFingerprints.length];
|
||||
for (int i = 0; i < subKeyIds.length; i++) {
|
||||
subKeyIds[i] = KeyFormattingUtils.getKeyIdFromFingerprint(mFingerprints[i]);
|
||||
}
|
||||
data.putLongArray(KeychainService.PROMOTE_SUBKEY_IDS, subKeyIds);
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(saveHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
// start service with intent
|
||||
getActivity().startService(intent);
|
||||
// mMasterKeyId and mCardAid are already set
|
||||
mSubKeyIds = subKeyIds;
|
||||
|
||||
cryptoOperation();
|
||||
}
|
||||
|
||||
public static final String[] PROJECTION = new String[]{
|
||||
@@ -240,4 +203,14 @@ public class ViewKeyYubiKeyFragment extends Fragment
|
||||
public void onLoaderReset(Loader<Cursor> loader) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PromoteKeyringParcel createOperationInput() {
|
||||
return new PromoteKeyringParcel(mMasterKeyId, mCardAid, mSubKeyIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCryptoOperationResult(PromoteKeyResult result) {
|
||||
result.createNotify(getActivity()).show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ImportKeysListEntry> {
|
||||
}
|
||||
|
||||
/** 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<ImportKeysListEntry> getSelectedEntries() {
|
||||
ArrayList<ImportKeysListEntry> result = new ArrayList<>();
|
||||
|
||||
@@ -36,7 +36,8 @@ public abstract class CachingCryptoOperationFragment <T extends Parcelable, S ex
|
||||
mCachedActionsParcel = null;
|
||||
}
|
||||
|
||||
protected abstract T createOperationInput();
|
||||
@Override
|
||||
public abstract T createOperationInput();
|
||||
|
||||
protected T getCachedActionsParcel() {
|
||||
return mCachedActionsParcel;
|
||||
@@ -46,7 +47,7 @@ public abstract class CachingCryptoOperationFragment <T extends Parcelable, S ex
|
||||
mCachedActionsParcel = cachedActionsParcel;
|
||||
}
|
||||
|
||||
protected void onCryptoOperationCancelled() {
|
||||
public void onCryptoOperationCancelled() {
|
||||
mCachedActionsParcel = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,217 +18,78 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.ui.base;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v4.app.Fragment;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.service.KeychainNewService;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
import org.sufficientlysecure.keychain.ui.NfcOperationActivity;
|
||||
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
|
||||
|
||||
|
||||
/**
|
||||
* All fragments executing crypto operations need to extend this class.
|
||||
*/
|
||||
public abstract class CryptoOperationFragment <T extends Parcelable, S extends OperationResult>
|
||||
extends Fragment {
|
||||
public abstract class CryptoOperationFragment<T extends Parcelable, S extends OperationResult>
|
||||
extends Fragment implements CryptoOperationHelper.Callback<T, S> {
|
||||
|
||||
public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
|
||||
public static final int REQUEST_CODE_NFC = 0x00008002;
|
||||
private CryptoOperationHelper<T, S> mOperationHelper;
|
||||
|
||||
private void initiateInputActivity(RequiredInputParcel requiredInput) {
|
||||
public CryptoOperationFragment() {
|
||||
|
||||
switch (requiredInput.mType) {
|
||||
case NFC_MOVE_KEY_TO_CARD:
|
||||
case NFC_DECRYPT:
|
||||
case NFC_SIGN: {
|
||||
Intent intent = new Intent(getActivity(), NfcOperationActivity.class);
|
||||
intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
|
||||
startActivityForResult(intent, REQUEST_CODE_NFC);
|
||||
return;
|
||||
}
|
||||
|
||||
case PASSPHRASE:
|
||||
case PASSPHRASE_SYMMETRIC: {
|
||||
Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class);
|
||||
intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, requiredInput);
|
||||
startActivityForResult(intent, REQUEST_CODE_PASSPHRASE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unhandled pending result!");
|
||||
mOperationHelper = new CryptoOperationHelper<>(this, this);
|
||||
}
|
||||
|
||||
public void setProgressMessageResource(int id) {
|
||||
mOperationHelper.setProgressMessageResource(id);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (resultCode == Activity.RESULT_CANCELED) {
|
||||
onCryptoOperationCancelled();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_PASSPHRASE: {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
CryptoInputParcel cryptoInput =
|
||||
data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
|
||||
cryptoOperation(cryptoInput);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case REQUEST_CODE_NFC: {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
CryptoInputParcel cryptoInput =
|
||||
data.getParcelableExtra(NfcOperationActivity.RESULT_DATA);
|
||||
cryptoOperation(cryptoInput);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
}
|
||||
mOperationHelper.handleActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
protected void dismissProgress() {
|
||||
|
||||
ProgressDialogFragment progressDialogFragment =
|
||||
(ProgressDialogFragment) getFragmentManager().findFragmentByTag("progressDialog");
|
||||
|
||||
if (progressDialogFragment == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
progressDialogFragment.dismissAllowingStateLoss();
|
||||
|
||||
}
|
||||
|
||||
protected abstract T createOperationInput();
|
||||
|
||||
protected void cryptoOperation(CryptoInputParcel cryptoInput) {
|
||||
cryptoOperation(cryptoInput, true);
|
||||
}
|
||||
@Override
|
||||
public abstract T createOperationInput();
|
||||
|
||||
protected void cryptoOperation() {
|
||||
cryptoOperation(new CryptoInputParcel());
|
||||
}
|
||||
|
||||
protected void cryptoOperation(CryptoInputParcel cryptoInput) {
|
||||
cryptoOperation(cryptoInput);
|
||||
}
|
||||
|
||||
protected void cryptoOperation(CryptoInputParcel cryptoInput, boolean showProgress) {
|
||||
|
||||
T operationInput = createOperationInput();
|
||||
if (operationInput == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send all information needed to service to edit key in other thread
|
||||
Intent intent = new Intent(getActivity(), KeychainNewService.class);
|
||||
|
||||
intent.putExtra(KeychainNewService.EXTRA_OPERATION_INPUT, operationInput);
|
||||
intent.putExtra(KeychainNewService.EXTRA_CRYPTO_INPUT, cryptoInput);
|
||||
|
||||
ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
|
||||
// get returned data bundle
|
||||
Bundle returnData = message.getData();
|
||||
if (returnData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final OperationResult result =
|
||||
returnData.getParcelable(OperationResult.EXTRA_RESULT);
|
||||
|
||||
onHandleResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetProgress(String msg, int progress, int max) {
|
||||
// allow handling of progress in fragment, or delegate upwards
|
||||
if ( ! onCryptoSetProgress(msg, progress, max)) {
|
||||
super.onSetProgress(msg, progress, max);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(saveHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
if (showProgress) {
|
||||
saveHandler.showProgressDialog(
|
||||
getString(R.string.progress_building_key),
|
||||
ProgressDialog.STYLE_HORIZONTAL, false);
|
||||
}
|
||||
|
||||
getActivity().startService(intent);
|
||||
|
||||
mOperationHelper.cryptoOperation(cryptoInput, showProgress);
|
||||
}
|
||||
|
||||
protected void onCryptoOperationResult(S result) {
|
||||
if (result.success()) {
|
||||
onCryptoOperationSuccess(result);
|
||||
} else {
|
||||
onCryptoOperationError(result);
|
||||
}
|
||||
}
|
||||
|
||||
abstract protected void onCryptoOperationSuccess(S result);
|
||||
|
||||
protected void onCryptoOperationError(S result) {
|
||||
result.createNotify(getActivity()).show();
|
||||
}
|
||||
|
||||
protected void onCryptoOperationCancelled() {
|
||||
}
|
||||
|
||||
public void onHandleResult(OperationResult result) {
|
||||
|
||||
if (result instanceof InputPendingResult) {
|
||||
InputPendingResult pendingResult = (InputPendingResult) result;
|
||||
if (pendingResult.isPending()) {
|
||||
RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel();
|
||||
initiateInputActivity(requiredInput);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dismissProgress();
|
||||
|
||||
try {
|
||||
// noinspection unchecked, because type erasure :(
|
||||
onCryptoOperationResult((S) result);
|
||||
} catch (ClassCastException e) {
|
||||
throw new AssertionError("bad return class ("
|
||||
+ result.getClass().getSimpleName() + "), this is a programming error!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(S result) {
|
||||
onCryptoOperationResult(result);
|
||||
result.createNotify(getActivity()).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(S result) {
|
||||
onCryptoOperationResult(result);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* To be overriden by subclasses, if desired. Provides a way to access the method by the
|
||||
* same name in CryptoOperationHelper, if super.onCryptoOperationSuccess and
|
||||
* super.onCryptoOperationError are called at the start of the respective functions in the
|
||||
* subclass overriding them
|
||||
* @param result
|
||||
*/
|
||||
protected void onCryptoOperationResult(S result) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,326 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2015 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
||||
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
|
||||
*
|
||||
* 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.ui.base;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
import org.sufficientlysecure.keychain.ui.NfcOperationActivity;
|
||||
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
/**
|
||||
* Designed to be integrated into activities or fragments used for CryptoOperations.
|
||||
* Encapsulates the execution of a crypto operation and handling of input pending cases.s
|
||||
*
|
||||
* @param <T> The type of input parcel sent to the operation
|
||||
* @param <S> The type of result returned by the operation
|
||||
*/
|
||||
public class CryptoOperationHelper<T extends Parcelable, S extends OperationResult> {
|
||||
|
||||
public interface Callback <T extends Parcelable, S extends OperationResult> {
|
||||
T createOperationInput();
|
||||
void onCryptoOperationSuccess(S result);
|
||||
void onCryptoOperationCancelled();
|
||||
void onCryptoOperationError(S result);
|
||||
boolean onCryptoSetProgress(String msg, int progress, int max);
|
||||
}
|
||||
|
||||
public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
|
||||
public static final int REQUEST_CODE_NFC = 0x00008002;
|
||||
|
||||
// keeps track of request code used to start an activity from this CryptoOperationHelper.
|
||||
// this is necessary when multiple CryptoOperationHelpers are used in the same fragment/activity
|
||||
// otherwise all CryptoOperationHandlers may respond to the same onActivityResult
|
||||
private int mRequestedCode = -1;
|
||||
|
||||
private int mProgressMessageResource;
|
||||
|
||||
private FragmentActivity mActivity;
|
||||
private Fragment mFragment;
|
||||
private Callback<T, S> mCallback;
|
||||
|
||||
private boolean mUseFragment; // short hand for mActivity == null
|
||||
|
||||
/**
|
||||
* If OperationHelper is being integrated into an activity
|
||||
*
|
||||
* @param activity
|
||||
*/
|
||||
public CryptoOperationHelper(FragmentActivity activity, Callback<T, S> callback, int progressMessageString) {
|
||||
mActivity = activity;
|
||||
mUseFragment = false;
|
||||
mCallback = callback;
|
||||
mProgressMessageResource = progressMessageString;
|
||||
}
|
||||
|
||||
/**
|
||||
* if OperationHelper is being integrated into a fragment
|
||||
*
|
||||
* @param fragment
|
||||
*/
|
||||
public CryptoOperationHelper(Fragment fragment, Callback<T, S> callback, int progressMessageString) {
|
||||
mFragment = fragment;
|
||||
mUseFragment = true;
|
||||
mProgressMessageResource = progressMessageString;
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* if OperationHelper is being integrated into a fragment with default message for the progress dialog
|
||||
*
|
||||
* @param fragment
|
||||
*/
|
||||
public CryptoOperationHelper(Fragment fragment, Callback<T, S> callback) {
|
||||
mFragment = fragment;
|
||||
mUseFragment = true;
|
||||
mProgressMessageResource = R.string.progress_building_key;
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
public void setProgressMessageResource(int id) {
|
||||
mProgressMessageResource = id;
|
||||
}
|
||||
|
||||
private void initiateInputActivity(RequiredInputParcel requiredInput) {
|
||||
|
||||
Activity activity = mUseFragment ? mFragment.getActivity() : mActivity;
|
||||
|
||||
switch (requiredInput.mType) {
|
||||
case NFC_MOVE_KEY_TO_CARD:
|
||||
case NFC_DECRYPT:
|
||||
case NFC_SIGN: {
|
||||
Intent intent = new Intent(activity, NfcOperationActivity.class);
|
||||
intent.putExtra(NfcOperationActivity.EXTRA_REQUIRED_INPUT, requiredInput);
|
||||
mRequestedCode = REQUEST_CODE_NFC;
|
||||
if (mUseFragment) {
|
||||
mFragment.startActivityForResult(intent, mRequestedCode);
|
||||
} else {
|
||||
mActivity.startActivityForResult(intent, mRequestedCode);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case PASSPHRASE:
|
||||
case PASSPHRASE_SYMMETRIC: {
|
||||
Intent intent = new Intent(activity, PassphraseDialogActivity.class);
|
||||
intent.putExtra(PassphraseDialogActivity.EXTRA_REQUIRED_INPUT, requiredInput);
|
||||
mRequestedCode = REQUEST_CODE_PASSPHRASE;
|
||||
if (mUseFragment) {
|
||||
mFragment.startActivityForResult(intent, mRequestedCode);
|
||||
} else {
|
||||
mActivity.startActivityForResult(intent, mRequestedCode);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unhandled pending result!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts the result of an activity started by this helper. Returns true if requestCode is recognized,
|
||||
* false otherwise.
|
||||
*
|
||||
* @param requestCode
|
||||
* @param resultCode
|
||||
* @param data
|
||||
* @return true if requestCode was recognized, false otherwise
|
||||
*/
|
||||
public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
Log.d(Constants.TAG, "received activity result in OperationHelper");
|
||||
|
||||
if (mRequestedCode != requestCode) {
|
||||
// this wasn't meant for us to handle
|
||||
return false;
|
||||
} else {
|
||||
mRequestedCode = -1;
|
||||
}
|
||||
if (resultCode == Activity.RESULT_CANCELED) {
|
||||
mCallback.onCryptoOperationCancelled();
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_PASSPHRASE: {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
CryptoInputParcel cryptoInput =
|
||||
data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT);
|
||||
cryptoOperation(cryptoInput);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case REQUEST_CODE_NFC: {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
CryptoInputParcel cryptoInput =
|
||||
data.getParcelableExtra(NfcOperationActivity.RESULT_DATA);
|
||||
cryptoOperation(cryptoInput);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void dismissProgress() {
|
||||
FragmentManager fragmentManager =
|
||||
mUseFragment ? mFragment.getFragmentManager() :
|
||||
mActivity.getSupportFragmentManager();
|
||||
|
||||
if (fragmentManager == null) { // the fragment holding us has died
|
||||
// fragmentManager was null when used with DialogFragments. (they close on click?)
|
||||
return;
|
||||
}
|
||||
|
||||
ProgressDialogFragment progressDialogFragment =
|
||||
(ProgressDialogFragment) fragmentManager.findFragmentByTag(
|
||||
ServiceProgressHandler.TAG_PROGRESS_DIALOG);
|
||||
|
||||
if (progressDialogFragment == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
progressDialogFragment.dismissAllowingStateLoss();
|
||||
|
||||
}
|
||||
|
||||
public void cryptoOperation(CryptoInputParcel cryptoInput, boolean showProgress) {
|
||||
|
||||
FragmentActivity activity = mUseFragment ? mFragment.getActivity() : mActivity;
|
||||
|
||||
T operationInput = mCallback.createOperationInput();
|
||||
if (operationInput == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send all information needed to service to edit key in other thread
|
||||
Intent intent = new Intent(activity, KeychainService.class);
|
||||
|
||||
intent.putExtra(KeychainService.EXTRA_OPERATION_INPUT, operationInput);
|
||||
intent.putExtra(KeychainService.EXTRA_CRYPTO_INPUT, cryptoInput);
|
||||
|
||||
ServiceProgressHandler saveHandler = new ServiceProgressHandler(activity) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
|
||||
// get returned data bundle
|
||||
Bundle returnData = message.getData();
|
||||
if (returnData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final OperationResult result =
|
||||
returnData.getParcelable(OperationResult.EXTRA_RESULT);
|
||||
|
||||
onHandleResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetProgress(String msg, int progress, int max) {
|
||||
// allow handling of progress in fragment, or delegate upwards
|
||||
if ( ! mCallback.onCryptoSetProgress(msg, progress, max)) {
|
||||
super.onSetProgress(msg, progress, max);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(saveHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
if (showProgress) {
|
||||
saveHandler.showProgressDialog(
|
||||
activity.getString(mProgressMessageResource),
|
||||
ProgressDialog.STYLE_HORIZONTAL, false);
|
||||
}
|
||||
|
||||
activity.startService(intent);
|
||||
}
|
||||
|
||||
public void cryptoOperation(CryptoInputParcel cryptoInputParcel) {
|
||||
cryptoOperation(cryptoInputParcel, true);
|
||||
}
|
||||
|
||||
public void cryptoOperation() {
|
||||
cryptoOperation(new CryptoInputParcel());
|
||||
}
|
||||
|
||||
protected void onCryptoOperationResult(S result) {
|
||||
if (result.success()) {
|
||||
mCallback.onCryptoOperationSuccess(result);
|
||||
} else {
|
||||
mCallback.onCryptoOperationError(result);
|
||||
}
|
||||
}
|
||||
|
||||
public void onHandleResult(OperationResult result) {
|
||||
Log.d(Constants.TAG, "Handling result in OperationHelper success: " + result.success());
|
||||
|
||||
if (result instanceof InputPendingResult) {
|
||||
InputPendingResult pendingResult = (InputPendingResult) result;
|
||||
if (pendingResult.isPending()) {
|
||||
|
||||
RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel();
|
||||
initiateInputActivity(requiredInput);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dismissProgress();
|
||||
|
||||
try {
|
||||
// noinspection unchecked, because type erasure :(
|
||||
onCryptoOperationResult((S) result);
|
||||
} catch (ClassCastException e) {
|
||||
throw new AssertionError("bad return class ("
|
||||
+ result.getClass().getSimpleName() + "), this is a programming error!");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@
|
||||
package org.sufficientlysecure.keychain.ui.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
@@ -33,16 +32,20 @@ import android.widget.TextView;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.DeleteResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.DeleteKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class DeleteKeyDialogFragment extends DialogFragment {
|
||||
public class DeleteKeyDialogFragment extends DialogFragment
|
||||
implements CryptoOperationHelper.Callback<DeleteKeyringParcel, DeleteResult> {
|
||||
private static final String ARG_MESSENGER = "messenger";
|
||||
private static final String ARG_DELETE_MASTER_KEY_IDS = "delete_master_key_ids";
|
||||
|
||||
@@ -52,6 +55,13 @@ public class DeleteKeyDialogFragment extends DialogFragment {
|
||||
private TextView mMainMessage;
|
||||
private View mInflateView;
|
||||
|
||||
private Messenger mMessenger;
|
||||
|
||||
// for CryptoOperationHelper.Callback
|
||||
private long[] mMasterKeyIds;
|
||||
private boolean mHasSecret;
|
||||
private CryptoOperationHelper<DeleteKeyringParcel, DeleteResult> mDeleteOpHelper;
|
||||
|
||||
/**
|
||||
* Creates new instance of this delete file dialog fragment
|
||||
*/
|
||||
@@ -67,10 +77,18 @@ public class DeleteKeyDialogFragment extends DialogFragment {
|
||||
return frag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (mDeleteOpHelper != null) {
|
||||
mDeleteOpHelper.handleActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final FragmentActivity activity = getActivity();
|
||||
final Messenger messenger = getArguments().getParcelable(ARG_MESSENGER);
|
||||
mMessenger = getArguments().getParcelable(ARG_MESSENGER);
|
||||
|
||||
final long[] masterKeyIds = getArguments().getLongArray(ARG_DELETE_MASTER_KEY_IDS);
|
||||
|
||||
@@ -129,47 +147,16 @@ public class DeleteKeyDialogFragment extends DialogFragment {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
|
||||
// Send all information needed to service to import key in other thread
|
||||
Intent intent = new Intent(getActivity(), KeychainService.class);
|
||||
mMasterKeyIds = masterKeyIds;
|
||||
mHasSecret = hasSecret;
|
||||
|
||||
intent.setAction(KeychainService.ACTION_DELETE);
|
||||
|
||||
// Message is received after importing is done in KeychainService
|
||||
ServiceProgressHandler saveHandler = new ServiceProgressHandler(getActivity()) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
super.handleMessage(message);
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
try {
|
||||
Message msg = Message.obtain();
|
||||
msg.copyFrom(message);
|
||||
messenger.send(msg);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(Constants.TAG, "messenger error", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
data.putLongArray(KeychainService.DELETE_KEY_LIST, masterKeyIds);
|
||||
data.putBoolean(KeychainService.DELETE_IS_SECRET, hasSecret);
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(saveHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
// show progress dialog
|
||||
saveHandler.showProgressDialog(getString(R.string.progress_deleting),
|
||||
ProgressDialog.STYLE_HORIZONTAL, true);
|
||||
|
||||
// start service with intent
|
||||
getActivity().startService(intent);
|
||||
|
||||
dismiss();
|
||||
mDeleteOpHelper = new CryptoOperationHelper<>
|
||||
(DeleteKeyDialogFragment.this, DeleteKeyDialogFragment.this,
|
||||
R.string.progress_deleting);
|
||||
mDeleteOpHelper.cryptoOperation();
|
||||
// do NOT dismiss here, it'll give
|
||||
// OperationHelper a null fragmentManager
|
||||
// dismiss();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
@@ -183,4 +170,42 @@ public class DeleteKeyDialogFragment extends DialogFragment {
|
||||
return builder.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteKeyringParcel createOperationInput() {
|
||||
return new DeleteKeyringParcel(mMasterKeyIds, mHasSecret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(DeleteResult result) {
|
||||
handleResult(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(DeleteResult result) {
|
||||
handleResult(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void handleResult(DeleteResult result) {
|
||||
try {
|
||||
Bundle data = new Bundle();
|
||||
data.putParcelable(OperationResult.EXTRA_RESULT, result);
|
||||
Message msg = Message.obtain();
|
||||
msg.arg1 = ServiceProgressHandler.MessageStatus.OKAY.ordinal();
|
||||
msg.setData(data);
|
||||
mMessenger.send(msg);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(Constants.TAG, "messenger error", e);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,15 +18,14 @@
|
||||
package org.sufficientlysecure.keychain.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Messenger;
|
||||
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
|
||||
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
|
||||
import org.sufficientlysecure.keychain.keyimport.Keyserver;
|
||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
@@ -35,24 +34,35 @@ import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public class EmailKeyHelper {
|
||||
// to import keys, simply use CryptoOperationHelper with this callback
|
||||
public abstract class ImportContactKeysCallback
|
||||
implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
||||
|
||||
public static void importContacts(Context context, Messenger messenger) {
|
||||
importAll(context, messenger, ContactHelper.getContactMails(context));
|
||||
}
|
||||
private ArrayList<ParcelableKeyRing> mKeyList;
|
||||
private String mKeyserver;
|
||||
|
||||
public static void importAll(Context context, Messenger messenger, List<String> mails) {
|
||||
// Collect all candidates as ImportKeysListEntry (set for deduplication)
|
||||
Set<ImportKeysListEntry> entries = new HashSet<>();
|
||||
for (String mail : mails) {
|
||||
entries.addAll(getEmailKeys(context, mail));
|
||||
public ImportContactKeysCallback(Context context, String keyserver) {
|
||||
this(context, ContactHelper.getContactMails(context), keyserver);
|
||||
}
|
||||
|
||||
// Put them in a list and import
|
||||
ArrayList<ParcelableKeyRing> keys = new ArrayList<>(entries.size());
|
||||
for (ImportKeysListEntry entry : entries) {
|
||||
keys.add(new ParcelableKeyRing(entry.getFingerprintHex(), entry.getKeyIdHex(), null));
|
||||
public ImportContactKeysCallback(Context context, List<String> mails, String keyserver) {
|
||||
Set<ImportKeysListEntry> entries = new HashSet<>();
|
||||
for (String mail : mails) {
|
||||
entries.addAll(getEmailKeys(context, mail));
|
||||
}
|
||||
|
||||
// Put them in a list and import
|
||||
ArrayList<ParcelableKeyRing> keys = new ArrayList<>(entries.size());
|
||||
for (ImportKeysListEntry entry : entries) {
|
||||
keys.add(new ParcelableKeyRing(entry.getFingerprintHex(), entry.getKeyIdHex(), null));
|
||||
}
|
||||
mKeyList = keys;
|
||||
mKeyserver = keyserver;
|
||||
}
|
||||
@Override
|
||||
public ImportKeyringParcel createOperationInput() {
|
||||
return new ImportKeyringParcel(mKeyList, mKeyserver);
|
||||
}
|
||||
importKeys(context, messenger, keys);
|
||||
}
|
||||
|
||||
public static Set<ImportKeysListEntry> getEmailKeys(Context context, String mail) {
|
||||
@@ -78,17 +88,6 @@ public class EmailKeyHelper {
|
||||
return keys;
|
||||
}
|
||||
|
||||
private static void importKeys(Context context, Messenger messenger, ArrayList<ParcelableKeyRing> keys) {
|
||||
Intent importIntent = new Intent(context, KeychainService.class);
|
||||
importIntent.setAction(KeychainService.ACTION_IMPORT_KEYRING);
|
||||
Bundle importData = new Bundle();
|
||||
importData.putParcelableArrayList(KeychainService.IMPORT_KEY_LIST, keys);
|
||||
importIntent.putExtra(KeychainService.EXTRA_DATA, importData);
|
||||
importIntent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
context.startService(importIntent);
|
||||
}
|
||||
|
||||
public static List<ImportKeysListEntry> getEmailKeys(String mail, Keyserver keyServer) {
|
||||
Set<ImportKeysListEntry> keys = new HashSet<>();
|
||||
try {
|
||||
|
||||
@@ -17,26 +17,26 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.util;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.ExportResult;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class ExportHelper {
|
||||
public class ExportHelper
|
||||
implements CryptoOperationHelper.Callback <ExportKeyringParcel, ExportResult> {
|
||||
protected File mExportFile;
|
||||
|
||||
FragmentActivity mActivity;
|
||||
|
||||
private CryptoOperationHelper<ExportKeyringParcel, ExportResult> mExportOpHelper;
|
||||
private boolean mExportSecret;
|
||||
private long[] mMasterKeyIds;
|
||||
|
||||
public ExportHelper(FragmentActivity activity) {
|
||||
super();
|
||||
this.mActivity = activity;
|
||||
@@ -71,60 +71,43 @@ public class ExportHelper {
|
||||
}, mActivity.getSupportFragmentManager() ,title, message, exportFile, checkMsg);
|
||||
}
|
||||
|
||||
// TODO: If ExportHelper requires pending data (see CryptoOPerationHelper), activities using
|
||||
// TODO: this class should be able to call mExportOpHelper.handleActivity
|
||||
|
||||
/**
|
||||
* Export keys
|
||||
*/
|
||||
public void exportKeys(long[] masterKeyIds, boolean exportSecret) {
|
||||
Log.d(Constants.TAG, "exportKeys started");
|
||||
mExportSecret = exportSecret;
|
||||
mMasterKeyIds = masterKeyIds; // if masterKeyIds is null it means export all
|
||||
|
||||
// Send all information needed to service to export key in other thread
|
||||
final Intent intent = new Intent(mActivity, KeychainService.class);
|
||||
|
||||
intent.setAction(KeychainService.ACTION_EXPORT_KEYRING);
|
||||
|
||||
// fill values for this action
|
||||
Bundle data = new Bundle();
|
||||
|
||||
data.putString(KeychainService.EXPORT_FILENAME, mExportFile.getAbsolutePath());
|
||||
data.putBoolean(KeychainService.EXPORT_SECRET, exportSecret);
|
||||
|
||||
if (masterKeyIds == null) {
|
||||
data.putBoolean(KeychainService.EXPORT_ALL, true);
|
||||
} else {
|
||||
data.putLongArray(KeychainService.EXPORT_KEY_RING_MASTER_KEY_ID, masterKeyIds);
|
||||
}
|
||||
|
||||
intent.putExtra(KeychainService.EXTRA_DATA, data);
|
||||
|
||||
// Message is received after exporting is done in KeychainService
|
||||
ServiceProgressHandler exportHandler = new ServiceProgressHandler(mActivity) {
|
||||
@Override
|
||||
public void handleMessage(Message message) {
|
||||
// handle messages by standard KeychainIntentServiceHandler first
|
||||
super.handleMessage(message);
|
||||
|
||||
if (message.arg1 == MessageStatus.OKAY.ordinal()) {
|
||||
// get returned data bundle
|
||||
Bundle data = message.getData();
|
||||
|
||||
ExportResult result = data.getParcelable(ExportResult.EXTRA_RESULT);
|
||||
result.createNotify(mActivity).show();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(exportHandler);
|
||||
intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger);
|
||||
|
||||
// show progress dialog
|
||||
exportHandler.showProgressDialog(
|
||||
mActivity.getString(R.string.progress_exporting),
|
||||
ProgressDialog.STYLE_HORIZONTAL, false
|
||||
);
|
||||
|
||||
// start service with intent
|
||||
mActivity.startService(intent);
|
||||
mExportOpHelper = new CryptoOperationHelper(mActivity, this, R.string.progress_exporting);
|
||||
mExportOpHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExportKeyringParcel createOperationInput() {
|
||||
return new ExportKeyringParcel(mMasterKeyIds, mExportSecret, mExportFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(ExportResult result) {
|
||||
result.createNotify(mActivity).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationCancelled() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(ExportResult result) {
|
||||
result.createNotify(mActivity).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1214,6 +1214,15 @@
|
||||
|
||||
<string name="msg_download_query_failed">"An error occurred when searching for keys."</string>
|
||||
|
||||
<!-- Messages for Keybase Verification operation -->
|
||||
<string name="msg_keybase_verification">"Attempting keybase verification for %s"</string>
|
||||
<string name="msg_keybase_error_no_prover">"No proof checker found for %s"</string>
|
||||
<string name="msg_keybase_error_fetching_evidence">"Problem with fetching proof"</string>
|
||||
<string name="msg_keybase_error_key_mismatch">"Key fingerprint doesn’t match that in proof post"</string>
|
||||
<string name="msg_keybase_error_dns_fail">"DNS TXT Record retrieval failed"</string>
|
||||
<string name="msg_keybase_error_specific">"%s"</string>
|
||||
<string name="msg_keybase_error_msg_payload_mismatch">"Decrypted proof post does not match expected value"</string>
|
||||
|
||||
<!-- Messages for Export Log operation -->
|
||||
<string name="msg_export_log_start">"Exporting log"</string>
|
||||
<string name="msg_export_log_error_fopen">"Error opening file"</string>
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user