From 911fa020c2fec2d83696e22db506b78072c16ee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 27 Oct 2016 16:19:43 +0200 Subject: [PATCH] Preparations for support of onion keyservers. Refactoring of Keyserver and proxy related classes --- .../keychain/Constants.java | 2 +- .../keychain/keyimport/CloudSearch.java | 13 +- .../keychain/keyimport/FacebookKeyserver.java | 29 +- .../keychain/keyimport/KeybaseKeyserver.java | 17 +- .../keychain/keyimport/Keyserver.java | 8 +- ...erver.java => ParcelableHkpKeyserver.java} | 305 +++++++++++------- .../keychain/operations/ImportOperation.java | 53 +-- .../KeybaseVerificationOperation.java | 3 +- .../keychain/operations/UploadOperation.java | 75 ++--- .../service/CertifyActionsParcel.java | 10 +- .../keychain/service/ImportKeyringParcel.java | 10 +- .../service/KeyserverSyncAdapterService.java | 3 +- .../keychain/service/RevokeKeyringParcel.java | 10 +- .../keychain/service/SaveKeyringParcel.java | 13 +- .../keychain/service/UploadKeyringParcel.java | 12 +- .../keychain/ui/CreateKeyFinalFragment.java | 3 +- ...reateSecurityTokenImportResetFragment.java | 3 +- .../keychain/ui/DecryptFragment.java | 3 +- .../keychain/ui/DecryptListFragment.java | 3 +- .../keychain/ui/DeleteKeyDialogActivity.java | 3 +- .../keychain/ui/EditIdentitiesFragment.java | 3 +- .../keychain/ui/ImportKeysActivity.java | 6 +- .../keychain/ui/ImportKeysProxyActivity.java | 3 +- .../keychain/ui/KeyListFragment.java | 3 +- .../keychain/ui/SafeSlingerActivity.java | 3 +- .../keychain/ui/SettingsActivity.java | 6 +- .../ui/SettingsKeyServerActivity.java | 7 +- .../ui/SettingsKeyserverFragment.java | 36 +-- .../keychain/ui/UploadKeyActivity.java | 23 +- .../keychain/ui/ViewKeyActivity.java | 3 +- .../keychain/ui/ViewKeyKeybaseFragment.java | 8 +- .../ui/adapter/ImportKeysListCloudLoader.java | 9 +- .../AddEditKeyserverDialogFragment.java | 155 ++++----- .../keychain/util/EmailKeyHelper.java | 30 +- .../keychain/util/ParcelableProxy.java | 19 +- .../keychain/util/Preferences.java | 149 ++++----- .../keychain/util/orbot/OrbotHelper.java | 14 +- 37 files changed, 576 insertions(+), 479 deletions(-) rename OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/{HkpKeyserver.java => ParcelableHkpKeyserver.java} (66%) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index fd6e903fa..bafadec3c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -147,7 +147,7 @@ public final class Constants { public static final class Defaults { public static final String KEY_SERVERS = "hkps://hkps.pool.sks-keyservers.net, hkps://pgp.mit.edu"; - public static final int PREF_VERSION = 6; + public static final int PREF_VERSION = 7; } public static final class key { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java index efb62d80b..18cbd4561 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/CloudSearch.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.keyimport; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.Preferences; import java.net.Proxy; @@ -35,7 +36,7 @@ public class CloudSearch { private final static long SECONDS = 1000; public static ArrayList search( - @NonNull final String query, Preferences.CloudSearchPrefs cloudPrefs, @NonNull Proxy proxy) + @NonNull final String query, Preferences.CloudSearchPrefs cloudPrefs, @NonNull final ParcelableProxy proxy) throws Keyserver.CloudSearchFailureException { final ArrayList servers = new ArrayList<>(); @@ -43,13 +44,13 @@ public class CloudSearch { final Vector problems = new Vector<>(); if (cloudPrefs.searchKeyserver) { - servers.add(new HkpKeyserver(cloudPrefs.keyserver, proxy)); + servers.add(cloudPrefs.keyserver); } if (cloudPrefs.searchKeybase) { - servers.add(new KeybaseKeyserver(proxy)); + servers.add(new KeybaseKeyserver()); } if (cloudPrefs.searchFacebook) { - servers.add(new FacebookKeyserver(proxy)); + servers.add(new FacebookKeyserver()); } final ImportKeysList results = new ImportKeysList(servers.size()); @@ -59,7 +60,7 @@ public class CloudSearch { @Override public void run() { try { - results.addAll(keyserver.search(query)); + results.addAll(keyserver.search(query, proxy)); } catch (Keyserver.CloudSearchFailureException e) { problems.add(e); } @@ -74,7 +75,7 @@ public class CloudSearch { // wait for either all the searches to come back, or 10 seconds. If using proxy, wait 30 seconds. synchronized (results) { try { - if (proxy == Proxy.NO_PROXY) { + if (proxy.getProxy() == Proxy.NO_PROXY) { results.wait(30 * SECONDS); } else { results.wait(10 * SECONDS); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java index 6217d1a01..661b50424 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/FacebookKeyserver.java @@ -27,6 +27,7 @@ import android.support.annotation.Nullable; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; + import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; @@ -35,11 +36,12 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.OkHttpClientFactory; +import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.TlsHelper; import java.io.IOException; -import java.net.Proxy; +import java.net.Proxy; import java.net.URL; import java.util.ArrayList; import java.util.List; @@ -56,18 +58,15 @@ public class FacebookKeyserver extends Keyserver { public static final String ORIGIN = FB_URL; - private final Proxy mProxy; - - public FacebookKeyserver(Proxy proxy) { - mProxy = proxy; + public FacebookKeyserver() { } @Override - public List search(String fbUsername) + public List search(String fbUsername, ParcelableProxy proxy) throws QueryFailedException, QueryNeedsRepairException { List entry = new ArrayList<>(1); - String data = get(fbUsername); + String data = get(fbUsername, proxy); // if we're here that means key retrieval succeeded, // would have thrown an exception otherwise try { @@ -85,10 +84,10 @@ public class FacebookKeyserver extends Keyserver { } @Override - public String get(String fbUsername) throws QueryFailedException { - Log.d(Constants.TAG, "FacebookKeyserver get: " + fbUsername + " using Proxy: " + mProxy); + public String get(String fbUsername, ParcelableProxy proxy) throws QueryFailedException { + Log.d(Constants.TAG, "FacebookKeyserver get: " + fbUsername + " using Proxy: " + proxy); - String data = query(fbUsername); + String data = query(fbUsername, proxy); if (data == null) { throw new QueryFailedException("data is null"); @@ -101,14 +100,14 @@ public class FacebookKeyserver extends Keyserver { throw new QueryFailedException("data is null"); } - private String query(String fbUsername) throws QueryFailedException { + private String query(String fbUsername, ParcelableProxy proxy) throws QueryFailedException { try { String request = String.format(FB_KEY_URL_FORMAT, fbUsername); - Log.d(Constants.TAG, "fetching from Facebook with: " + request + " proxy: " + mProxy); + Log.d(Constants.TAG, "fetching from Facebook with: " + request + " proxy: " + proxy); URL url = new URL(request); - OkHttpClient client = OkHttpClientFactory.getClientPinnedIfAvailable(url, mProxy); + OkHttpClient client = OkHttpClientFactory.getClientPinnedIfAvailable(url, proxy.getProxy()); Response response = client.newCall(new Request.Builder().url(url).build()).execute(); @@ -126,7 +125,7 @@ public class FacebookKeyserver extends Keyserver { Log.e(Constants.TAG, "IOException at Facebook key download", e); throw new QueryFailedException("Cannot connect to Facebook. " + "Check your Internet connection!" - + (mProxy == Proxy.NO_PROXY ? "" : " Using proxy " + mProxy)); + + (proxy.getProxy() == Proxy.NO_PROXY ? "" : " Using proxy " + proxy.getProxy())); } catch (TlsHelper.TlsHelperException e) { Log.e(Constants.TAG, "Exception in cert pinning", e); throw new QueryFailedException("Exception in cert pinning. "); @@ -134,7 +133,7 @@ public class FacebookKeyserver extends Keyserver { } @Override - public void add(String armoredKey) throws AddKeyException { + public void add(String armoredKey, ParcelableProxy proxy) throws AddKeyException { // Implementing will require usage of FB API throw new UnsupportedOperationException("Uploading keys not supported yet"); } 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 9243926df..6a87167fd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/KeybaseKeyserver.java @@ -26,22 +26,19 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.OkHttpKeybaseClient; +import org.sufficientlysecure.keychain.util.ParcelableProxy; -import java.net.Proxy; import java.util.ArrayList; import java.util.List; public class KeybaseKeyserver extends Keyserver { public static final String ORIGIN = "keybase:keybase.io"; - Proxy mProxy; - - public KeybaseKeyserver(Proxy proxy) { - mProxy = proxy; + public KeybaseKeyserver() { } @Override - public ArrayList search(String query) throws QueryFailedException, + public ArrayList search(String query, ParcelableProxy proxy) throws QueryFailedException, QueryNeedsRepairException { ArrayList results = new ArrayList<>(); @@ -55,7 +52,7 @@ public class KeybaseKeyserver extends Keyserver { try { KeybaseQuery keybaseQuery = new KeybaseQuery(new OkHttpKeybaseClient()); - keybaseQuery.setProxy(mProxy); + keybaseQuery.setProxy(proxy.getProxy()); Iterable matches = keybaseQuery.search(query); for (Match match : matches) { results.add(makeEntry(match, query)); @@ -107,10 +104,10 @@ public class KeybaseKeyserver extends Keyserver { } @Override - public String get(String id) throws QueryFailedException { + public String get(String id, ParcelableProxy proxy) throws QueryFailedException { try { KeybaseQuery keybaseQuery = new KeybaseQuery(new OkHttpKeybaseClient()); - keybaseQuery.setProxy(mProxy); + keybaseQuery.setProxy(proxy.getProxy()); return User.keyForUsername(keybaseQuery, id); } catch (KeybaseException e) { throw new QueryFailedException(e.getMessage()); @@ -118,7 +115,7 @@ public class KeybaseKeyserver extends Keyserver { } @Override - public void add(String armoredKey) throws AddKeyException { + public void add(String armoredKey, ParcelableProxy proxy) throws AddKeyException { throw new AddKeyException(); } } 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 53e71f7a1..fd28c0221 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/Keyserver.java @@ -18,6 +18,8 @@ package org.sufficientlysecure.keychain.keyimport; +import org.sufficientlysecure.keychain.util.ParcelableProxy; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -68,12 +70,12 @@ public abstract class Keyserver { private static final long serialVersionUID = -507574859137295530L; } - public abstract List search(String query) + public abstract List search(String query, ParcelableProxy proxy) throws QueryFailedException, QueryNeedsRepairException; - public abstract String get(String keyIdHex) throws QueryFailedException; + public abstract String get(String keyIdHex, ParcelableProxy proxy) throws QueryFailedException; - public abstract void add(String armoredKey) throws AddKeyException; + public abstract void add(String armoredKey, ParcelableProxy proxy) throws AddKeyException; public static String readAll(InputStream in, String encoding) throws IOException { ByteArrayOutputStream raw = new ByteArrayOutputStream(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableHkpKeyserver.java similarity index 66% rename from OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java rename to OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableHkpKeyserver.java index 5e3d2ebc6..9e4f64de2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/ParcelableHkpKeyserver.java @@ -1,40 +1,40 @@ /* - * Copyright (C) 2012-2014 Dominik Schürmann + * Copyright (C) 2016 Dominik Schürmann * Copyright (C) 2011-2014 Thialfihar - * Copyright (C) 2011 Senecaso * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 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 android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; - -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.OkHttpClientFactory; +import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.TlsHelper; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.Proxy; +import java.net.URI; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; @@ -47,38 +47,17 @@ import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; -import android.support.annotation.NonNull; - import de.measite.minidns.Client; import de.measite.minidns.Question; import de.measite.minidns.Record; import de.measite.minidns.record.SRV; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; -public class HkpKeyserver extends Keyserver { - private static class HttpError extends Exception { - private static final long serialVersionUID = 1718783705229428893L; - private int mCode; - private String mData; - - public HttpError(int code, String data) { - super("" + code + ": " + data); - mCode = code; - mData = data; - } - - public int getCode() { - return mCode; - } - - public String getData() { - return mData; - } - } - - private String mHost; - private short mPort; - private Proxy mProxy; - private boolean mSecure; +public class ParcelableHkpKeyserver extends Keyserver implements Parcelable { /** * pub:%keyid%:%algo%:%keylen%:%creationdate%:%expirationdate%:%flags% @@ -148,64 +127,76 @@ public class HkpKeyserver extends Keyserver { private static final short PORT_DEFAULT = 11371; private static final short PORT_DEFAULT_HKPS = 443; - /** - * @param hostAndPort may be just - * "hostname" (eg. "pool.sks-keyservers.net"), then it will - * connect using {@link #PORT_DEFAULT}. However, port may be specified after colon - * ("hostname:port", eg. "p80.pool.sks-keyservers.net:80"). - */ - public HkpKeyserver(String hostAndPort, Proxy proxy) { - String host = hostAndPort; - short port = PORT_DEFAULT; - boolean secure = false; - String[] parts = hostAndPort.split(":"); - if (parts.length > 1) { - if (!parts[0].contains(".")) { // This is not a domain or ip, so it must be a protocol name - if ("hkps".equalsIgnoreCase(parts[0]) || "https".equalsIgnoreCase(parts[0])) { - secure = true; - port = PORT_DEFAULT_HKPS; - } else if (!"hkp".equalsIgnoreCase(parts[0]) && !"http".equalsIgnoreCase(parts[0])) { - throw new IllegalArgumentException("Protocol " + parts[0] + " is unknown"); - } - host = parts[1]; - if (host.startsWith("//")) { // People tend to type https:// and hkps://, so we'll support that as well - host = host.substring(2); - } - if (parts.length > 2) { - port = Short.decode(parts[2]); - } - } else { - host = parts[0]; - port = Short.decode(parts[1]); - } + private String mUrl; + private String mOnion; + + public ParcelableHkpKeyserver(@NonNull String url, String onion) { + mUrl = url.trim(); + mOnion = onion == null ? null : onion.trim(); + } + + public ParcelableHkpKeyserver(@NonNull String url) { + this(url, null); + } + + public String getUrl() { + return mUrl; + } + + public String getOnion() { + return mOnion; + } + + public URI getUrlURI() throws URISyntaxException { + return getURI(mUrl); + } + + public URI getOnionURI() throws URISyntaxException { + return getURI(mOnion); + } + + public URI getProxiedURL(ParcelableProxy proxy) throws URISyntaxException { + if (proxy.isTorEnabled()) { + return getOnionURI(); + } else { + return getUrlURI(); } - mHost = host; - mPort = port; - mProxy = proxy; - mSecure = secure; } - public HkpKeyserver(String host, short port, Proxy proxy) { - this(host, port, proxy, false); + /** + * @param keyserverUrl "hostname" (eg. "pool.sks-keyservers.net"), then it will + * connect using {@link #PORT_DEFAULT}. However, port may be specified after colon + * ("hostname:port", eg. "p80.pool.sks-keyservers.net:80"). + */ + private URI getURI(String keyserverUrl) throws URISyntaxException { + URI originalURI = new URI(keyserverUrl); + + String scheme = originalURI.getScheme(); + if (scheme == null + || (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme) + && !"hkp".equalsIgnoreCase(scheme) && !"hkps".equalsIgnoreCase(scheme))) { + throw new URISyntaxException(scheme, "unsupported scheme!"); + } + + int port = originalURI.getPort(); + + if ("hkps".equalsIgnoreCase(scheme)) { + scheme = "https"; + port = port == -1 ? PORT_DEFAULT_HKPS : port; + } else if ("hkp".equalsIgnoreCase(scheme)) { + scheme = "http"; + port = port == -1 ? PORT_DEFAULT : port; + } + + return new URI(scheme, originalURI.getUserInfo(), originalURI.getHost(), port, + originalURI.getPath(), originalURI.getQuery(), originalURI.getFragment()); } - public HkpKeyserver(String host, short port, Proxy proxy, boolean secure) { - mHost = host; - mPort = port; - mProxy = proxy; - mSecure = secure; - } - - private String getUrlPrefix() { - return mSecure ? "https://" : "http://"; - } - - - private String query(String request, @NonNull Proxy proxy) throws QueryFailedException, HttpError { + private String query(String request, @NonNull ParcelableProxy proxy) throws Keyserver.QueryFailedException, HttpError { try { - URL url = new URL(getUrlPrefix() + mHost + ":" + mPort + request); + URL url = new URL(getProxiedURL(proxy).toString() + request); Log.d(Constants.TAG, "hkp keyserver query: " + url + " Proxy: " + proxy); - OkHttpClient client = OkHttpClientFactory.getClientPinnedIfAvailable(url, proxy); + OkHttpClient client = OkHttpClientFactory.getClientPinnedIfAvailable(url, proxy.getProxy()); Response response = client.newCall(new Request.Builder().url(url).build()).execute(); String responseBody = response.body().string(); // contains body both in case of success or failure @@ -217,11 +208,14 @@ public class HkpKeyserver extends Keyserver { } } catch (IOException e) { Log.e(Constants.TAG, "IOException at HkpKeyserver", e); - throw new QueryFailedException("Keyserver '" + mHost + "' is unavailable. Check your Internet connection!" + - (proxy == Proxy.NO_PROXY ? "" : " Using proxy " + proxy)); + throw new Keyserver.QueryFailedException("Keyserver '" + mUrl + "' is unavailable. Check your Internet connection!" + + (proxy.getProxy() == Proxy.NO_PROXY ? "" : " Using proxy " + proxy.getProxy())); } catch (TlsHelper.TlsHelperException e) { Log.e(Constants.TAG, "Exception in pinning certs", e); - throw new QueryFailedException("Exception in pinning certs"); + throw new Keyserver.QueryFailedException("Exception in pinning certs"); + } catch (URISyntaxException e) { + Log.e(Constants.TAG, "Unsupported keyserver URI", e); + throw new Keyserver.QueryFailedException("Unsupported keyserver URI"); } } @@ -229,12 +223,12 @@ public class HkpKeyserver extends Keyserver { * Results are sorted by creation date of key! */ @Override - public ArrayList search(String query) throws QueryFailedException, - QueryNeedsRepairException { + public ArrayList search(String query, ParcelableProxy proxy) throws Keyserver.QueryFailedException, + Keyserver.QueryNeedsRepairException { ArrayList results = new ArrayList<>(); if (query.length() < 3) { - throw new QueryTooShortException(); + throw new Keyserver.QueryTooShortException(); } String encodedQuery; @@ -247,7 +241,7 @@ public class HkpKeyserver extends Keyserver { String data; try { - data = query(request, mProxy); + data = query(request, proxy); } catch (HttpError e) { if (e.getData() != null) { Log.d(Constants.TAG, "returned error data: " + e.getData().toLowerCase(Locale.ENGLISH)); @@ -256,26 +250,26 @@ public class HkpKeyserver extends Keyserver { // NOTE: This is also a 404 error for some keyservers! return results; } else if (e.getData().toLowerCase(Locale.ENGLISH).contains("too many")) { - throw new TooManyResponsesException(); + throw new Keyserver.TooManyResponsesException(); } else if (e.getData().toLowerCase(Locale.ENGLISH).contains("insufficient")) { - throw new QueryTooShortException(); + throw new Keyserver.QueryTooShortException(); } else if (e.getCode() == 404) { // NOTE: handle this 404 at last, maybe it was a "no keys found" error - throw new QueryFailedException("Keyserver '" + mHost + "' not found. Error 404"); + throw new Keyserver.QueryFailedException("Keyserver '" + mUrl + "' not found. Error 404"); } else { // NOTE: some keyserver do not provide a more detailed error response - throw new QueryTooShortOrTooManyResponsesException(); + throw new Keyserver.QueryTooShortOrTooManyResponsesException(); } } - throw new QueryFailedException("Querying server(s) for '" + mHost + "' failed."); + throw new Keyserver.QueryFailedException("Querying server(s) for '" + mUrl + "' failed."); } final Matcher matcher = PUB_KEY_LINE.matcher(data); while (matcher.find()) { final ImportKeysListEntry entry = new ImportKeysListEntry(); entry.setQuery(query); - entry.addOrigin(getUrlPrefix() + mHost + ":" + mPort); + entry.addOrigin(getHostID()); // group 1 contains the full fingerprint (v4) or the long key id if available // see https://bitbucket.org/skskeyserver/sks-keyserver/pull-request/12/fixes-for-machine-readable-indexes/diff @@ -351,37 +345,37 @@ public class HkpKeyserver extends Keyserver { } @Override - public String get(String keyIdHex) throws QueryFailedException { + public String get(String keyIdHex, ParcelableProxy proxy) throws Keyserver.QueryFailedException { String request = "/pks/lookup?op=get&options=mr&search=" + keyIdHex; - Log.d(Constants.TAG, "hkp keyserver get: " + request + " using Proxy: " + mProxy); + Log.d(Constants.TAG, "hkp keyserver get: " + request + " using Proxy: " + proxy); String data; try { - data = query(request, mProxy); + data = query(request, proxy); } catch (HttpError httpError) { Log.d(Constants.TAG, "Failed to get key at HkpKeyserver", httpError); - throw new QueryFailedException("not found"); + throw new Keyserver.QueryFailedException("not found"); } if (data == null) { - throw new QueryFailedException("data is null"); + throw new Keyserver.QueryFailedException("data is null"); } Matcher matcher = PgpHelper.PGP_PUBLIC_KEY.matcher(data); if (matcher.find()) { return matcher.group(1); } - throw new QueryFailedException("data is null"); + throw new Keyserver.QueryFailedException("data is null"); } @Override - public void add(String armoredKey) throws AddKeyException { + public void add(String armoredKey, ParcelableProxy proxy) throws Keyserver.AddKeyException { try { String path = "/pks/add"; String params; try { params = "keytext=" + URLEncoder.encode(armoredKey, "UTF-8"); } catch (UnsupportedEncodingException e) { - throw new AddKeyException(); + throw new Keyserver.AddKeyException(); } - URL url = new URL(getUrlPrefix() + mHost + ":" + mPort + path); + URL url = new URL(getProxiedURL(proxy).toString() + path); Log.d(Constants.TAG, "hkp keyserver add: " + url); Log.d(Constants.TAG, "params: " + params); @@ -396,27 +390,40 @@ public class HkpKeyserver extends Keyserver { .post(body) .build(); - Response response = OkHttpClientFactory.getClientPinnedIfAvailable(url, mProxy).newCall(request).execute(); + Response response = + OkHttpClientFactory.getClientPinnedIfAvailable(url, proxy.getProxy()) + .newCall(request).execute(); Log.d(Constants.TAG, "response code: " + response.code()); Log.d(Constants.TAG, "answer: " + response.body().string()); if (response.code() != 200) { - throw new AddKeyException(); + throw new Keyserver.AddKeyException(); } } catch (IOException e) { Log.e(Constants.TAG, "IOException", e); - throw new AddKeyException(); + throw new Keyserver.AddKeyException(); } catch (TlsHelper.TlsHelperException e) { Log.e(Constants.TAG, "Exception in pinning certs", e); - throw new AddKeyException(); + throw new Keyserver.AddKeyException(); + } catch (URISyntaxException e) { + Log.e(Constants.TAG, "Unsupported keyserver URI", e); + throw new Keyserver.AddKeyException(); + } + } + + private String getHostID() { + try { + return (new URI(mUrl)).getHost(); + } catch (URISyntaxException e) { + return mUrl; } } @Override public String toString() { - return getUrlPrefix() + mHost + ":" + mPort; + return getHostID(); } /** @@ -424,7 +431,7 @@ public class HkpKeyserver extends Keyserver { * * @return A responsible Keyserver or null if not found. */ - public static HkpKeyserver resolve(String domain, Proxy proxy) { + public static ParcelableHkpKeyserver resolve(String domain) { try { Record[] records = new Client().query(new Question("_hkp._tcp." + domain, Record.TYPE.SRV)).getAnswers(); if (records.length > 0) { @@ -438,12 +445,60 @@ public class HkpKeyserver extends Keyserver { }); Record record = records[0]; // This is our best choice if (record.getPayload().getType() == Record.TYPE.SRV) { - return new HkpKeyserver(((SRV) record.getPayload()).getName(), - (short) ((SRV) record.getPayload()).getPort(), proxy); + SRV payload = (SRV) record.getPayload(); + return new ParcelableHkpKeyserver(payload.getName() + ":" + payload.getPort()); } } } catch (Exception ignored) { } return null; } + + private static class HttpError extends Exception { + private static final long serialVersionUID = 1718783705229428893L; + private int mCode; + private String mData; + + HttpError(int code, String data) { + super("" + code + ": " + data); + mCode = code; + mData = data; + } + + public int getCode() { + return mCode; + } + + public String getData() { + return mData; + } + } + + protected ParcelableHkpKeyserver(Parcel in) { + mUrl = in.readString(); + mOnion = in.readString(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mUrl); + dest.writeString(mOnion); + } + + @Override + public int describeContents() { + return 0; + } + + public static final Creator CREATOR = new Creator() { + @Override + public ParcelableHkpKeyserver createFromParcel(Parcel in) { + return new ParcelableHkpKeyserver(in); + } + + @Override + public ParcelableHkpKeyserver[] newArray(int size) { + return new ParcelableHkpKeyserver[size]; + } + }; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java index a20181a00..078bf305d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java @@ -39,9 +39,9 @@ import android.support.annotation.NonNull; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.FacebookKeyserver; -import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver; import org.sufficientlysecure.keychain.keyimport.Keyserver; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.operations.results.ConsolidateResult; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; @@ -60,6 +60,7 @@ import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ParcelableFileCache; import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; +import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; @@ -83,7 +84,7 @@ import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; */ public class ImportOperation extends BaseOperation { - public static final int MAX_THREADS = 10; + private static final int MAX_THREADS = 10; public ImportOperation(Context context, ProviderHelper providerHelper, Progressable progressable) { @@ -97,20 +98,20 @@ public class ImportOperation extends BaseOperation { // Overloaded functions for using progressable supplied in constructor during import public ImportKeyResult serialKeyRingImport(Iterator entries, int num, - String keyServerUri, Proxy proxy) { - return serialKeyRingImport(entries, num, keyServerUri, mProgressable, proxy); + ParcelableHkpKeyserver hkpKeyserver, ParcelableProxy proxy) { + return serialKeyRingImport(entries, num, hkpKeyserver, mProgressable, proxy); } @NonNull private ImportKeyResult serialKeyRingImport(ParcelableFileCache cache, - String keyServerUri, Proxy proxy) { + ParcelableHkpKeyserver hkpKeyserver, ParcelableProxy proxy) { // get entries from cached file try { IteratorWithSize it = cache.readCache(); int numEntries = it.getSize(); - return serialKeyRingImport(it, numEntries, keyServerUri, mProgressable, proxy); + return serialKeyRingImport(it, numEntries, hkpKeyserver, mProgressable, proxy); } catch (IOException e) { // Special treatment here, we need a lot @@ -129,14 +130,14 @@ public class ImportOperation extends BaseOperation { * * @param entries keys to import * @param num number of keys to import - * @param keyServerUri contains uri of keyserver to import from, if it is an import from cloud + * @param hkpKeyserver contains uri of keyserver to import from, if it is an import from cloud * @param progressable Allows multi-threaded import to supply a progressable that ignores the * progress of a single key being imported */ @NonNull private ImportKeyResult serialKeyRingImport(Iterator entries, int num, - String keyServerUri, Progressable progressable, - @NonNull Proxy proxy) { + ParcelableHkpKeyserver hkpKeyserver, Progressable progressable, + @NonNull ParcelableProxy proxy) { if (progressable != null) { progressable.setProgress(R.string.progress_importing, 0, 100); } @@ -158,7 +159,7 @@ public class ImportOperation extends BaseOperation { KeybaseKeyserver keybaseServer = null; FacebookKeyserver facebookServer = null; - HkpKeyserver keyServer = null; + ParcelableHkpKeyserver keyServer = null; // iterate over all entries while (entries.hasNext()) { @@ -187,12 +188,12 @@ public class ImportOperation extends BaseOperation { // If we have a keyServerUri and a fingerprint or at least a keyId, // download from HKP - if (keyServerUri != null + if (hkpKeyserver != null && (entry.mKeyIdHex != null || entry.mExpectedFingerprint != null)) { // Make sure we have the keyserver instance cached if (keyServer == null) { - log.add(LogType.MSG_IMPORT_KEYSERVER, 1, keyServerUri); - keyServer = new HkpKeyserver(keyServerUri, proxy); + log.add(LogType.MSG_IMPORT_KEYSERVER, 1, hkpKeyserver); + keyServer = hkpKeyserver; } try { @@ -201,10 +202,10 @@ public class ImportOperation extends BaseOperation { if (entry.mExpectedFingerprint != null) { log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, "0x" + entry.mExpectedFingerprint.substring(24)); - data = keyServer.get("0x" + entry.mExpectedFingerprint).getBytes(); + data = keyServer.get("0x" + entry.mExpectedFingerprint, proxy).getBytes(); } else { log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, entry.mKeyIdHex); - data = keyServer.get(entry.mKeyIdHex).getBytes(); + data = keyServer.get(entry.mKeyIdHex, proxy).getBytes(); } key = UncachedKeyRing.decodeFromData(data); if (key != null) { @@ -222,12 +223,12 @@ public class ImportOperation extends BaseOperation { if (entry.mKeybaseName != null) { // Make sure we have this cached if (keybaseServer == null) { - keybaseServer = new KeybaseKeyserver(proxy); + keybaseServer = new KeybaseKeyserver(); } try { log.add(LogType.MSG_IMPORT_FETCH_KEYBASE, 2, entry.mKeybaseName); - byte[] data = keybaseServer.get(entry.mKeybaseName).getBytes(); + byte[] data = keybaseServer.get(entry.mKeybaseName, proxy).getBytes(); UncachedKeyRing keybaseKey = UncachedKeyRing.decodeFromData(data); if (keybaseKey != null) { @@ -260,12 +261,12 @@ public class ImportOperation extends BaseOperation { if (entry.mFbUsername != null) { // Make sure we have this cached if (facebookServer == null) { - facebookServer = new FacebookKeyserver(proxy); + facebookServer = new FacebookKeyserver(); } try { log.add(LogType.MSG_IMPORT_FETCH_FACEBOOK, 2, entry.mFbUsername); - byte[] data = facebookServer.get(entry.mFbUsername).getBytes(); + byte[] data = facebookServer.get(entry.mFbUsername, proxy).getBytes(); UncachedKeyRing facebookKey = UncachedKeyRing.decodeFromData(data); if (facebookKey != null) { @@ -425,7 +426,7 @@ public class ImportOperation extends BaseOperation { @Override public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) { ArrayList keyList = importInput.mKeyList; - String keyServer = importInput.mKeyserver; + ParcelableHkpKeyserver keyServer = importInput.mKeyserver; ImportKeyResult result; @@ -435,7 +436,7 @@ public class ImportOperation extends BaseOperation { result = serialKeyRingImport(cache, null, null); } else { - Proxy proxy; + ParcelableProxy proxy; if (cryptoInput.getParcelableProxy() == null) { // explicit proxy not set if(!OrbotHelper.isOrbotInRequiredState(mContext)) { @@ -443,9 +444,9 @@ public class ImportOperation extends BaseOperation { return new ImportKeyResult(null, RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput); } - proxy = Preferences.getPreferences(mContext).getProxyPrefs().getProxy(); + proxy = Preferences.getPreferences(mContext).getParcelableProxy(); } else { - proxy = cryptoInput.getParcelableProxy().getProxy(); + proxy = cryptoInput.getParcelableProxy(); } result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer, proxy); @@ -457,8 +458,8 @@ public class ImportOperation extends BaseOperation { @NonNull private ImportKeyResult multiThreadedKeyImport(@NonNull Iterator keyListIterator, - int totKeys, final String keyServer, - final Proxy proxy) { + int totKeys, final ParcelableHkpKeyserver hkpKeyserver, + final ParcelableProxy proxy) { Log.d(Constants.TAG, "Multi-threaded key import starting"); KeyImportAccumulator accumulator = new KeyImportAccumulator(totKeys, mProgressable); @@ -487,7 +488,7 @@ public class ImportOperation extends BaseOperation { ArrayList list = new ArrayList<>(); list.add(pkRing); - return serialKeyRingImport(list.iterator(), 1, keyServer, ignoreProgressable, proxy); + return serialKeyRingImport(list.iterator(), 1, hkpKeyserver, ignoreProgressable, proxy); } }; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java index f67465132..50a67c627 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java @@ -75,8 +75,7 @@ public class KeybaseVerificationOperation extends BaseOperation { public UploadOperation(Context context, ProviderHelper providerHelper, - Progressable progressable, AtomicBoolean cancelled) { + Progressable progressable, AtomicBoolean cancelled) { super(context, providerHelper, progressable, cancelled); } @@ -71,41 +70,32 @@ public class UploadOperation extends BaseOperation { log.add(LogType.MSG_UPLOAD, 0); updateProgress(R.string.progress_uploading, 0, 1); - Proxy proxy; - { - boolean proxyIsTor = false; - - // Proxy priorities: - // 1. explicit proxy - // 2. orbot proxy state - // 3. proxy from preferences - ParcelableProxy parcelableProxy = cryptoInput.getParcelableProxy(); - if (parcelableProxy != null) { - proxy = parcelableProxy.getProxy(); - } else { - if ( ! OrbotHelper.isOrbotInRequiredState(mContext)) { - return new UploadResult(log, RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput); - } - ProxyPrefs proxyPrefs = Preferences.getPreferences(mContext).getProxyPrefs(); - if (proxyPrefs.torEnabled) { - proxyIsTor = true; - } - proxy = proxyPrefs.getProxy(); - } - - if (proxyIsTor) { - log.add(LogType.MSG_UPLOAD_PROXY_TOR, 1); - } else if (proxy == Proxy.NO_PROXY) { - log.add(LogType.MSG_UPLOAD_PROXY_DIRECT, 1); - } else { - log.add(LogType.MSG_UPLOAD_PROXY, 1, proxy.toString()); + // Proxy priorities: + // 1. explicit proxy + // 2. orbot proxy state + // 3. proxy from preferences + ParcelableProxy parcelableProxy = cryptoInput.getParcelableProxy(); + if (parcelableProxy == null) { + if (!OrbotHelper.isOrbotInRequiredState(mContext)) { + return new UploadResult(log, RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput); } + parcelableProxy = Preferences.getPreferences(mContext).getParcelableProxy(); } - HkpKeyserver hkpKeyserver; + boolean proxyIsTor = parcelableProxy.isTorEnabled(); + + if (proxyIsTor) { + log.add(LogType.MSG_UPLOAD_PROXY_TOR, 1); + } else if (parcelableProxy.getProxy() == Proxy.NO_PROXY) { + log.add(LogType.MSG_UPLOAD_PROXY_DIRECT, 1); + } else { + log.add(LogType.MSG_UPLOAD_PROXY, 1, parcelableProxy.getProxy().toString()); + } + + ParcelableHkpKeyserver hkpKeyserver; { - hkpKeyserver = new HkpKeyserver(uploadInput.mKeyserver, proxy); + hkpKeyserver = uploadInput.mKeyserver; log.add(LogType.MSG_UPLOAD_SERVER, 1, hkpKeyserver.toString()); } @@ -114,7 +104,7 @@ public class UploadOperation extends BaseOperation { return new UploadResult(UploadResult.RESULT_ERROR, log); } - return uploadKeyRingToServer(log, hkpKeyserver, keyring); + return uploadKeyRingToServer(log, hkpKeyserver, keyring, parcelableProxy); } @Nullable @@ -136,7 +126,7 @@ public class UploadOperation extends BaseOperation { CanonicalizedKeyRing canonicalizedRing = UncachedKeyRing.decodeFromData(uploadInput.mUncachedKeyringBytes) .canonicalize(new OperationLog(), 0, true); - if ( ! CanonicalizedPublicKeyRing.class.isInstance(canonicalizedRing)) { + if (!CanonicalizedPublicKeyRing.class.isInstance(canonicalizedRing)) { throw new IllegalArgumentException("keyring bytes must contain public key ring!"); } log.add(LogType.MSG_UPLOAD_KEY, 0, KeyFormattingUtils.convertKeyIdToHex(canonicalizedRing.getMasterKeyId())); @@ -155,7 +145,8 @@ public class UploadOperation extends BaseOperation { @NonNull private UploadResult uploadKeyRingToServer( - OperationLog log, HkpKeyserver server, CanonicalizedPublicKeyRing keyring) { + OperationLog log, ParcelableHkpKeyserver server, CanonicalizedPublicKeyRing keyring, + ParcelableProxy proxy) { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ArmoredOutputStream aos = null; @@ -166,7 +157,7 @@ public class UploadOperation extends BaseOperation { aos.close(); String armoredKey = bos.toString("UTF-8"); - server.add(armoredKey); + server.add(armoredKey, proxy); updateProgress(R.string.progress_uploading, 1, 1); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java index 3cdbca633..b7cc470e3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java @@ -24,11 +24,9 @@ import android.os.Parcelable; import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import java.util.Date; -import java.util.Map; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.util.ParcelableProxy; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; /** @@ -43,7 +41,7 @@ public class CertifyActionsParcel implements Parcelable { public ArrayList mCertifyActions = new ArrayList<>(); - public String keyServerUri; + public ParcelableHkpKeyserver keyServerUri; public CertifyActionsParcel(long masterKeyId) { mMasterKeyId = masterKeyId; @@ -54,7 +52,7 @@ public class CertifyActionsParcel implements Parcelable { mMasterKeyId = source.readLong(); // just like parcelables, this is meant for ad-hoc IPC only and is NOT portable! mLevel = CertifyLevel.values()[source.readInt()]; - keyServerUri = source.readString(); + keyServerUri = source.readParcelable(ParcelableHkpKeyserver.class.getClassLoader()); mCertifyActions = (ArrayList) source.readSerializable(); } @@ -67,7 +65,7 @@ public class CertifyActionsParcel implements Parcelable { public void writeToParcel(Parcel destination, int flags) { destination.writeLong(mMasterKeyId); destination.writeInt(mLevel.ordinal()); - destination.writeString(keyServerUri); + destination.writeParcelable(keyServerUri, flags); destination.writeSerializable(mCertifyActions); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java index a41dd71cb..ad609d72a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ImportKeyringParcel.java @@ -20,16 +20,18 @@ package org.sufficientlysecure.keychain.service; import android.os.Parcel; import android.os.Parcelable; + import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import java.util.ArrayList; public class ImportKeyringParcel implements Parcelable { // if null, keys are expected to be read from a cache file in ImportExportOperations public ArrayList mKeyList; - public String mKeyserver; // must be set if keys are to be imported from a keyserver + public ParcelableHkpKeyserver mKeyserver; // must be set if keys are to be imported from a keyserver - public ImportKeyringParcel (ArrayList keyList, String keyserver) { + public ImportKeyringParcel(ArrayList keyList, ParcelableHkpKeyserver keyserver) { mKeyList = keyList; mKeyserver = keyserver; } @@ -41,7 +43,7 @@ public class ImportKeyringParcel implements Parcelable { } else { mKeyList = null; } - mKeyserver = in.readString(); + mKeyserver = in.readParcelable(ParcelableHkpKeyserver.class.getClassLoader()); } @Override @@ -57,7 +59,7 @@ public class ImportKeyringParcel implements Parcelable { dest.writeByte((byte) (0x01)); dest.writeList(mKeyList); } - dest.writeString(mKeyserver); + dest.writeParcelable(mKeyserver, flags); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java index b71fbada8..ed66283e8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java @@ -11,7 +11,6 @@ import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.PeriodicSync; import android.content.SyncResult; import android.database.Cursor; import android.graphics.Bitmap; @@ -308,7 +307,7 @@ public class KeyserverSyncAdapterService extends Service { if (cryptoInputParcel.getParcelableProxy() == null) { // no explicit proxy, retrieve from preferences. Check if we should do a staggered sync - if (Preferences.getPreferences(context).getProxyPrefs().torEnabled) { + if (Preferences.getPreferences(context).getParcelableProxy().isTorEnabled()) { return staggeredUpdate(context, keyList, cryptoInputParcel); } else { return directUpdate(context, keyList, cryptoInputParcel); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/RevokeKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/RevokeKeyringParcel.java index 02e8fda46..fb5de2713 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/RevokeKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/RevokeKeyringParcel.java @@ -22,13 +22,15 @@ package org.sufficientlysecure.keychain.service; import android.os.Parcel; import android.os.Parcelable; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; + public class RevokeKeyringParcel implements Parcelable { final public long mMasterKeyId; final public boolean mUpload; - final public String mKeyserver; + final public ParcelableHkpKeyserver mKeyserver; - public RevokeKeyringParcel(long masterKeyId, boolean upload, String keyserver) { + public RevokeKeyringParcel(long masterKeyId, boolean upload, ParcelableHkpKeyserver keyserver) { mMasterKeyId = masterKeyId; mUpload = upload; mKeyserver = keyserver; @@ -37,7 +39,7 @@ public class RevokeKeyringParcel implements Parcelable { protected RevokeKeyringParcel(Parcel in) { mMasterKeyId = in.readLong(); mUpload = in.readByte() != 0x00; - mKeyserver = in.readString(); + mKeyserver = in.readParcelable(ParcelableHkpKeyserver.class.getClassLoader()); } @Override @@ -49,7 +51,7 @@ public class RevokeKeyringParcel implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeLong(mMasterKeyId); dest.writeByte((byte) (mUpload ? 0x01 : 0x00)); - dest.writeString(mKeyserver); + dest.writeParcelable(mKeyserver, flags); } public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { 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 db6bbcbdb..fe0f14598 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -22,6 +22,7 @@ import android.os.Parcel; import android.os.Parcelable; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.util.Passphrase; import java.io.Serializable; @@ -66,7 +67,7 @@ public class SaveKeyringParcel implements Parcelable { // private because they have to be set together with setUpdateOptions private boolean mUpload; private boolean mUploadAtomic; - private String mKeyserver; + private ParcelableHkpKeyserver mKeyserver; // private because we have to set other details like key id private ChangeUnlockParcel mNewUnlock; @@ -97,10 +98,10 @@ public class SaveKeyringParcel implements Parcelable { mKeyserver = null; } - public void setUpdateOptions(boolean upload, boolean uploadAtomic, String keysever) { + public void setUpdateOptions(boolean upload, boolean uploadAtomic, ParcelableHkpKeyserver keyserver) { mUpload = upload; mUploadAtomic = uploadAtomic; - mKeyserver = keysever; + mKeyserver = keyserver; } public void setNewUnlock(ChangeUnlockParcel parcel) { @@ -123,7 +124,7 @@ public class SaveKeyringParcel implements Parcelable { return mUploadAtomic; } - public String getUploadKeyserver() { + public ParcelableHkpKeyserver getUploadKeyserver() { return mKeyserver; } @@ -278,7 +279,7 @@ public class SaveKeyringParcel implements Parcelable { mUpload = source.readByte() != 0; mUploadAtomic = source.readByte() != 0; - mKeyserver = source.readString(); + mKeyserver = source.readParcelable(ParcelableHkpKeyserver.class.getClassLoader()); } @Override @@ -307,7 +308,7 @@ public class SaveKeyringParcel implements Parcelable { destination.writeByte((byte) (mUpload ? 1 : 0)); destination.writeByte((byte) (mUploadAtomic ? 1 : 0)); - destination.writeString(mKeyserver); + destination.writeParcelable(mKeyserver, flags); } public static final Creator CREATOR = new Creator() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/UploadKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/UploadKeyringParcel.java index 0a14f3dc6..2db386afa 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/UploadKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/UploadKeyringParcel.java @@ -23,27 +23,29 @@ package org.sufficientlysecure.keychain.service; import android.os.Parcel; import android.os.Parcelable; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; + public class UploadKeyringParcel implements Parcelable { - public String mKeyserver; + public ParcelableHkpKeyserver mKeyserver; public final Long mMasterKeyId; public final byte[] mUncachedKeyringBytes; - public UploadKeyringParcel(String keyserver, long masterKeyId) { + public UploadKeyringParcel(ParcelableHkpKeyserver keyserver, long masterKeyId) { mKeyserver = keyserver; mMasterKeyId = masterKeyId; mUncachedKeyringBytes = null; } - public UploadKeyringParcel(String keyserver, byte[] uncachedKeyringBytes) { + public UploadKeyringParcel(ParcelableHkpKeyserver keyserver, byte[] uncachedKeyringBytes) { mKeyserver = keyserver; mMasterKeyId = null; mUncachedKeyringBytes = uncachedKeyringBytes; } protected UploadKeyringParcel(Parcel in) { - mKeyserver = in.readString(); + mKeyserver = in.readParcelable(ParcelableHkpKeyserver.class.getClassLoader()); mMasterKeyId = in.readInt() != 0 ? in.readLong() : null; mUncachedKeyringBytes = in.createByteArray(); } @@ -55,7 +57,7 @@ public class UploadKeyringParcel implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeString(mKeyserver); + dest.writeParcelable(mKeyserver, flags); if (mMasterKeyId != null) { dest.writeInt(1); dest.writeLong(mMasterKeyId); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java index 99a4b0201..0e94a1168 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyFinalFragment.java @@ -53,6 +53,7 @@ import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Preferences; @@ -501,7 +502,7 @@ public class CreateKeyFinalFragment extends Fragment { // set data uri as path to keyring final long masterKeyId = saveKeyResult.mMasterKeyId; // upload to favorite keyserver - final String keyserver = Preferences.getPreferences(activity).getPreferredKeyserver(); + final ParcelableHkpKeyserver keyserver = Preferences.getPreferences(activity).getPreferredKeyserver(); CryptoOperationHelper.Callback callback = new CryptoOperationHelper.Callback() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java index 795d27b6c..fd01c0ad2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateSecurityTokenImportResetFragment.java @@ -45,6 +45,7 @@ import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.SecurityTokenListenerFragment; import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.util.Preferences; @@ -73,7 +74,7 @@ public class CreateSecurityTokenImportResetFragment private View mResetWarning; // for CryptoOperationFragment key import - private String mKeyserver; + private ParcelableHkpKeyserver mKeyserver; private ArrayList mKeyList; public static Fragment newInstance(byte[] scannedFingerprints, byte[] nfcAid, String userId) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java index b15985fc7..4521ae659 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java @@ -56,6 +56,7 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.util.Preferences; public abstract class DecryptFragment extends Fragment implements LoaderManager.LoaderCallbacks { @@ -137,7 +138,7 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager. private void lookupUnknownKey(long unknownKeyId) { final ArrayList keyList; - final String keyserver; + final ParcelableHkpKeyserver keyserver; // search config keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java index 79bf55d79..c43894b79 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java @@ -94,6 +94,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ParcelableHashMap; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.util.Preferences; @@ -759,7 +760,7 @@ public class DecryptListFragment private void lookupUnknownKey(final Uri inputUri, long unknownKeyId) { final ArrayList keyList; - final String keyserver; + final ParcelableHkpKeyserver keyserver; // search config keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java index a32dc175c..b580ba790 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java @@ -38,6 +38,7 @@ import android.widget.TextView; import org.openintents.openpgp.util.OpenPgpUtils; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.operations.results.DeleteResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.RevokeResult; @@ -154,7 +155,7 @@ public class DeleteKeyDialogActivity extends FragmentActivity { @Override public RevokeKeyringParcel createOperationInput() { return new RevokeKeyringParcel(mMasterKeyIds[0], true, - getIntent().getStringExtra(EXTRA_KEYSERVER)); + (ParcelableHkpKeyserver) getIntent().getParcelableExtra(EXTRA_KEYSERVER)); } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditIdentitiesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditIdentitiesFragment.java index 6c5ad43ca..248cd71b3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditIdentitiesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditIdentitiesFragment.java @@ -62,6 +62,7 @@ import org.sufficientlysecure.keychain.ui.dialog.AddUserIdDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.util.Preferences; public class EditIdentitiesFragment extends Fragment @@ -387,7 +388,7 @@ public class EditIdentitiesFragment extends Fragment final long masterKeyId = editKeyResult.mMasterKeyId; // upload to favorite keyserver - final String keyserver = Preferences.getPreferences(activity).getPreferredKeyserver(); + final ParcelableHkpKeyserver keyserver = Preferences.getPreferences(activity).getPreferredKeyserver(); CryptoOperationHelper.Callback callback = new CryptoOperationHelper.Callback() { 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 7d2d30c35..c741f8d88 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ParcelableFileCache; import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.util.Preferences; import java.io.IOException; @@ -79,7 +80,7 @@ public class ImportKeysActivity extends BaseActivity public static final String TAG_FRAG_TOP = "frag_top"; // for CryptoOperationHelper.Callback - private String mKeyserver; + private ParcelableHkpKeyserver mKeyserver; private ArrayList mKeyList; private CryptoOperationHelper mOperationHelper; @@ -233,8 +234,9 @@ public class ImportKeysActivity extends BaseActivity Notify.create(this, R.string.import_url_warn_no_search_parameter, Notify.LENGTH_INDEFINITE, Notify.Style.WARN).show(); } + ParcelableHkpKeyserver keyserver = new ParcelableHkpKeyserver(dataUri.getAuthority()); Preferences.CloudSearchPrefs cloudSearchPrefs = new Preferences.CloudSearchPrefs( - true, true, true, dataUri.getAuthority()); + true, true, true, keyserver); // we allow our users to edit the query if they wish startTopCloudFragment(query, false, cloudSearchPrefs); // search immediately (if query is not null) diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java index 3969f4039..4c5151fe9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysProxyActivity.java @@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.util.Preferences; import java.util.ArrayList; @@ -62,7 +63,7 @@ public class ImportKeysProxyActivity extends FragmentActivity public static final String EXTRA_FINGERPRINT = "fingerprint"; // for CryptoOperationHelper - private String mKeyserver; + private ParcelableHkpKeyserver mKeyserver; private ArrayList mKeyList; private CryptoOperationHelper mImportOpHelper; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 77139f5de..6b88f4e12 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -76,6 +76,7 @@ import org.sufficientlysecure.keychain.ui.util.ContentDescriptionHint; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.FabContainer; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.util.Preferences; import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; import se.emilsjolander.stickylistheaders.StickyListHeadersListView; @@ -111,7 +112,7 @@ public class KeyListFragment extends LoaderFragment // for CryptoOperationHelper import private ArrayList mKeyList; - private String mKeyserver; + private ParcelableHkpKeyserver mKeyserver; private CryptoOperationHelper mImportOpHelper; // for ConsolidateOperation diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java index 534dbfd05..30612857c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SafeSlingerActivity.java @@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ParcelableFileCache; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import java.io.IOException; import java.util.ArrayList; @@ -61,7 +62,7 @@ public class SafeSlingerActivity extends BaseActivity // for CryptoOperationHelper private ArrayList mKeyList; - private String mKeyserver; + private ParcelableHkpKeyserver mKeyserver; private CryptoOperationHelper mOperationHelper; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index 894ce7846..aa1d3647b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -53,10 +53,12 @@ import org.sufficientlysecure.keychain.service.ContactSyncAdapterService; import org.sufficientlysecure.keychain.ui.base.BaseActivity; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.ThemeChanger; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; +import java.util.ArrayList; import java.util.List; public class SettingsActivity extends AppCompatPreferenceActivity { @@ -180,9 +182,9 @@ public class SettingsActivity extends AppCompatPreferenceActivity { } public static String keyserverSummary(Context context) { - String[] servers = sPreferences.getKeyServers(); + ArrayList servers = sPreferences.getKeyServers(); String serverSummary = context.getResources().getQuantityString( - R.plurals.n_keyservers, servers.length, servers.length); + R.plurals.n_keyservers, servers.size(), servers.size()); return serverSummary + "; " + context.getString(R.string.label_preferred) + ": " + sPreferences .getPreferredKeyserver(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java index 7dd92c45f..3388a0e2b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java @@ -22,8 +22,11 @@ import android.os.Bundle; import android.view.MenuItem; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.ui.base.BaseActivity; +import java.util.ArrayList; + public class SettingsKeyServerActivity extends BaseActivity { public static final String EXTRA_KEY_SERVERS = "key_servers"; @@ -33,7 +36,7 @@ public class SettingsKeyServerActivity extends BaseActivity { super.onCreate(savedInstanceState); Intent intent = getIntent(); - String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS); + ArrayList servers = intent.getParcelableArrayListExtra(EXTRA_KEY_SERVERS); loadFragment(savedInstanceState, servers); getSupportActionBar().setDisplayHomeAsUpEnabled(true); @@ -55,7 +58,7 @@ public class SettingsKeyServerActivity extends BaseActivity { setContentView(R.layout.key_server_preference); } - private void loadFragment(Bundle savedInstanceState, String[] keyservers) { + private void loadFragment(Bundle savedInstanceState, ArrayList keyservers) { // 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. diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java index 488558aa3..2175b9af9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyserverFragment.java @@ -18,7 +18,6 @@ package org.sufficientlysecure.keychain.ui; -import android.graphics.Color; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -41,29 +40,28 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.dialog.AddEditKeyserverDialogFragment; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; -import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperAdapter; -import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperViewHolder; -import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperDragCallback; import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperAdapter; +import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperDragCallback; +import org.sufficientlysecure.keychain.ui.util.recyclerview.ItemTouchHelperViewHolder; import org.sufficientlysecure.keychain.ui.util.recyclerview.RecyclerItemClickListener; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.util.Preferences; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; -import java.util.List; public class SettingsKeyserverFragment extends Fragment implements RecyclerItemClickListener.OnItemClickListener { private static final String ARG_KEYSERVER_ARRAY = "arg_keyserver_array"; private ItemTouchHelper mItemTouchHelper; - private ArrayList mKeyservers; + private ArrayList mKeyservers; private KeyserverListAdapter mAdapter; - public static SettingsKeyserverFragment newInstance(String[] keyservers) { + public static SettingsKeyserverFragment newInstance(ArrayList keyservers) { Bundle args = new Bundle(); - args.putStringArray(ARG_KEYSERVER_ARRAY, keyservers); + args.putParcelableArrayList(ARG_KEYSERVER_ARRAY, keyservers); SettingsKeyserverFragment fragment = new SettingsKeyserverFragment(); fragment.setArguments(args); @@ -82,8 +80,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); - String keyservers[] = getArguments().getStringArray(ARG_KEYSERVER_ARRAY); - mKeyservers = new ArrayList<>(Arrays.asList(keyservers)); + mKeyservers = getArguments().getParcelableArrayList(ARG_KEYSERVER_ARRAY); mAdapter = new KeyserverListAdapter(mKeyservers); @@ -133,7 +130,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC } private void startEditKeyserverDialog(AddEditKeyserverDialogFragment.DialogAction action, - String keyserver, final int position) { + ParcelableHkpKeyserver keyserver, final int position) { Handler returnHandler = new Handler() { @Override public void handleMessage(Message message) { @@ -162,7 +159,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC R.string.add_keyserver_without_verification, Notify.Style.WARN).show(); } - String keyserver = data.getString( + ParcelableHkpKeyserver keyserver = data.getParcelable( AddEditKeyserverDialogFragment.MESSAGE_KEYSERVER); AddEditKeyserverDialogFragment.DialogAction dialogAction @@ -189,13 +186,13 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC dialogFragment.show(getFragmentManager(), "addKeyserverDialog"); } - private void addKeyserver(String keyserver) { + private void addKeyserver(ParcelableHkpKeyserver keyserver) { mKeyservers.add(keyserver); mAdapter.notifyItemInserted(mKeyservers.size() - 1); saveKeyserverList(); } - private void editKeyserver(String newKeyserver, int position) { + private void editKeyserver(ParcelableHkpKeyserver newKeyserver, int position) { mKeyservers.set(position, newKeyserver); mAdapter.notifyItemChanged(position); saveKeyserverList(); @@ -218,8 +215,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC } private void saveKeyserverList() { - String servers[] = mKeyservers.toArray(new String[mKeyservers.size()]); - Preferences.getPreferences(getActivity()).setKeyServers(servers); + Preferences.getPreferences(getActivity()).setKeyServers(mKeyservers); } @Override @@ -231,9 +227,9 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC public class KeyserverListAdapter extends RecyclerView.Adapter implements ItemTouchHelperAdapter { - private final List mKeyservers; + private final ArrayList mKeyservers; - public KeyserverListAdapter(List keyservers) { + public KeyserverListAdapter(ArrayList keyservers) { mKeyservers = keyservers; } @@ -246,7 +242,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC @Override public void onBindViewHolder(final ViewHolder holder, int position) { - holder.keyserverUrl.setText(mKeyservers.get(position)); + holder.keyserverUrl.setText(mKeyservers.get(position).getUrl()); // Start a drag whenever the handle view it touched holder.dragHandleView.setOnTouchListener(new View.OnTouchListener() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java index 306b022c1..005ab7ac1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java @@ -35,9 +35,12 @@ import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.service.UploadKeyringParcel; import org.sufficientlysecure.keychain.ui.base.BaseActivity; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Preferences; +import java.util.ArrayList; + /** * Sends the selected public key to a keyserver */ @@ -49,7 +52,7 @@ public class UploadKeyActivity extends BaseActivity private Uri mDataUri; // CryptoOperationHelper.Callback vars - private String mKeyserver; + private ParcelableHkpKeyserver mKeyserver; private CryptoOperationHelper mUploadOpHelper; @Override @@ -64,8 +67,7 @@ public class UploadKeyActivity extends BaseActivity mMultiUserIdsFragment.setCheckboxVisibility(false); ArrayAdapter adapter = new ArrayAdapter<>(this, - android.R.layout.simple_spinner_item, Preferences.getPreferences(this) - .getKeyServers() + android.R.layout.simple_spinner_item, getKeyserversArray() ); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mKeyServerSpinner.setAdapter(adapter); @@ -91,6 +93,18 @@ public class UploadKeyActivity extends BaseActivity } + private String[] getKeyserversArray() { + ArrayList keyservers = Preferences.getPreferences(this) + .getKeyServers(); + String[] keyserversArray = new String[keyservers.size()]; + int i = 0; + for (ParcelableHkpKeyserver k : keyservers) { + keyserversArray[i] = k.getUrl(); + i++; + } + return keyserversArray; + } + @Override protected void initLayout() { setContentView(R.layout.upload_key_activity); @@ -105,8 +119,7 @@ public class UploadKeyActivity extends BaseActivity } private void uploadKey() { - String server = (String) mKeyServerSpinner.getSelectedItem(); - mKeyserver = server; + mKeyserver = (ParcelableHkpKeyserver) mKeyServerSpinner.getSelectedItem(); mUploadOpHelper = new CryptoOperationHelper<>(1, this, this, R.string.progress_uploading); mUploadOpHelper.cryptoOperation(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 4dceb94f0..7961ab4b6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -100,6 +100,7 @@ import org.sufficientlysecure.keychain.ui.util.QrCodeUtils; import org.sufficientlysecure.keychain.util.ContactHelper; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.NfcHelper; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Preferences; @@ -128,7 +129,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements protected Uri mDataUri; // For CryptoOperationHelper.Callback - private String mKeyserver; + private ParcelableHkpKeyserver mKeyserver; private ArrayList mKeyList; private CryptoOperationHelper mImportOpHelper; private CryptoOperationHelper mEditOpHelper; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java index 11c032517..f8798e26e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java @@ -182,13 +182,13 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements } private void startSearch(final String fingerprint) { - final Preferences.ProxyPrefs proxyPrefs = - Preferences.getPreferences(getActivity()).getProxyPrefs(); + final ParcelableProxy parcelableProxy = + Preferences.getPreferences(getActivity()).getParcelableProxy(); OrbotHelper.DialogActions dialogActions = new OrbotHelper.DialogActions() { @Override public void onOrbotStarted() { - new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint); + new DescribeKey(parcelableProxy).execute(fingerprint); } @Override @@ -204,7 +204,7 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements }; if (OrbotHelper.putOrbotInRequiredState(dialogActions, getActivity())) { - new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint); + new DescribeKey(parcelableProxy).execute(fingerprint); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java index e77c92923..5725989b4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListCloudLoader.java @@ -112,13 +112,12 @@ public class ImportKeysListCloudLoader * Query keyserver */ private void queryServer(boolean enforceFingerprint) { - ParcelableProxy parcelableProxy; + ParcelableProxy proxy; if (mParcelableProxy == null) { // no explicit proxy specified, fetch from preferences if (OrbotHelper.isOrbotInRequiredState(mContext)) { - parcelableProxy = Preferences.getPreferences(mContext).getProxyPrefs() - .parcelableProxy; + proxy = Preferences.getPreferences(mContext).getParcelableProxy(); } else { // user needs to enable/install orbot mEntryList.clear(); @@ -129,14 +128,14 @@ public class ImportKeysListCloudLoader return; } } else { - parcelableProxy = mParcelableProxy; + proxy = mParcelableProxy; } try { ArrayList searchResult = CloudSearch.search( mServerQuery, mCloudPrefs, - parcelableProxy.getProxy() + proxy ); mEntryList.clear(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java index 3dcc2f58b..d01bedf00 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/AddEditKeyserverDialogFragment.java @@ -18,26 +18,20 @@ package org.sufficientlysecure.keychain.ui.dialog; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; - import android.app.Activity; -import android.support.design.widget.TextInputLayout; -import android.support.v7.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; -import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.support.annotation.NonNull; +import android.support.design.widget.TextInputLayout; import android.support.v4.app.DialogFragment; +import android.support.v7.app.AlertDialog; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -50,18 +44,23 @@ import android.widget.EditText; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; - -import okhttp3.OkHttpClient; -import okhttp3.Request; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.OkHttpClientFactory; +import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.TlsHelper; import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; -import java.net.Proxy; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; + +import okhttp3.OkHttpClient; +import okhttp3.Request; public class AddEditKeyserverDialogFragment extends DialogFragment implements OnEditorActionListener { private static final String ARG_MESSENGER = "arg_messenger"; @@ -91,21 +90,22 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On EDIT } - public enum FailureReason { + public enum VerifyReturn { INVALID_URL, CONNECTION_FAILED, - NO_PINNED_CERTIFICATE + NO_PINNED_CERTIFICATE, + GOOD } public static AddEditKeyserverDialogFragment newInstance(Messenger messenger, DialogAction action, - String keyserver, + ParcelableHkpKeyserver keyserver, int position) { AddEditKeyserverDialogFragment frag = new AddEditKeyserverDialogFragment(); Bundle args = new Bundle(); args.putParcelable(ARG_MESSENGER, messenger); args.putSerializable(ARG_ACTION, action); - args.putString(ARG_KEYSERVER, keyserver); + args.putParcelable(ARG_KEYSERVER, keyserver); args.putInt(ARG_POSITION, position); frag.setArguments(args); @@ -146,7 +146,8 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On } case EDIT: { alert.setTitle(R.string.edit_keyserver_dialog_title); - mKeyserverEditText.setText(getArguments().getString(ARG_KEYSERVER)); + ParcelableHkpKeyserver keyserver = getArguments().getParcelable(ARG_KEYSERVER); + mKeyserverEditText.setText(keyserver.getUrl()); break; } } @@ -226,16 +227,19 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On mKeyserverEditTextLayout.setErrorEnabled(false); // behaviour same for edit and add - final String keyserverUrl = mKeyserverEditText.getText().toString(); + String keyserverUrl = mKeyserverEditText.getText().toString(); + // TODO! + String keyserverOnion = mKeyserverEditText.getText().toString(); + final ParcelableHkpKeyserver keyserver = new ParcelableHkpKeyserver(keyserverUrl, keyserverOnion); if (mVerifyKeyserverCheckBox.isChecked()) { - final Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(getActivity()) - .getProxyPrefs(); + final ParcelableProxy proxy = Preferences.getPreferences(getActivity()) + .getParcelableProxy(); OrbotHelper.DialogActions dialogActions = new OrbotHelper.DialogActions() { @Override public void onOrbotStarted() { verifyConnection( - keyserverUrl, - proxyPrefs.parcelableProxy.getProxy(), + keyserver, + proxy, mOnlyTrustedKeyserverCheckBox.isChecked() ); } @@ -243,7 +247,7 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On @Override public void onNeutralButton() { verifyConnection( - keyserverUrl, + keyserver, null, mOnlyTrustedKeyserverCheckBox.isChecked() ); @@ -257,26 +261,26 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On if (OrbotHelper.putOrbotInRequiredState(dialogActions, getActivity())) { verifyConnection( - keyserverUrl, - proxyPrefs.parcelableProxy.getProxy(), + keyserver, + proxy, mOnlyTrustedKeyserverCheckBox.isChecked() ); } } else { dismiss(); // return unverified keyserver back to activity - keyserverEdited(keyserverUrl, false); + keyserverEdited(new ParcelableHkpKeyserver(keyserverUrl, null), false); } } }); } } - public void keyserverEdited(String keyserver, boolean verified) { + public void keyserverEdited(ParcelableHkpKeyserver keyserver, boolean verified) { dismiss(); Bundle data = new Bundle(); data.putSerializable(MESSAGE_DIALOG_ACTION, mDialogAction); - data.putString(MESSAGE_KEYSERVER, keyserver); + data.putParcelable(MESSAGE_KEYSERVER, keyserver); data.putBoolean(MESSAGE_VERIFIED, verified); if (mDialogAction == DialogAction.EDIT) { @@ -296,8 +300,8 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On sendMessageToHandler(MESSAGE_OKAY, data); } - public void verificationFailed(FailureReason failureReason) { - switch (failureReason) { + public void verificationFailed(VerifyReturn verifyReturn) { + switch (verifyReturn) { case CONNECTION_FAILED: { mKeyserverEditTextLayout.setError( getString(R.string.add_keyserver_connection_failed)); @@ -317,11 +321,11 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On } - public void verifyConnection(String keyserver, final Proxy proxy, final boolean onlyTrustedKeyserver) { + public void verifyConnection(ParcelableHkpKeyserver keyserver, final ParcelableProxy proxy, final boolean onlyTrustedKeyserver) { - new AsyncTask() { + new AsyncTask() { ProgressDialog mProgressDialog; - String mKeyserver; + ParcelableHkpKeyserver mKeyserver; @Override protected void onPreExecute() { @@ -332,61 +336,62 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On } @Override - protected FailureReason doInBackground(String... keyservers) { + protected VerifyReturn doInBackground(ParcelableHkpKeyserver... keyservers) { mKeyserver = keyservers[0]; - FailureReason reason = null; - try { - // replace hkps/hkp scheme and reconstruct Uri - Uri keyserverUri = Uri.parse(mKeyserver); - String scheme = keyserverUri.getScheme(); - String schemeSpecificPart = keyserverUri.getSchemeSpecificPart(); - String fragment = keyserverUri.getFragment(); - if (scheme == null) { - throw new MalformedURLException(); - } - if ("hkps".equalsIgnoreCase(scheme)) { - scheme = "https"; - } else if ("hkp".equalsIgnoreCase(scheme)) { - scheme = "http"; - } - URI newKeyserver = new URI(scheme, schemeSpecificPart, fragment); - Log.d("Converted URL", newKeyserver.toString()); - - if (onlyTrustedKeyserver - && TlsHelper.getPinnedSslSocketFactory(newKeyserver.toURL()) == null) { - Log.w(Constants.TAG, "No pinned certificate for this host in OpenKeychain's assets."); - reason = FailureReason.NO_PINNED_CERTIFICATE; - return reason; - } - - OkHttpClient client = OkHttpClientFactory.getClientPinnedIfAvailable(newKeyserver.toURL(), proxy); - - client.newCall(new Request.Builder().url(newKeyserver.toURL()).build()).execute(); - } catch (TlsHelper.TlsHelperException e) { - reason = FailureReason.CONNECTION_FAILED; - } catch (MalformedURLException | URISyntaxException e) { - Log.w(Constants.TAG, "Invalid keyserver URL entered by user."); - reason = FailureReason.INVALID_URL; - } catch (IOException e) { - Log.w(Constants.TAG, "Could not connect to entered keyserver url"); - reason = FailureReason.CONNECTION_FAILED; - } - return reason; + return verifyKeyserver(mKeyserver, proxy, onlyTrustedKeyserver); } @Override - protected void onPostExecute(FailureReason failureReason) { + protected void onPostExecute(VerifyReturn verifyReturn) { mProgressDialog.dismiss(); - if (failureReason == null) { + if (verifyReturn == VerifyReturn.GOOD) { keyserverEdited(mKeyserver, true); } else { - verificationFailed(failureReason); + verificationFailed(verifyReturn); } } }.execute(keyserver); } + private VerifyReturn verifyKeyserver(ParcelableHkpKeyserver keyserver, final ParcelableProxy proxy, final boolean onlyTrustedKeyserver) { + VerifyReturn reason = VerifyReturn.GOOD; + try { + URI keyserverUriHttp = keyserver.getUrlURI(); + + // check TLS pinning only for non-Tor keyservers + if (onlyTrustedKeyserver + && TlsHelper.getPinnedSslSocketFactory(keyserverUriHttp.toURL()) == null) { + Log.w(Constants.TAG, "No pinned certificate for this host in OpenKeychain's assets."); + reason = VerifyReturn.NO_PINNED_CERTIFICATE; + return reason; + } + + OkHttpClient client = OkHttpClientFactory.getClientPinnedIfAvailable( + keyserverUriHttp.toURL(), proxy.getProxy()); + client.newCall(new Request.Builder().url(keyserverUriHttp.toURL()).build()).execute(); + + // try out onion keyserver if Tor is enabled + if (proxy.isTorEnabled()) { + URI keyserverUriOnion = keyserver.getOnionURI(); + + OkHttpClient clientTor = OkHttpClientFactory.getClientPinnedIfAvailable( + keyserverUriOnion.toURL(), proxy.getProxy()); + clientTor.newCall(new Request.Builder().url(keyserverUriOnion.toURL()).build()).execute(); + } + } catch (TlsHelper.TlsHelperException e) { + reason = VerifyReturn.CONNECTION_FAILED; + } catch (MalformedURLException | URISyntaxException e) { + Log.w(Constants.TAG, "Invalid keyserver URL entered by user."); + reason = VerifyReturn.INVALID_URL; + } catch (IOException e) { + Log.w(Constants.TAG, "Could not connect to entered keyserver url"); + reason = VerifyReturn.CONNECTION_FAILED; + } + + return reason; + } + @Override public void onDismiss(DialogInterface dialog) { super.onDismiss(dialog); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java index 15ff4d47a..0d00b4937 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/EmailKeyHelper.java @@ -19,15 +19,14 @@ package org.sufficientlysecure.keychain.util; import android.content.Context; -import org.sufficientlysecure.keychain.keyimport.HkpKeyserver; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.keyimport.Keyserver; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; -import java.net.Proxy; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -41,14 +40,15 @@ public class EmailKeyHelper { implements CryptoOperationHelper.Callback { private ArrayList mKeyList; - private String mKeyserver; + private ParcelableHkpKeyserver mKeyserver; - public ImportContactKeysCallback(Context context, String keyserver, Proxy proxy) { + public ImportContactKeysCallback(Context context, ParcelableHkpKeyserver keyserver, + ParcelableProxy proxy) { this(context, new ContactHelper(context).getContactMails(), keyserver, proxy); } - public ImportContactKeysCallback(Context context, List mails, String keyserver, - Proxy proxy) { + public ImportContactKeysCallback(Context context, List mails, + ParcelableHkpKeyserver keyserver, ParcelableProxy proxy) { Set entries = new HashSet<>(); for (String mail : mails) { entries.addAll(getEmailKeys(context, mail, proxy)); @@ -62,39 +62,41 @@ public class EmailKeyHelper { mKeyList = keys; mKeyserver = keyserver; } + @Override public ImportKeyringParcel createOperationInput() { return new ImportKeyringParcel(mKeyList, mKeyserver); } } - public static Set getEmailKeys(Context context, String mail, Proxy proxy) { + public static Set getEmailKeys(Context context, String mail, + ParcelableProxy proxy) { Set keys = new HashSet<>(); // Try _hkp._tcp SRV record first String[] mailparts = mail.split("@"); if (mailparts.length == 2) { - HkpKeyserver hkp = HkpKeyserver.resolve(mailparts[1], proxy); + ParcelableHkpKeyserver hkp = ParcelableHkpKeyserver.resolve(mailparts[1]); if (hkp != null) { - keys.addAll(getEmailKeys(mail, hkp)); + keys.addAll(getEmailKeys(mail, hkp, proxy)); } } if (keys.isEmpty()) { // Most users don't have the SRV record, so ask a default server as well - String server = Preferences.getPreferences(context).getPreferredKeyserver(); + ParcelableHkpKeyserver server = Preferences.getPreferences(context).getPreferredKeyserver(); if (server != null) { - HkpKeyserver hkp = new HkpKeyserver(server, proxy); - keys.addAll(getEmailKeys(mail, hkp)); + keys.addAll(getEmailKeys(mail, server, proxy)); } } return keys; } - public static List getEmailKeys(String mail, Keyserver keyServer) { + public static List getEmailKeys(String mail, Keyserver keyServer, + ParcelableProxy proxy) { Set keys = new HashSet<>(); try { - for (ImportKeysListEntry key : keyServer.search(mail)) { + for (ImportKeysListEntry key : keyServer.search(mail, proxy)) { if (key.isRevoked() || key.isExpired()) continue; for (String userId : key.getUserIds()) { if (userId.toLowerCase().contains(mail.toLowerCase(Locale.ENGLISH))) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableProxy.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableProxy.java index 7e2328e99..e655f9d1d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableProxy.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ParcelableProxy.java @@ -32,21 +32,28 @@ public class ParcelableProxy implements Parcelable { private String mProxyHost; private int mProxyPort; private Proxy.Type mProxyType; + private int mProxyMode; - public ParcelableProxy(String hostName, int port, Proxy.Type type) { + public static final int PROXY_MODE_NORMAL = 0; + public static final int PROXY_MODE_TOR = 1; + + public ParcelableProxy(String hostName, int port, Proxy.Type type, int proxyMode) { mProxyHost = hostName; - - if (hostName == null) { + if (mProxyHost == null) { return; // represents a null proxy } mProxyPort = port; - mProxyType = type; + mProxyMode = proxyMode; } public static ParcelableProxy getForNoProxy() { - return new ParcelableProxy(null, -1, null); + return new ParcelableProxy(null, -1, null, PROXY_MODE_NORMAL); + } + + public boolean isTorEnabled() { + return (mProxyMode == PROXY_MODE_TOR); } @NonNull @@ -65,6 +72,7 @@ public class ParcelableProxy implements Parcelable { mProxyHost = in.readString(); mProxyPort = in.readInt(); mProxyType = (Proxy.Type) in.readSerializable(); + mProxyMode = in.readInt(); } @Override @@ -77,6 +85,7 @@ public class ParcelableProxy implements Parcelable { dest.writeString(mProxyHost); dest.writeInt(mProxyPort); dest.writeSerializable(mProxyType); + dest.writeInt(mProxyMode); } @SuppressWarnings("unused") diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java index 6a6d8d18a..98a571466 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java @@ -27,19 +27,17 @@ import android.content.SharedPreferences; import android.os.Parcel; import android.os.Parcelable; import android.preference.PreferenceManager; -import android.support.annotation.NonNull; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants.Pref; import org.sufficientlysecure.keychain.KeychainApplication; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver; import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService; -import org.sufficientlysecure.keychain.util.orbot.OrbotStatusReceiver; import java.io.Serializable; import java.net.Proxy; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; @@ -47,7 +45,6 @@ import java.util.HashSet; import java.util.ListIterator; import java.util.Map; import java.util.Set; -import java.util.Vector; /** * Singleton Implementation of a Preference Helper @@ -131,40 +128,50 @@ public class Preferences { editor.commit(); } - public String[] getKeyServers() { + public ArrayList getKeyServers() { String rawData = mSharedPreferences.getString(Constants.Pref.KEY_SERVERS, Constants.Defaults.KEY_SERVERS); if ("".equals(rawData)) { - return new String[0]; + return new ArrayList<>(); } - Vector servers = new Vector<>(); - String chunks[] = rawData.split(","); - for (String c : chunks) { - String tmp = c.trim(); - if (tmp.length() > 0) { - servers.add(tmp); - } - } - return servers.toArray(chunks); - } + ArrayList servers = new ArrayList<>(); + String[] entries = rawData.split(","); + for (String entry : entries) { - public String getPreferredKeyserver() { - String[] keyservers = getKeyServers(); - return keyservers.length == 0 ? null : keyservers[0]; - } + String[] addresses = entry.trim().split(";"); + String url = addresses[0]; + String onion = addresses.length == 1 ? null : addresses[1]; - public void setKeyServers(String[] value) { - SharedPreferences.Editor editor = mSharedPreferences.edit(); - String rawData = ""; - for (String v : value) { - String tmp = v.trim(); - if (tmp.length() == 0) { + if (url.isEmpty()) { continue; } + + servers.add(new ParcelableHkpKeyserver(url, onion)); + } + return servers; + } + + public ParcelableHkpKeyserver getPreferredKeyserver() { + ArrayList keyservers = getKeyServers(); + return keyservers.size() == 0 ? null : keyservers.get(0); + } + + public void setKeyServers(ArrayList keyservers) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + String rawData = ""; + for (ParcelableHkpKeyserver server : keyservers) { + if (server.getUrl().isEmpty()) { + continue; + } + rawData += server.getUrl(); + + if (server.getOnion() != null && !server.getOnion().isEmpty()) { + rawData += ";" + server.getOnion(); + } + if (!"".equals(rawData)) { rawData += ","; } - rawData += tmp; } editor.putString(Constants.Pref.KEY_SERVERS, rawData); editor.commit(); @@ -249,7 +256,7 @@ public class Preferences { return Integer.parseInt(mSharedPreferences.getString(Pref.PROXY_PORT, "-1")); } - public Proxy.Type getProxyType() { + Proxy.Type getProxyType() { final String typeHttp = Pref.ProxyType.TYPE_HTTP; final String typeSocks = Pref.ProxyType.TYPE_SOCKS; @@ -266,47 +273,24 @@ public class Preferences { } } - public ProxyPrefs getProxyPrefs() { + public ParcelableProxy getParcelableProxy() { boolean useTor = getUseTorProxy(); boolean useNormalProxy = getUseNormalProxy(); if (useTor) { //TODO: Replace Constants.Orbot.PROXY_PORT with OrbotStatusReceiver.getProxyPortHttp() //TODO: in order to always have the actual port Orbot is offering? - return new ProxyPrefs(true, false, Constants.Orbot.PROXY_HOST, Constants.Orbot.PROXY_PORT, - Constants.Orbot.PROXY_TYPE); + + return new ParcelableProxy(Constants.Orbot.PROXY_HOST, Constants.Orbot.PROXY_PORT, + Constants.Orbot.PROXY_TYPE, ParcelableProxy.PROXY_MODE_TOR); } else if (useNormalProxy) { - return new ProxyPrefs(false, true, getProxyHost(), getProxyPort(), getProxyType()); + return new ParcelableProxy(getProxyHost(), getProxyPort(), getProxyType(), + ParcelableProxy.PROXY_MODE_NORMAL); } else { - return new ProxyPrefs(false, false, null, -1, null); + return new ParcelableProxy(null, -1, null, ParcelableProxy.PROXY_MODE_NORMAL); } } - public static class ProxyPrefs { - public final ParcelableProxy parcelableProxy; - public final boolean torEnabled; - public final boolean normalPorxyEnabled; - - /** - * torEnabled and normalProxyEnabled are not expected to both be true - * - * @param torEnabled if Tor is to be used - * @param normalPorxyEnabled if user-specified proxy is to be used - */ - public ProxyPrefs(boolean torEnabled, boolean normalPorxyEnabled, String hostName, int port, Proxy.Type type) { - this.torEnabled = torEnabled; - this.normalPorxyEnabled = normalPorxyEnabled; - if (!torEnabled && !normalPorxyEnabled) this.parcelableProxy = new ParcelableProxy(null, -1, null); - else this.parcelableProxy = new ParcelableProxy(hostName, port, type); - } - - @NonNull - public Proxy getProxy() { - return parcelableProxy.getProxy(); - } - - } - /** * @return true if a periodic sync exists and is set to run automatically, false otherwise */ @@ -339,10 +323,11 @@ public class Preferences { } public static class CacheTTLPrefs implements Serializable { - public static final Map CACHE_TTL_NAMES; + public static final Map CACHE_TTL_NAMES; public static final ArrayList CACHE_TTLS; + static { - HashMap cacheTtlNames = new HashMap<>(); + HashMap cacheTtlNames = new HashMap<>(); cacheTtlNames.put(0, R.string.cache_ttl_lock_screen); cacheTtlNames.put(60 * 10, R.string.cache_ttl_ten_minutes); cacheTtlNames.put(60 * 30, R.string.cache_ttl_thirty_minutes); @@ -397,7 +382,7 @@ public class Preferences { public final boolean searchKeyserver; public final boolean searchKeybase; public final boolean searchFacebook; - public final String keyserver; + public final ParcelableHkpKeyserver keyserver; /** * @param searchKeyserver should passed keyserver be searched @@ -405,7 +390,7 @@ public class Preferences { * @param keyserver the keyserver url authority to search on */ public CloudSearchPrefs(boolean searchKeyserver, boolean searchKeybase, - boolean searchFacebook, String keyserver) { + boolean searchFacebook, ParcelableHkpKeyserver keyserver) { this.searchKeyserver = searchKeyserver; this.searchKeybase = searchKeybase; this.searchFacebook = searchFacebook; @@ -416,7 +401,7 @@ public class Preferences { searchKeyserver = in.readByte() != 0x00; searchKeybase = in.readByte() != 0x00; searchFacebook = in.readByte() != 0x00; - keyserver = in.readString(); + keyserver = in.readParcelable(ParcelableHkpKeyserver.class.getClassLoader()); } @Override @@ -429,7 +414,7 @@ public class Preferences { dest.writeByte((byte) (searchKeyserver ? 0x01 : 0x00)); dest.writeByte((byte) (searchKeybase ? 0x01 : 0x00)); dest.writeByte((byte) (searchFacebook ? 0x01 : 0x00)); - dest.writeString(keyserver); + dest.writeParcelable(keyserver, flags); } public static final Parcelable.Creator CREATOR @@ -476,22 +461,21 @@ public class Preferences { // fall through case 3: { // migrate keyserver to hkps - String[] serversArray = getKeyServers(); - ArrayList servers = new ArrayList<>(Arrays.asList(serversArray)); - ListIterator it = servers.listIterator(); + ArrayList servers = getKeyServers(); + ListIterator it = servers.listIterator(); while (it.hasNext()) { - String server = it.next(); + ParcelableHkpKeyserver server = it.next(); if (server == null) { continue; } - switch (server) { + switch (server.getUrl()) { case "pool.sks-keyservers.net": // use HKPS! - it.set("hkps://hkps.pool.sks-keyservers.net"); + it.set(new ParcelableHkpKeyserver("hkps://hkps.pool.sks-keyservers.net", null)); break; case "pgp.mit.edu": // use HKPS! - it.set("hkps://pgp.mit.edu"); + it.set(new ParcelableHkpKeyserver("hkps://pgp.mit.edu", null)); break; case "subkeys.pgp.net": // remove, because often down and no HKPS! @@ -500,7 +484,7 @@ public class Preferences { } } - setKeyServers(servers.toArray(new String[servers.size()])); + setKeyServers(servers); } // fall through case 4: { @@ -513,6 +497,27 @@ public class Preferences { // fall through case 6: { } + // fall through + case 7: { + // add onion address to sks-keyservers.net + ArrayList servers = getKeyServers(); + ListIterator it = servers.listIterator(); + while (it.hasNext()) { + ParcelableHkpKeyserver server = it.next(); + if (server == null) { + continue; + } + switch (server.getUrl()) { + case "hkps://hkps.pool.sks-keyservers.net": + it.set(new ParcelableHkpKeyserver( + "hkps://hkps.pool.sks-keyservers.net", + "hkp://jirk5u4osbsr34t5.onion")); + break; + } + + } + setKeyServers(servers); + } } // write new preference version diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java index b2809d576..f3aabe3f0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java @@ -72,6 +72,7 @@ import org.sufficientlysecure.keychain.ui.dialog.OrbotStartDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.PreferenceInstallDialogFragment; import org.sufficientlysecure.keychain.ui.util.ThemeChanger; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.Preferences; import java.util.List; @@ -153,6 +154,7 @@ public class OrbotHelper { /** * Initialize the OrbotStatusReceiver (if not already happened) and check, whether Orbot is * running or not. + * * @param context context * @return if Orbot is running */ @@ -285,8 +287,8 @@ public class OrbotHelper { * otherwise */ public static boolean isOrbotInRequiredState(Context context) { - Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(context).getProxyPrefs(); - if (!proxyPrefs.torEnabled) { + ParcelableProxy proxy = Preferences.getPreferences(context).getParcelableProxy(); + if (!proxy.isTorEnabled()) { return true; } else if (!OrbotHelper.isOrbotInstalled(context) || !OrbotHelper.isOrbotRunning(context)) { return false; @@ -298,15 +300,15 @@ public class OrbotHelper { * checks if Tor is enabled and if it is, that Orbot is installed and running. Generates appropriate dialogs. * * @param middleButton resourceId of string to display as the middle button of install and enable dialogs - * @param proxyPrefs proxy preferences used to determine if Tor is required to be started + * @param proxy proxy preferences used to determine if Tor is required to be started * @return true if Tor is not enabled or Tor is enabled and Orbot is installed and running, else false */ public static boolean putOrbotInRequiredState(final int middleButton, final DialogActions dialogActions, - Preferences.ProxyPrefs proxyPrefs, + ParcelableProxy proxy, final FragmentActivity fragmentActivity) { - if (!proxyPrefs.torEnabled) { + if (!proxy.isTorEnabled()) { return true; } @@ -377,7 +379,7 @@ public class OrbotHelper { FragmentActivity fragmentActivity) { return putOrbotInRequiredState(R.string.orbot_ignore_tor, dialogActions, - Preferences.getPreferences(fragmentActivity).getProxyPrefs(), + Preferences.getPreferences(fragmentActivity).getParcelableProxy(), fragmentActivity); }