diff options
| author | Dominik Schürmann <dominik@dominikschuermann.de> | 2015-06-30 18:49:46 +0200 | 
|---|---|---|
| committer | Dominik Schürmann <dominik@dominikschuermann.de> | 2015-06-30 18:49:46 +0200 | 
| commit | 88f8a388541404674703c9e1f69029ec2a09c148 (patch) | |
| tree | 6771ba3e13df96574e093668c77e34678511b5f7 /OpenKeychain/src | |
| parent | 677afa90fce133b81c195488e07f07a5a83e2f0b (diff) | |
| parent | a5257ec71d41532ac62594f59c10ba47d6a9ca38 (diff) | |
| download | open-keychain-88f8a388541404674703c9e1f69029ec2a09c148.tar.gz open-keychain-88f8a388541404674703c9e1f69029ec2a09c148.tar.bz2 open-keychain-88f8a388541404674703c9e1f69029ec2a09c148.zip  | |
Merge pull request #1362 from open-keychain/operation-migration
Migrated all operations to KeychainNewService
Diffstat (limited to 'OpenKeychain/src')
46 files changed, 2622 insertions, 2172 deletions
diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index 5b95423ac..b3754d360 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -710,9 +710,6 @@              android:exported="false"              android:process=":remote_api" />          <service -            android:name=".service.KeychainNewService" -            android:exported="false" /> -        <service              android:name=".service.KeychainService"              android:exported="false" /> diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java index 439260b74..0806e6a16 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/CertifyOperation.java @@ -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); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ConsolidateOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ConsolidateOperation.java new file mode 100644 index 000000000..bda574e0a --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ConsolidateOperation.java @@ -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); +        } +    } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java index 5ef04ab05..50b2ef69b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/DeleteOperation.java @@ -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(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java new file mode 100644 index 000000000..01a45bc79 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2012-2014 Dominik Schürmann <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; +            } +        } +    } +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java index cdda42dae..ace059dac 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java @@ -19,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. - * @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 + * iterator, so this should be ensured beforehand.   * + * @see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries()   */ -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(); +    public ImportKeyResult serialKeyRingImport(List<ParcelableKeyRing> entries, +                                               String keyServerUri) { + +        Iterator<ParcelableKeyRing> it = entries.iterator(); +        int numEntries = entries.size(); + +        return serialKeyRingImport(it, numEntries, keyServerUri, mProgressable); -            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, +                                               Progressable progressable) {          Iterator<ParcelableKeyRing> it = entries.iterator();          int numEntries = entries.size(); -        return importKeyRings(it, numEntries, keyServerUri); +        return serialKeyRingImport(it, numEntries, keyServerUri, progressable);      } -    public ImportKeyResult importKeyRings(ParcelableFileCache<ParcelableKeyRing> cache, String keyServerUri) { +    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); -        } else { -            log.add(LogType.MSG_EXPORT_ALL, 0); -        } +    public ImportKeyResult importKeys(ArrayList<ParcelableKeyRing> keyList, String keyServer) { -        // 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); -        } +        ImportKeyResult result; -        // check if storage is ready -        if (!FileHelper.isStorageMounted(outputFile)) { -            log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1); -            return new ExportResult(ExportResult.RESULT_ERROR, log); -        } +        if (keyList == null) {// import from file, do serially +            ParcelableFileCache<ParcelableKeyRing> cache = new ParcelableFileCache<>(mContext, +                    "key_import.pcl"); -        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(); +            result = serialKeyRingImport(cache, keyServer); +        } 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 +                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 { +                        return serialKeyRingImport(list, keyServer, ignoreProgressable); +                    } +                }; -            String selection = null, ids[] = null; +                importCompletionService.submit(importOperationCallable); +            } -            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(",?"); +            while (!accumulator.isImportFinished()) { // accumulate the results of each import +                try { +                    accumulator.accumulateKeyImport(importCompletionService.take().get()); +                } catch (InterruptedException | ExecutionException e) { +                    Log.e(Constants.TAG, "A key could not be imported during multi-threaded " + +                            "import", e); +                    // 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();                      }                  } - -                // put together selection string -                selection = Tables.KEY_RINGS_PUBLIC + "." + KeyRings.MASTER_KEY_ID -                        + " IN (" + placeholders + ")";              } +            return accumulator.getConsolidatedResult(); +        } +        return null; // TODO: Decide if we should just crash instead of returning null +    } -            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); -            } +    /** +     * 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); +        } -            int numKeys = cursor.getCount(); +        public int getTotalKeys() { +            return mTotalKeys; +        } -            updateProgress( -                    mContext.getResources().getQuantityString(R.plurals.progress_exporting_key, -                            numKeys), 0, numKeys); +        public int getImportedKeys() { +            return mImportedKeys; +        } -            // For each public masterKey id -            while (!cursor.isAfterLast()) { +        public synchronized void accumulateKeyImport(ImportKeyResult result) { +            mImportedKeys++; -                long keyId = cursor.getLong(0); -                ArmoredOutputStream arOutStream = null; +            mProgressable.setProgress(mImportedKeys, mTotalKeys); -                // Create an output stream -                try { -                    arOutStream = new ArmoredOutputStream(outStream); +            mImportLog.addAll(result.getLog().toList());//accumulates log +            mBadKeys += result.mBadKeys; +            mNewKeys += result.mNewKeys; +            mUpdatedKeys += result.mUpdatedKeys; +            mSecret += result.mSecret; -                    log.add(LogType.MSG_EXPORT_PUBLIC, 1, KeyFormattingUtils.beautifyKeyId(keyId)); +            long[] masterKeyIds = result.getImportedMasterKeyIds(); +            for (long masterKeyId : masterKeyIds) { +                mImportedMasterKeyIds.add(masterKeyId); +            } -                    byte[] data = cursor.getBlob(1); -                    CanonicalizedKeyRing ring = -                            UncachedKeyRing.decodeFromData(data).canonicalize(log, 2, true); -                    ring.encode(arOutStream); +            // 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; +        } -                    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; +        /** +         * 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 (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(); -                        } +                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;                      }                  } - -                updateProgress(progress++, numKeys); - -                cursor.moveToNext(); +                if (mImportLog.containsWarnings()) { +                    mResultType |= ImportKeyResult.RESULT_WARNINGS; +                }              } -            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(); +            long masterKeyIds[] = new long[mImportedMasterKeyIds.size()]; +            for (int i = 0; i < masterKeyIds.length; i++) { +                masterKeyIds[i] = mImportedMasterKeyIds.get(i);              } -        } - -        log.add(LogType.MSG_EXPORT_SUCCESS, 1); -        return new ExportResult(ExportResult.RESULT_OK, log, okPublic, okSecret); +            return new ImportKeyResult(mResultType, mImportLog, mNewKeys, mUpdatedKeys, mBadKeys, +                    mSecret, masterKeyIds); +        } +        public boolean isImportFinished() { +            return mTotalKeys == mImportedKeys; +        }      } -} +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java new file mode 100644 index 000000000..57b99951d --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java @@ -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; +    } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java index 558756378..efe0c466a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/PromoteKeyOperation.java @@ -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(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/KeybaseVerificationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/KeybaseVerificationResult.java new file mode 100644 index 000000000..420cbbf01 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/KeybaseVerificationResult.java @@ -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]; +        } +    }; +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index c9e427462..f0561bef2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -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), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index bf7014853..590c58f97 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -32,7 +32,7 @@ import org.spongycastle.bcpg.CompressionAlgorithmTags;  import org.sufficientlysecure.keychain.Constants;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; -import org.sufficientlysecure.keychain.operations.ImportExportOperation; +import org.sufficientlysecure.keychain.operations.ImportOperation;  import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;  import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;  import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; @@ -1248,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); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ConsolidateInputParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ConsolidateInputParcel.java new file mode 100644 index 000000000..15d109814 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ConsolidateInputParcel.java @@ -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]; +        } +    }; +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DeleteKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DeleteKeyringParcel.java new file mode 100644 index 000000000..b412a6e2b --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/DeleteKeyringParcel.java @@ -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]; +        } +    }; +} + diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java new file mode 100644 index 000000000..ef5b48df3 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ExportKeyringParcel.java @@ -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]; +        } +    }; +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java new file mode 100644 index 000000000..a41dd71cb --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java @@ -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]; +        } +    }; +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeybaseVerificationParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeybaseVerificationParcel.java new file mode 100644 index 000000000..1872191af --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeybaseVerificationParcel.java @@ -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]; +        } +    }; +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainNewService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainNewService.java deleted file mode 100644 index 9e33a1421..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainNewService.java +++ /dev/null @@ -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); -    } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java index ba877c2a2..1cd76b462 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java @@ -20,70 +20,35 @@ 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; +import java.util.concurrent.atomic.AtomicBoolean;  /**   * This Service contains all important long lasting operations for OpenKeychain. It receives Intents with @@ -91,75 +56,19 @@ import de.measite.minidns.record.TXT;   */  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,315 +93,62 @@ 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!"); -                    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!"); +                // 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;                  } -                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; -                } +                @SuppressWarnings("unchecked") // this is unchecked, we make sure it's the correct op above! +                OperationResult result = op.execute(inputParcel, cryptoInput); +                sendMessageToHandler(MessageStatus.OKAY, result); -                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); +        return START_NOT_STICKY;      }      private void sendMessageToHandler(MessageStatus status, Integer arg2, Bundle data) { @@ -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; -        } -    }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java new file mode 100644 index 000000000..d268c3694 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PromoteKeyringParcel.java @@ -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]; +        } +    }; +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java index 989b0c4bd..d294e5057 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ServiceProgressHandler.java @@ -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);              }          }); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java index e39a3a0bf..891c2268c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java @@ -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();      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java index 6a9bb7b11..7a473e49f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ConsolidateDialogActivity.java @@ -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?) - -                    // 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); + +        mRecovery = recovery; + +        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; +    }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java index 09ab1e663..52083a552 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -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); +        CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult> createKeyCallback +                = new CryptoOperationHelper.Callback<SaveKeyringParcel, EditKeyResult>() { +            @Override +            public SaveKeyringParcel createOperationInput() { +                return mSaveKeyringParcel; +            } -        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 EditKeyResult result = -                            returnData.getParcelable(OperationResult.EXTRA_RESULT); -                    if (result == null) { -                        Log.e(Constants.TAG, "result == null"); -                        return; -                    } - -                    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(); +            public void onCryptoOperationSuccess(EditKeyResult result) { + +                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();              } -        }; -        Bundle data = new Bundle(); -        data.putParcelable(KeychainService.EDIT_KEYRING_PARCEL, mSaveKeyringParcel); -        intent.putExtra(KeychainService.EXTRA_DATA, data); +            @Override +            public void onCryptoOperationCancelled() { + +            } -        // Create a new Messenger for the communication back -        Messenger messenger = new Messenger(saveHandler); -        intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger); +            @Override +            public void onCryptoOperationError(EditKeyResult result) { +                result.createNotify(getActivity()).show(); +            } -        saveHandler.showProgressDialog(getString(R.string.progress_building_key), -                ProgressDialog.STYLE_HORIZONTAL, false); +            @Override +            public boolean onCryptoSetProgress(String msg, int progress, int max) { +                return false; +            } +        }; + +        mCreateOpHelper = new CryptoOperationHelper<>(this, createKeyCallback, +                R.string.progress_building_key); -        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); +        // upload to favorite keyserver +        final String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver(); -        Bundle data = new Bundle(); +        CryptoOperationHelper.Callback<ExportKeyringParcel, ExportResult> callback +                = new CryptoOperationHelper.Callback<ExportKeyringParcel, ExportResult>() { -        // upload to favorite keyserver -        String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver(); -        data.putString(KeychainService.UPLOAD_KEY_SERVER, keyserver); +            @Override +            public ExportKeyringParcel createOperationInput() { +                return new ExportKeyringParcel(keyserver, blobUri); +            } -        intent.putExtra(KeychainService.EXTRA_DATA, data); +            @Override +            public void onCryptoOperationSuccess(ExportResult result) { +                handleResult(result); +            } -        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()) { -                    // 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(); - -                    Intent data = new Intent(); -                    data.putExtra(OperationResult.EXTRA_RESULT, saveKeyResult); -                    getActivity().setResult(Activity.RESULT_OK, data); -                    getActivity().finish(); -                } +            public void onCryptoOperationCancelled() { +              } -        }; -        // Create a new Messenger for the communication back -        Messenger messenger = new Messenger(saveHandler); -        intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger); +            @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; +            } +        }; -        // 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();      }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java index a575dc6b9..8de874ec5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java @@ -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); - -        // Create a new Messenger for the communication back -        Messenger messenger = new Messenger(saveHandler); -        intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger); +        super.setProgressMessageResource(R.string.progress_importing); -        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(); +    }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java index c8ae867b2..4eb8cd5e8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java @@ -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>() { + +            @Override +            public ImportKeyringParcel createOperationInput() { +                return new ImportKeyringParcel(keyList, keyserver); +            } + +            @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; +            } +        }; -        // Create a new Messenger for the communication back -        Messenger messenger = new Messenger(serviceHandler); -        intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger); +        mImportOpHelper = new CryptoOperationHelper<>(this, callback, R.string.progress_importing); -        getActivity().startService(intent); +        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); +    }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java index d2bff8336..96767463e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java @@ -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()) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java index 48aa3016d..1363d44f2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -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(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index d7c6b2049..215af5885 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -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(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java index e0629eb73..886c52651 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptTextFragment.java @@ -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 diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 07ab88b02..ba4f759e6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -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); - -                // 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 -                ); +                mKeyList = null; +                mKeyserver = null; +                mOperationHelper.cryptoOperation(); -                // 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); - -            // 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 -            ); +            mKeyList = keys; +            mKeyserver = sls.mCloudPrefs.keyserver; +            mOperationHelper.cryptoOperation(); -            // 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; +    }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java index 9f3beff43..da713d0d8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java @@ -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; + +        mImportOpHelper = new CryptoOperationHelper<>(this, this, R.string.progress_importing); + +        mImportOpHelper.cryptoOperation(); +    } + + +    // CryptoOperationHelper.Callback methods -        // 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); +    @Override +    public ImportKeyringParcel createOperationInput() { +        return new ImportKeyringParcel(mKeyList, mKeyserver); +    } -        // Create a new Messenger for the communication back -        Messenger messenger = new Messenger(serviceHandler); -        intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger); +    @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); +    } -        // show progress dialog -        serviceHandler.showProgressDialog( -                getString(R.string.progress_importing), -                ProgressDialog.STYLE_HORIZONTAL, true); +    @Override +    public void onCryptoOperationCancelled() { -        // start service with intent -        startService(intent); +    } + +    @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;      }      /** diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 780558b27..e038cf948 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -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()) { -            @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 ConsolidateResult result = -                            returnData.getParcelable(OperationResult.EXTRA_RESULT); -                    if (result == null) { -                        return; -                    } -                    result.createNotify(getActivity()).show(); -                } +        CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult> callback +                = new CryptoOperationHelper.Callback<ConsolidateInputParcel, ConsolidateResult>() { + +            @Override +            public ConsolidateInputParcel createOperationInput() { +                return new ConsolidateInputParcel(false); // we want to perform a full consolidate              } -        }; -        // Send all information needed to service to import key in other thread -        Intent intent = new Intent(getActivity(), KeychainService.class); +            @Override +            public void onCryptoOperationSuccess(ConsolidateResult result) { +                result.createNotify(getActivity()).show(); +            } -        intent.setAction(KeychainService.ACTION_CONSOLIDATE); +            @Override +            public void onCryptoOperationCancelled() { -        // fill values for this action -        Bundle data = new Bundle(); +            } -        intent.putExtra(KeychainService.EXTRA_DATA, data); +            @Override +            public void onCryptoOperationError(ConsolidateResult result) { +                result.createNotify(getActivity()).show(); +            } -        // Create a new Messenger for the communication back -        Messenger messenger = new Messenger(saveHandler); -        intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger); +            @Override +            public boolean onCryptoSetProgress(String msg, int progress, int max) { +                return false; +            } +        }; -        // show progress dialog -        saveHandler.showProgressDialog( -                getString(R.string.progress_importing), -                ProgressDialog.STYLE_HORIZONTAL, false); +        mConsolidateOpHelper = +                new CryptoOperationHelper<>(this, callback, R.string.progress_importing); -        // 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<>(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java index 7408135ae..a1cb77546 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java @@ -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); - -                // 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 -                ); +                mOperationHelper = +                        new CryptoOperationHelper(this, this, R.string.progress_importing); -                // 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; +    }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java index e4dff6083..8b49f3b96 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java @@ -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);      } -    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); +    @Override +    protected void onActivityResult(int requestCode, int resultCode, Intent data) { +        if (mUploadOpHelper != null) { +            mUploadOpHelper.handleActivityResult(requestCode, resultCode, data); +        } +        super.onActivityResult(requestCode, resultCode, data); +    } -        // set data uri as path to keyring +    private void uploadKey() {          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); - -        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); +        mKeyserver = server; -        // 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; +    }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 5f6a32e5a..d3849c892 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -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; +    }  }
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java index c33485adc..6052eec16 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java @@ -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); - -                if (message.arg1 == MessageStatus.OKAY.ordinal()) { -                    Bundle returnData = message.getData(); -                    String msg = returnData.getString(ServiceProgressHandler.DATA_MESSAGE); -                    SpannableStringBuilder ssb = new SpannableStringBuilder(); - -                    if ((msg != null) && msg.equals("OK")) { - -                        //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); - -                        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; -                        } +        mKeybaseOpHelper = new CryptoOperationHelper<>(this, this, +                R.string.progress_verifying_signature); +        mKeybaseOpHelper.cryptoOperation(); +    } -                        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); -                } -            } -        }; +    @Override +    public void onActivityResult(int requestCode, int resultCode, Intent data) { +        super.onActivityResult(requestCode, resultCode, data); +        if (mKeybaseOpHelper != null) { +            mKeybaseOpHelper.handleActivityResult(requestCode, resultCode, data); +        } +    } + +    // CryptoOperationHelper.Callback methods +    @Override +    public KeybaseVerificationParcel createOperationInput() { +        return new KeybaseVerificationParcel(mKeybaseProof, mKeybaseFingerprint); +    } + +    @Override +    public void onCryptoOperationSuccess(KeybaseVerificationResult result) { + +        result.createNotify(getActivity()).show(); + +        String proofUrl = result.mProofUrl; +        String presenceUrl = result.mPresenceUrl; +        String presenceLabel = result.mPresenceLabel; + +        Proof proof = mProof; // TODO: should ideally be contained in 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; +        } + +        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(); -        // Create a new Messenger for the communication back -        Messenger messenger = new Messenger(handler); -        intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger); +        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); +    } -        // show progress dialog -        handler.showProgressDialog( -                getString(R.string.progress_verifying_signature), -                ProgressDialog.STYLE_HORIZONTAL, false -        ); +    @Override +    public boolean onCryptoSetProgress(String msg, int progress, int max) { +        return false; +    } -        // start service with intent -        getActivity().startService(intent); +    private void displaySpannableResult(SpannableStringBuilder ssb) { +        mProofVerifyHeader.setVisibility(View.VISIBLE); +        mProofVerifyDetail.setVisibility(View.VISIBLE); +        mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance()); +        mProofVerifyDetail.setText(ssb);      }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java index ab25341d3..f8c3b59ea 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyYubiKeyFragment.java @@ -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(); +    }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index db88de676..93c6593ab 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -33,6 +33,7 @@ import android.widget.TextView;  import org.sufficientlysecure.keychain.R;  import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; +import org.sufficientlysecure.keychain.operations.ImportOperation;  import org.sufficientlysecure.keychain.pgp.KeyRing;  import org.sufficientlysecure.keychain.ui.util.FormattingUtils;  import org.sufficientlysecure.keychain.ui.util.Highlighter; @@ -92,8 +93,8 @@ public class ImportKeysAdapter extends ArrayAdapter<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<>(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java index 87c6ee3ca..17e4e6ede 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CachingCryptoOperationFragment.java @@ -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;      } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java index 245abc21f..19d808ba6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationFragment.java @@ -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 static final int REQUEST_CODE_PASSPHRASE = 0x00008001; -    public static final int REQUEST_CODE_NFC = 0x00008002; - -    private void initiateInputActivity(RequiredInputParcel requiredInput) { - -        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!"); -    } - -    @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); -            } -        } -    } +public abstract class CryptoOperationFragment<T extends Parcelable, S extends OperationResult> +        extends Fragment implements CryptoOperationHelper.Callback<T, S> { -    protected void dismissProgress() { +    private CryptoOperationHelper<T, S> mOperationHelper; -        ProgressDialogFragment progressDialogFragment = -                (ProgressDialogFragment) getFragmentManager().findFragmentByTag("progressDialog"); +    public CryptoOperationFragment() { -        if (progressDialogFragment == null) { -            return; -        } - -        progressDialogFragment.dismissAllowingStateLoss(); +        mOperationHelper = new CryptoOperationHelper<>(this, this); +    } +    public void setProgressMessageResource(int id) { +        mOperationHelper.setProgressMessageResource(id);      } -    protected abstract T createOperationInput(); -    protected void cryptoOperation(CryptoInputParcel cryptoInput) { -        cryptoOperation(cryptoInput, true); +    @Override +    public void onActivityResult(int requestCode, int resultCode, Intent data) { +        mOperationHelper.handleActivityResult(requestCode, resultCode, data);      } +    @Override +    public abstract T createOperationInput(); +      protected void cryptoOperation() {          cryptoOperation(new CryptoInputParcel());      } -    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); - +    protected void cryptoOperation(CryptoInputParcel cryptoInput) { +        cryptoOperation(cryptoInput);      } -    protected void onCryptoOperationResult(S result) { -        if (result.success()) { -            onCryptoOperationSuccess(result); -        } else { -            onCryptoOperationError(result); -        } +    protected void cryptoOperation(CryptoInputParcel cryptoInput, boolean showProgress) { +        mOperationHelper.cryptoOperation(cryptoInput, showProgress);      } -    abstract protected void onCryptoOperationSuccess(S result); +    public boolean onCryptoSetProgress(String msg, int progress, int max) { +        return false; +    } -    protected void onCryptoOperationError(S result) { +    @Override +    public void onCryptoOperationError(S result) { +        onCryptoOperationResult(result);          result.createNotify(getActivity()).show();      } -    protected void onCryptoOperationCancelled() { +    @Override +    public 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!"); -        } - +    @Override +    public void onCryptoOperationSuccess(S result) { +        onCryptoOperationResult(result);      } -    protected boolean onCryptoSetProgress(String msg, int progress, int max) { -        return false; +    /** +     * +     * 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) {      } -  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java new file mode 100644 index 000000000..240dd0972 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java @@ -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!"); +        } + +    } +}
\ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java index 076876b5b..7fc9da9ec 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java @@ -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       */ @@ -68,9 +78,17 @@ public class DeleteKeyDialogFragment extends DialogFragment {      }      @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); - -                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); +                mMasterKeyIds = masterKeyIds; +                mHasSecret = hasSecret; -                // 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(); +    }  } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java index bbc08a2aa..b814f72b2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java @@ -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 { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java index 2fd09dc79..9567fc9c0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java @@ -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); +        mExportOpHelper = new CryptoOperationHelper(mActivity, this, R.string.progress_exporting); +        mExportOpHelper.cryptoOperation(); +    } -                if (message.arg1 == MessageStatus.OKAY.ordinal()) { -                    // get returned data bundle -                    Bundle data = message.getData(); +    @Override +    public ExportKeyringParcel createOperationInput() { +        return new ExportKeyringParcel(mMasterKeyIds, mExportSecret, mExportFile.getAbsolutePath()); +    } -                    ExportResult result = data.getParcelable(ExportResult.EXTRA_RESULT); -                    result.createNotify(mActivity).show(); -                } -            } -        }; +    @Override +    public void onCryptoOperationSuccess(ExportResult result) { +        result.createNotify(mActivity).show(); +    } -        // Create a new Messenger for the communication back -        Messenger messenger = new Messenger(exportHandler); -        intent.putExtra(KeychainService.EXTRA_MESSENGER, messenger); +    @Override +    public void onCryptoOperationCancelled() { -        // show progress dialog -        exportHandler.showProgressDialog( -                mActivity.getString(R.string.progress_exporting), -                ProgressDialog.STYLE_HORIZONTAL, false -        ); +    } -        // start service with intent -        mActivity.startService(intent); +    @Override +    public void onCryptoOperationError(ExportResult result) { +        result.createNotify(mActivity).show();      } +    @Override +    public boolean onCryptoSetProgress(String msg, int progress, int max) { +        return false; +    }  } diff --git a/OpenKeychain/src/main/res/values/strings.xml b/OpenKeychain/src/main/res/values/strings.xml index 873b8f615..26f1216be 100644 --- a/OpenKeychain/src/main/res/values/strings.xml +++ b/OpenKeychain/src/main/res/values/strings.xml @@ -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> diff --git a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java index fa1288e08..a659dc7da 100644 --- a/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java +++ b/OpenKeychain/src/test/java/org/sufficientlysecure/keychain/operations/ExportTest.java @@ -30,7 +30,6 @@ import org.robolectric.annotation.Config;  import org.robolectric.shadows.ShadowLog;  import org.spongycastle.bcpg.sig.KeyFlags;  import org.spongycastle.jce.provider.BouncyCastleProvider; -import org.sufficientlysecure.keychain.BuildConfig;  import org.sufficientlysecure.keychain.WorkaroundBuildConfig;  import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult;  import org.sufficientlysecure.keychain.operations.results.ExportResult; @@ -127,7 +126,7 @@ public class ExportTest {      @Test      public void testExportAll() throws Exception { -        ImportExportOperation op = new ImportExportOperation(RuntimeEnvironment.application, +        ExportOperation op = new ExportOperation(RuntimeEnvironment.application,                  new ProviderHelper(RuntimeEnvironment.application), null);          // make sure there is a local cert (so the later checks that there are none are meaningful)  | 
