From 1c32d1df8801968ea6423ee4f1a9160e0c4ff0c3 Mon Sep 17 00:00:00 2001 From: Tim Bray Date: Fri, 12 Sep 2014 08:34:51 -0700 Subject: Add cloud search tab, lose Keybase/Keyserver tabs, re-organize prefs --- .../keychain/keyimport/CloudSearch.java | 90 ++++++++++++++++++++++ .../keychain/keyimport/ImportKeysList.java | 84 ++++++++++++++++++++ .../keychain/keyimport/ImportKeysListEntry.java | 7 ++ .../keychain/keyimport/Keyserver.java | 16 +++- 4 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java new file mode 100644 index 000000000..27d826c59 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java @@ -0,0 +1,90 @@ +/* + * 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.keyimport; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.helper.Preferences; +import org.sufficientlysecure.keychain.util.Log; + +import java.util.ArrayList; +import java.util.Vector; + +/** + * Search two or more types of server for online keys. + */ +public class CloudSearch { + + private final static long SECONDS = 1000; + + public static ArrayList search(final String query, Preferences.CloudSearchPrefs cloudPrefs) + throws Keyserver.CloudSearchFailureException { + final ArrayList servers = new ArrayList(); + + // it's a Vector for sync, multiple threads might report problems + final Vector problems = new Vector(); + + if (cloudPrefs.searchKeyserver) { + servers.add(new HkpKeyserver(cloudPrefs.keyserver)); + } + if (cloudPrefs.searchKeybase) { + servers.add(new KeybaseKeyserver()); + } + final ImportKeysList results = new ImportKeysList(servers.size()); + + for (final Keyserver keyserver : servers) { + Runnable r = new Runnable() { + @Override + public void run() { + try { + results.addAll(keyserver.search(query)); + } catch (Keyserver.CloudSearchFailureException e) { + problems.add(e); + } + results.finishedAdding(); // notifies if all searchers done + } + }; + new Thread(r).start(); + } + + // wait for either all the searches to come back, or 10 seconds + synchronized(results) { + try { + results.wait(10 * SECONDS); + } catch (InterruptedException e) { + } + } + + if (results.outstandingSuppliers() > 0) { + String message = "Launched " + servers.size() + " cloud searchers, but" + + results.outstandingSuppliers() + "failed to complete."; + problems.add(new Keyserver.QueryFailedException(message)); + } + + if (!problems.isEmpty()) { + for (Keyserver.CloudSearchFailureException e : problems) { + Log.d(Constants.TAG, "Cloud search exception: " + e.getLocalizedMessage()); + } + + // only throw exception if we didn’t get any results + if (results.isEmpty()) { + throw problems.get(0); + } + } + + return results; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java new file mode 100644 index 000000000..15de8cb8f --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java @@ -0,0 +1,84 @@ +/* + * 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.keyimport; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Just an ArrayList, only with a synchronized dupe-merging add/addAll, and a sign-off method + */ +public class ImportKeysList extends ArrayList { + + private int mSupplierCount; + + public ImportKeysList(int supplierCount) { + mSupplierCount = supplierCount; + } + + @Override + public boolean add(ImportKeysListEntry toAdd) { + addOrMerge(toAdd); + return true; // that’s what the ArrayList#add contract says + } + + @Override + public boolean addAll(Collection addThese) { + boolean modified = false; + for (ImportKeysListEntry toAdd : addThese) { + modified = addOrMerge(toAdd) || modified; + } + return modified; + } + + // NOTE: side-effects + // NOTE: synchronized + private synchronized boolean addOrMerge(ImportKeysListEntry toAdd) { + for (ImportKeysListEntry existing : this) { + if (toAdd.hasSameKeyAs(existing)) { + return mergeIDs(toAdd, existing); + } + } + return super.add(toAdd); + } + + // being a little anal about the ArrayList#addAll contract here + private boolean mergeIDs(ImportKeysListEntry incoming, ImportKeysListEntry existing) { + boolean modified = false; + ArrayList incomingIDs = incoming.getUserIds(); + ArrayList existingIDs = existing.getUserIds(); + for (String incomingID : incomingIDs) { + if (!existingIDs.contains(incomingID)) { + existingIDs.add(incomingID); + modified = true; + } + } + return modified; + } + + // NOTE: synchronized + public synchronized void finishedAdding() { + mSupplierCount--; + if (mSupplierCount == 0) { + this.notify(); + } + } + + public int outstandingSuppliers() { + return mSupplierCount; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java index da70f1505..c10cd2e70 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -107,6 +107,13 @@ public class ImportKeysListEntry implements Serializable, Parcelable { return super.hashCode(); } + public boolean hasSameKeyAs(Object o) { + if (mFingerprintHex == null || o == null || !(o instanceof ImportKeysListEntry)) { + return false; + } + return mFingerprintHex.equals(((ImportKeysListEntry) o).mFingerprintHex); + } + public String getKeyIdHex() { return mKeyIdHex; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java index b726529f8..5e4bd0b70 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java @@ -24,7 +24,19 @@ import java.io.InputStream; import java.util.List; public abstract class Keyserver { - public static class QueryFailedException extends Exception { + + public static class CloudSearchFailureException extends Exception { + private static final long serialVersionUID = 2703768928624654515L; + + public CloudSearchFailureException(String message) { + super(message); + } + public CloudSearchFailureException() { + super(); + } + } + + public static class QueryFailedException extends CloudSearchFailureException { private static final long serialVersionUID = 2703768928624654512L; public QueryFailedException(String message) { @@ -32,7 +44,7 @@ public abstract class Keyserver { } } - public static class QueryNeedsRepairException extends Exception { + public static class QueryNeedsRepairException extends CloudSearchFailureException { private static final long serialVersionUID = 2693768928624654512L; } -- cgit v1.2.3 From 7de86a09bc3bae47c44b9dcb65bbda1298ccf843 Mon Sep 17 00:00:00 2001 From: Tim Bray Date: Sat, 13 Sep 2014 09:39:10 -0700 Subject: Change shuffle to rotate, and when merging ImportKeysList entries, if either side is revoked/expired, the merge reflects that --- .../keychain/keyimport/ImportKeysList.java | 12 ++++++++++-- .../keychain/keyimport/KeybaseKeyserver.java | 2 ++ 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java index 15de8cb8f..00bbca69c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java @@ -50,15 +50,23 @@ public class ImportKeysList extends ArrayList { private synchronized boolean addOrMerge(ImportKeysListEntry toAdd) { for (ImportKeysListEntry existing : this) { if (toAdd.hasSameKeyAs(existing)) { - return mergeIDs(toAdd, existing); + return mergeDupes(toAdd, existing); } } return super.add(toAdd); } // being a little anal about the ArrayList#addAll contract here - private boolean mergeIDs(ImportKeysListEntry incoming, ImportKeysListEntry existing) { + private boolean mergeDupes(ImportKeysListEntry incoming, ImportKeysListEntry existing) { boolean modified = false; + if (incoming.isRevoked()) { + existing.setRevoked(true); + modified = true; + } + if (incoming.isExpired()) { + existing.setExpired(true); + modified = true; + } ArrayList incomingIDs = incoming.getUserIds(); ArrayList existingIDs = existing.getUserIds(); for (String incomingID : incomingIDs) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java index cbd06da90..ff582e5ed 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java @@ -65,6 +65,8 @@ public class KeybaseKeyserver extends Keyserver { entry.setQuery(mQuery); entry.setOrigin(ORIGIN); + entry.setRevoked(false); // keybase doesn’t say anything about revoked keys + String username = match.getUsername(); String fullName = match.getFullName(); String fingerprint = match.getFingerprint(); -- cgit v1.2.3 From c13ccda65846f71c07e5e8f3b22b087db5d38eca Mon Sep 17 00:00:00 2001 From: Tim Bray Date: Sat, 13 Sep 2014 21:15:14 -0700 Subject: Keys found on multiple servers are now imported from all of them --- .../keychain/keyimport/HkpKeyserver.java | 2 +- .../keychain/keyimport/ImportKeysList.java | 3 +++ .../keychain/keyimport/ImportKeysListEntry.java | 16 +++++++++------- .../keychain/keyimport/KeybaseKeyserver.java | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java index c4c12cd3a..a6f1334ea 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -278,7 +278,7 @@ public class HkpKeyserver extends Keyserver { while (matcher.find()) { final ImportKeysListEntry entry = new ImportKeysListEntry(); entry.setQuery(query); - entry.setOrigin(getUrlPrefix() + mHost + ":" + mPort); + entry.addOrigin(getUrlPrefix() + mHost + ":" + mPort); int bitSize = Integer.parseInt(matcher.group(3)); entry.setBitStrength(bitSize); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java index 00bbca69c..15bcd8576 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysList.java @@ -67,6 +67,9 @@ public class ImportKeysList extends ArrayList { existing.setExpired(true); modified = true; } + for (String origin : incoming.getOrigins()) { + existing.addOrigin(origin); + } ArrayList incomingIDs = incoming.getUserIds(); ArrayList existingIDs = existing.getUserIds(); for (String incomingID : incomingIDs) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java index c10cd2e70..b3df67c0c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ImportKeysListEntry.java @@ -46,7 +46,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { private String mPrimaryUserId; private String mExtraData; private String mQuery; - private String mOrigin; + private ArrayList mOrigins; private Integer mHashCode = null; private boolean mSelected; @@ -70,7 +70,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { dest.writeByte((byte) (mSecretKey ? 1 : 0)); dest.writeByte((byte) (mSelected ? 1 : 0)); dest.writeString(mExtraData); - dest.writeString(mOrigin); + dest.writeStringList(mOrigins); } public static final Creator CREATOR = new Creator() { @@ -90,7 +90,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable { vr.mSecretKey = source.readByte() == 1; vr.mSelected = source.readByte() == 1; vr.mExtraData = source.readString(); - vr.mOrigin = source.readString(); + vr.mOrigins = new ArrayList(); + source.readStringList(vr.mOrigins); return vr; } @@ -230,12 +231,12 @@ public class ImportKeysListEntry implements Serializable, Parcelable { mQuery = query; } - public String getOrigin() { - return mOrigin; + public ArrayList getOrigins() { + return mOrigins; } - public void setOrigin(String origin) { - mOrigin = origin; + public void addOrigin(String origin) { + mOrigins.add(origin); } /** @@ -247,6 +248,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { // do not select by default mSelected = false; mUserIds = new ArrayList(); + mOrigins = new ArrayList(); } /** diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java index ff582e5ed..0a55fccc8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java @@ -63,7 +63,7 @@ public class KeybaseKeyserver extends Keyserver { private ImportKeysListEntry makeEntry(Match match) throws KeybaseException { final ImportKeysListEntry entry = new ImportKeysListEntry(); entry.setQuery(mQuery); - entry.setOrigin(ORIGIN); + entry.addOrigin(ORIGIN); entry.setRevoked(false); // keybase doesn’t say anything about revoked keys -- cgit v1.2.3