diff --git a/.gitmodules b/.gitmodules index a549f6cec..6b1646b89 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "extern/dnsjava"] path = extern/dnsjava url = https://github.com/open-keychain/dnsjava.git +[submodule "extern/KeybaseLib"] + path = extern/KeybaseLib + url = https://github.com/timbray/KeybaseLib.git diff --git a/OpenKeychain/build.gradle b/OpenKeychain/build.gradle index ba527948a..ff6f603f4 100644 --- a/OpenKeychain/build.gradle +++ b/OpenKeychain/build.gradle @@ -1,13 +1,5 @@ apply plugin: 'android' -//apply plugin: 'android-test' - -sourceSets { - //androidTest { - //java.srcDir file('src/test/java') - // configure the set of classes for JUnit tests - // include '**/*Test.class' - //} -} +apply plugin: 'robolectric' dependencies { // NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information @@ -28,27 +20,15 @@ dependencies { compile project(':extern:AppMsg:library') compile project(':extern:SuperToasts:supertoasts') compile project(':extern:dnsjava') + compile project(':extern:KeybaseLib:Lib') - // Dependencies for the `instrumentTest` task, make sure to list all your global dependencies here as well - androidTestCompile 'junit:junit:4.11' - androidTestCompile 'org.robolectric:robolectric:2.3' - androidTestCompile 'com.squareup:fest-android:1.0.8' - androidTestCompile 'com.google.android:android:4.1.1.4' - androidTestCompile 'com.android.support:support-v4:19.1.0' - androidTestCompile 'com.android.support:appcompat-v7:19.1.0' - androidTestCompile project(':extern:openpgp-api-lib') - androidTestCompile project(':extern:openkeychain-api-lib') - androidTestCompile project(':extern:html-textview') - androidTestCompile project(':extern:StickyListHeaders:library') - androidTestCompile project(':extern:AndroidBootstrap:AndroidBootstrap') - androidTestCompile project(':extern:zxing-qr-code') - androidTestCompile project(':extern:zxing-android-integration') - androidTestCompile project(':extern:spongycastle:core') - androidTestCompile project(':extern:spongycastle:pg') - androidTestCompile project(':extern:spongycastle:pkix') - androidTestCompile project(':extern:spongycastle:prov') - androidTestCompile project(':extern:AppMsg:library') - androidTestCompile project(':extern:SuperToasts:supertoasts') + + // Unit tests are run with Robolectric + testCompile 'junit:junit:4.11' + testCompile 'org.robolectric:robolectric:2.3' + testCompile 'com.squareup:fest-android:1.0.8' + testCompile 'com.google.android:android:4.1.1.4' + // compile dependencies are automatically also included in testCompile } diff --git a/OpenKeychain/src/androidTest/java/tests/SomeTest.java b/OpenKeychain/src/androidTest/java/tests/SomeTest.java deleted file mode 100644 index edf5f7bcc..000000000 --- a/OpenKeychain/src/androidTest/java/tests/SomeTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package tests; - -import org.junit.Assert; -import org.junit.Test; - -public class SomeTest { - @Test - public void willFail() { - // stub - // Assert.assertThat(); - } -} \ No newline at end of file diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index 48677431c..c6e528f4d 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -84,6 +84,11 @@ android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:label="@string/title_edit_key" android:windowSoftInputMode="stateHidden" /> + matches = Search.search(query); + for (Match match : matches) { + results.add(makeEntry(match)); } - } catch (Exception e) { + } catch (KeybaseException e) { Log.e(Constants.TAG, "keybase result parsing error", e); throw new QueryFailedException("Unexpected structure in keybase search result: " + e.getMessage()); } @@ -73,103 +57,43 @@ public class KeybaseKeyserver extends Keyserver { return results; } - private JSONObject getUser(String keybaseId) throws QueryFailedException { - try { - return getFromKeybase("_/api/1.0/user/lookup.json?username=", keybaseId); - } catch (Exception e) { - String detail = ""; - if (keybaseId != null) { - detail = ". Query was for user '" + keybaseId + "'"; - } - throw new QueryFailedException(e.getMessage() + detail); - } - } - - private ImportKeysListEntry makeEntry(JSONObject match) throws QueryFailedException, JSONException { - + private ImportKeysListEntry makeEntry(Match match) throws KeybaseException { final ImportKeysListEntry entry = new ImportKeysListEntry(); entry.setQuery(mQuery); entry.setOrigin(ORIGIN); - String keybaseId = JWalk.getString(match, "components", "username", "val"); - String fullName = JWalk.getString(match, "components", "full_name", "val"); - String fingerprint = JWalk.getString(match, "components", "key_fingerprint", "val"); - fingerprint = fingerprint.replace(" ", "").toUpperCase(); // not strictly necessary but doesn't hurt + String username = match.getUsername(); + String fullName = match.getFullName(); + String fingerprint = match.getFingerprint(); entry.setFingerprintHex(fingerprint); - entry.setKeyIdHex("0x" + fingerprint.substring(Math.max(0, fingerprint.length() - 16))); + entry.setKeyIdHex("0x" + match.getKeyID()); // store extra info, so we can query for the keybase id directly - entry.setExtraData(keybaseId); + entry.setExtraData(username); - final int algorithmId = JWalk.getInt(match, "components", "key_fingerprint", "algo"); + final int algorithmId = match.getAlgorithmId(); entry.setAlgorithm(PgpKeyHelper.getAlgorithmInfo(algorithmId)); - final int bitStrength = JWalk.getInt(match, "components", "key_fingerprint", "nbits"); + final int bitStrength = match.getBitStrength(); entry.setBitStrength(bitStrength); ArrayList userIds = new ArrayList(); - String name = fullName + " "; + String name = fullName + " "; userIds.add(name); - try { - userIds.add("github.com/" + JWalk.getString(match, "components", "github", "val")); - } catch (JSONException e) { - // ignore - } - try { - userIds.add("twitter.com/" + JWalk.getString(match, "components", "twitter", "val")); - } catch (JSONException e) { - // ignore - } - try { - JSONArray array = JWalk.getArray(match, "components", "websites"); - JSONObject website = array.getJSONObject(0); - userIds.add(JWalk.getString(website, "val")); - } catch (JSONException e) { - // ignore + + List proofLabels = match.getProofLabels(); + for (String proofLabel : proofLabels) { + userIds.add(proofLabel); } entry.setUserIds(userIds); entry.setPrimaryUserId(name); return entry; } - private JSONObject getFromKeybase(String path, String query) throws QueryFailedException { - try { - String url = "https://keybase.io/" + path + URLEncoder.encode(query, "utf8"); - Log.d(Constants.TAG, "keybase query: " + url); - - URL realUrl = new URL(url); - HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); - conn.setConnectTimeout(5000); // TODO: Reasonable values for keybase - conn.setReadTimeout(25000); - conn.connect(); - int response = conn.getResponseCode(); - if (response >= 200 && response < 300) { - String text = readAll(conn.getInputStream(), conn.getContentEncoding()); - try { - JSONObject json = new JSONObject(text); - if (JWalk.getInt(json, "status", "code") != 0) { - throw new QueryFailedException("Keybase.io query failed: " + path + "?" + - query); - } - return json; - } catch (JSONException e) { - throw new QueryFailedException("Keybase.io query returned broken JSON"); - } - } else { - String message = readAll(conn.getErrorStream(), conn.getContentEncoding()); - throw new QueryFailedException("Keybase.io query error (status=" + response + - "): " + message); - } - } catch (Exception e) { - throw new QueryFailedException("Keybase.io query error"); - } - } - @Override public String get(String id) throws QueryFailedException { try { - JSONObject user = getUser(id); - return JWalk.getString(user, "them", "public_keys", "primary", "bundle"); - } catch (Exception e) { + return User.keyForUsername(id); + } catch (KeybaseException e) { throw new QueryFailedException(e.getMessage()); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java index fdf561aaf..066c51a13 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableKeyRing.java @@ -27,7 +27,9 @@ public class ParcelableKeyRing implements Parcelable { public static final Creator CREATOR = new Creator() { public ParcelableKeyRing createFromParcel(final Parcel source) { - return new ParcelableKeyRing(source.createByteArray()); + byte[] bytes = source.createByteArray(); + String expectedFingerprint = source.readString(); + return new ParcelableKeyRing(bytes, expectedFingerprint); } public ParcelableKeyRing[] newArray(final int size) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/NullProgressable.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/NullProgressable.java new file mode 100644 index 000000000..68312dca3 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/NullProgressable.java @@ -0,0 +1,19 @@ +package org.sufficientlysecure.keychain.pgp; + +/** + * No-op implementation of Progressable + */ +public class NullProgressable implements Progressable { + + @Override + public void setProgress(String message, int current, int total) { + } + + @Override + public void setProgress(int resourceId, int current, int total) { + } + + @Override + public void setProgress(int current, int total) { + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index 7544f7b86..c50d92f7a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -139,6 +139,8 @@ public class PgpImportExport { String expectedFp = entry.getExpectedFingerprint(); if(expectedFp != null) { if(!PgpKeyHelper.convertFingerprintToHex(key.getFingerprint()).equals(expectedFp)) { + Log.d(Constants.TAG, "fingerprint: " + PgpKeyHelper.convertFingerprintToHex(key.getFingerprint())); + Log.d(Constants.TAG, "expected fingerprint: " + expectedFp); Log.e(Constants.TAG, "Actual key fingerprint is not the same as expected!"); badKeys += 1; continue; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index 291b07639..1ba028006 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -104,7 +104,7 @@ public class PgpKeyHelper { * @return */ public static String convertFingerprintToHex(byte[] fingerprint) { - String hexString = Hex.toHexString(fingerprint); + String hexString = Hex.toHexString(fingerprint).toLowerCase(Locale.US); return hexString; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index c590200ee..9b3e5bc54 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -481,58 +481,4 @@ public class PgpKeyOperation { } - - /** - * Certify the given pubkeyid with the given masterkeyid. - * - * @param certificationKey Certifying key - * @param publicKey public key to certify - * @param userIds User IDs to certify, must not be null or empty - * @param passphrase Passphrase of the secret key - * @return A keyring with added certifications - */ - public PGPPublicKey certifyKey(PGPSecretKey certificationKey, PGPPublicKey publicKey, - List userIds, String passphrase) - throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException, - PGPException, SignatureException { - - // create a signatureGenerator from the supplied masterKeyId and passphrase - PGPSignatureGenerator signatureGenerator; - { - - if (certificationKey == null) { - throw new PgpGeneralMsgIdException(R.string.error_no_signature_key); - } - - PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); - PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor); - if (signaturePrivateKey == null) { - throw new PgpGeneralMsgIdException(R.string.error_could_not_extract_private_key); - } - - // TODO: SHA256 fixed? - JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( - certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - - signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); - signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, signaturePrivateKey); - } - - { // supply signatureGenerator with a SubpacketVector - PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); - PGPSignatureSubpacketVector packetVector = spGen.generate(); - signatureGenerator.setHashedSubpackets(packetVector); - } - - // fetch public key ring, add the certification and return it - for (String userId : new IterableIterator(userIds.iterator())) { - PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey); - publicKey = PGPPublicKey.addCertification(publicKey, userId, sig); - } - - return publicKey; - } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 955fb90ba..f4e19d21a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -29,6 +29,7 @@ import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.NullProgressable; import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.WrappedPublicKey; import org.sufficientlysecure.keychain.service.OperationResultParcel.LogType; @@ -497,13 +498,12 @@ public class ProviderHelper { } } - mIndent -= 1; - } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); Log.e(Constants.TAG, "IOException during import", e); - mIndent -= 1; return SaveKeyringResult.RESULT_ERROR; + } finally { + mIndent -= 1; } try { @@ -522,19 +522,16 @@ public class ProviderHelper { mContentResolver.applyBatch(KeychainContract.CONTENT_AUTHORITY, operations); log(LogLevel.OK, LogType.MSG_IP_SUCCESS); - mIndent -= 1; progress.setProgress(LogType.MSG_IP_SUCCESS.getMsgId(), 90, 100); return result; } catch (RemoteException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_REMOTE_EX); Log.e(Constants.TAG, "RemoteException during import", e); - mIndent -= 1; return SaveKeyringResult.RESULT_ERROR; } catch (OperationApplicationException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_OP_EXC); Log.e(Constants.TAG, "OperationApplicationException during import", e); - mIndent -= 1; return SaveKeyringResult.RESULT_ERROR; } @@ -580,94 +577,87 @@ public class ProviderHelper { log(LogLevel.START, LogType.MSG_IS, new String[]{ PgpKeyHelper.convertKeyIdToHex(masterKeyId) }); mIndent += 1; - - // Canonicalize this key, to assert a number of assumptions made about it. - keyRing = keyRing.canonicalize(mLog, mIndent); - if (keyRing == null) { - return SaveKeyringResult.RESULT_ERROR; - } - - // IF this is successful, it's a secret key - int result = SaveKeyringResult.SAVED_SECRET; - - // save secret keyring try { - ContentValues values = new ContentValues(); - values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); - values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); - // insert new version of this keyRing - Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)); - if (mContentResolver.insert(uri, values) == null) { - log(LogLevel.ERROR, LogType.MSG_IS_DB_EXCEPTION); + + // Canonicalize this key, to assert a number of assumptions made about it. + keyRing = keyRing.canonicalize(mLog, mIndent); + if (keyRing == null) { return SaveKeyringResult.RESULT_ERROR; } - } catch (IOException e) { - Log.e(Constants.TAG, "Failed to encode key!", e); - log(LogLevel.ERROR, LogType.MSG_IS_FAIL_IO_EXC); - return SaveKeyringResult.RESULT_ERROR; - } - { - Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); + // IF this is successful, it's a secret key + int result = SaveKeyringResult.SAVED_SECRET; - // first, mark all keys as not available - ContentValues values = new ContentValues(); - values.put(Keys.HAS_SECRET, 0); - mContentResolver.update(uri, values, null, null); + // save secret keyring + try { + ContentValues values = new ContentValues(); + values.put(KeyRingData.MASTER_KEY_ID, masterKeyId); + values.put(KeyRingData.KEY_RING_DATA, keyRing.getEncoded()); + // insert new version of this keyRing + Uri uri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)); + if (mContentResolver.insert(uri, values) == null) { + log(LogLevel.ERROR, LogType.MSG_IS_DB_EXCEPTION); + return SaveKeyringResult.RESULT_ERROR; + } + } catch (IOException e) { + Log.e(Constants.TAG, "Failed to encode key!", e); + log(LogLevel.ERROR, LogType.MSG_IS_FAIL_IO_EXC); + return SaveKeyringResult.RESULT_ERROR; + } - values.put(Keys.HAS_SECRET, 1); - // then, mark exactly the keys we have available - log(LogLevel.INFO, LogType.MSG_IS_IMPORTING_SUBKEYS); - mIndent += 1; - Set available = keyRing.getAvailableSubkeys(); - for (UncachedPublicKey sub : - new IterableIterator(keyRing.getPublicKeys())) { - long id = sub.getKeyId(); - if(available.contains(id)) { - int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", - new String[] { Long.toString(id) }); - if (upd == 1) { - log(LogLevel.DEBUG, LogType.MSG_IS_SUBKEY_OK, new String[]{ - PgpKeyHelper.convertKeyIdToHex(id) - }); + { + Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId)); + + // first, mark all keys as not available + ContentValues values = new ContentValues(); + values.put(Keys.HAS_SECRET, 0); + mContentResolver.update(uri, values, null, null); + + values.put(Keys.HAS_SECRET, 1); + // then, mark exactly the keys we have available + log(LogLevel.INFO, LogType.MSG_IS_IMPORTING_SUBKEYS); + mIndent += 1; + Set available = keyRing.getAvailableSubkeys(); + for (UncachedPublicKey sub : + new IterableIterator(keyRing.getPublicKeys())) { + long id = sub.getKeyId(); + if (available.contains(id)) { + int upd = mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", + new String[]{Long.toString(id)}); + if (upd == 1) { + log(LogLevel.DEBUG, LogType.MSG_IS_SUBKEY_OK, new String[]{ + PgpKeyHelper.convertKeyIdToHex(id) + }); + } else { + log(LogLevel.WARN, LogType.MSG_IS_SUBKEY_NONEXISTENT, new String[]{ + PgpKeyHelper.convertKeyIdToHex(id) + }); + } } else { - log(LogLevel.WARN, LogType.MSG_IS_SUBKEY_NONEXISTENT, new String[]{ + log(LogLevel.INFO, LogType.MSG_IS_SUBKEY_STRIPPED, new String[]{ PgpKeyHelper.convertKeyIdToHex(id) }); } - } else { - log(LogLevel.INFO, LogType.MSG_IS_SUBKEY_STRIPPED, new String[]{ - PgpKeyHelper.convertKeyIdToHex(id) - }); } + mIndent -= 1; + + // this implicitly leaves all keys which were not in the secret key ring + // with has_secret = 0 } + + log(LogLevel.OK, LogType.MSG_IS_SUCCESS); + return result; + + } finally { mIndent -= 1; - - // this implicitly leaves all keys which were not in the secret key ring - // with has_secret = 0 } - log(LogLevel.OK, LogType.MSG_IS_SUCCESS); - return result; - } @Deprecated public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { - return savePublicKeyRing(keyRing, new Progressable() { - @Override - public void setProgress(String message, int current, int total) { - } - - @Override - public void setProgress(int resourceId, int current, int total) { - } - - @Override - public void setProgress(int current, int total) { - } - }); + return savePublicKeyRing(keyRing, new NullProgressable()); } /** Save a public keyring into the database. @@ -749,12 +739,13 @@ public class ProviderHelper { } } - mIndent -= 1; return new SaveKeyringResult(result, mLog); } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IP_FAIL_IO_EXC); return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } finally { + mIndent -= 1; } } @@ -844,6 +835,8 @@ public class ProviderHelper { } catch (IOException e) { log(LogLevel.ERROR, LogType.MSG_IS_FAIL_IO_EXC, null); return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog); + } finally { + mIndent -= 1; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index c68b7c189..47a6cab1d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -4,7 +4,7 @@ import android.os.Parcel; import android.os.Parcelable; import java.io.Serializable; -import java.util.HashMap; +import java.util.ArrayList; /** This class is a a transferable representation for a collection of changes * to be done on a keyring. @@ -29,14 +29,14 @@ public class SaveKeyringParcel implements Parcelable { public String newPassphrase; - public String[] addUserIds; - public SubkeyAdd[] addSubKeys; + public ArrayList addUserIds; + public ArrayList addSubKeys; - public SubkeyChange[] changeSubKeys; + public ArrayList changeSubKeys; public String changePrimaryUserId; - public String[] revokeUserIds; - public long[] revokeSubKeys; + public ArrayList revokeUserIds; + public ArrayList revokeSubKeys; public SaveKeyringParcel(long masterKeyId, byte[] fingerprint) { mMasterKeyId = masterKeyId; @@ -73,14 +73,14 @@ public class SaveKeyringParcel implements Parcelable { mMasterKeyId = source.readLong(); mFingerprint = source.createByteArray(); - addUserIds = source.createStringArray(); - addSubKeys = (SubkeyAdd[]) source.readSerializable(); + addUserIds = source.createStringArrayList(); + addSubKeys = (ArrayList) source.readSerializable(); - changeSubKeys = (SubkeyChange[]) source.readSerializable(); + changeSubKeys = (ArrayList) source.readSerializable(); changePrimaryUserId = source.readString(); - revokeUserIds = source.createStringArray(); - revokeSubKeys = source.createLongArray(); + revokeUserIds = source.createStringArrayList(); + revokeSubKeys = (ArrayList) source.readSerializable(); } @Override @@ -88,14 +88,14 @@ public class SaveKeyringParcel implements Parcelable { destination.writeLong(mMasterKeyId); destination.writeByteArray(mFingerprint); - destination.writeStringArray(addUserIds); + destination.writeStringList(addUserIds); destination.writeSerializable(addSubKeys); destination.writeSerializable(changeSubKeys); destination.writeString(changePrimaryUserId); - destination.writeStringArray(revokeUserIds); - destination.writeLongArray(revokeSubKeys); + destination.writeStringList(revokeUserIds); + destination.writeSerializable(revokeSubKeys); } public static final Creator CREATOR = new Creator() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java new file mode 100644 index 000000000..d4bc6c541 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/KeyringTestingHelper.java @@ -0,0 +1,56 @@ +package org.sufficientlysecure.keychain.testsupport; + +import android.content.Context; + +import org.sufficientlysecure.keychain.pgp.NullProgressable; +import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.remote.AppSettings; +import org.sufficientlysecure.keychain.service.OperationResults; + +/** + * Helper for tests of the Keyring import in ProviderHelper. + */ +public class KeyringTestingHelper { + + private final Context context; + + public KeyringTestingHelper(Context robolectricContext) { + this.context = robolectricContext; + } + + public boolean addKeyring() throws Exception { + + ProviderHelper providerHelper = new ProviderHelper(context); + +// providerHelper.insertApiApp(new AppSettings("robo-test-package", new byte[]{5, 4, 3, 2, 1})); + + byte[] data = TestDataUtil.readFully(getClass().getResourceAsStream("/public-key-for-sample.blob")); + UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data); + long masterKeyId = ring.getMasterKeyId(); + + // Should throw an exception; key is not yet saved + retrieveKeyAndExpectNotFound(providerHelper, masterKeyId); + + OperationResults.SaveKeyringResult saveKeyringResult = providerHelper.savePublicKeyRing(ring, new NullProgressable()); + + boolean saveSuccess = saveKeyringResult.success(); + + // Now re-retrieve the saved key. Should not throw an exception. + providerHelper.getWrappedPublicKeyRing(masterKeyId); + + // A different ID should still fail + retrieveKeyAndExpectNotFound(providerHelper, masterKeyId - 1); + + return saveSuccess; + } + + private void retrieveKeyAndExpectNotFound(ProviderHelper providerHelper, long masterKeyId) { + try { + providerHelper.getWrappedPublicKeyRing(masterKeyId); + throw new AssertionError("Was expecting the previous call to fail!"); + } catch (ProviderHelper.NotFoundException expectedException) { + // good + } + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java new file mode 100644 index 000000000..1ab5878cc --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/PgpVerifyTestingHelper.java @@ -0,0 +1,49 @@ +package org.sufficientlysecure.keychain.testsupport; + +import android.content.Context; + +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.InputData; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * For functional tests of PgpDecryptVerify + */ +public class PgpVerifyTestingHelper { + + private final Context context; + + public PgpVerifyTestingHelper(Context robolectricContext) { + this.context = robolectricContext; + } + + public int doTestFile(String testFileName) throws Exception { + ProviderHelper providerHelper = new ProviderHelperStub(context); + + PgpDecryptVerify.PassphraseCache passphraseCache = new PgpDecryptVerify.PassphraseCache() { + public String getCachedPassphrase(long masterKeyId) { + return "I am a passphrase"; + } + }; + + byte[] sampleInputBytes = TestDataUtil.readFully(getClass().getResourceAsStream(testFileName)); + + InputStream sampleInput = new ByteArrayInputStream(sampleInputBytes); + + InputData data = new InputData(sampleInput, sampleInputBytes.length); + OutputStream outStream = new ByteArrayOutputStream(); + + PgpDecryptVerify verify = new PgpDecryptVerify.Builder(providerHelper, passphraseCache, data, outStream).build(); + PgpDecryptVerifyResult result = verify.execute(); + + return result.getSignatureResult().getStatus(); + } + + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java new file mode 100644 index 000000000..c6d834bf9 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/ProviderHelperStub.java @@ -0,0 +1,22 @@ +package org.sufficientlysecure.keychain.testsupport; + +import android.content.Context; +import android.net.Uri; + +import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing; +import org.sufficientlysecure.keychain.provider.ProviderHelper; + +/** + * Created by art on 21/06/14. + */ +class ProviderHelperStub extends ProviderHelper { + public ProviderHelperStub(Context context) { + super(context); + } + + @Override + public WrappedPublicKeyRing getWrappedPublicKeyRing(Uri id) throws NotFoundException { + byte[] data = TestDataUtil.readFully(getClass().getResourceAsStream("/public-key-for-sample.blob")); + return new WrappedPublicKeyRing(data, false, 0); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java new file mode 100644 index 000000000..06dc08eab --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/TestDataUtil.java @@ -0,0 +1,25 @@ +package org.sufficientlysecure.keychain.testsupport; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Misc support functions. Would just use Guava / Apache Commons but + * avoiding extra dependencies. + */ +public class TestDataUtil { + public static byte[] readFully(InputStream input) { + byte[] buffer = new byte[8192]; + int bytesRead; + ByteArrayOutputStream output = new ByteArrayOutputStream(); + try { + while ((bytesRead = input.read(buffer)) != -1) { + output.write(buffer, 0, bytesRead); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + return output.toByteArray(); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java new file mode 100644 index 000000000..1cc0f9a95 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/testsupport/package-info.java @@ -0,0 +1,7 @@ +/** + * Test support classes. + * This is only in main code because of gradle-Android Studio-robolectric issues. Having + * classes in main code means IDE autocomplete, class detection, etc., all works. + * TODO Move into test package when possible + */ +package org.sufficientlysecure.keychain.testsupport; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityNew.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityNew.java new file mode 100644 index 000000000..f2b5e68eb --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivityNew.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.ui; + +import android.net.Uri; +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; +import android.view.View; +import android.view.View.OnClickListener; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper; +import org.sufficientlysecure.keychain.remote.ui.AccountsListFragment; +import org.sufficientlysecure.keychain.util.Log; + +public class EditKeyActivityNew extends ActionBarActivity { + + private Uri mDataUri; + + private EditKeyFragment mEditKeyFragment; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.edit_key_activity_new); + +// // Inflate a "Done"/"Cancel" custom action bar view +// ActionBarHelper.setTwoButtonView(getSupportActionBar(), +// R.string.btn_save, R.drawable.ic_action_save, +// new OnClickListener() { +// @Override +// public void onClick(View v) { +// // Save +// +// } +// }, R.string.menu_key_edit_cancel, R.drawable.ic_action_cancel, +// new OnClickListener() { +// @Override +// public void onClick(View v) { +// // Cancel +// +// } +// } +// ); + + Uri dataUri = getIntent().getData(); + if (dataUri == null) { + Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); + finish(); + return; + } + + loadFragment(savedInstanceState, dataUri); + } + + private void loadFragment(Bundle savedInstanceState, Uri dataUri) { + // However, if we're being restored from a previous state, + // then we don't need to do anything and should return or else + // we could end up with overlapping fragments. + if (savedInstanceState != null) { + return; + } + + // Create an instance of the fragment + mEditKeyFragment = EditKeyFragment.newInstance(dataUri); + + // Add the fragment to the 'fragment_container' FrameLayout + // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! + getSupportFragmentManager().beginTransaction() + .replace(R.id.edit_key_fragment_container, mEditKeyFragment) + .commitAllowingStateLoss(); + // do it immediately! + getSupportFragmentManager().executePendingTransactions(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java new file mode 100644 index 000000000..9231ce142 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package org.sufficientlysecure.keychain.ui; + +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v7.app.ActionBarActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.ListView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.ui.adapter.ViewKeyKeysAdapter; +import org.sufficientlysecure.keychain.ui.adapter.ViewKeyUserIdsAdapter; +import org.sufficientlysecure.keychain.util.Log; + +public class EditKeyFragment extends LoaderFragment implements + LoaderManager.LoaderCallbacks { + + public static final String ARG_DATA_URI = "uri"; + + private ListView mUserIds; + private ListView mKeys; + + private static final int LOADER_ID_USER_IDS = 0; + private static final int LOADER_ID_KEYS = 1; + + private ViewKeyUserIdsAdapter mUserIdsAdapter; + private ViewKeyKeysAdapter mKeysAdapter; + + private Uri mDataUri; + + /** + * Creates new instance of this fragment + */ + public static EditKeyFragment newInstance(Uri dataUri) { + EditKeyFragment frag = new EditKeyFragment(); + + Bundle args = new Bundle(); + args.putParcelable(ARG_DATA_URI, dataUri); + + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { + View root = super.onCreateView(inflater, superContainer, savedInstanceState); + View view = inflater.inflate(R.layout.edit_key_fragment, getContainer()); + + mUserIds = (ListView) view.findViewById(R.id.edit_key_user_ids); + mKeys = (ListView) view.findViewById(R.id.edit_key_keys); +// mActionEdit = view.findViewById(R.id.view_key_action_edit); + + return root; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + + // Inflate a "Done"/"Cancel" custom action bar view + ActionBarHelper.setTwoButtonView(((ActionBarActivity) getActivity()).getSupportActionBar(), + R.string.btn_save, R.drawable.ic_action_save, + new OnClickListener() { + @Override + public void onClick(View v) { + // Save + save(); + } + }, R.string.menu_key_edit_cancel, R.drawable.ic_action_cancel, + new OnClickListener() { + @Override + public void onClick(View v) { + // Cancel + getActivity().finish(); + } + } + ); + + Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); + if (dataUri == null) { + Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); + getActivity().finish(); + return; + } + + loadData(dataUri); + } + + private void loadData(Uri dataUri) { + mDataUri = dataUri; + + Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); + +// mActionEncrypt.setOnClickListener(new View.OnClickListener() { +// @Override +// public void onClick(View v) { +// encrypt(mDataUri); +// } +// }); + + + mUserIdsAdapter = new ViewKeyUserIdsAdapter(getActivity(), null, 0); + mUserIds.setAdapter(mUserIdsAdapter); + mKeysAdapter = new ViewKeyKeysAdapter(getActivity(), null, 0); + mKeys.setAdapter(mKeysAdapter); + + // Prepare the loaders. Either re-connect with an existing ones, + // or start new ones. + getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); + getLoaderManager().initLoader(LOADER_ID_KEYS, null, this); + } + + public Loader onCreateLoader(int id, Bundle args) { + setContentShown(false); + + switch (id) { + case LOADER_ID_USER_IDS: { + Uri baseUri = KeychainContract.UserIds.buildUserIdsUri(mDataUri); + return new CursorLoader(getActivity(), baseUri, + ViewKeyUserIdsAdapter.USER_IDS_PROJECTION, null, null, null); + } + + case LOADER_ID_KEYS: { + Uri baseUri = KeychainContract.Keys.buildKeysUri(mDataUri); + return new CursorLoader(getActivity(), baseUri, + ViewKeyKeysAdapter.KEYS_PROJECTION, null, null, null); + } + + default: + return null; + } + } + + public void onLoadFinished(Loader loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + switch (loader.getId()) { + case LOADER_ID_USER_IDS: + mUserIdsAdapter.swapCursor(data); + break; + + case LOADER_ID_KEYS: + mKeysAdapter.swapCursor(data); + break; + + } + setContentShown(true); + } + + /** + * This is called when the last Cursor provided to onLoadFinished() above is about to be closed. + * We need to make sure we are no longer using it. + */ + public void onLoaderReset(Loader loader) { + switch (loader.getId()) { + case LOADER_ID_USER_IDS: + mUserIdsAdapter.swapCursor(null); + break; + case LOADER_ID_KEYS: + mKeysAdapter.swapCursor(null); + break; + } + } + + private void save() { + getActivity().finish(); + // TODO + } + + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index e829df7a0..f7455905d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -44,6 +44,7 @@ import com.github.johnpersano.supertoasts.util.Style; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.OtherHelper; +import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; @@ -88,8 +89,6 @@ public class ImportKeysActivity extends ActionBarActivity { // view private ImportKeysListFragment mListFragment; - private String[] mNavigationStrings; - private Fragment mCurrentFragment; private View mImportButton; private ViewPager mViewPager; private SlidingTabLayout mSlidingTabLayout; @@ -97,12 +96,12 @@ public class ImportKeysActivity extends ActionBarActivity { public static final int VIEW_PAGER_HEIGHT = 64; // dp - private static final int NAV_SERVER = 0; - private static final int NAV_QR_CODE = 1; - private static final int NAV_FILE = 2; - private static final int NAV_KEYBASE = 3; + private static final int TAB_KEYSERVER = 0; + private static final int TAB_QR_CODE = 1; + private static final int TAB_FILE = 2; + private static final int TAB_KEYBASE = 3; - private int mSwitchToTab = NAV_SERVER; + private int mSwitchToTab = TAB_KEYSERVER; @Override protected void onCreate(Bundle savedInstanceState) { @@ -121,12 +120,6 @@ public class ImportKeysActivity extends ActionBarActivity { } }); - mNavigationStrings = getResources().getStringArray(R.array.import_action_list); - - // TODO: add actionbar button for this action? -// if (ACTION_IMPORT_KEY_FROM_KEYSERVER_AND_RETURN.equals(getIntent().getAction())) { -// } - handleActions(savedInstanceState, getIntent()); } @@ -142,26 +135,31 @@ public class ImportKeysActivity extends ActionBarActivity { if (Intent.ACTION_VIEW.equals(action)) { // Android's Action when opening file associated to Keychain (see AndroidManifest.xml) - // override action to delegate it to Keychain's ACTION_IMPORT_KEY + // delegate action to ACTION_IMPORT_KEY action = ACTION_IMPORT_KEY; } + if (scheme != null && scheme.toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) { + /* Scanning a fingerprint directly with Barcode Scanner */ + // delegate action to ACTION_IMPORT_KEY_FROM_KEYSERVER + String fingerprint = getFingerprintFromUri(dataUri); + action = ACTION_IMPORT_KEY_FROM_KEYSERVER; + extras.putString(EXTRA_FINGERPRINT, fingerprint); + } + Bundle serverBundle = null; boolean serverOnly = false; - if (scheme != null && scheme.toLowerCase(Locale.ENGLISH).equals(Constants.FINGERPRINT_SCHEME)) { - /* Scanning a fingerprint directly with Barcode Scanner */ - loadFromFingerprintUri(savedInstanceState, dataUri); - } else if (ACTION_IMPORT_KEY.equals(action)) { + if (ACTION_IMPORT_KEY.equals(action)) { /* Keychain's own Actions */ // display file fragment - mViewPager.setCurrentItem(NAV_FILE); + mViewPager.setCurrentItem(TAB_FILE); if (dataUri != null) { // action: directly load data startListFragment(savedInstanceState, null, dataUri, null); } else if (extras.containsKey(EXTRA_KEY_BYTES)) { - byte[] importData = intent.getByteArrayExtra(EXTRA_KEY_BYTES); + byte[] importData = extras.getByteArray(EXTRA_KEY_BYTES); // action: directly load data startListFragment(savedInstanceState, importData, null, null); @@ -180,7 +178,7 @@ public class ImportKeysActivity extends ActionBarActivity { if (extras.containsKey(EXTRA_QUERY)) { query = extras.getString(EXTRA_QUERY); } else if (extras.containsKey(EXTRA_KEY_ID)) { - long keyId = intent.getLongExtra(EXTRA_KEY_ID, 0); + long keyId = extras.getLong(EXTRA_KEY_ID, 0); if (keyId != 0) { query = PgpKeyHelper.convertKeyIdToHex(keyId); } @@ -190,7 +188,7 @@ public class ImportKeysActivity extends ActionBarActivity { // display keyserver fragment with query serverBundle = new Bundle(); serverBundle.putString(ImportKeysServerFragment.ARG_QUERY, query); - mSwitchToTab = NAV_SERVER; + mSwitchToTab = TAB_KEYSERVER; // action: search immediately startListFragment(savedInstanceState, null, null, query); @@ -204,7 +202,7 @@ public class ImportKeysActivity extends ActionBarActivity { * if the right key has been downloaded */ - String fingerprint = intent.getStringExtra(EXTRA_FINGERPRINT); + String fingerprint = extras.getString(EXTRA_FINGERPRINT); if (isFingerprintValid(fingerprint)) { String query = "0x" + fingerprint; @@ -212,8 +210,9 @@ public class ImportKeysActivity extends ActionBarActivity { serverBundle = new Bundle(); serverBundle.putString(ImportKeysServerFragment.ARG_QUERY, query); serverBundle.putBoolean(ImportKeysServerFragment.ARG_DISABLE_QUERY_EDIT, true); + // display server tab only serverOnly = true; - mSwitchToTab = NAV_SERVER; + mSwitchToTab = TAB_KEYSERVER; // action: search immediately startListFragment(savedInstanceState, null, null, query); @@ -227,7 +226,7 @@ public class ImportKeysActivity extends ActionBarActivity { } } else if (ACTION_IMPORT_KEY_FROM_FILE.equals(action)) { // NOTE: this only displays the appropriate fragment, no actions are taken - mSwitchToTab = NAV_FILE; + mSwitchToTab = TAB_FILE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); @@ -235,20 +234,20 @@ public class ImportKeysActivity extends ActionBarActivity { // also exposed in AndroidManifest // NOTE: this only displays the appropriate fragment, no actions are taken - mSwitchToTab = NAV_QR_CODE; + mSwitchToTab = TAB_QR_CODE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); } else if (ACTION_IMPORT_KEY_FROM_NFC.equals(action)) { // NOTE: this only displays the appropriate fragment, no actions are taken - mSwitchToTab = NAV_QR_CODE; + mSwitchToTab = TAB_QR_CODE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); } else if (ACTION_IMPORT_KEY_FROM_KEYBASE.equals(action)) { // NOTE: this only displays the appropriate fragment, no actions are taken - mSwitchToTab = NAV_KEYBASE; + mSwitchToTab = TAB_KEYBASE; // no immediate actions! startListFragment(savedInstanceState, null, null, null); @@ -273,6 +272,8 @@ public class ImportKeysActivity extends ActionBarActivity { @Override public void onPageSelected(int position) { + // cancel loader and clear list + mListFragment.destroyLoader(); } @Override @@ -329,23 +330,45 @@ public class ImportKeysActivity extends ActionBarActivity { return OtherHelper.pxToDp(this, params.height); } - public void loadFromFingerprintUri(Bundle savedInstanceState, Uri dataUri) { + private String getFingerprintFromUri(Uri dataUri) { String fingerprint = dataUri.toString().split(":")[1].toLowerCase(Locale.ENGLISH); - Log.d(Constants.TAG, "fingerprint: " + fingerprint); + return fingerprint; + } - // TODO: reload fragment when coming from qr code! -// loadFromFingerprint(savedInstanceState, fingerprint); + public void loadFromFingerprintUri(Uri dataUri) { + String query = "0x" + getFingerprintFromUri(dataUri); + // setCurrentItem does not work directly after onResume (from qr code scanner) + // see http://stackoverflow.com/q/19316729 + // so, reset adapter completely! + if (mViewPager.getAdapter() != null) + mViewPager.setAdapter(null); + mViewPager.setAdapter(mTabsAdapter); + mViewPager.setCurrentItem(TAB_KEYSERVER); -// String query = "0x" + fingerprint; -// -// // display keyserver fragment with query -// Bundle serverBundle = new Bundle(); -// serverBundle.putString(ImportKeysServerFragment.ARG_QUERY, query); -// serverBundle.putBoolean(ImportKeysServerFragment.ARG_DISABLE_QUERY_EDIT, true); -// -// return serverBundle; + ImportKeysServerFragment f = (ImportKeysServerFragment) + getActiveFragment(mViewPager, TAB_KEYSERVER); + + // TODO: Currently it simply uses keyserver nr 0 + String keyserver = Preferences.getPreferences(ImportKeysActivity.this) + .getKeyServers()[0]; + + // set fields of ImportKeysServerFragment + f.setQueryAndKeyserver(query, keyserver); + // search directly + loadCallback(new ImportKeysListFragment.KeyserverLoaderState(query, keyserver)); + } + + // http://stackoverflow.com/a/9293207 + public Fragment getActiveFragment(ViewPager container, int position) { + String name = makeFragmentName(container.getId(), position); + return getSupportFragmentManager().findFragmentByTag(name); + } + + // http://stackoverflow.com/a/9293207 + private static String makeFragmentName(int viewId, int index) { + return "android:switcher:" + viewId + ":" + index; } private boolean isFingerprintValid(String fingerprint) { @@ -372,7 +395,13 @@ public class ImportKeysActivity extends ActionBarActivity { boolean result = super.onTouchEvent(event); if (!result) { - mViewPager.onTouchEvent(event); + try { + mViewPager.onTouchEvent(event); + } catch (IllegalArgumentException e) { + // workaround for Android bug? + // http://stackoverflow.com/q/16459196 + Log.d(Constants.TAG, "Workaround: Catched IllegalArgumentException"); + } } return result; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java index f24cb379e..cb53647f6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java @@ -81,7 +81,7 @@ public class ImportKeysFileFragment extends Fragment { if (clipboardText != null) { sendText = clipboardText.toString(); if (sendText.toLowerCase(Locale.ENGLISH).startsWith(Constants.FINGERPRINT_SCHEME)) { - mImportActivity.loadFromFingerprintUri(null, Uri.parse(sendText)); + mImportActivity.loadFromFingerprintUri(Uri.parse(sendText)); return; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java index cba6dd78f..84fd513a0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysListFragment.java @@ -24,7 +24,9 @@ import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; import android.support.v4.util.LongSparseArray; +import android.view.MotionEvent; import android.view.View; +import android.view.View.OnTouchListener; import android.widget.ListView; import org.sufficientlysecure.keychain.Constants; @@ -77,7 +79,7 @@ public class ImportKeysListFragment extends ListFragment implements public ArrayList getSelectedData() { ArrayList result = new ArrayList(); for (ImportKeysListEntry entry : getSelectedEntries()) { - result.add(mCachedKeyData.get(entry.getKeyId())); + result.add(mCachedKeyData.get(entry.hashCode())); } return result; } @@ -149,19 +151,30 @@ public class ImportKeysListFragment extends ListFragment implements mAdapter = new ImportKeysAdapter(mActivity); setListAdapter(mAdapter); - if (getArguments().containsKey(ARG_DATA_URI) || getArguments().containsKey(ARG_BYTES)) { - Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); - byte[] bytes = getArguments().getByteArray(ARG_BYTES); + Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); + byte[] bytes = getArguments().getByteArray(ARG_BYTES); + String query = getArguments().getString(ARG_SERVER_QUERY); + + if (dataUri != null || bytes != null) { mLoaderState = new BytesLoaderState(bytes, dataUri); - } else if (getArguments().containsKey(ARG_SERVER_QUERY)) { - String query = getArguments().getString(ARG_SERVER_QUERY); - // TODO: this is used when scanning QR Code or updating a key. + } else if (query != null) { + // TODO: this is used when updating a key. // Currently it simply uses keyserver nr 0 String keyserver = Preferences.getPreferences(getActivity()) .getKeyServers()[0]; mLoaderState = new KeyserverLoaderState(query, keyserver); } + getListView().setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (!mAdapter.isEmpty()) { + mActivity.onTouchEvent(event); + } + return false; + } + }); + restartLoaders(); } @@ -184,27 +197,34 @@ public class ImportKeysListFragment extends ListFragment implements restartLoaders(); } + public void destroyLoader() { + if (getLoaderManager().getLoader(LOADER_ID_BYTES) != null) { + getLoaderManager().destroyLoader(LOADER_ID_BYTES); + } + if (getLoaderManager().getLoader(LOADER_ID_SERVER_QUERY) != null) { + getLoaderManager().destroyLoader(LOADER_ID_SERVER_QUERY); + } + if (getLoaderManager().getLoader(LOADER_ID_KEYBASE) != null) { + getLoaderManager().destroyLoader(LOADER_ID_KEYBASE); + } + setListShown(true); + } + private void restartLoaders() { if (mLoaderState instanceof BytesLoaderState) { // Start out with a progress indicator. setListShown(false); getLoaderManager().restartLoader(LOADER_ID_BYTES, null, this); - getLoaderManager().destroyLoader(LOADER_ID_SERVER_QUERY); - getLoaderManager().destroyLoader(LOADER_ID_KEYBASE); } else if (mLoaderState instanceof KeyserverLoaderState) { // Start out with a progress indicator. setListShown(false); - getLoaderManager().destroyLoader(LOADER_ID_BYTES); getLoaderManager().restartLoader(LOADER_ID_SERVER_QUERY, null, this); - getLoaderManager().destroyLoader(LOADER_ID_KEYBASE); } else if (mLoaderState instanceof KeybaseLoaderState) { // Start out with a progress indicator. setListShown(false); - getLoaderManager().destroyLoader(LOADER_ID_BYTES); - getLoaderManager().destroyLoader(LOADER_ID_SERVER_QUERY); getLoaderManager().restartLoader(LOADER_ID_KEYBASE, null, this); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java index 5766fc189..5f54daa3c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysQrCodeFragment.java @@ -132,7 +132,7 @@ public class ImportKeysQrCodeFragment extends Fragment { } public void importFingerprint(Uri dataUri) { - mImportActivity.loadFromFingerprintUri(null, dataUri); + mImportActivity.loadFromFingerprintUri(dataUri); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java index c7467d789..d339bc132 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysServerFragment.java @@ -40,7 +40,7 @@ import org.sufficientlysecure.keychain.util.Log; public class ImportKeysServerFragment extends Fragment { public static final String ARG_QUERY = "query"; - public static final String ARG_KEY_SERVER = "key_server"; + public static final String ARG_KEYSERVER = "keyserver"; public static final String ARG_DISABLE_QUERY_EDIT = "disable_query_edit"; private ImportKeysActivity mImportActivity; @@ -55,12 +55,12 @@ public class ImportKeysServerFragment extends Fragment { /** * Creates new instance of this fragment */ - public static ImportKeysServerFragment newInstance(String query, String keyServer) { + public static ImportKeysServerFragment newInstance(String query, String keyserver) { ImportKeysServerFragment frag = new ImportKeysServerFragment(); Bundle args = new Bundle(); args.putString(ARG_QUERY, query); - args.putString(ARG_KEY_SERVER, keyServer); + args.putString(ARG_KEYSERVER, keyserver); frag.setArguments(args); @@ -149,12 +149,12 @@ public class ImportKeysServerFragment extends Fragment { Log.d(Constants.TAG, "query: " + query); } - if (getArguments().containsKey(ARG_KEY_SERVER)) { - String keyServer = getArguments().getString(ARG_KEY_SERVER); - int keyServerPos = mServerAdapter.getPosition(keyServer); - mServerSpinner.setSelection(keyServerPos); + if (getArguments().containsKey(ARG_KEYSERVER)) { + String keyserver = getArguments().getString(ARG_KEYSERVER); + int keyserverPos = mServerAdapter.getPosition(keyserver); + mServerSpinner.setSelection(keyserverPos); - Log.d(Constants.TAG, "keyServer: " + keyServer); + Log.d(Constants.TAG, "keyserver: " + keyserver); } if (getArguments().getBoolean(ARG_DISABLE_QUERY_EDIT, false)) { @@ -174,4 +174,10 @@ public class ImportKeysServerFragment extends Fragment { mImportActivity.loadCallback(new ImportKeysListFragment.KeyserverLoaderState(query, keyserver)); } + public void setQueryAndKeyserver(String query, String keyserver) { + mQueryEditText.setText(query, TextView.BufferType.EDITABLE); + int keyServerPos = mServerAdapter.getPosition(keyserver); + mServerSpinner.setSelection(keyServerPos); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java index a0d449195..b8386b144 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayActivity.java @@ -1,13 +1,28 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package org.sufficientlysecure.keychain.ui; import android.os.Bundle; -import android.support.v4.view.GestureDetectorCompat; import android.support.v7.app.ActionBarActivity; -import android.view.GestureDetector; -import android.view.GestureDetector.SimpleOnGestureListener; -import android.view.MotionEvent; +import android.view.View; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper; public class LogDisplayActivity extends ActionBarActivity { @@ -15,6 +30,18 @@ public class LogDisplayActivity extends ActionBarActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + // Inflate a "Done" custom action bar + ActionBarHelper.setOneButtonView(getSupportActionBar(), + R.string.btn_okay, R.drawable.ic_action_done, + new View.OnClickListener() { + @Override + public void onClick(View v) { + // "Done" + finish(); + } + } + ); + setContentView(R.layout.log_display_activity); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index 496e98c18..e42d9d00b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + package org.sufficientlysecure.keychain.ui; import android.content.Context; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java index f4a44f526..e01a0140c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeysFragment.java @@ -83,17 +83,11 @@ public class ViewKeyKeysFragment extends LoaderFragment implements getLoaderManager().initLoader(0, null, this); } - static final String[] KEYS_PROJECTION = new String[] { - Keys._ID, - Keys.KEY_ID, Keys.RANK, Keys.ALGORITHM, Keys.KEY_SIZE, Keys.HAS_SECRET, - Keys.CAN_CERTIFY, Keys.CAN_ENCRYPT, Keys.CAN_SIGN, Keys.IS_REVOKED, - Keys.CREATION, Keys.EXPIRY, Keys.FINGERPRINT - }; - public Loader onCreateLoader(int id, Bundle args) { setContentShown(false); Uri baseUri = Keys.buildKeysUri(mDataUri); - return new CursorLoader(getActivity(), baseUri, KEYS_PROJECTION, null, null, null); + return new CursorLoader(getActivity(), baseUri, + ViewKeyKeysAdapter.KEYS_PROJECTION, null, null, null); } public void onLoadFinished(Loader loader, Cursor data) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index ae7cef70c..bd29f3820 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -49,6 +49,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements public static final String ARG_DATA_URI = "uri"; private View mActionEdit; + private View mActionEditNew; private View mActionEditDivider; private View mActionEncrypt; private View mActionCertify; @@ -73,6 +74,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids); mActionEdit = view.findViewById(R.id.view_key_action_edit); + mActionEditNew = view.findViewById(R.id.view_key_action_edit_new); mActionEditDivider = view.findViewById(R.id.view_key_action_edit_divider); mActionEncrypt = view.findViewById(R.id.view_key_action_encrypt); mActionCertify = view.findViewById(R.id.view_key_action_certify); @@ -116,6 +118,11 @@ public class ViewKeyMainFragment extends LoaderFragment implements editKey(mDataUri); } }); + mActionEditNew.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + editKeyNew(mDataUri); + } + }); mUserIdsAdapter = new ViewKeyUserIdsAdapter(getActivity(), null, 0); mUserIds.setAdapter(mUserIdsAdapter); @@ -256,4 +263,12 @@ public class ViewKeyMainFragment extends LoaderFragment implements startActivityForResult(editIntent, 0); } + private void editKeyNew(Uri dataUri) { + Intent editIntent = new Intent(getActivity(), EditKeyActivityNew.class); +// editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri)); + editIntent.setData(KeychainContract.KeyRingData.buildSecretKeyRingUri(dataUri)); + editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY); + startActivityForResult(editIntent, 0); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 233b1fca8..a44d32e5b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -170,7 +170,7 @@ public class ImportKeysAdapter extends ArrayAdapter { } else { holder.userIdsList.setVisibility(View.VISIBLE); - // clear view from holder + // destroyLoader view from holder holder.userIdsList.removeAllViews(); Iterator it = entry.getUserIds().iterator(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java index c2712e89e..99f959035 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListLoader.java @@ -138,7 +138,7 @@ public class ImportKeysListLoader for(UncachedKeyRing key : rings) { ImportKeysListEntry item = new ImportKeysListEntry(getContext(), key); mData.add(item); - mParcelableRings.put(key.getMasterKeyId(), new ParcelableKeyRing(key.getEncoded())); + mParcelableRings.put(item.hashCode(), new ParcelableKeyRing(key.getEncoded())); isEmpty = false; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java index 4eb6f158b..d62c2db43 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListServerLoader.java @@ -47,7 +47,6 @@ public class ImportKeysListServerLoader @Override public AsyncTaskResultWrapper> loadInBackground() { - mEntryListWrapper = new AsyncTaskResultWrapper>(mEntryList, null); if (mServerQuery == null) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java index f4942a2a0..dae287bbc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java @@ -53,6 +53,22 @@ public class ViewKeyKeysAdapter extends CursorAdapter { private ColorStateList mDefaultTextColor; + public static final String[] KEYS_PROJECTION = new String[] { + Keys._ID, + Keys.KEY_ID, + Keys.RANK, + Keys.ALGORITHM, + Keys.KEY_SIZE, + Keys.HAS_SECRET, + Keys.CAN_CERTIFY, + Keys.CAN_ENCRYPT, + Keys.CAN_SIGN, + Keys.IS_REVOKED, + Keys.CREATION, + Keys.EXPIRY, + Keys.FINGERPRINT + }; + public ViewKeyKeysAdapter(Context context, Cursor c, int flags) { super(context, c, flags); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java deleted file mode 100644 index 76797811d..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/JWalk.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2014 Tim Bray - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.util; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Minimal hierarchy selector - * - * This is for picking out an item in a large multilevel JSON object, for example look at - * the Keybase.io User object, documentation at https://keybase.io/__/api-docs/1.0#user-objects - * an example available via - * curl https://keybase.io/_/api/1.0/user/lookup.json?username=timbray - * - * If you want to retrieve the ascii-armored key, you'd say - * String key = JWalk.getString(match,"them", "public_keys", "primary", "bundle"); - */ -public class JWalk { - - /** - * Returns an int member value from the JSON sub-object addressed by the path - * - * @param json The object - * @param path list of string object member selectors - * @return the int addressed by the path, assuming such a thing exists - * @throws JSONException if any step in the path doesn’t work - */ - public static int getInt(JSONObject json, String... path) throws JSONException { - json = walk(json, path); - return json.getInt(path[path.length - 1]); - } - - /** - * Returns a long member value from the JSON sub-object addressed by the path - * - * @param json The object - * @param path list of string object member selectors - * @return the int addressed by the path, assuming such a thing exists - * @throws JSONException if any step in the path doesn’t work - */ - public static long getLong(JSONObject json, String... path) throws JSONException { - json = walk(json, path); - return json.getLong(path[path.length - 1]); - } - - /** - * Returns a String member value from the JSON sub-object addressed by the path - * - * @param json The object - * @param path list of string object member selectors - * @return the int addressed by the path, assuming such a thing exists - * @throws JSONException if any step in the path doesn’t work - */ - public static String getString(JSONObject json, String... path) throws JSONException { - json = walk(json, path); - return json.getString(path[path.length - 1]); - } - - /** - * Returns a JSONArray member value from the JSON sub-object addressed by the path - * - * @param json The object - * @param path list of string object member selectors - * @return the int addressed by the path, assuming such a thing exists - * @throws JSONException if any step in the path doesn’t work - */ - public static JSONArray getArray(JSONObject json, String... path) throws JSONException { - json = walk(json, path); - return json.getJSONArray(path[path.length - 1]); - } - - /** - * Returns a JSONObject member value from the JSON sub-object addressed by the path, or null - * - * @param json The object - * @param path list of string object member selectors - * @return the int addressed by the path, assuming such a thing exists - * @throws JSONException if any step in the path, except for the last, doesn’t work - */ - public static JSONObject optObject(JSONObject json, String... path) throws JSONException { - json = walk(json, path); - return json.optJSONObject(path[path.length - 1]); - } - - private static JSONObject walk(JSONObject json, String... path) throws JSONException { - int len = path.length - 1; - int pathIndex = 0; - try { - while (pathIndex < len) { - json = json.getJSONObject(path[pathIndex]); - pathIndex++; - } - } catch (JSONException e) { - // try to give ’em a nice-looking error - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < len; i++) { - sb.append(path[i]).append('.'); - } - sb.append(path[len]); - throw new JSONException("JWalk error at step " + pathIndex + " of " + sb); - } - return json; - } -} diff --git a/OpenKeychain/src/main/res/layout/edit_key_activity_new.xml b/OpenKeychain/src/main/res/layout/edit_key_activity_new.xml new file mode 100644 index 000000000..f96b993c5 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/edit_key_activity_new.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/edit_key_fragment.xml b/OpenKeychain/src/main/res/layout/edit_key_fragment.xml new file mode 100644 index 000000000..dbc0c3941 --- /dev/null +++ b/OpenKeychain/src/main/res/layout/edit_key_fragment.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + diff --git a/OpenKeychain/src/main/res/layout/log_display_activity.xml b/OpenKeychain/src/main/res/layout/log_display_activity.xml index 591e2650c..1dc45a9a8 100644 --- a/OpenKeychain/src/main/res/layout/log_display_activity.xml +++ b/OpenKeychain/src/main/res/layout/log_display_activity.xml @@ -13,34 +13,4 @@ android:layout_marginRight="8dp" android:layout_marginLeft="8dp" /> - - - - - - - - \ No newline at end of file diff --git a/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml b/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml index d93420a99..96871aacf 100644 --- a/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml +++ b/OpenKeychain/src/main/res/layout/view_key_main_fragment.xml @@ -74,6 +74,22 @@ android:drawablePadding="8dp" android:gravity="center_vertical" /> + + @string/key_size_1024 @string/key_size_custom - - @string/menu_import_from_key_server - @string/menu_import_from_file - @string/menu_import_from_qr_code - @string/import_from_clipboard - @string/menu_import_from_nfc - @string/menu_import_from_keybase - diff --git a/OpenKeychain/src/test/java/tests/PgpDecryptVerifyTest.java b/OpenKeychain/src/test/java/tests/PgpDecryptVerifyTest.java new file mode 100644 index 000000000..346a1f9df --- /dev/null +++ b/OpenKeychain/src/test/java/tests/PgpDecryptVerifyTest.java @@ -0,0 +1,37 @@ +package tests; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.*; +import org.openintents.openpgp.OpenPgpSignatureResult; +import org.sufficientlysecure.keychain.testsupport.PgpVerifyTestingHelper; + +@RunWith(RobolectricTestRunner.class) +@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 +public class PgpDecryptVerifyTest { + + @Test + public void testVerifySuccess() throws Exception { + + String testFileName = "/sample.txt"; + int expectedSignatureResult = OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED; + + int status = new PgpVerifyTestingHelper(Robolectric.application).doTestFile(testFileName); + + Assert.assertEquals(expectedSignatureResult, status); + } + + + @Test + public void testVerifyFailure() throws Exception { + + String testFileName = "/sample-altered.txt"; + int expectedSignatureResult = OpenPgpSignatureResult.SIGNATURE_ERROR; + + int status = new PgpVerifyTestingHelper(Robolectric.application).doTestFile(testFileName); + + Assert.assertEquals(expectedSignatureResult, status); + } + +} diff --git a/OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java b/OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java new file mode 100644 index 000000000..265e01170 --- /dev/null +++ b/OpenKeychain/src/test/java/tests/ProviderHelperKeyringTest.java @@ -0,0 +1,20 @@ +package tests; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.*; +import org.openintents.openpgp.OpenPgpSignatureResult; +import org.sufficientlysecure.keychain.testsupport.KeyringTestingHelper; +import org.sufficientlysecure.keychain.testsupport.PgpVerifyTestingHelper; + +@RunWith(RobolectricTestRunner.class) +@org.robolectric.annotation.Config(emulateSdk = 18) // Robolectric doesn't yet support 19 +public class ProviderHelperKeyringTest { + + @Test + public void testSavePublicKeyring() throws Exception { + Assert.assertTrue(new KeyringTestingHelper(Robolectric.application).addKeyring()); + } + +} diff --git a/OpenKeychain/src/test/resources/public-key-for-sample.blob b/OpenKeychain/src/test/resources/public-key-for-sample.blob new file mode 100644 index 000000000..4aa91510b Binary files /dev/null and b/OpenKeychain/src/test/resources/public-key-for-sample.blob differ diff --git a/OpenKeychain/src/test/resources/sample-altered.txt b/OpenKeychain/src/test/resources/sample-altered.txt new file mode 100644 index 000000000..458821f81 --- /dev/null +++ b/OpenKeychain/src/test/resources/sample-altered.txt @@ -0,0 +1,26 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +This is a simple text document, which is used to illustrate +the concept of signing simple text files. There are no +control characters or special formatting commands in this +text, just simple printable ASCII characters. +MALICIOUS TEXT +To make this a slightly less uninteresting document, there +follows a short shopping list. + + eggs, 1 doz + milk, 1 gal + bacon, 1 lb + olive oil + bread, 1 loaf + +That's all there is to this document. + +-----BEGIN PGP SIGNATURE----- +Version: PGPfreeware 5.5.5 for non-commercial use + +iQA/AwUBN78ib3S9RCOKzj55EQKqDACg1NV2/iyPKrDlOVJvJwz6ArcQ0UQAnjNC +CDxKAFyaaGa835l1vpbFkAJk +=3r/N +-----END PGP SIGNATURE----- diff --git a/OpenKeychain/src/test/resources/sample.txt b/OpenKeychain/src/test/resources/sample.txt new file mode 100644 index 000000000..c0065f78d --- /dev/null +++ b/OpenKeychain/src/test/resources/sample.txt @@ -0,0 +1,26 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +This is a simple text document, which is used to illustrate +the concept of signing simple text files. There are no +control characters or special formatting commands in this +text, just simple printable ASCII characters. + +To make this a slightly less uninteresting document, there +follows a short shopping list. + + eggs, 1 doz + milk, 1 gal + bacon, 1 lb + olive oil + bread, 1 loaf + +That's all there is to this document. + +-----BEGIN PGP SIGNATURE----- +Version: PGPfreeware 5.5.5 for non-commercial use + +iQA/AwUBN78ib3S9RCOKzj55EQKqDACg1NV2/iyPKrDlOVJvJwz6ArcQ0UQAnjNC +CDxKAFyaaGa835l1vpbFkAJk +=3r/N +-----END PGP SIGNATURE----- diff --git a/README.md b/README.md index f940e04f6..9a80eaf07 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Expand the Extras directory and install "Android Support Repository" Select everything for the newest SDK Platform (API-Level 19) 4. Export ANDROID_HOME pointing to your Android SDK 5. Execute ``./gradlew build`` -6. You can install the app with ``adb install -r OpenKeychain/build/apk/OpenKeychain-debug-unaligned.apk`` +6. You can install the app with ``adb install -r OpenKeychain/build/outputs/apk/OpenKeychain-debug-unaligned.apk`` ### Build API Demo with Gradle diff --git a/build.gradle b/build.gradle index e3cafe3ce..e0da6b52f 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ buildscript { dependencies { // NOTE: Always use fixed version codes not dynamic ones, e.g. 0.7.3 instead of 0.7.+, see README for more information classpath 'com.android.tools.build:gradle:0.11.1' - //classpath 'org.robolectric.gradle:gradle-android-test-plugin:0.10.0' + classpath 'org.robolectric:robolectric-gradle-plugin:0.11.0' } } diff --git a/extern/KeybaseLib b/extern/KeybaseLib new file mode 160000 index 000000000..838800d2c --- /dev/null +++ b/extern/KeybaseLib @@ -0,0 +1 @@ +Subproject commit 838800d2cb57fdd4caca1bdbd919e9d96dae13e9 diff --git a/extern/openpgp-api-lib b/extern/openpgp-api-lib index a77887d32..289d48b63 160000 --- a/extern/openpgp-api-lib +++ b/extern/openpgp-api-lib @@ -1 +1 @@ -Subproject commit a77887d32fae68171fcd0d2989bf537c0c11f0b9 +Subproject commit 289d48b633b7493f2a760583c88670b9fc4ef96f diff --git a/settings.gradle b/settings.gradle index 282c8a234..24eb03490 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,3 +13,4 @@ include ':extern:spongycastle:prov' include ':extern:AppMsg:library' include ':extern:SuperToasts:supertoasts' include ':extern:dnsjava' +include ':extern:KeybaseLib:Lib'