diff options
author | Daniel Hammann <github@honnel.de> | 2014-03-05 17:05:01 +0100 |
---|---|---|
committer | Daniel Hammann <github@honnel.de> | 2014-03-05 17:05:01 +0100 |
commit | bc5f2a9305d6a043133dd89ef5e3e886f220885a (patch) | |
tree | 5b51eddfe447831acde7cd43eb83813e86ac3852 /OpenPGP-Keychain/src/main/java | |
parent | 76876a57c216eff92b4f0417c9ca2e09663d451e (diff) | |
parent | a1230bbe53d279b44038268d05d85f86ae0ff840 (diff) | |
download | open-keychain-bc5f2a9305d6a043133dd89ef5e3e886f220885a.tar.gz open-keychain-bc5f2a9305d6a043133dd89ef5e3e886f220885a.tar.bz2 open-keychain-bc5f2a9305d6a043133dd89ef5e3e886f220885a.zip |
Merge branch 'master' of https://github.com/openpgp-keychain/openpgp-keychain
Diffstat (limited to 'OpenPGP-Keychain/src/main/java')
26 files changed, 902 insertions, 549 deletions
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java index fb97f3a5c..c568f462a 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java @@ -18,8 +18,8 @@ package org.sufficientlysecure.keychain.pgp; import android.content.Context; -import android.os.Bundle; +import org.openintents.openpgp.OpenPgpSignatureResult; import org.spongycastle.bcpg.ArmoredInputStream; import org.spongycastle.bcpg.SignatureSubpacketTags; import org.spongycastle.openpgp.PGPCompressedData; @@ -36,6 +36,7 @@ import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPSecretKey; +import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignatureList; import org.spongycastle.openpgp.PGPSignatureSubpacketVector; @@ -53,7 +54,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ProgressDialogUpdater; @@ -75,9 +76,10 @@ public class PgpDecryptVerify { private InputData data; private OutputStream outStream; - private ProgressDialogUpdater progress; - boolean assumeSymmetric; - String passphrase; + private ProgressDialogUpdater progressDialogUpdater; + private boolean assumeSymmetric; + private String passphrase; + private long enforcedKeyId; private PgpDecryptVerify(Builder builder) { // private Constructor can only be called from Builder @@ -85,9 +87,10 @@ public class PgpDecryptVerify { this.data = builder.data; this.outStream = builder.outStream; - this.progress = builder.progress; + this.progressDialogUpdater = builder.progressDialogUpdater; this.assumeSymmetric = builder.assumeSymmetric; this.passphrase = builder.passphrase; + this.enforcedKeyId = builder.enforcedKeyId; } public static class Builder { @@ -97,9 +100,10 @@ public class PgpDecryptVerify { private OutputStream outStream; // optional - private ProgressDialogUpdater progress = null; + private ProgressDialogUpdater progressDialogUpdater = null; private boolean assumeSymmetric = false; private String passphrase = ""; + private long enforcedKeyId = 0; public Builder(Context context, InputData data, OutputStream outStream) { this.context = context; @@ -107,8 +111,8 @@ public class PgpDecryptVerify { this.outStream = outStream; } - public Builder progress(ProgressDialogUpdater progress) { - this.progress = progress; + public Builder progressDialogUpdater(ProgressDialogUpdater progressDialogUpdater) { + this.progressDialogUpdater = progressDialogUpdater; return this; } @@ -122,20 +126,32 @@ public class PgpDecryptVerify { return this; } + /** + * Allow this key id alone for decryption. + * This means only ciphertexts encrypted for this private key can be decrypted. + * + * @param enforcedKeyId + * @return + */ + public Builder enforcedKeyId(long enforcedKeyId) { + this.enforcedKeyId = enforcedKeyId; + return this; + } + public PgpDecryptVerify build() { return new PgpDecryptVerify(this); } } public void updateProgress(int message, int current, int total) { - if (progress != null) { - progress.setProgress(message, current, total); + if (progressDialogUpdater != null) { + progressDialogUpdater.setProgress(message, current, total); } } public void updateProgress(int current, int total) { - if (progress != null) { - progress.setProgress(current, total); + if (progressDialogUpdater != null) { + progressDialogUpdater.setProgress(current, total); } } @@ -177,9 +193,8 @@ public class PgpDecryptVerify { * @throws PGPException * @throws SignatureException */ - public Bundle execute() + public PgpDecryptVerifyResult execute() throws IOException, PgpGeneralException, PGPException, SignatureException { - // automatically works with ascii armor input and binary InputStream in = PGPUtil.getDecoderStream(data.getInputStream()); if (in instanceof ArmoredInputStream) { @@ -207,9 +222,9 @@ public class PgpDecryptVerify { * @throws PGPException * @throws SignatureException */ - private Bundle decryptVerify(InputStream in) + private PgpDecryptVerifyResult decryptVerify(InputStream in) throws IOException, PgpGeneralException, PGPException, SignatureException { - Bundle returnData = new Bundle(); + PgpDecryptVerifyResult returnData = new PgpDecryptVerifyResult(); PGPObjectFactory pgpF = new PGPObjectFactory(in); PGPEncryptedDataList enc; @@ -277,9 +292,40 @@ public class PgpDecryptVerify { PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; secretKey = ProviderHelper.getPGPSecretKeyByKeyId(context, encData.getKeyID()); if (secretKey != null) { + // secret key exists in database + + // allow only a specific key for decryption? + if (enforcedKeyId != 0) { + // TODO: improve this code! get master key directly! + PGPSecretKeyRing secretKeyRing = ProviderHelper.getPGPSecretKeyRingByKeyId(context, encData.getKeyID()); + long masterKeyId = PgpKeyHelper.getMasterKey(secretKeyRing).getKeyID(); + Log.d(Constants.TAG, "encData.getKeyID():" + encData.getKeyID()); + Log.d(Constants.TAG, "enforcedKeyId: " + enforcedKeyId); + Log.d(Constants.TAG, "masterKeyId: " + masterKeyId); + + if (enforcedKeyId != masterKeyId) { + throw new PgpGeneralException(context.getString(R.string.error_no_secret_key_found)); + } + } + pbe = encData; + + // if no passphrase was explicitly set try to get it from the cache service + if (passphrase == null) { + // returns "" if key has no passphrase + passphrase = PassphraseCacheService.getCachedPassphrase(context, encData.getKeyID()); + + // if passphrase was not cached, return here indicating that a passphrase is missing! + if (passphrase == null) { + returnData.setKeyPassphraseNeeded(true); + return returnData; + } + } + break; } + + } } @@ -317,6 +363,7 @@ public class PgpDecryptVerify { PGPObjectFactory plainFact = new PGPObjectFactory(clear); Object dataChunk = plainFact.nextObject(); PGPOnePassSignature signature = null; + OpenPgpSignatureResult signatureResult = null; PGPPublicKey signatureKey = null; int signatureIndex = -1; @@ -334,7 +381,7 @@ public class PgpDecryptVerify { if (dataChunk instanceof PGPOnePassSignatureList) { updateProgress(R.string.progress_processing_signature, currentProgress, 100); - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true); + signatureResult = new OpenPgpSignatureResult(); PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk; for (int i = 0; i < sigList.size(); ++i) { signature = sigList.get(i); @@ -354,12 +401,12 @@ public class PgpDecryptVerify { if (signKeyRing != null) { userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing)); } - returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId); + signatureResult.setUserId(userId); break; } } - returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); + signatureResult.setKeyId(signatureKeyId); if (signature != null) { JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() @@ -367,7 +414,7 @@ public class PgpDecryptVerify { signature.init(contentVerifierBuilderProvider, signatureKey); } else { - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true); + signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY); } dataChunk = plainFact.nextObject(); @@ -405,8 +452,7 @@ public class PgpDecryptVerify { try { signature.update(buffer, 0, n); } catch (SignatureException e) { - returnData - .putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false); + signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_ERROR); signature = null; } } @@ -430,17 +476,20 @@ public class PgpDecryptVerify { PGPSignature messageSignature = signatureList.get(signatureIndex); // these are not cleartext signatures! - returnData.putBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, false); + // TODO: what about binary signatures? + signatureResult.setSignatureOnly(false); //Now check binding signatures - boolean keyBinding_isok = verifyKeyBinding(context, messageSignature, signatureKey); - boolean sig_isok = signature.verify(messageSignature); + boolean validKeyBinding = verifyKeyBinding(context, messageSignature, signatureKey); + boolean validSignature = signature.verify(messageSignature); - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, keyBinding_isok & sig_isok); + // TODO: implement CERTIFIED! + if (validKeyBinding & validSignature) { + signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED); + } } } - // TODO: test if this integrity really check works! if (encryptedData.isIntegrityProtected()) { updateProgress(R.string.progress_verifying_integrity, 95, 100); @@ -455,9 +504,12 @@ public class PgpDecryptVerify { } else { // no integrity check Log.e(Constants.TAG, "Encrypted data was not integrity protected!"); + // TODO: inform user? } updateProgress(R.string.progress_done, 100, 100); + + returnData.setSignatureResult(signatureResult); return returnData; } @@ -474,11 +526,12 @@ public class PgpDecryptVerify { * @throws PGPException * @throws SignatureException */ - private Bundle verifyCleartextSignature(ArmoredInputStream aIn) + private PgpDecryptVerifyResult verifyCleartextSignature(ArmoredInputStream aIn) throws IOException, PgpGeneralException, PGPException, SignatureException { - Bundle returnData = new Bundle(); + PgpDecryptVerifyResult returnData = new PgpDecryptVerifyResult(); + OpenPgpSignatureResult signatureResult = new OpenPgpSignatureResult(); // cleartext signatures are never encrypted ;) - returnData.putBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, true); + signatureResult.setSignatureOnly(true); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -504,8 +557,6 @@ public class PgpDecryptVerify { byte[] clearText = out.toByteArray(); outStream.write(clearText); - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE, true); - updateProgress(R.string.progress_processing_signature, 60, 100); PGPObjectFactory pgpFact = new PGPObjectFactory(aIn); @@ -533,15 +584,15 @@ public class PgpDecryptVerify { if (signKeyRing != null) { userId = PgpKeyHelper.getMainUserId(PgpKeyHelper.getMasterKey(signKeyRing)); } - returnData.putString(KeychainIntentService.RESULT_SIGNATURE_USER_ID, userId); + signatureResult.setUserId(userId); break; } } - returnData.putLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, signatureKeyId); + signatureResult.setKeyId(signatureKeyId); if (signature == null) { - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, true); + signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY); updateProgress(R.string.progress_done, 100, 100); return returnData; } @@ -569,47 +620,52 @@ public class PgpDecryptVerify { } while (lookAhead != -1); } - boolean sig_isok = signature.verify(); - //Now check binding signatures - boolean keyBinding_isok = verifyKeyBinding(context, signature, signatureKey); + boolean validKeyBinding = verifyKeyBinding(context, signature, signatureKey); + boolean validSignature = signature.verify(); - returnData.putBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, sig_isok & keyBinding_isok); + if (validSignature & validKeyBinding) { + signatureResult.setStatus(OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED); + } + + // TODO: what about SIGNATURE_SUCCESS_CERTIFIED and SIGNATURE_ERROR???? updateProgress(R.string.progress_done, 100, 100); + + returnData.setSignatureResult(signatureResult); return returnData; } private static boolean verifyKeyBinding(Context context, PGPSignature signature, PGPPublicKey signatureKey) { long signatureKeyId = signature.getKeyID(); - boolean keyBinding_isok = false; - String userId = null; + boolean validKeyBinding = false; + PGPPublicKeyRing signKeyRing = ProviderHelper.getPGPPublicKeyRingByKeyId(context, signatureKeyId); PGPPublicKey mKey = null; if (signKeyRing != null) { mKey = PgpKeyHelper.getMasterKey(signKeyRing); } + if (signature.getKeyID() != mKey.getKeyID()) { - keyBinding_isok = verifyKeyBinding(mKey, signatureKey); + validKeyBinding = verifyKeyBinding(mKey, signatureKey); } else { //if the key used to make the signature was the master key, no need to check binding sigs - keyBinding_isok = true; + validKeyBinding = true; } - return keyBinding_isok; + return validKeyBinding; } private static boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) { - boolean subkeyBinding_isok = false; - boolean tmp_subkeyBinding_isok = false; - boolean primkeyBinding_isok = false; - JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + boolean validSubkeyBinding = false; + boolean validTempSubkeyBinding = false; + boolean validPrimaryKeyBinding = false; + + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = + new JcaPGPContentVerifierBuilderProvider() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); Iterator<PGPSignature> itr = signingPublicKey.getSignatures(); - subkeyBinding_isok = false; - tmp_subkeyBinding_isok = false; - primkeyBinding_isok = false; while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong? //gpg has an invalid subkey binding error on key import I think, but doesn't shout //about keys without subkey signing. Can't get it to import a slightly broken one @@ -619,32 +675,36 @@ public class PgpDecryptVerify { //check and if ok, check primary key binding. try { sig.init(contentVerifierBuilderProvider, masterPublicKey); - tmp_subkeyBinding_isok = sig.verifyCertification(masterPublicKey, signingPublicKey); + validTempSubkeyBinding = sig.verifyCertification(masterPublicKey, signingPublicKey); } catch (PGPException e) { continue; } catch (SignatureException e) { continue; } - if (tmp_subkeyBinding_isok) - subkeyBinding_isok = true; - if (tmp_subkeyBinding_isok) { - primkeyBinding_isok = verifyPrimaryBinding(sig.getUnhashedSubPackets(), masterPublicKey, signingPublicKey); - if (primkeyBinding_isok) + if (validTempSubkeyBinding) + validSubkeyBinding = true; + if (validTempSubkeyBinding) { + validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(), + masterPublicKey, signingPublicKey); + if (validPrimaryKeyBinding) break; - primkeyBinding_isok = verifyPrimaryBinding(sig.getHashedSubPackets(), masterPublicKey, signingPublicKey); - if (primkeyBinding_isok) + validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(), + masterPublicKey, signingPublicKey); + if (validPrimaryKeyBinding) break; } } } - return (subkeyBinding_isok & primkeyBinding_isok); + return (validSubkeyBinding & validPrimaryKeyBinding); } - private static boolean verifyPrimaryBinding(PGPSignatureSubpacketVector Pkts, PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) { - boolean primkeyBinding_isok = false; - JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = new JcaPGPContentVerifierBuilderProvider() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + private static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector Pkts, + PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) { + boolean validPrimaryKeyBinding = false; + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = + new JcaPGPContentVerifierBuilderProvider() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); PGPSignatureList eSigList; if (Pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) { @@ -660,8 +720,8 @@ public class PgpDecryptVerify { if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) { try { emSig.init(contentVerifierBuilderProvider, signingPublicKey); - primkeyBinding_isok = emSig.verifyCertification(masterPublicKey, signingPublicKey); - if (primkeyBinding_isok) + validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey); + if (validPrimaryKeyBinding) break; } catch (PGPException e) { continue; @@ -671,7 +731,8 @@ public class PgpDecryptVerify { } } } - return primkeyBinding_isok; + + return validPrimaryKeyBinding; } /** diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java new file mode 100644 index 000000000..0477c4fdf --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyResult.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +package org.sufficientlysecure.keychain.pgp; + +import android.os.Parcel; +import android.os.Parcelable; + +import org.openintents.openpgp.OpenPgpSignatureResult; + +public class PgpDecryptVerifyResult implements Parcelable { + boolean symmetricPassphraseNeeded; + boolean keyPassphraseNeeded; + OpenPgpSignatureResult signatureResult; + + public boolean isSymmetricPassphraseNeeded() { + return symmetricPassphraseNeeded; + } + + public void setSymmetricPassphraseNeeded(boolean symmetricPassphraseNeeded) { + this.symmetricPassphraseNeeded = symmetricPassphraseNeeded; + } + + public boolean isKeyPassphraseNeeded() { + return keyPassphraseNeeded; + } + + public void setKeyPassphraseNeeded(boolean keyPassphraseNeeded) { + this.keyPassphraseNeeded = keyPassphraseNeeded; + } + + public OpenPgpSignatureResult getSignatureResult() { + return signatureResult; + } + + public void setSignatureResult(OpenPgpSignatureResult signatureResult) { + this.signatureResult = signatureResult; + } + + public PgpDecryptVerifyResult() { + + } + + public PgpDecryptVerifyResult(PgpDecryptVerifyResult b) { + this.symmetricPassphraseNeeded = b.symmetricPassphraseNeeded; + this.keyPassphraseNeeded = b.keyPassphraseNeeded; + this.signatureResult = b.signatureResult; + } + + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int flags) { + dest.writeByte((byte) (symmetricPassphraseNeeded ? 1 : 0)); + dest.writeByte((byte) (keyPassphraseNeeded ? 1 : 0)); + dest.writeParcelable(signatureResult, 0); + } + + public static final Creator<PgpDecryptVerifyResult> CREATOR = new Creator<PgpDecryptVerifyResult>() { + public PgpDecryptVerifyResult createFromParcel(final Parcel source) { + PgpDecryptVerifyResult vr = new PgpDecryptVerifyResult(); + vr.symmetricPassphraseNeeded = source.readByte() == 1; + vr.keyPassphraseNeeded = source.readByte() == 1; + vr.signatureResult = source.readParcelable(OpenPgpSignatureResult.class.getClassLoader()); + return vr; + } + + public PgpDecryptVerifyResult[] newArray(final int size) { + return new PgpDecryptVerifyResult[size]; + } + }; +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 3904a91d8..cf507826e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -44,6 +44,7 @@ import org.sufficientlysecure.keychain.helper.OtherHelper; import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpImportExport; import org.sufficientlysecure.keychain.pgp.PgpKeyOperation; @@ -181,13 +182,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial // decrypt/verify public static final String RESULT_DECRYPTED_STRING = "decrypted_message"; public static final String RESULT_DECRYPTED_BYTES = "decrypted_data"; - public static final String RESULT_SIGNATURE = "signature"; - public static final String RESULT_SIGNATURE_KEY_ID = "signature_key_id"; - public static final String RESULT_SIGNATURE_USER_ID = "signature_user_id"; - public static final String RESULT_CLEARTEXT_SIGNATURE_ONLY = "signature_only"; - - public static final String RESULT_SIGNATURE_SUCCESS = "signature_success"; - public static final String RESULT_SIGNATURE_UNKNOWN = "signature_unknown"; + public static final String RESULT_DECRYPT_VERIFY_RESULT = "signature"; // import public static final String RESULT_IMPORT_ADDED = "added"; @@ -203,8 +198,16 @@ public class KeychainIntentService extends IntentService implements ProgressDial Messenger mMessenger; + private boolean mIsCanceled; + public KeychainIntentService() { - super("ApgService"); + super("KeychainIntentService"); + } + + @Override + public void onDestroy() { + super.onDestroy(); + this.mIsCanceled = true; } /** @@ -481,15 +484,17 @@ public class KeychainIntentService extends IntentService implements ProgressDial // verifyText and decrypt returning additional resultData values for the // verification of signatures PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, outStream); - builder.progress(this); + builder.progressDialogUpdater(this); builder.assumeSymmetric(assumeSymmetricEncryption) .passphrase(PassphraseCacheService.getCachedPassphrase(this, secretKeyId)); - resultData = builder.build().execute(); + PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute(); outStream.close(); + resultData.putParcelable(RESULT_DECRYPT_VERIFY_RESULT, decryptVerifyResult); + /* Output */ switch (target) { @@ -815,6 +820,10 @@ public class KeychainIntentService extends IntentService implements ProgressDial } private void sendErrorToHandler(Exception e) { + // Service was canceled. Do not send error to handler. + if (this.mIsCanceled) + return; + Log.e(Constants.TAG, "ApgService Exception: ", e); e.printStackTrace(); @@ -824,6 +833,10 @@ public class KeychainIntentService extends IntentService implements ProgressDial } private void sendMessageToHandler(Integer arg1, Integer arg2, Bundle data) { + // Service was canceled. Do not send message to handler. + if (this.mIsCanceled) + return; + Message msg = Message.obtain(); msg.arg1 = arg1; if (arg2 != null) { @@ -851,10 +864,10 @@ public class KeychainIntentService extends IntentService implements ProgressDial } /** - * Set progress of ProgressDialog by sending message to handler on UI thread + * Set progressDialogUpdater of ProgressDialog by sending message to handler on UI thread */ public void setProgress(String message, int progress, int max) { - Log.d(Constants.TAG, "Send message by setProgress with progress=" + progress + ", max=" + Log.d(Constants.TAG, "Send message by setProgress with progressDialogUpdater=" + progress + ", max=" + max); Bundle data = new Bundle(); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java index dfea7eb04..6eba9cc83 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentServiceHandler.java @@ -21,6 +21,8 @@ import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.R; import android.app.Activity; +import android.content.DialogInterface; +import android.content.DialogInterface.OnCancelListener; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -55,9 +57,15 @@ public class KeychainIntentServiceHandler extends Handler { } public KeychainIntentServiceHandler(Activity activity, int progressDialogMessageId, int progressDialogStyle) { + this(activity, progressDialogMessageId, progressDialogStyle, false, null); + } + + public KeychainIntentServiceHandler(Activity activity, int progressDialogMessageId, + int progressDialogStyle, boolean cancelable, + OnCancelListener onCancelListener) { this.mActivity = activity; this.mProgressDialogFragment = ProgressDialogFragment.newInstance(progressDialogMessageId, - progressDialogStyle); + progressDialogStyle, cancelable, onCancelListener); } public void showProgressDialog(FragmentActivity activity) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java index 6d8a8beb3..8b34c4421 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java @@ -21,21 +21,21 @@ import android.app.PendingIntent; import android.content.Intent; import android.database.Cursor; import android.net.Uri; -import android.os.Bundle; import android.os.IBinder; import android.os.ParcelFileDescriptor; import org.openintents.openpgp.IOpenPgpService; import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.OpenPgpSignatureResult; -import org.openintents.openpgp.util.OpenPgpConstants; +import org.openintents.openpgp.util.OpenPgpApi; import org.spongycastle.util.Arrays; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt; +import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; @@ -48,7 +48,7 @@ public class OpenPgpService extends RemoteService { private static final int PRIVATE_REQUEST_CODE_PASSPHRASE = 551; private static final int PRIVATE_REQUEST_CODE_USER_IDS = 552; - + private static final int PRIVATE_REQUEST_CODE_GET_KEYS = 553; /** * Search database for key ids based on emails. @@ -56,7 +56,7 @@ public class OpenPgpService extends RemoteService { * @param encryptionUserIds * @return */ - private Bundle getKeyIdsFromEmails(Bundle params, String[] encryptionUserIds) { + private Intent getKeyIdsFromEmails(Intent data, String[] encryptionUserIds) { // find key ids to given emails in database ArrayList<Long> keyIds = new ArrayList<Long>(); @@ -91,20 +91,20 @@ public class OpenPgpService extends RemoteService { // allow the user to verify pub key selection if (missingUserIdsCheck || dublicateUserIdsCheck) { - // build PendingIntent for passphrase input + // build PendingIntent Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class); intent.setAction(RemoteServiceActivity.ACTION_SELECT_PUB_KEYS); intent.putExtra(RemoteServiceActivity.EXTRA_SELECTED_MASTER_KEY_IDS, keyIdsArray); intent.putExtra(RemoteServiceActivity.EXTRA_MISSING_USER_IDS, missingUserIds); intent.putExtra(RemoteServiceActivity.EXTRA_DUBLICATE_USER_IDS, dublicateUserIds); - intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params); + intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_USER_IDS, intent, 0); // return PendingIntent to be executed by client - Bundle result = new Bundle(); - result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED); - result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi); + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED); + result.putExtra(OpenPgpApi.RESULT_INTENT, pi); return result; } @@ -113,44 +113,44 @@ public class OpenPgpService extends RemoteService { return null; } - Bundle result = new Bundle(); - result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS); - result.putLongArray(OpenPgpConstants.PARAMS_KEY_IDS, keyIdsArray); + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); + result.putExtra(OpenPgpApi.EXTRA_KEY_IDS, keyIdsArray); return result; } - private Bundle getPassphraseBundleIntent(Bundle params, long keyId) { + private Intent getPassphraseBundleIntent(Intent data, long keyId) { // build PendingIntent for passphrase input Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class); intent.setAction(RemoteServiceActivity.ACTION_CACHE_PASSPHRASE); intent.putExtra(RemoteServiceActivity.EXTRA_SECRET_KEY_ID, keyId); // pass params through to activity that it can be returned again later to repeat pgp operation - intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params); + intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_PASSPHRASE, intent, 0); // return PendingIntent to be executed by client - Bundle result = new Bundle(); - result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED); - result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi); + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED); + result.putExtra(OpenPgpApi.RESULT_INTENT, pi); return result; } - private Bundle signImpl(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output, - AppSettings appSettings) { + private Intent signImpl(Intent data, ParcelFileDescriptor input, + ParcelFileDescriptor output, AppSettings appSettings) { try { - boolean asciiArmor = params.getBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true); + boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); // get passphrase from cache, if key has "no" passphrase, this returns an empty String String passphrase; - if (params.containsKey(OpenPgpConstants.PARAMS_PASSPHRASE)) { - passphrase = params.getString(OpenPgpConstants.PARAMS_PASSPHRASE); + if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) { + passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); } else { passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId()); } if (passphrase == null) { // get PendingIntent for passphrase input, add it to given params and return to client - Bundle passphraseBundle = getPassphraseBundleIntent(params, appSettings.getKeyId()); + Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId()); return passphraseBundle; } @@ -174,43 +174,42 @@ public class OpenPgpService extends RemoteService { os.close(); } - Bundle result = new Bundle(); - result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS); + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); return result; } catch (Exception e) { - Bundle result = new Bundle(); - result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR); - result.putParcelable(OpenPgpConstants.RESULT_ERRORS, + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); + result.putExtra(OpenPgpApi.RESULT_ERRORS, new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); return result; } } - private Bundle encryptAndSignImpl(Bundle params, ParcelFileDescriptor input, - ParcelFileDescriptor output, AppSettings appSettings, - boolean sign) { + private Intent encryptAndSignImpl(Intent data, ParcelFileDescriptor input, + ParcelFileDescriptor output, AppSettings appSettings, boolean sign) { try { - boolean asciiArmor = params.getBoolean(OpenPgpConstants.PARAMS_REQUEST_ASCII_ARMOR, true); + boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); long[] keyIds; - if (params.containsKey(OpenPgpConstants.PARAMS_KEY_IDS)) { - keyIds = params.getLongArray(OpenPgpConstants.PARAMS_KEY_IDS); - } else if (params.containsKey(OpenPgpConstants.PARAMS_USER_IDS)) { + if (data.hasExtra(OpenPgpApi.EXTRA_KEY_IDS)) { + keyIds = data.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS); + } else if (data.hasExtra(OpenPgpApi.EXTRA_USER_IDS)) { // get key ids based on given user ids - String[] userIds = params.getStringArray(OpenPgpConstants.PARAMS_USER_IDS); + String[] userIds = data.getStringArrayExtra(OpenPgpApi.EXTRA_USER_IDS); // give params through to activity... - Bundle result = getKeyIdsFromEmails(params, userIds); + Intent result = getKeyIdsFromEmails(data, userIds); - if (result.getInt(OpenPgpConstants.RESULT_CODE, 0) == OpenPgpConstants.RESULT_CODE_SUCCESS) { - keyIds = result.getLongArray(OpenPgpConstants.PARAMS_KEY_IDS); + if (result.getIntExtra(OpenPgpApi.RESULT_CODE, 0) == OpenPgpApi.RESULT_CODE_SUCCESS) { + keyIds = result.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS); } else { // if not success -> result contains a PendingIntent for user interaction return result; } } else { - Bundle result = new Bundle(); - result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR); - result.putParcelable(OpenPgpConstants.RESULT_ERRORS, + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); + result.putExtra(OpenPgpApi.RESULT_ERRORS, new OpenPgpError(OpenPgpError.GENERIC_ERROR, "Missing parameter user_ids or key_ids!")); return result; } @@ -235,15 +234,15 @@ public class OpenPgpService extends RemoteService { if (sign) { String passphrase; - if (params.containsKey(OpenPgpConstants.PARAMS_PASSPHRASE)) { - passphrase = params.getString(OpenPgpConstants.PARAMS_PASSPHRASE); + if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) { + passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); } else { passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId()); } if (passphrase == null) { // get PendingIntent for passphrase input, add it to given params and return to client - Bundle passphraseBundle = getPassphraseBundleIntent(params, appSettings.getKeyId()); + Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId()); return passphraseBundle; } @@ -263,144 +262,88 @@ public class OpenPgpService extends RemoteService { os.close(); } - Bundle result = new Bundle(); - result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS); + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); return result; } catch (Exception e) { - Bundle result = new Bundle(); - result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR); - result.putParcelable(OpenPgpConstants.RESULT_ERRORS, + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); + result.putExtra(OpenPgpApi.RESULT_ERRORS, new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); return result; } } - private Bundle decryptAndVerifyImpl(Bundle params, ParcelFileDescriptor input, + private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output, AppSettings appSettings) { try { // Get Input- and OutputStream from ParcelFileDescriptor InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input); OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(output); - Bundle result = new Bundle(); + Intent result = new Intent(); try { - // TODO: - // fix the mess: http://stackoverflow.com/questions/148130/how-do-i-peek-at-the-first-two-bytes-in-an-inputstream - // should we allow to decrypt everything under every key id or only the one set? - // TODO: instead of trying to get the passphrase before - // pause stream when passphrase is missing and then resume - - // TODO: put this code into PgpDecryptVerify class - - // TODO: This allows to decrypt messages with ALL secret keys, not only the one for the - // app, Fix this? -// String passphrase = null; -// if (!signedOnly) { -// // BEGIN Get key -// // TODO: this input stream is consumed after PgpMain.getDecryptionKeyId()... do it -// // better! -// InputStream inputStream2 = new ByteArrayInputStream(inputBytes); -// -// // TODO: duplicates functions from DecryptActivity! -// long secretKeyId; -// try { -// if (inputStream2.markSupported()) { -// // should probably set this to the max size of two -// // pgpF objects, if it even needs to be anything other -// // than 0. -// inputStream2.mark(200); -// } -// secretKeyId = PgpHelper.getDecryptionKeyId(this, inputStream2); -// if (secretKeyId == Id.key.none) { -// throw new PgpGeneralException(getString(R.string.error_no_secret_key_found)); -// } -// } catch (NoAsymmetricEncryptionException e) { -// if (inputStream2.markSupported()) { -// inputStream2.reset(); -// } -// secretKeyId = Id.key.symmetric; -// if (!PgpDecryptVerify.hasSymmetricEncryption(this, inputStream2)) { -// throw new PgpGeneralException( -// getString(R.string.error_no_known_encryption_found)); -// } -// // we do not support symmetric decryption from the API! -// throw new Exception("Symmetric decryption is not supported!"); -// } -// -// Log.d(Constants.TAG, "secretKeyId " + secretKeyId); - - // NOTE: currently this only gets the passphrase for the key set for this client - String passphrase; - if (params.containsKey(OpenPgpConstants.PARAMS_PASSPHRASE)) { - passphrase = params.getString(OpenPgpConstants.PARAMS_PASSPHRASE); - } else { - passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId()); - } - if (passphrase == null) { - // get PendingIntent for passphrase input, add it to given params and return to client - Bundle passphraseBundle = getPassphraseBundleIntent(params, appSettings.getKeyId()); - return passphraseBundle; - } - + String passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); long inputLength = is.available(); InputData inputData = new InputData(is, inputLength); - Bundle outputBundle; PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, os); - - builder.assumeSymmetric(false) + builder.assumeSymmetric(false) // no support for symmetric encryption + .enforcedKeyId(appSettings.getKeyId()) // allow only the private key for this app for decryption .passphrase(passphrase); - // TODO: this also decrypts with other secret keys that have no passphrase!!! - outputBundle = builder.build().execute(); - - //TODO: instead of using all these wrapping use OpenPgpSignatureResult directly - // in DecryptVerify class and then in DecryptActivity - boolean signature = outputBundle.getBoolean(KeychainIntentService.RESULT_SIGNATURE, false); - if (signature) { - long signatureKeyId = outputBundle - .getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID, 0); - String signatureUserId = outputBundle - .getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID); - boolean signatureSuccess = outputBundle - .getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS, false); - boolean signatureUnknown = outputBundle - .getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN, false); - boolean signatureOnly = outputBundle - .getBoolean(KeychainIntentService.RESULT_CLEARTEXT_SIGNATURE_ONLY, false); - - int signatureStatus = OpenPgpSignatureResult.SIGNATURE_ERROR; - if (signatureSuccess) { - signatureStatus = OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED; - } else if (signatureUnknown) { - signatureStatus = OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY; + // TODO: currently does not support binary signed-only content + PgpDecryptVerifyResult decryptVerifyResult = builder.build().execute(); + + if (decryptVerifyResult.isKeyPassphraseNeeded()) { + // get PendingIntent for passphrase input, add it to given params and return to client + Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId()); + return passphraseBundle; + } else if (decryptVerifyResult.isSymmetricPassphraseNeeded()) { + throw new PgpGeneralException("Decryption of symmetric content not supported by API!"); + } + + OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult(); + if (signatureResult != null) { + if (signatureResult.getStatus() == OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY) { + // If signature is unknown we return an additional PendingIntent + // to retrieve the missing key + // TODO!!! + Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class); + intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE); + intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, "todo"); + intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); + + PendingIntent pi = PendingIntent.getActivity(getBaseContext(), + PRIVATE_REQUEST_CODE_GET_KEYS, intent, 0); + + result.putExtra(OpenPgpApi.RESULT_INTENT, pi); } - OpenPgpSignatureResult sigResult = new OpenPgpSignatureResult(signatureStatus, - signatureUserId, signatureOnly, signatureKeyId); - result.putParcelable(OpenPgpConstants.RESULT_SIGNATURE, sigResult); + result.putExtra(OpenPgpApi.RESULT_SIGNATURE, signatureResult); } + } finally { is.close(); os.close(); } - result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_SUCCESS); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); return result; } catch (Exception e) { - Bundle result = new Bundle(); - result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR); - result.putParcelable(OpenPgpConstants.RESULT_ERRORS, + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); + result.putExtra(OpenPgpApi.RESULT_ERRORS, new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); return result; } } - private Bundle getKeyIdsImpl(Bundle params) { + private Intent getKeyIdsImpl(Intent data) { // get key ids based on given user ids - String[] userIds = params.getStringArray(OpenPgpConstants.PARAMS_USER_IDS); - Bundle result = getKeyIdsFromEmails(params, userIds); + String[] userIds = data.getStringArrayExtra(OpenPgpApi.EXTRA_USER_IDS); + Intent result = getKeyIdsFromEmails(data, userIds); return result; } @@ -410,30 +353,30 @@ public class OpenPgpService extends RemoteService { * - has supported API version * - is allowed to call the service (access has been granted) * - * @param params + * @param data * @return null if everything is okay, or a Bundle with an error/PendingIntent */ - private Bundle checkRequirements(Bundle params) { + private Intent checkRequirements(Intent data) { // params Bundle is required! - if (params == null) { - Bundle result = new Bundle(); + if (data == null) { + Intent result = new Intent(); OpenPgpError error = new OpenPgpError(OpenPgpError.GENERIC_ERROR, "params Bundle required!"); - result.putParcelable(OpenPgpConstants.RESULT_ERRORS, error); - result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR); + result.putExtra(OpenPgpApi.RESULT_ERRORS, error); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); return result; } // version code is required and needs to correspond to version code of service! - if (params.getInt(OpenPgpConstants.PARAMS_API_VERSION) != OpenPgpConstants.API_VERSION) { - Bundle result = new Bundle(); + if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != OpenPgpApi.API_VERSION) { + Intent result = new Intent(); OpenPgpError error = new OpenPgpError(OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!"); - result.putParcelable(OpenPgpConstants.RESULT_ERRORS, error); - result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR); + result.putExtra(OpenPgpApi.RESULT_ERRORS, error); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); return result; } // check if caller is allowed to access openpgp keychain - Bundle result = isAllowed(params); + Intent result = isAllowed(data); if (result != null) { return result; } @@ -445,61 +388,31 @@ public class OpenPgpService extends RemoteService { private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() { @Override - public Bundle sign(Bundle params, final ParcelFileDescriptor input, final ParcelFileDescriptor output) { - final AppSettings appSettings = getAppSettings(); - - Bundle errorResult = checkRequirements(params); - if (errorResult != null) { - return errorResult; - } - - return signImpl(params, input, output, appSettings); - } - - @Override - public Bundle encrypt(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output) { - final AppSettings appSettings = getAppSettings(); - - Bundle errorResult = checkRequirements(params); - if (errorResult != null) { - return errorResult; - } - - return encryptAndSignImpl(params, input, output, appSettings, false); - } - - @Override - public Bundle signAndEncrypt(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output) { - final AppSettings appSettings = getAppSettings(); - - Bundle errorResult = checkRequirements(params); + public Intent execute(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output) { + Intent errorResult = checkRequirements(data); if (errorResult != null) { return errorResult; } - return encryptAndSignImpl(params, input, output, appSettings, true); - } - - @Override - public Bundle decryptAndVerify(Bundle params, ParcelFileDescriptor input, ParcelFileDescriptor output) { final AppSettings appSettings = getAppSettings(); - Bundle errorResult = checkRequirements(params); - if (errorResult != null) { - return errorResult; - } - - return decryptAndVerifyImpl(params, input, output, appSettings); - } - - @Override - public Bundle getKeyIds(Bundle params) { - Bundle errorResult = checkRequirements(params); - if (errorResult != null) { - return errorResult; + String action = data.getAction(); + if (OpenPgpApi.ACTION_SIGN.equals(action)) { + return signImpl(data, input, output, appSettings); + } else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) { + return encryptAndSignImpl(data, input, output, appSettings, false); + } else if (OpenPgpApi.ACTION_SIGN_AND_ENCTYPT.equals(action)) { + return encryptAndSignImpl(data, input, output, appSettings, true); + } else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) { + return decryptAndVerifyImpl(data, input, output, appSettings); + } else if (OpenPgpApi.ACTION_DOWNLOAD_KEYS.equals(action)) { + // TODO! + return null; + } else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) { + return getKeyIdsImpl(data); + } else { + return null; } - - return getKeyIdsImpl(params); } }; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java index cfd2b9ec3..e7b3b2945 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java @@ -21,7 +21,7 @@ import java.util.ArrayList; import java.util.Arrays; import org.openintents.openpgp.OpenPgpError; -import org.openintents.openpgp.util.OpenPgpConstants; +import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.provider.KeychainContract; @@ -38,10 +38,6 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import android.net.Uri; import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.os.Messenger; /** * Abstract service class for remote APIs that handle app registration and user input. @@ -57,7 +53,7 @@ public abstract class RemoteService extends Service { return mContext; } - protected Bundle isAllowed(Bundle params) { + protected Intent isAllowed(Intent data) { try { if (isCallerAllowed(false)) { @@ -74,9 +70,9 @@ public abstract class RemoteService extends Service { } catch (NameNotFoundException e) { Log.e(Constants.TAG, "Should not happen, returning!", e); // return error - Bundle result = new Bundle(); - result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_ERROR); - result.putParcelable(OpenPgpConstants.RESULT_ERRORS, + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); + result.putExtra(OpenPgpApi.RESULT_ERRORS, new OpenPgpError(OpenPgpError.GENERIC_ERROR, e.getMessage())); return result; } @@ -86,14 +82,14 @@ public abstract class RemoteService extends Service { intent.setAction(RemoteServiceActivity.ACTION_REGISTER); intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName); intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_SIGNATURE, packageSignature); - intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params); + intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_REGISTER, intent, 0); // return PendingIntent to be executed by client - Bundle result = new Bundle(); - result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED); - result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi); + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED); + result.putExtra(OpenPgpApi.RESULT_INTENT, pi); return result; } @@ -103,14 +99,14 @@ public abstract class RemoteService extends Service { Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class); intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE); intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, getString(R.string.api_error_wrong_signature)); - intent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params); + intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); PendingIntent pi = PendingIntent.getActivity(getBaseContext(), PRIVATE_REQUEST_CODE_ERROR, intent, 0); // return PendingIntent to be executed by client - Bundle result = new Bundle(); - result.putInt(OpenPgpConstants.RESULT_CODE, OpenPgpConstants.RESULT_CODE_USER_INTERACTION_REQUIRED); - result.putParcelable(OpenPgpConstants.RESULT_INTENT, pi); + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED); + result.putExtra(OpenPgpApi.RESULT_INTENT, pi); return result; } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java index af8e3ade8..11b3ee217 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java @@ -25,7 +25,7 @@ import android.os.Messenger; import android.support.v7.app.ActionBarActivity; import android.view.View; -import org.openintents.openpgp.util.OpenPgpConstants; +import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.htmltextview.HtmlTextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; @@ -51,6 +51,8 @@ public class RemoteServiceActivity extends ActionBarActivity { public static final String EXTRA_MESSENGER = "messenger"; + public static final String EXTRA_DATA = "data"; + // passphrase action public static final String EXTRA_SECRET_KEY_ID = "secret_key_id"; // register action @@ -100,12 +102,9 @@ public class RemoteServiceActivity extends ActionBarActivity { ProviderHelper.insertApiApp(RemoteServiceActivity.this, mSettingsFragment.getAppSettings()); - // give params through for new service call - Bundle oldParams = extras.getBundle(OpenPgpConstants.PI_RESULT_PARAMS); - - Intent finishIntent = new Intent(); - finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, oldParams); - RemoteServiceActivity.this.setResult(RESULT_OK, finishIntent); + // give data through for new service call + Intent resultData = extras.getParcelable(EXTRA_DATA); + RemoteServiceActivity.this.setResult(RESULT_OK, resultData); RemoteServiceActivity.this.finish(); } } @@ -128,9 +127,9 @@ public class RemoteServiceActivity extends ActionBarActivity { mSettingsFragment.setAppSettings(settings); } else if (ACTION_CACHE_PASSPHRASE.equals(action)) { long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID); - Bundle oldParams = extras.getBundle(OpenPgpConstants.PI_RESULT_PARAMS); + Intent resultData = extras.getParcelable(EXTRA_DATA); - showPassphraseDialog(oldParams, secretKeyId); + showPassphraseDialog(resultData, secretKeyId); } else if (ACTION_SELECT_PUB_KEYS.equals(action)) { long[] selectedMasterKeyIds = intent.getLongArrayExtra(EXTRA_SELECTED_MASTER_KEY_IDS); ArrayList<String> missingUserIds = intent @@ -167,13 +166,11 @@ public class RemoteServiceActivity extends ActionBarActivity { @Override public void onClick(View v) { // add key ids to params Bundle for new request - Bundle params = extras.getBundle(OpenPgpConstants.PI_RESULT_PARAMS); - params.putLongArray(OpenPgpConstants.PARAMS_KEY_IDS, + Intent resultData = extras.getParcelable(EXTRA_DATA); + resultData.putExtra(OpenPgpApi.EXTRA_KEY_IDS, mSelectFragment.getSelectedMasterKeyIds()); - Intent finishIntent = new Intent(); - finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params); - RemoteServiceActivity.this.setResult(RESULT_OK, finishIntent); + RemoteServiceActivity.this.setResult(RESULT_OK, resultData); RemoteServiceActivity.this.finish(); } }, R.string.btn_do_not_save, new View.OnClickListener() { @@ -222,7 +219,7 @@ public class RemoteServiceActivity extends ActionBarActivity { @Override public void onClick(View v) { - RemoteServiceActivity.this.setResult(RESULT_OK); + RemoteServiceActivity.this.setResult(RESULT_CANCELED); RemoteServiceActivity.this.finish(); } }); @@ -244,16 +241,14 @@ public class RemoteServiceActivity extends ActionBarActivity { * encryption. Based on mSecretKeyId it asks for a passphrase to open a private key or it asks * for a symmetric passphrase */ - private void showPassphraseDialog(final Bundle params, long secretKeyId) { + private void showPassphraseDialog(final Intent data, long secretKeyId) { // Message is received after passphrase is cached Handler returnHandler = new Handler() { @Override public void handleMessage(Message message) { if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { // return given params again, for calling the service method again - Intent finishIntent = new Intent(); - finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params); - RemoteServiceActivity.this.setResult(RESULT_OK, finishIntent); + RemoteServiceActivity.this.setResult(RESULT_OK, data); } else { RemoteServiceActivity.this.setResult(RESULT_CANCELED); } @@ -273,9 +268,7 @@ public class RemoteServiceActivity extends ActionBarActivity { } catch (PgpGeneralException e) { Log.d(Constants.TAG, "No passphrase for this secret key, do pgp operation directly!"); // return given params again, for calling the service method again - Intent finishIntent = new Intent(); - finishIntent.putExtra(OpenPgpConstants.PI_RESULT_PARAMS, params); - setResult(RESULT_OK, finishIntent); + setResult(RESULT_OK, data); finish(); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java index 9bb675db0..42288ca37 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java @@ -25,6 +25,7 @@ import java.io.FileNotFoundException; import java.io.InputStream; import java.util.regex.Matcher; +import org.openintents.openpgp.OpenPgpSignatureResult; import org.spongycastle.openpgp.PGPPublicKeyRing; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; @@ -32,6 +33,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.helper.ActionBarHelper; import org.sufficientlysecure.keychain.helper.FileHelper; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; @@ -690,11 +692,15 @@ public class DecryptActivity extends DrawerActivity { } - if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE)) { - String userId = returnData - .getString(KeychainIntentService.RESULT_SIGNATURE_USER_ID); - mSignatureKeyId = returnData - .getLong(KeychainIntentService.RESULT_SIGNATURE_KEY_ID); + PgpDecryptVerifyResult decryptVerifyResult = + returnData.getParcelable(KeychainIntentService.RESULT_DECRYPT_VERIFY_RESULT); + + OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult(); + + if (signatureResult != null) { + + String userId = signatureResult.getUserId(); + mSignatureKeyId = signatureResult.getKeyId(); mUserIdRest.setText("id: " + PgpKeyHelper.convertKeyIdToHex(mSignatureKeyId)); if (userId == null) { @@ -707,19 +713,32 @@ public class DecryptActivity extends DrawerActivity { } mUserId.setText(userId); - if (returnData.getBoolean(KeychainIntentService.RESULT_SIGNATURE_SUCCESS)) { - mSignatureStatusImage.setImageResource(R.drawable.overlay_ok); - mLookupKey.setVisibility(View.GONE); - } else if (returnData - .getBoolean(KeychainIntentService.RESULT_SIGNATURE_UNKNOWN)) { - mSignatureStatusImage.setImageResource(R.drawable.overlay_error); - mLookupKey.setVisibility(View.VISIBLE); - AppMsg.makeText(DecryptActivity.this, - R.string.unknown_signature, - AppMsg.STYLE_ALERT).show(); - } else { - mSignatureStatusImage.setImageResource(R.drawable.overlay_error); - mLookupKey.setVisibility(View.GONE); + switch (signatureResult.getStatus()) { + case OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED: { + mSignatureStatusImage.setImageResource(R.drawable.overlay_ok); + mLookupKey.setVisibility(View.GONE); + break; + } + + // TODO! +// case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED: { +// break; +// } + + case OpenPgpSignatureResult.SIGNATURE_UNKNOWN_PUB_KEY: { + mSignatureStatusImage.setImageResource(R.drawable.overlay_error); + mLookupKey.setVisibility(View.VISIBLE); + AppMsg.makeText(DecryptActivity.this, + R.string.unknown_signature, + AppMsg.STYLE_ALERT).show(); + break; + } + + default: { + mSignatureStatusImage.setImageResource(R.drawable.overlay_error); + mLookupKey.setVisibility(View.GONE); + break; + } } mSignatureLayout.setVisibility(View.VISIBLE); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java index 53a57c4cd..08ca262c3 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity; +import org.sufficientlysecure.keychain.util.Log; import android.app.Activity; import android.content.Context; @@ -52,6 +53,7 @@ public class DrawerActivity extends ActionBarActivity { private static Class[] mItemsClass = new Class[] { KeyListPublicActivity.class, EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class, KeyListSecretActivity.class, RegisteredAppsListActivity.class }; + private Class mSelectedItem; private static final int MENU_ID_PREFERENCE = 222; private static final int MENU_ID_HELP = 223; @@ -94,6 +96,17 @@ public class DrawerActivity extends ActionBarActivity { getSupportActionBar().setTitle(mTitle); // creates call to onPrepareOptionsMenu() supportInvalidateOptionsMenu(); + + // call intent activity if selected + if(mSelectedItem != null) { + finish(); + overridePendingTransition(0, 0); + + Intent intent = new Intent(DrawerActivity.this, mSelectedItem); + startActivity(intent); + // disable animation of activity start + overridePendingTransition(0, 0); + } } public void onDrawerOpened(View drawerView) { @@ -182,14 +195,8 @@ public class DrawerActivity extends ActionBarActivity { mDrawerList.setItemChecked(position, true); // setTitle(mDrawerTitles[position]); mDrawerLayout.closeDrawer(mDrawerList); - - finish(); - overridePendingTransition(0, 0); - - Intent intent = new Intent(this, mItemsClass[position]); - startActivity(intent); - // disable animation of activity start - overridePendingTransition(0, 0); + // set selected class + mSelectedItem = mItemsClass[position]; } /** diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 73426e32d..0bed6f264 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -44,8 +44,10 @@ import org.sufficientlysecure.keychain.ui.widget.UserIdEditor; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; +import android.app.Activity; import android.app.ProgressDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; @@ -173,7 +175,7 @@ public class EditKeyActivity extends ActionBarActivity { if (generateDefaultKeys) { // Send all information needed to service generate keys in other thread - Intent serviceIntent = new Intent(this, KeychainIntentService.class); + final Intent serviceIntent = new Intent(this, KeychainIntentService.class); serviceIntent.setAction(KeychainIntentService.ACTION_GENERATE_DEFAULT_RSA_KEYS); // fill values for this action @@ -185,7 +187,19 @@ public class EditKeyActivity extends ActionBarActivity { // Message is received after generating is done in ApgService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( - this, R.string.progress_generating, ProgressDialog.STYLE_SPINNER) { + this, R.string.progress_generating, ProgressDialog.STYLE_SPINNER, true, + + new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + // Stop key generation on cancel + stopService(serviceIntent); + EditKeyActivity.this.setResult(Activity.RESULT_CANCELED); + EditKeyActivity.this.finish(); + } + }) { + + @Override public void handleMessage(Message message) { // handle messages by standard ApgHandler first super.handleMessage(message); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index a5027ac1c..5ac421a44 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -17,22 +17,9 @@ package org.sufficientlysecure.keychain.ui; -import java.util.ArrayList; -import java.util.Locale; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry; -import org.sufficientlysecure.keychain.util.Log; - import android.annotation.SuppressLint; -import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.nfc.NdefMessage; @@ -51,6 +38,18 @@ import android.widget.ArrayAdapter; import com.beardedhen.androidbootstrap.BootstrapButton; import com.devspark.appmsg.AppMsg; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry; +import org.sufficientlysecure.keychain.ui.dialog.BadImportKeyDialogFragment; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.ArrayList; +import java.util.Locale; + public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNavigationListener { public static final String ACTION_IMPORT_KEY = Constants.INTENT_PREFIX + "IMPORT_KEY"; public static final String ACTION_IMPORT_KEY_FROM_QR_CODE = Constants.INTENT_PREFIX @@ -236,10 +235,10 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa * inside your Activity." * <p/> * from http://stackoverflow.com/questions/10983396/fragment-oncreateview-and-onactivitycreated-called-twice/14295474#14295474 - * + * <p/> * In our case, if we start ImportKeysActivity with parameters to directly search using a fingerprint, * the fragment would be loaded twice resulting in the query being empty after the second load. - * + * <p/> * Our solution: * To prevent that a fragment will be loaded again even if it was already loaded loadNavFragment * checks against mCurrentNavPostition. @@ -395,23 +394,8 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa AppMsg.makeText(ImportKeysActivity.this, toastMessage, AppMsg.STYLE_INFO) .show(); if (bad > 0) { - AlertDialog.Builder alert = new AlertDialog.Builder( - ImportKeysActivity.this); - - alert.setIcon(android.R.drawable.ic_dialog_alert); - alert.setTitle(R.string.warning); - - alert.setMessage(ImportKeysActivity.this.getResources() - .getQuantityString(R.plurals.bad_keys_encountered, bad, bad)); - - alert.setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.cancel(); - } - }); - alert.setCancelable(true); - alert.create().show(); + BadImportKeyDialogFragment badImportKeyDialogFragment = BadImportKeyDialogFragment.newInstance(bad); + badImportKeyDialogFragment.show(getSupportFragmentManager(), "badKeyDialog"); } } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index b52de5bc9..1118f0264 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -27,11 +27,13 @@ import java.util.List; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.ui.adapter.AsyncTaskResultWrapper; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListServerLoader; import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.KeyServer; import org.sufficientlysecure.keychain.util.Log; import android.app.Activity; @@ -42,10 +44,11 @@ import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.view.View; import android.widget.ListView; -import android.widget.Toast; + +import com.devspark.appmsg.AppMsg; public class ImportKeysListFragment extends ListFragment implements - LoaderManager.LoaderCallbacks<List<ImportKeysListEntry>> { + LoaderManager.LoaderCallbacks<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { private static final String ARG_DATA_URI = "uri"; private static final String ARG_BYTES = "bytes"; private static final String ARG_SERVER_QUERY = "query"; @@ -181,11 +184,10 @@ public class ImportKeysListFragment extends ListFragment implements } @Override - public Loader<List<ImportKeysListEntry>> onCreateLoader(int id, Bundle args) { + public Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> onCreateLoader(int id, Bundle args) { switch (id) { case LOADER_ID_BYTES: { InputData inputData = getInputData(mKeyBytes, mDataUri); - return new ImportKeysListLoader(mActivity, inputData); } case LOADER_ID_SERVER_QUERY: { @@ -198,15 +200,15 @@ public class ImportKeysListFragment extends ListFragment implements } @Override - public void onLoadFinished(Loader<List<ImportKeysListEntry>> loader, - List<ImportKeysListEntry> data) { + public void onLoadFinished(Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> loader, + AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> data) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) - Log.d(Constants.TAG, "data: " + data); + Log.d(Constants.TAG, "data: " + data.getResult()); // swap in the real data! - mAdapter.setData(data); + mAdapter.setData(data.getResult()); mAdapter.notifyDataSetChanged(); setListAdapter(mAdapter); @@ -222,11 +224,25 @@ public class ImportKeysListFragment extends ListFragment implements break; case LOADER_ID_SERVER_QUERY: - Toast.makeText( - getActivity(), getResources().getQuantityString(R.plurals.keys_found, - mAdapter.getCount(), mAdapter.getCount()), - Toast.LENGTH_SHORT - ).show(); + + Exception error = data.getError(); + + if(error == null){ + AppMsg.makeText( + getActivity(), getResources().getQuantityString(R.plurals.keys_found, + mAdapter.getCount(), mAdapter.getCount()), + AppMsg.STYLE_INFO + ).show(); + } else if(error instanceof KeyServer.InsufficientQuery){ + AppMsg.makeText(getActivity(), R.string.error_keyserver_insufficient_query, + AppMsg.STYLE_ALERT).show(); + }else if(error instanceof KeyServer.QueryException){ + AppMsg.makeText(getActivity(), R.string.error_keyserver_query, + AppMsg.STYLE_ALERT).show(); + }else if(error instanceof KeyServer.TooManyResponses){ + AppMsg.makeText(getActivity(), R.string.error_keyserver_too_many_responses, + AppMsg.STYLE_ALERT).show(); + } break; default: @@ -235,7 +251,7 @@ public class ImportKeysListFragment extends ListFragment implements } @Override - public void onLoaderReset(Loader<List<ImportKeysListEntry>> loader) { + public void onLoaderReset(Loader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> loader) { switch (loader.getId()) { case LOADER_ID_BYTES: // Clear the data in the adapter. diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java index c28d57627..0afa556cb 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListPublicFragment.java @@ -33,6 +33,7 @@ import se.emilsjolander.stickylistheaders.ApiLevelTooLowException; import se.emilsjolander.stickylistheaders.StickyListHeadersListView; import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.content.Intent; import android.database.Cursor; import android.net.Uri; @@ -276,7 +277,8 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte viewIntent.setData(KeychainContract.KeyRings.buildPublicKeyRingsUri(Long.toString(id))); startActivity(viewIntent); } - + + @TargetApi(11) public void encrypt(ActionMode mode, long[] keyRingRowIds) { // get master key ids from row ids long[] keyRingIds = new long[keyRingRowIds.length]; @@ -298,6 +300,7 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte * * @param keyRingRowIds */ + @TargetApi(11) public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) { // Message is received after key is deleted Handler returnHandler = new Handler() { @@ -332,4 +335,4 @@ public class KeyListPublicFragment extends Fragment implements AdapterView.OnIte deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog"); } -}
\ No newline at end of file +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java index f9d267f27..7bb77b60f 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListSecretFragment.java @@ -29,6 +29,7 @@ import org.sufficientlysecure.keychain.ui.adapter.KeyListSecretAdapter; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; import android.annotation.SuppressLint; +import android.annotation.TargetApi; import android.content.Intent; import android.database.Cursor; import android.net.Uri; @@ -209,6 +210,7 @@ public class KeyListSecretFragment extends ListFragment implements * * @param keyRingRowIds */ + @TargetApi(11) public void showDeleteKeyDialog(final ActionMode mode, long[] keyRingRowIds) { // Message is received after key is deleted Handler returnHandler = new Handler() { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java new file mode 100644 index 000000000..2ac19c1d9 --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/AsyncTaskResultWrapper.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.adapter; + +/** + * The AsyncTaskResultWrapper is used to wrap a result from a AsyncTask (for example: Loader). + * You can pass the result and an exception in it if an error occurred. + * Concept found at: + * https://stackoverflow.com/questions/19593577/how-to-handle-errors-in-custom-asynctaskloader + * @param <T> - Typ of the result which is wrapped + */ +public class AsyncTaskResultWrapper <T>{ + + private final T result; + private final Exception error; + + public AsyncTaskResultWrapper(T result, Exception error){ + this.result = result; + this.error = error; + } + + public T getResult() { + return result; + } + + public Exception getError() { + return error; + } + +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java index 00ad8c957..29e418db7 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java @@ -33,12 +33,13 @@ import org.sufficientlysecure.keychain.util.PositionAwareInputStream; import android.content.Context; import android.support.v4.content.AsyncTaskLoader; -public class ImportKeysListLoader extends AsyncTaskLoader<List<ImportKeysListEntry>> { +public class ImportKeysListLoader extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { Context mContext; InputData mInputData; ArrayList<ImportKeysListEntry> data = new ArrayList<ImportKeysListEntry>(); + AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> entryListWrapper; public ImportKeysListLoader(Context context, InputData inputData) { super(context); @@ -47,15 +48,18 @@ public class ImportKeysListLoader extends AsyncTaskLoader<List<ImportKeysListEnt } @Override - public List<ImportKeysListEntry> loadInBackground() { + public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() { + + entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(data, null); + if (mInputData == null) { Log.e(Constants.TAG, "Input data is null!"); - return data; + return entryListWrapper; } generateListOfKeyrings(mInputData); - return data; + return entryListWrapper; } @Override @@ -77,7 +81,7 @@ public class ImportKeysListLoader extends AsyncTaskLoader<List<ImportKeysListEnt } @Override - public void deliverResult(List<ImportKeysListEntry> data) { + public void deliverResult(AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> data) { super.deliverResult(data); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java index b3bc39127..3a3b6e58b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java @@ -26,15 +26,15 @@ import org.sufficientlysecure.keychain.util.KeyServer; import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; -import java.util.List; -public class ImportKeysListServerLoader extends AsyncTaskLoader<List<ImportKeysListEntry>> { +public class ImportKeysListServerLoader extends AsyncTaskLoader<AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>> { Context mContext; String mServerQuery; String mKeyServer; - ArrayList<ImportKeysListEntry> data = new ArrayList<ImportKeysListEntry>(); + private ArrayList<ImportKeysListEntry> entryList = new ArrayList<ImportKeysListEntry>(); + private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> entryListWrapper; public ImportKeysListServerLoader(Context context, String serverQuery, String keyServer) { super(context); @@ -44,15 +44,18 @@ public class ImportKeysListServerLoader extends AsyncTaskLoader<List<ImportKeysL } @Override - public List<ImportKeysListEntry> loadInBackground() { + public AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> loadInBackground() { + + entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, null); + if (mServerQuery == null) { Log.e(Constants.TAG, "mServerQuery is null!"); - return data; + return entryListWrapper; } queryServer(mServerQuery, mKeyServer); - return data; + return entryListWrapper; } @Override @@ -74,7 +77,7 @@ public class ImportKeysListServerLoader extends AsyncTaskLoader<List<ImportKeysL } @Override - public void deliverResult(List<ImportKeysListEntry> data) { + public void deliverResult(AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> data) { super.deliverResult(data); } @@ -87,13 +90,17 @@ public class ImportKeysListServerLoader extends AsyncTaskLoader<List<ImportKeysL ArrayList<ImportKeysListEntry> searchResult = server.search(query); // add result to data - data.addAll(searchResult); + entryList.addAll(searchResult); + entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, null); } catch (KeyServer.InsufficientQuery e) { Log.e(Constants.TAG, "InsufficientQuery", e); + entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, e); } catch (KeyServer.QueryException e) { Log.e(Constants.TAG, "QueryException", e); + entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, e); } catch (KeyServer.TooManyResponses e) { Log.e(Constants.TAG, "TooManyResponses", e); + entryListWrapper = new AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>>(entryList, e); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java new file mode 100644 index 000000000..2b8cba857 --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/BadImportKeyDialogFragment.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.dialog; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; + +import org.sufficientlysecure.keychain.R; + +public class BadImportKeyDialogFragment extends DialogFragment { + private static final String ARG_BAD_IMPORT = "bad_import"; + + /** + * Creates a new instance of this Bad Import Key DialogFragment + * + * @param bad + * @return + */ + public static BadImportKeyDialogFragment newInstance(int bad) { + BadImportKeyDialogFragment frag = new BadImportKeyDialogFragment(); + Bundle args = new Bundle(); + + args.putInt(ARG_BAD_IMPORT, bad); + frag.setArguments(args); + + return frag; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final FragmentActivity activity = getActivity(); + final int badImport = getArguments().getInt(ARG_BAD_IMPORT); + + AlertDialog.Builder alert = new AlertDialog.Builder(activity); + alert.setIcon(R.drawable.ic_dialog_alert_holo_light); + alert.setTitle(R.string.warning); + alert.setMessage(activity.getResources() + .getQuantityString(R.plurals.bad_keys_encountered, badImport, badImport)); + alert.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.cancel(); + } + }); + alert.setCancelable(true); + + return alert.create(); + } +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java new file mode 100644 index 000000000..98b677511 --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2012-2013 Dominik Schürmann <dominik@dominikschuermann.de> + * + * 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.dialog; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ArrayAdapter; +import android.widget.Spinner; + +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.util.Choice; + +import java.util.Vector; + +public class CreateKeyDialogFragment extends DialogFragment { + + public interface OnAlgorithmSelectedListener { + public void onAlgorithmSelected(Choice algorithmChoice, int keySize); + } + + private static final String ARG_EDITOR_CHILD_COUNT = "child_count"; + + private int mNewKeySize; + private Choice mNewKeyAlgorithmChoice; + private OnAlgorithmSelectedListener mAlgorithmSelectedListener; + + public void setOnAlgorithmSelectedListener(OnAlgorithmSelectedListener listener) { + mAlgorithmSelectedListener = listener; + } + + public static CreateKeyDialogFragment newInstance(int mEditorChildCount) { + CreateKeyDialogFragment frag = new CreateKeyDialogFragment(); + Bundle args = new Bundle(); + + args.putInt(ARG_EDITOR_CHILD_COUNT, mEditorChildCount); + + frag.setArguments(args); + + return frag; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + final FragmentActivity context = getActivity(); + final LayoutInflater mInflater; + + final int childCount = getArguments().getInt(ARG_EDITOR_CHILD_COUNT); + mInflater = context.getLayoutInflater(); + + AlertDialog.Builder dialog = new AlertDialog.Builder(context); + + View view = mInflater.inflate(R.layout.create_key_dialog, null); + dialog.setView(view); + dialog.setTitle(R.string.title_create_key); + + boolean wouldBeMasterKey = (childCount == 0); + + final Spinner algorithm = (Spinner) view.findViewById(R.id.create_key_algorithm); + Vector<Choice> choices = new Vector<Choice>(); + choices.add(new Choice(Id.choice.algorithm.dsa, getResources().getString( + R.string.dsa))); + if (!wouldBeMasterKey) { + choices.add(new Choice(Id.choice.algorithm.elgamal, getResources().getString( + R.string.elgamal))); + } + + choices.add(new Choice(Id.choice.algorithm.rsa, getResources().getString( + R.string.rsa))); + + ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(context, + android.R.layout.simple_spinner_item, choices); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + algorithm.setAdapter(adapter); + // make RSA the default + for (int i = 0; i < choices.size(); ++i) { + if (choices.get(i).getId() == Id.choice.algorithm.rsa) { + algorithm.setSelection(i); + break; + } + } + + final Spinner keySize = (Spinner) view.findViewById(R.id.create_key_size); + ArrayAdapter<CharSequence> keySizeAdapter = ArrayAdapter.createFromResource( + context, R.array.key_size_spinner_values, + android.R.layout.simple_spinner_item); + keySizeAdapter + .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + keySize.setAdapter(keySizeAdapter); + keySize.setSelection(3); // Default to 4096 for the key length + dialog.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface di, int id) { + di.dismiss(); + try { + int nKeyIndex = keySize.getSelectedItemPosition(); + switch (nKeyIndex) { + case 0: + mNewKeySize = 512; + break; + case 1: + mNewKeySize = 1024; + break; + case 2: + mNewKeySize = 2048; + break; + case 3: + mNewKeySize = 4096; + break; + } + } catch (NumberFormatException e) { + mNewKeySize = 0; + } + + mNewKeyAlgorithmChoice = (Choice) algorithm.getSelectedItem(); + mAlgorithmSelectedListener.onAlgorithmSelected(mNewKeyAlgorithmChoice, mNewKeySize); + } + }); + + dialog.setCancelable(true); + dialog.setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface di, int id) { + di.dismiss(); + } + }); + + return dialog.create(); + } + +}
\ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java index a859bb045..cd8bc79a9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java @@ -83,7 +83,7 @@ public class DeleteFileDialogFragment extends DialogFragment { intent.putExtra(KeychainIntentService.EXTRA_DATA, data); ProgressDialogFragment deletingDialog = ProgressDialogFragment.newInstance( - R.string.progress_deleting_securely, ProgressDialog.STYLE_HORIZONTAL); + R.string.progress_deleting_securely, ProgressDialog.STYLE_HORIZONTAL, false, null); // Message is received after deleting is done in ApgService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(activity, deletingDialog) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java index 8a8c6a1d6..dc40bab2a 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java @@ -17,17 +17,6 @@ package org.sufficientlysecure.keychain.ui.dialog; -import org.spongycastle.openpgp.PGPPublicKeyRing; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.KeychainDatabase; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.util.Log; - import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; @@ -40,6 +29,14 @@ import android.os.RemoteException; import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentActivity; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainDatabase; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.Log; + import java.util.ArrayList; public class DeleteKeyDialogFragment extends DialogFragment { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java index 9b5d233e8..6c62d14e0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ProgressDialogFragment.java @@ -21,28 +21,40 @@ import android.app.Activity; import android.app.Dialog; import android.app.ProgressDialog; import android.content.DialogInterface; +import android.content.DialogInterface.OnCancelListener; import android.content.DialogInterface.OnKeyListener; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.view.KeyEvent; +import org.sufficientlysecure.keychain.R; + public class ProgressDialogFragment extends DialogFragment { private static final String ARG_MESSAGE_ID = "message_id"; private static final String ARG_STYLE = "style"; + private static final String ARG_CANCELABLE = "cancelable"; + + private OnCancelListener mOnCancelListener; /** * Creates new instance of this fragment * - * @param id + * @param messageId + * @param style + * @param cancelable * @return */ - public static ProgressDialogFragment newInstance(int messageId, int style) { + public static ProgressDialogFragment newInstance(int messageId, int style, boolean cancelable, + OnCancelListener onCancelListener) { ProgressDialogFragment frag = new ProgressDialogFragment(); Bundle args = new Bundle(); args.putInt(ARG_MESSAGE_ID, messageId); args.putInt(ARG_STYLE, style); + args.putBoolean(ARG_CANCELABLE, cancelable); frag.setArguments(args); + frag.mOnCancelListener = onCancelListener; + return frag; } @@ -60,7 +72,6 @@ public class ProgressDialogFragment extends DialogFragment { /** * Updates progress of dialog * - * @param messageId * @param progress * @param max */ @@ -74,7 +85,7 @@ public class ProgressDialogFragment extends DialogFragment { /** * Updates progress of dialog * - * @param messageId + * @param message * @param progress * @param max */ @@ -86,12 +97,20 @@ public class ProgressDialogFragment extends DialogFragment { dialog.setMax(max); } + @Override + public void onCancel(DialogInterface dialog) { + super.onCancel(dialog); + + if (this.mOnCancelListener != null) + this.mOnCancelListener.onCancel(dialog); + } + /** * Creates dialog */ @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - Activity activity = getActivity(); + final Activity activity = getActivity(); ProgressDialog dialog = new ProgressDialog(activity); dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); @@ -100,10 +119,22 @@ public class ProgressDialogFragment extends DialogFragment { int messageId = getArguments().getInt(ARG_MESSAGE_ID); int style = getArguments().getInt(ARG_STYLE); + boolean cancelable = getArguments().getBoolean(ARG_CANCELABLE); dialog.setMessage(getString(messageId)); dialog.setProgressStyle(style); + if (cancelable) { + dialog.setButton(DialogInterface.BUTTON_NEGATIVE, + activity.getString(R.string.progress_cancel), + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + } + // Disable the back button OnKeyListener keyListener = new OnKeyListener() { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java index 9f3270250..f26cd35c0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java @@ -17,28 +17,26 @@ package org.sufficientlysecure.keychain.ui.dialog; -import java.util.ArrayList; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.QrCodeUtils; - import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.DialogFragment; -import android.view.ContextThemeWrapper; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.QrCodeUtils; + +import java.util.ArrayList; + public class ShareQrCodeDialogFragment extends DialogFragment { private static final String ARG_KEY_URI = "uri"; private static final String ARG_FINGERPRINT_ONLY = "fingerprint_only"; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 9d3643914..a95d80a4e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -16,20 +16,6 @@ package org.sufficientlysecure.keychain.ui.widget; -import java.util.Vector; - -import org.spongycastle.openpgp.PGPSecretKey; -import org.sufficientlysecure.keychain.Id; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.service.PassphraseCacheService; -import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; -import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener; -import org.sufficientlysecure.keychain.util.Choice; - -import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; @@ -43,13 +29,25 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import android.widget.LinearLayout; -import android.widget.Spinner; import android.widget.TextView; import com.beardedhen.androidbootstrap.BootstrapButton; +import org.spongycastle.openpgp.PGPSecretKey; +import org.sufficientlysecure.keychain.Id; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.ui.dialog.CreateKeyDialogFragment; +import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; +import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener; +import org.sufficientlysecure.keychain.util.Choice; + +import java.util.Vector; + public class SectionView extends LinearLayout implements OnClickListener, EditorListener { private LayoutInflater mInflater; private BootstrapButton mPlusButton; @@ -149,84 +147,16 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor } case Id.type.key: { - AlertDialog.Builder dialog = new AlertDialog.Builder(getContext()); - - View view = mInflater.inflate(R.layout.create_key_dialog, null); - dialog.setView(view); - dialog.setTitle(R.string.title_create_key); - - boolean wouldBeMasterKey = (mEditors.getChildCount() == 0); - - final Spinner algorithm = (Spinner) view.findViewById(R.id.create_key_algorithm); - Vector<Choice> choices = new Vector<Choice>(); - choices.add(new Choice(Id.choice.algorithm.dsa, getResources().getString( - R.string.dsa))); - if (!wouldBeMasterKey) { - choices.add(new Choice(Id.choice.algorithm.elgamal, getResources().getString( - R.string.elgamal))); - } - - choices.add(new Choice(Id.choice.algorithm.rsa, getResources().getString( - R.string.rsa))); - - ArrayAdapter<Choice> adapter = new ArrayAdapter<Choice>(getContext(), - android.R.layout.simple_spinner_item, choices); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - algorithm.setAdapter(adapter); - // make RSA the default - for (int i = 0; i < choices.size(); ++i) { - if (choices.get(i).getId() == Id.choice.algorithm.rsa) { - algorithm.setSelection(i); - break; + CreateKeyDialogFragment mCreateKeyDialogFragment = CreateKeyDialogFragment.newInstance(mEditors.getChildCount()); + mCreateKeyDialogFragment.setOnAlgorithmSelectedListener(new CreateKeyDialogFragment.OnAlgorithmSelectedListener() { + @Override + public void onAlgorithmSelected(Choice algorithmChoice, int keySize) { + mNewKeyAlgorithmChoice = algorithmChoice; + mNewKeySize = keySize; + createKey(); } - } - - final Spinner keySize = (Spinner) view.findViewById(R.id.create_key_size); - ArrayAdapter<CharSequence> keySizeAdapter = ArrayAdapter.createFromResource( - getContext(), R.array.key_size_spinner_values, - android.R.layout.simple_spinner_item); - keySizeAdapter - .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - keySize.setAdapter(keySizeAdapter); - keySize.setSelection(3); // Default to 4096 for the key length - dialog.setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface di, int id) { - di.dismiss(); - try { - int nKeyIndex = keySize.getSelectedItemPosition(); - switch (nKeyIndex) { - case 0: - mNewKeySize = 512; - break; - case 1: - mNewKeySize = 1024; - break; - case 2: - mNewKeySize = 2048; - break; - case 3: - mNewKeySize = 4096; - break; - } - } catch (NumberFormatException e) { - mNewKeySize = 0; - } - - mNewKeyAlgorithmChoice = (Choice) algorithm.getSelectedItem(); - createKey(); - } - }); - - dialog.setCancelable(true); - dialog.setNegativeButton(android.R.string.cancel, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface di, int id) { - di.dismiss(); - } - }); - - dialog.create().show(); + }); + mCreateKeyDialogFragment.show(mActivity.getSupportFragmentManager(), "createKeyDialog"); break; } @@ -282,7 +212,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor private void createKey() { // Send all information needed to service to edit key in other thread - Intent intent = new Intent(mActivity, KeychainIntentService.class); + final Intent intent = new Intent(mActivity, KeychainIntentService.class); intent.setAction(KeychainIntentService.ACTION_GENERATE_KEY); @@ -309,7 +239,12 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor // show progress dialog mGeneratingDialog = ProgressDialogFragment.newInstance(R.string.progress_generating, - ProgressDialog.STYLE_SPINNER); + ProgressDialog.STYLE_SPINNER, true, new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + mActivity.stopService(intent); + } + }); // Message is received after generating is done in ApgService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(mActivity, diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java index 61fe13ffb..921d22f21 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java @@ -1,6 +1,8 @@ /* + * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2011 Thialfihar <thi@thialfihar.org> * Copyright (C) 2011 Senecaso - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -32,6 +34,7 @@ import java.util.List; import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.Locale; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; @@ -172,11 +175,11 @@ public class HkpKeyServer extends KeyServer { if (e.getCode() == 404) { return results; } else { - if (e.getData().toLowerCase().contains("no keys found")) { + if (e.getData().toLowerCase(Locale.US).contains("no keys found")) { return results; - } else if (e.getData().toLowerCase().contains("too many")) { + } else if (e.getData().toLowerCase(Locale.US).contains("too many")) { throw new TooManyResponses(); - } else if (e.getData().toLowerCase().contains("insufficient")) { + } else if (e.getData().toLowerCase(Locale.US).contains("insufficient")) { throw new InsufficientQuery(); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java index 072affb1f..b1e6b3c71 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java @@ -1,6 +1,8 @@ /* + * Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> + * Copyright (C) 2011 Thialfihar <thi@thialfihar.org> * Copyright (C) 2011 Senecaso - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -16,14 +18,8 @@ package org.sufficientlysecure.keychain.util; -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Date; import java.util.List; -import android.os.Parcel; -import android.os.Parcelable; - import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry; public abstract class KeyServer { |