Preparations for support of onion keyservers. Refactoring of Keyserver and proxy related classes

This commit is contained in:
Dominik Schürmann
2016-10-27 16:19:43 +02:00
parent bf382ec59d
commit 911fa020c2
37 changed files with 576 additions and 479 deletions

View File

@@ -147,7 +147,7 @@ public final class Constants {
public static final class Defaults { public static final class Defaults {
public static final String KEY_SERVERS = "hkps://hkps.pool.sks-keyservers.net, hkps://pgp.mit.edu"; 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 { public static final class key {

View File

@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.keyimport;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import java.net.Proxy; import java.net.Proxy;
@@ -35,7 +36,7 @@ public class CloudSearch {
private final static long SECONDS = 1000; private final static long SECONDS = 1000;
public static ArrayList<ImportKeysListEntry> search( public static ArrayList<ImportKeysListEntry> 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 { throws Keyserver.CloudSearchFailureException {
final ArrayList<Keyserver> servers = new ArrayList<>(); final ArrayList<Keyserver> servers = new ArrayList<>();
@@ -43,13 +44,13 @@ public class CloudSearch {
final Vector<Keyserver.CloudSearchFailureException> problems = new Vector<>(); final Vector<Keyserver.CloudSearchFailureException> problems = new Vector<>();
if (cloudPrefs.searchKeyserver) { if (cloudPrefs.searchKeyserver) {
servers.add(new HkpKeyserver(cloudPrefs.keyserver, proxy)); servers.add(cloudPrefs.keyserver);
} }
if (cloudPrefs.searchKeybase) { if (cloudPrefs.searchKeybase) {
servers.add(new KeybaseKeyserver(proxy)); servers.add(new KeybaseKeyserver());
} }
if (cloudPrefs.searchFacebook) { if (cloudPrefs.searchFacebook) {
servers.add(new FacebookKeyserver(proxy)); servers.add(new FacebookKeyserver());
} }
final ImportKeysList results = new ImportKeysList(servers.size()); final ImportKeysList results = new ImportKeysList(servers.size());
@@ -59,7 +60,7 @@ public class CloudSearch {
@Override @Override
public void run() { public void run() {
try { try {
results.addAll(keyserver.search(query)); results.addAll(keyserver.search(query, proxy));
} catch (Keyserver.CloudSearchFailureException e) { } catch (Keyserver.CloudSearchFailureException e) {
problems.add(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. // wait for either all the searches to come back, or 10 seconds. If using proxy, wait 30 seconds.
synchronized (results) { synchronized (results) {
try { try {
if (proxy == Proxy.NO_PROXY) { if (proxy.getProxy() == Proxy.NO_PROXY) {
results.wait(30 * SECONDS); results.wait(30 * SECONDS);
} else { } else {
results.wait(10 * SECONDS); results.wait(10 * SECONDS);

View File

@@ -27,6 +27,7 @@ import android.support.annotation.Nullable;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; 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.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.OkHttpClientFactory; import org.sufficientlysecure.keychain.util.OkHttpClientFactory;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.TlsHelper; import org.sufficientlysecure.keychain.util.TlsHelper;
import java.io.IOException; import java.io.IOException;
import java.net.Proxy;
import java.net.Proxy;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -56,18 +58,15 @@ public class FacebookKeyserver extends Keyserver {
public static final String ORIGIN = FB_URL; public static final String ORIGIN = FB_URL;
private final Proxy mProxy; public FacebookKeyserver() {
public FacebookKeyserver(Proxy proxy) {
mProxy = proxy;
} }
@Override @Override
public List<ImportKeysListEntry> search(String fbUsername) public List<ImportKeysListEntry> search(String fbUsername, ParcelableProxy proxy)
throws QueryFailedException, QueryNeedsRepairException { throws QueryFailedException, QueryNeedsRepairException {
List<ImportKeysListEntry> entry = new ArrayList<>(1); List<ImportKeysListEntry> entry = new ArrayList<>(1);
String data = get(fbUsername); String data = get(fbUsername, proxy);
// if we're here that means key retrieval succeeded, // if we're here that means key retrieval succeeded,
// would have thrown an exception otherwise // would have thrown an exception otherwise
try { try {
@@ -85,10 +84,10 @@ public class FacebookKeyserver extends Keyserver {
} }
@Override @Override
public String get(String fbUsername) throws QueryFailedException { public String get(String fbUsername, ParcelableProxy proxy) throws QueryFailedException {
Log.d(Constants.TAG, "FacebookKeyserver get: " + fbUsername + " using Proxy: " + mProxy); Log.d(Constants.TAG, "FacebookKeyserver get: " + fbUsername + " using Proxy: " + proxy);
String data = query(fbUsername); String data = query(fbUsername, proxy);
if (data == null) { if (data == null) {
throw new QueryFailedException("data is null"); throw new QueryFailedException("data is null");
@@ -101,14 +100,14 @@ public class FacebookKeyserver extends Keyserver {
throw new QueryFailedException("data is null"); throw new QueryFailedException("data is null");
} }
private String query(String fbUsername) throws QueryFailedException { private String query(String fbUsername, ParcelableProxy proxy) throws QueryFailedException {
try { try {
String request = String.format(FB_KEY_URL_FORMAT, fbUsername); 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); 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(); 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); Log.e(Constants.TAG, "IOException at Facebook key download", e);
throw new QueryFailedException("Cannot connect to Facebook. " throw new QueryFailedException("Cannot connect to Facebook. "
+ "Check your Internet connection!" + "Check your Internet connection!"
+ (mProxy == Proxy.NO_PROXY ? "" : " Using proxy " + mProxy)); + (proxy.getProxy() == Proxy.NO_PROXY ? "" : " Using proxy " + proxy.getProxy()));
} catch (TlsHelper.TlsHelperException e) { } catch (TlsHelper.TlsHelperException e) {
Log.e(Constants.TAG, "Exception in cert pinning", e); Log.e(Constants.TAG, "Exception in cert pinning", e);
throw new QueryFailedException("Exception in cert pinning. "); throw new QueryFailedException("Exception in cert pinning. ");
@@ -134,7 +133,7 @@ public class FacebookKeyserver extends Keyserver {
} }
@Override @Override
public void add(String armoredKey) throws AddKeyException { public void add(String armoredKey, ParcelableProxy proxy) throws AddKeyException {
// Implementing will require usage of FB API // Implementing will require usage of FB API
throw new UnsupportedOperationException("Uploading keys not supported yet"); throw new UnsupportedOperationException("Uploading keys not supported yet");
} }

View File

@@ -26,22 +26,19 @@ import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.OkHttpKeybaseClient; import org.sufficientlysecure.keychain.util.OkHttpKeybaseClient;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import java.net.Proxy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class KeybaseKeyserver extends Keyserver { public class KeybaseKeyserver extends Keyserver {
public static final String ORIGIN = "keybase:keybase.io"; public static final String ORIGIN = "keybase:keybase.io";
Proxy mProxy; public KeybaseKeyserver() {
public KeybaseKeyserver(Proxy proxy) {
mProxy = proxy;
} }
@Override @Override
public ArrayList<ImportKeysListEntry> search(String query) throws QueryFailedException, public ArrayList<ImportKeysListEntry> search(String query, ParcelableProxy proxy) throws QueryFailedException,
QueryNeedsRepairException { QueryNeedsRepairException {
ArrayList<ImportKeysListEntry> results = new ArrayList<>(); ArrayList<ImportKeysListEntry> results = new ArrayList<>();
@@ -55,7 +52,7 @@ public class KeybaseKeyserver extends Keyserver {
try { try {
KeybaseQuery keybaseQuery = new KeybaseQuery(new OkHttpKeybaseClient()); KeybaseQuery keybaseQuery = new KeybaseQuery(new OkHttpKeybaseClient());
keybaseQuery.setProxy(mProxy); keybaseQuery.setProxy(proxy.getProxy());
Iterable<Match> matches = keybaseQuery.search(query); Iterable<Match> matches = keybaseQuery.search(query);
for (Match match : matches) { for (Match match : matches) {
results.add(makeEntry(match, query)); results.add(makeEntry(match, query));
@@ -107,10 +104,10 @@ public class KeybaseKeyserver extends Keyserver {
} }
@Override @Override
public String get(String id) throws QueryFailedException { public String get(String id, ParcelableProxy proxy) throws QueryFailedException {
try { try {
KeybaseQuery keybaseQuery = new KeybaseQuery(new OkHttpKeybaseClient()); KeybaseQuery keybaseQuery = new KeybaseQuery(new OkHttpKeybaseClient());
keybaseQuery.setProxy(mProxy); keybaseQuery.setProxy(proxy.getProxy());
return User.keyForUsername(keybaseQuery, id); return User.keyForUsername(keybaseQuery, id);
} catch (KeybaseException e) { } catch (KeybaseException e) {
throw new QueryFailedException(e.getMessage()); throw new QueryFailedException(e.getMessage());
@@ -118,7 +115,7 @@ public class KeybaseKeyserver extends Keyserver {
} }
@Override @Override
public void add(String armoredKey) throws AddKeyException { public void add(String armoredKey, ParcelableProxy proxy) throws AddKeyException {
throw new AddKeyException(); throw new AddKeyException();
} }
} }

View File

@@ -18,6 +18,8 @@
package org.sufficientlysecure.keychain.keyimport; package org.sufficientlysecure.keychain.keyimport;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -68,12 +70,12 @@ public abstract class Keyserver {
private static final long serialVersionUID = -507574859137295530L; private static final long serialVersionUID = -507574859137295530L;
} }
public abstract List<ImportKeysListEntry> search(String query) public abstract List<ImportKeysListEntry> search(String query, ParcelableProxy proxy)
throws QueryFailedException, QueryNeedsRepairException; 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 { public static String readAll(InputStream in, String encoding) throws IOException {
ByteArrayOutputStream raw = new ByteArrayOutputStream(); ByteArrayOutputStream raw = new ByteArrayOutputStream();

View File

@@ -1,40 +1,40 @@
/* /*
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de> * Copyright (C) 2016 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2011-2014 Thialfihar <thi@thialfihar.org> * Copyright (C) 2011-2014 Thialfihar <thi@thialfihar.org>
* Copyright (C) 2011 Senecaso
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * This program is free software: you can redistribute it and/or modify
* you may not use this file except in compliance with the License. * it under the terms of the GNU General Public License as published by
* You may obtain a copy of the License at * 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 * You should have received a copy of the GNU General Public License
* distributed under the License is distributed on an "AS IS" BASIS, * along with this program. If not, see <http://www.gnu.org/licenses/>.
* 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.
*/ */
package org.sufficientlysecure.keychain.keyimport; 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.Constants;
import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.OkHttpClientFactory; import org.sufficientlysecure.keychain.util.OkHttpClientFactory;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.TlsHelper; import org.sufficientlysecure.keychain.util.TlsHelper;
import java.io.IOException; import java.io.IOException;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.Proxy; import java.net.Proxy;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.net.URLEncoder; import java.net.URLEncoder;
@@ -47,38 +47,17 @@ import java.util.TimeZone;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import android.support.annotation.NonNull;
import de.measite.minidns.Client; import de.measite.minidns.Client;
import de.measite.minidns.Question; import de.measite.minidns.Question;
import de.measite.minidns.Record; import de.measite.minidns.Record;
import de.measite.minidns.record.SRV; 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 { public class ParcelableHkpKeyserver extends Keyserver implements Parcelable {
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;
/** /**
* pub:%keyid%:%algo%:%keylen%:%creationdate%:%expirationdate%:%flags% * 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 = 11371;
private static final short PORT_DEFAULT_HKPS = 443; private static final short PORT_DEFAULT_HKPS = 443;
/** private String mUrl;
* @param hostAndPort may be just private String mOnion;
* "<code>hostname</code>" (eg. "<code>pool.sks-keyservers.net</code>"), then it will
* connect using {@link #PORT_DEFAULT}. However, port may be specified after colon public ParcelableHkpKeyserver(@NonNull String url, String onion) {
* ("<code>hostname:port</code>", eg. "<code>p80.pool.sks-keyservers.net:80</code>"). mUrl = url.trim();
*/ mOnion = onion == null ? null : onion.trim();
public HkpKeyserver(String hostAndPort, Proxy proxy) { }
String host = hostAndPort;
short port = PORT_DEFAULT; public ParcelableHkpKeyserver(@NonNull String url) {
boolean secure = false; this(url, null);
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 public String getUrl() {
if ("hkps".equalsIgnoreCase(parts[0]) || "https".equalsIgnoreCase(parts[0])) { return mUrl;
secure = true; }
port = PORT_DEFAULT_HKPS;
} else if (!"hkp".equalsIgnoreCase(parts[0]) && !"http".equalsIgnoreCase(parts[0])) { public String getOnion() {
throw new IllegalArgumentException("Protocol " + parts[0] + " is unknown"); return mOnion;
} }
host = parts[1];
if (host.startsWith("//")) { // People tend to type https:// and hkps://, so we'll support that as well public URI getUrlURI() throws URISyntaxException {
host = host.substring(2); return getURI(mUrl);
} }
if (parts.length > 2) {
port = Short.decode(parts[2]); public URI getOnionURI() throws URISyntaxException {
} return getURI(mOnion);
} else { }
host = parts[0];
port = Short.decode(parts[1]); 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 "<code>hostname</code>" (eg. "<code>pool.sks-keyservers.net</code>"), then it will
* connect using {@link #PORT_DEFAULT}. However, port may be specified after colon
* ("<code>hostname:port</code>", eg. "<code>p80.pool.sks-keyservers.net:80</code>").
*/
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) { private String query(String request, @NonNull ParcelableProxy proxy) throws Keyserver.QueryFailedException, HttpError {
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 {
try { 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); 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(); 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 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) { } catch (IOException e) {
Log.e(Constants.TAG, "IOException at HkpKeyserver", e); Log.e(Constants.TAG, "IOException at HkpKeyserver", e);
throw new QueryFailedException("Keyserver '" + mHost + "' is unavailable. Check your Internet connection!" + throw new Keyserver.QueryFailedException("Keyserver '" + mUrl + "' is unavailable. Check your Internet connection!" +
(proxy == Proxy.NO_PROXY ? "" : " Using proxy " + proxy)); (proxy.getProxy() == Proxy.NO_PROXY ? "" : " Using proxy " + proxy.getProxy()));
} catch (TlsHelper.TlsHelperException e) { } catch (TlsHelper.TlsHelperException e) {
Log.e(Constants.TAG, "Exception in pinning certs", 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! * Results are sorted by creation date of key!
*/ */
@Override @Override
public ArrayList<ImportKeysListEntry> search(String query) throws QueryFailedException, public ArrayList<ImportKeysListEntry> search(String query, ParcelableProxy proxy) throws Keyserver.QueryFailedException,
QueryNeedsRepairException { Keyserver.QueryNeedsRepairException {
ArrayList<ImportKeysListEntry> results = new ArrayList<>(); ArrayList<ImportKeysListEntry> results = new ArrayList<>();
if (query.length() < 3) { if (query.length() < 3) {
throw new QueryTooShortException(); throw new Keyserver.QueryTooShortException();
} }
String encodedQuery; String encodedQuery;
@@ -247,7 +241,7 @@ public class HkpKeyserver extends Keyserver {
String data; String data;
try { try {
data = query(request, mProxy); data = query(request, proxy);
} catch (HttpError e) { } catch (HttpError e) {
if (e.getData() != null) { if (e.getData() != null) {
Log.d(Constants.TAG, "returned error data: " + e.getData().toLowerCase(Locale.ENGLISH)); 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! // NOTE: This is also a 404 error for some keyservers!
return results; return results;
} else if (e.getData().toLowerCase(Locale.ENGLISH).contains("too many")) { } 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")) { } else if (e.getData().toLowerCase(Locale.ENGLISH).contains("insufficient")) {
throw new QueryTooShortException(); throw new Keyserver.QueryTooShortException();
} else if (e.getCode() == 404) { } else if (e.getCode() == 404) {
// NOTE: handle this 404 at last, maybe it was a "no keys found" error // 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 { } else {
// NOTE: some keyserver do not provide a more detailed error response // 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); final Matcher matcher = PUB_KEY_LINE.matcher(data);
while (matcher.find()) { while (matcher.find()) {
final ImportKeysListEntry entry = new ImportKeysListEntry(); final ImportKeysListEntry entry = new ImportKeysListEntry();
entry.setQuery(query); 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 // 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 // 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 @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; 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; String data;
try { try {
data = query(request, mProxy); data = query(request, proxy);
} catch (HttpError httpError) { } catch (HttpError httpError) {
Log.d(Constants.TAG, "Failed to get key at HkpKeyserver", 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) { if (data == null) {
throw new QueryFailedException("data is null"); throw new Keyserver.QueryFailedException("data is null");
} }
Matcher matcher = PgpHelper.PGP_PUBLIC_KEY.matcher(data); Matcher matcher = PgpHelper.PGP_PUBLIC_KEY.matcher(data);
if (matcher.find()) { if (matcher.find()) {
return matcher.group(1); return matcher.group(1);
} }
throw new QueryFailedException("data is null"); throw new Keyserver.QueryFailedException("data is null");
} }
@Override @Override
public void add(String armoredKey) throws AddKeyException { public void add(String armoredKey, ParcelableProxy proxy) throws Keyserver.AddKeyException {
try { try {
String path = "/pks/add"; String path = "/pks/add";
String params; String params;
try { try {
params = "keytext=" + URLEncoder.encode(armoredKey, "UTF-8"); params = "keytext=" + URLEncoder.encode(armoredKey, "UTF-8");
} catch (UnsupportedEncodingException e) { } 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, "hkp keyserver add: " + url);
Log.d(Constants.TAG, "params: " + params); Log.d(Constants.TAG, "params: " + params);
@@ -396,27 +390,40 @@ public class HkpKeyserver extends Keyserver {
.post(body) .post(body)
.build(); .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, "response code: " + response.code());
Log.d(Constants.TAG, "answer: " + response.body().string()); Log.d(Constants.TAG, "answer: " + response.body().string());
if (response.code() != 200) { if (response.code() != 200) {
throw new AddKeyException(); throw new Keyserver.AddKeyException();
} }
} catch (IOException e) { } catch (IOException e) {
Log.e(Constants.TAG, "IOException", e); Log.e(Constants.TAG, "IOException", e);
throw new AddKeyException(); throw new Keyserver.AddKeyException();
} catch (TlsHelper.TlsHelperException e) { } catch (TlsHelper.TlsHelperException e) {
Log.e(Constants.TAG, "Exception in pinning certs", 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 @Override
public String toString() { 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. * @return A responsible Keyserver or null if not found.
*/ */
public static HkpKeyserver resolve(String domain, Proxy proxy) { public static ParcelableHkpKeyserver resolve(String domain) {
try { try {
Record[] records = new Client().query(new Question("_hkp._tcp." + domain, Record.TYPE.SRV)).getAnswers(); Record[] records = new Client().query(new Question("_hkp._tcp." + domain, Record.TYPE.SRV)).getAnswers();
if (records.length > 0) { if (records.length > 0) {
@@ -438,12 +445,60 @@ public class HkpKeyserver extends Keyserver {
}); });
Record record = records[0]; // This is our best choice Record record = records[0]; // This is our best choice
if (record.getPayload().getType() == Record.TYPE.SRV) { if (record.getPayload().getType() == Record.TYPE.SRV) {
return new HkpKeyserver(((SRV) record.getPayload()).getName(), SRV payload = (SRV) record.getPayload();
(short) ((SRV) record.getPayload()).getPort(), proxy); return new ParcelableHkpKeyserver(payload.getName() + ":" + payload.getPort());
} }
} }
} catch (Exception ignored) { } catch (Exception ignored) {
} }
return null; 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<ParcelableHkpKeyserver> CREATOR = new Creator<ParcelableHkpKeyserver>() {
@Override
public ParcelableHkpKeyserver createFromParcel(Parcel in) {
return new ParcelableHkpKeyserver(in);
}
@Override
public ParcelableHkpKeyserver[] newArray(int size) {
return new ParcelableHkpKeyserver[size];
}
};
} }

View File

@@ -39,9 +39,9 @@ import android.support.annotation.NonNull;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.FacebookKeyserver; import org.sufficientlysecure.keychain.keyimport.FacebookKeyserver;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver; import org.sufficientlysecure.keychain.keyimport.KeybaseKeyserver;
import org.sufficientlysecure.keychain.keyimport.Keyserver; import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult; import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; 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.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache; import org.sufficientlysecure.keychain.util.ParcelableFileCache;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.ProgressScaler;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
@@ -83,7 +84,7 @@ import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
*/ */
public class ImportOperation extends BaseOperation<ImportKeyringParcel> { public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
public static final int MAX_THREADS = 10; private static final int MAX_THREADS = 10;
public ImportOperation(Context context, ProviderHelper providerHelper, Progressable public ImportOperation(Context context, ProviderHelper providerHelper, Progressable
progressable) { progressable) {
@@ -97,20 +98,20 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
// Overloaded functions for using progressable supplied in constructor during import // Overloaded functions for using progressable supplied in constructor during import
public ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num, public ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
String keyServerUri, Proxy proxy) { ParcelableHkpKeyserver hkpKeyserver, ParcelableProxy proxy) {
return serialKeyRingImport(entries, num, keyServerUri, mProgressable, proxy); return serialKeyRingImport(entries, num, hkpKeyserver, mProgressable, proxy);
} }
@NonNull @NonNull
private ImportKeyResult serialKeyRingImport(ParcelableFileCache<ParcelableKeyRing> cache, private ImportKeyResult serialKeyRingImport(ParcelableFileCache<ParcelableKeyRing> cache,
String keyServerUri, Proxy proxy) { ParcelableHkpKeyserver hkpKeyserver, ParcelableProxy proxy) {
// get entries from cached file // get entries from cached file
try { try {
IteratorWithSize<ParcelableKeyRing> it = cache.readCache(); IteratorWithSize<ParcelableKeyRing> it = cache.readCache();
int numEntries = it.getSize(); int numEntries = it.getSize();
return serialKeyRingImport(it, numEntries, keyServerUri, mProgressable, proxy); return serialKeyRingImport(it, numEntries, hkpKeyserver, mProgressable, proxy);
} catch (IOException e) { } catch (IOException e) {
// Special treatment here, we need a lot // Special treatment here, we need a lot
@@ -129,14 +130,14 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
* *
* @param entries keys to import * @param entries keys to import
* @param num number of 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 * @param progressable Allows multi-threaded import to supply a progressable that ignores the
* progress of a single key being imported * progress of a single key being imported
*/ */
@NonNull @NonNull
private ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num, private ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
String keyServerUri, Progressable progressable, ParcelableHkpKeyserver hkpKeyserver, Progressable progressable,
@NonNull Proxy proxy) { @NonNull ParcelableProxy proxy) {
if (progressable != null) { if (progressable != null) {
progressable.setProgress(R.string.progress_importing, 0, 100); progressable.setProgress(R.string.progress_importing, 0, 100);
} }
@@ -158,7 +159,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
KeybaseKeyserver keybaseServer = null; KeybaseKeyserver keybaseServer = null;
FacebookKeyserver facebookServer = null; FacebookKeyserver facebookServer = null;
HkpKeyserver keyServer = null; ParcelableHkpKeyserver keyServer = null;
// iterate over all entries // iterate over all entries
while (entries.hasNext()) { while (entries.hasNext()) {
@@ -187,12 +188,12 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
// If we have a keyServerUri and a fingerprint or at least a keyId, // If we have a keyServerUri and a fingerprint or at least a keyId,
// download from HKP // download from HKP
if (keyServerUri != null if (hkpKeyserver != null
&& (entry.mKeyIdHex != null || entry.mExpectedFingerprint != null)) { && (entry.mKeyIdHex != null || entry.mExpectedFingerprint != null)) {
// Make sure we have the keyserver instance cached // Make sure we have the keyserver instance cached
if (keyServer == null) { if (keyServer == null) {
log.add(LogType.MSG_IMPORT_KEYSERVER, 1, keyServerUri); log.add(LogType.MSG_IMPORT_KEYSERVER, 1, hkpKeyserver);
keyServer = new HkpKeyserver(keyServerUri, proxy); keyServer = hkpKeyserver;
} }
try { try {
@@ -201,10 +202,10 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
if (entry.mExpectedFingerprint != null) { if (entry.mExpectedFingerprint != null) {
log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, "0x" + log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, "0x" +
entry.mExpectedFingerprint.substring(24)); entry.mExpectedFingerprint.substring(24));
data = keyServer.get("0x" + entry.mExpectedFingerprint).getBytes(); data = keyServer.get("0x" + entry.mExpectedFingerprint, proxy).getBytes();
} else { } else {
log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, entry.mKeyIdHex); 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); key = UncachedKeyRing.decodeFromData(data);
if (key != null) { if (key != null) {
@@ -222,12 +223,12 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
if (entry.mKeybaseName != null) { if (entry.mKeybaseName != null) {
// Make sure we have this cached // Make sure we have this cached
if (keybaseServer == null) { if (keybaseServer == null) {
keybaseServer = new KeybaseKeyserver(proxy); keybaseServer = new KeybaseKeyserver();
} }
try { try {
log.add(LogType.MSG_IMPORT_FETCH_KEYBASE, 2, entry.mKeybaseName); 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); UncachedKeyRing keybaseKey = UncachedKeyRing.decodeFromData(data);
if (keybaseKey != null) { if (keybaseKey != null) {
@@ -260,12 +261,12 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
if (entry.mFbUsername != null) { if (entry.mFbUsername != null) {
// Make sure we have this cached // Make sure we have this cached
if (facebookServer == null) { if (facebookServer == null) {
facebookServer = new FacebookKeyserver(proxy); facebookServer = new FacebookKeyserver();
} }
try { try {
log.add(LogType.MSG_IMPORT_FETCH_FACEBOOK, 2, entry.mFbUsername); 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); UncachedKeyRing facebookKey = UncachedKeyRing.decodeFromData(data);
if (facebookKey != null) { if (facebookKey != null) {
@@ -425,7 +426,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
@Override @Override
public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) { public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) {
ArrayList<ParcelableKeyRing> keyList = importInput.mKeyList; ArrayList<ParcelableKeyRing> keyList = importInput.mKeyList;
String keyServer = importInput.mKeyserver; ParcelableHkpKeyserver keyServer = importInput.mKeyserver;
ImportKeyResult result; ImportKeyResult result;
@@ -435,7 +436,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
result = serialKeyRingImport(cache, null, null); result = serialKeyRingImport(cache, null, null);
} else { } else {
Proxy proxy; ParcelableProxy proxy;
if (cryptoInput.getParcelableProxy() == null) { if (cryptoInput.getParcelableProxy() == null) {
// explicit proxy not set // explicit proxy not set
if(!OrbotHelper.isOrbotInRequiredState(mContext)) { if(!OrbotHelper.isOrbotInRequiredState(mContext)) {
@@ -443,9 +444,9 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
return new ImportKeyResult(null, return new ImportKeyResult(null,
RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput); RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput);
} }
proxy = Preferences.getPreferences(mContext).getProxyPrefs().getProxy(); proxy = Preferences.getPreferences(mContext).getParcelableProxy();
} else { } else {
proxy = cryptoInput.getParcelableProxy().getProxy(); proxy = cryptoInput.getParcelableProxy();
} }
result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer, proxy); result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer, proxy);
@@ -457,8 +458,8 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
@NonNull @NonNull
private ImportKeyResult multiThreadedKeyImport(@NonNull Iterator<ParcelableKeyRing> keyListIterator, private ImportKeyResult multiThreadedKeyImport(@NonNull Iterator<ParcelableKeyRing> keyListIterator,
int totKeys, final String keyServer, int totKeys, final ParcelableHkpKeyserver hkpKeyserver,
final Proxy proxy) { final ParcelableProxy proxy) {
Log.d(Constants.TAG, "Multi-threaded key import starting"); Log.d(Constants.TAG, "Multi-threaded key import starting");
KeyImportAccumulator accumulator = new KeyImportAccumulator(totKeys, mProgressable); KeyImportAccumulator accumulator = new KeyImportAccumulator(totKeys, mProgressable);
@@ -487,7 +488,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
ArrayList<ParcelableKeyRing> list = new ArrayList<>(); ArrayList<ParcelableKeyRing> list = new ArrayList<>();
list.add(pkRing); list.add(pkRing);
return serialKeyRingImport(list.iterator(), 1, keyServer, ignoreProgressable, proxy); return serialKeyRingImport(list.iterator(), 1, hkpKeyserver, ignoreProgressable, proxy);
} }
}; };

View File

@@ -75,8 +75,7 @@ public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificat
return new KeybaseVerificationResult(null, return new KeybaseVerificationResult(null,
RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput); RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput);
} }
proxy = Preferences.getPreferences(mContext).getProxyPrefs() proxy = Preferences.getPreferences(mContext).getParcelableProxy().getProxy();
.parcelableProxy.getProxy();
} else { } else {
proxy = cryptoInput.getParcelableProxy().getProxy(); proxy = cryptoInput.getParcelableProxy().getProxy();
} }

View File

@@ -20,11 +20,6 @@
package org.sufficientlysecure.keychain.operations; package org.sufficientlysecure.keychain.operations;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.Proxy;
import java.util.concurrent.atomic.AtomicBoolean;
import android.content.Context; import android.content.Context;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@@ -32,8 +27,8 @@ import android.support.annotation.Nullable;
import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException; import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
import org.sufficientlysecure.keychain.operations.results.UploadResult; import org.sufficientlysecure.keychain.operations.results.UploadResult;
@@ -50,9 +45,13 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.Preferences.ProxyPrefs;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.Proxy;
import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* An operation class which implements the upload of a single key to a key server. * An operation class which implements the upload of a single key to a key server.
@@ -60,7 +59,7 @@ import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
public class UploadOperation extends BaseOperation<UploadKeyringParcel> { public class UploadOperation extends BaseOperation<UploadKeyringParcel> {
public UploadOperation(Context context, ProviderHelper providerHelper, public UploadOperation(Context context, ProviderHelper providerHelper,
Progressable progressable, AtomicBoolean cancelled) { Progressable progressable, AtomicBoolean cancelled) {
super(context, providerHelper, progressable, cancelled); super(context, providerHelper, progressable, cancelled);
} }
@@ -71,41 +70,32 @@ public class UploadOperation extends BaseOperation<UploadKeyringParcel> {
log.add(LogType.MSG_UPLOAD, 0); log.add(LogType.MSG_UPLOAD, 0);
updateProgress(R.string.progress_uploading, 0, 1); updateProgress(R.string.progress_uploading, 0, 1);
Proxy proxy; // Proxy priorities:
{ // 1. explicit proxy
boolean proxyIsTor = false; // 2. orbot proxy state
// 3. proxy from preferences
// Proxy priorities: ParcelableProxy parcelableProxy = cryptoInput.getParcelableProxy();
// 1. explicit proxy if (parcelableProxy == null) {
// 2. orbot proxy state if (!OrbotHelper.isOrbotInRequiredState(mContext)) {
// 3. proxy from preferences return new UploadResult(log, RequiredInputParcel.createOrbotRequiredOperation(), cryptoInput);
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());
} }
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()); log.add(LogType.MSG_UPLOAD_SERVER, 1, hkpKeyserver.toString());
} }
@@ -114,7 +104,7 @@ public class UploadOperation extends BaseOperation<UploadKeyringParcel> {
return new UploadResult(UploadResult.RESULT_ERROR, log); return new UploadResult(UploadResult.RESULT_ERROR, log);
} }
return uploadKeyRingToServer(log, hkpKeyserver, keyring); return uploadKeyRingToServer(log, hkpKeyserver, keyring, parcelableProxy);
} }
@Nullable @Nullable
@@ -136,7 +126,7 @@ public class UploadOperation extends BaseOperation<UploadKeyringParcel> {
CanonicalizedKeyRing canonicalizedRing = CanonicalizedKeyRing canonicalizedRing =
UncachedKeyRing.decodeFromData(uploadInput.mUncachedKeyringBytes) UncachedKeyRing.decodeFromData(uploadInput.mUncachedKeyringBytes)
.canonicalize(new OperationLog(), 0, true); .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!"); throw new IllegalArgumentException("keyring bytes must contain public key ring!");
} }
log.add(LogType.MSG_UPLOAD_KEY, 0, KeyFormattingUtils.convertKeyIdToHex(canonicalizedRing.getMasterKeyId())); log.add(LogType.MSG_UPLOAD_KEY, 0, KeyFormattingUtils.convertKeyIdToHex(canonicalizedRing.getMasterKeyId()));
@@ -155,7 +145,8 @@ public class UploadOperation extends BaseOperation<UploadKeyringParcel> {
@NonNull @NonNull
private UploadResult uploadKeyRingToServer( private UploadResult uploadKeyRingToServer(
OperationLog log, HkpKeyserver server, CanonicalizedPublicKeyRing keyring) { OperationLog log, ParcelableHkpKeyserver server, CanonicalizedPublicKeyRing keyring,
ParcelableProxy proxy) {
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = null; ArmoredOutputStream aos = null;
@@ -166,7 +157,7 @@ public class UploadOperation extends BaseOperation<UploadKeyringParcel> {
aos.close(); aos.close();
String armoredKey = bos.toString("UTF-8"); String armoredKey = bos.toString("UTF-8");
server.add(armoredKey); server.add(armoredKey, proxy);
updateProgress(R.string.progress_uploading, 1, 1); updateProgress(R.string.progress_uploading, 1, 1);

View File

@@ -24,11 +24,9 @@ import android.os.Parcelable;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Date;
import java.util.Map;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; 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<CertifyAction> mCertifyActions = new ArrayList<>(); public ArrayList<CertifyAction> mCertifyActions = new ArrayList<>();
public String keyServerUri; public ParcelableHkpKeyserver keyServerUri;
public CertifyActionsParcel(long masterKeyId) { public CertifyActionsParcel(long masterKeyId) {
mMasterKeyId = masterKeyId; mMasterKeyId = masterKeyId;
@@ -54,7 +52,7 @@ public class CertifyActionsParcel implements Parcelable {
mMasterKeyId = source.readLong(); mMasterKeyId = source.readLong();
// just like parcelables, this is meant for ad-hoc IPC only and is NOT portable! // just like parcelables, this is meant for ad-hoc IPC only and is NOT portable!
mLevel = CertifyLevel.values()[source.readInt()]; mLevel = CertifyLevel.values()[source.readInt()];
keyServerUri = source.readString(); keyServerUri = source.readParcelable(ParcelableHkpKeyserver.class.getClassLoader());
mCertifyActions = (ArrayList<CertifyAction>) source.readSerializable(); mCertifyActions = (ArrayList<CertifyAction>) source.readSerializable();
} }
@@ -67,7 +65,7 @@ public class CertifyActionsParcel implements Parcelable {
public void writeToParcel(Parcel destination, int flags) { public void writeToParcel(Parcel destination, int flags) {
destination.writeLong(mMasterKeyId); destination.writeLong(mMasterKeyId);
destination.writeInt(mLevel.ordinal()); destination.writeInt(mLevel.ordinal());
destination.writeString(keyServerUri); destination.writeParcelable(keyServerUri, flags);
destination.writeSerializable(mCertifyActions); destination.writeSerializable(mCertifyActions);
} }

View File

@@ -20,16 +20,18 @@ package org.sufficientlysecure.keychain.service;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import java.util.ArrayList; import java.util.ArrayList;
public class ImportKeyringParcel implements Parcelable { public class ImportKeyringParcel implements Parcelable {
// if null, keys are expected to be read from a cache file in ImportExportOperations // if null, keys are expected to be read from a cache file in ImportExportOperations
public ArrayList<ParcelableKeyRing> mKeyList; public ArrayList<ParcelableKeyRing> 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<ParcelableKeyRing> keyList, String keyserver) { public ImportKeyringParcel(ArrayList<ParcelableKeyRing> keyList, ParcelableHkpKeyserver keyserver) {
mKeyList = keyList; mKeyList = keyList;
mKeyserver = keyserver; mKeyserver = keyserver;
} }
@@ -41,7 +43,7 @@ public class ImportKeyringParcel implements Parcelable {
} else { } else {
mKeyList = null; mKeyList = null;
} }
mKeyserver = in.readString(); mKeyserver = in.readParcelable(ParcelableHkpKeyserver.class.getClassLoader());
} }
@Override @Override
@@ -57,7 +59,7 @@ public class ImportKeyringParcel implements Parcelable {
dest.writeByte((byte) (0x01)); dest.writeByte((byte) (0x01));
dest.writeList(mKeyList); dest.writeList(mKeyList);
} }
dest.writeString(mKeyserver); dest.writeParcelable(mKeyserver, flags);
} }
public static final Parcelable.Creator<ImportKeyringParcel> CREATOR = new Parcelable.Creator<ImportKeyringParcel>() { public static final Parcelable.Creator<ImportKeyringParcel> CREATOR = new Parcelable.Creator<ImportKeyringParcel>() {

View File

@@ -11,7 +11,6 @@ import android.content.ContentProviderClient;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.PeriodicSync;
import android.content.SyncResult; import android.content.SyncResult;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@@ -308,7 +307,7 @@ public class KeyserverSyncAdapterService extends Service {
if (cryptoInputParcel.getParcelableProxy() == null) { if (cryptoInputParcel.getParcelableProxy() == null) {
// no explicit proxy, retrieve from preferences. Check if we should do a staggered sync // 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); return staggeredUpdate(context, keyList, cryptoInputParcel);
} else { } else {
return directUpdate(context, keyList, cryptoInputParcel); return directUpdate(context, keyList, cryptoInputParcel);

View File

@@ -22,13 +22,15 @@ package org.sufficientlysecure.keychain.service;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
public class RevokeKeyringParcel implements Parcelable { public class RevokeKeyringParcel implements Parcelable {
final public long mMasterKeyId; final public long mMasterKeyId;
final public boolean mUpload; 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; mMasterKeyId = masterKeyId;
mUpload = upload; mUpload = upload;
mKeyserver = keyserver; mKeyserver = keyserver;
@@ -37,7 +39,7 @@ public class RevokeKeyringParcel implements Parcelable {
protected RevokeKeyringParcel(Parcel in) { protected RevokeKeyringParcel(Parcel in) {
mMasterKeyId = in.readLong(); mMasterKeyId = in.readLong();
mUpload = in.readByte() != 0x00; mUpload = in.readByte() != 0x00;
mKeyserver = in.readString(); mKeyserver = in.readParcelable(ParcelableHkpKeyserver.class.getClassLoader());
} }
@Override @Override
@@ -49,7 +51,7 @@ public class RevokeKeyringParcel implements Parcelable {
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mMasterKeyId); dest.writeLong(mMasterKeyId);
dest.writeByte((byte) (mUpload ? 0x01 : 0x00)); dest.writeByte((byte) (mUpload ? 0x01 : 0x00));
dest.writeString(mKeyserver); dest.writeParcelable(mKeyserver, flags);
} }
public static final Parcelable.Creator<RevokeKeyringParcel> CREATOR = new Parcelable.Creator<RevokeKeyringParcel>() { public static final Parcelable.Creator<RevokeKeyringParcel> CREATOR = new Parcelable.Creator<RevokeKeyringParcel>() {

View File

@@ -22,6 +22,7 @@ import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
import java.io.Serializable; import java.io.Serializable;
@@ -66,7 +67,7 @@ public class SaveKeyringParcel implements Parcelable {
// private because they have to be set together with setUpdateOptions // private because they have to be set together with setUpdateOptions
private boolean mUpload; private boolean mUpload;
private boolean mUploadAtomic; private boolean mUploadAtomic;
private String mKeyserver; private ParcelableHkpKeyserver mKeyserver;
// private because we have to set other details like key id // private because we have to set other details like key id
private ChangeUnlockParcel mNewUnlock; private ChangeUnlockParcel mNewUnlock;
@@ -97,10 +98,10 @@ public class SaveKeyringParcel implements Parcelable {
mKeyserver = null; mKeyserver = null;
} }
public void setUpdateOptions(boolean upload, boolean uploadAtomic, String keysever) { public void setUpdateOptions(boolean upload, boolean uploadAtomic, ParcelableHkpKeyserver keyserver) {
mUpload = upload; mUpload = upload;
mUploadAtomic = uploadAtomic; mUploadAtomic = uploadAtomic;
mKeyserver = keysever; mKeyserver = keyserver;
} }
public void setNewUnlock(ChangeUnlockParcel parcel) { public void setNewUnlock(ChangeUnlockParcel parcel) {
@@ -123,7 +124,7 @@ public class SaveKeyringParcel implements Parcelable {
return mUploadAtomic; return mUploadAtomic;
} }
public String getUploadKeyserver() { public ParcelableHkpKeyserver getUploadKeyserver() {
return mKeyserver; return mKeyserver;
} }
@@ -278,7 +279,7 @@ public class SaveKeyringParcel implements Parcelable {
mUpload = source.readByte() != 0; mUpload = source.readByte() != 0;
mUploadAtomic = source.readByte() != 0; mUploadAtomic = source.readByte() != 0;
mKeyserver = source.readString(); mKeyserver = source.readParcelable(ParcelableHkpKeyserver.class.getClassLoader());
} }
@Override @Override
@@ -307,7 +308,7 @@ public class SaveKeyringParcel implements Parcelable {
destination.writeByte((byte) (mUpload ? 1 : 0)); destination.writeByte((byte) (mUpload ? 1 : 0));
destination.writeByte((byte) (mUploadAtomic ? 1 : 0)); destination.writeByte((byte) (mUploadAtomic ? 1 : 0));
destination.writeString(mKeyserver); destination.writeParcelable(mKeyserver, flags);
} }
public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() { public static final Creator<SaveKeyringParcel> CREATOR = new Creator<SaveKeyringParcel>() {

View File

@@ -23,27 +23,29 @@ package org.sufficientlysecure.keychain.service;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
public class UploadKeyringParcel implements Parcelable { public class UploadKeyringParcel implements Parcelable {
public String mKeyserver; public ParcelableHkpKeyserver mKeyserver;
public final Long mMasterKeyId; public final Long mMasterKeyId;
public final byte[] mUncachedKeyringBytes; public final byte[] mUncachedKeyringBytes;
public UploadKeyringParcel(String keyserver, long masterKeyId) { public UploadKeyringParcel(ParcelableHkpKeyserver keyserver, long masterKeyId) {
mKeyserver = keyserver; mKeyserver = keyserver;
mMasterKeyId = masterKeyId; mMasterKeyId = masterKeyId;
mUncachedKeyringBytes = null; mUncachedKeyringBytes = null;
} }
public UploadKeyringParcel(String keyserver, byte[] uncachedKeyringBytes) { public UploadKeyringParcel(ParcelableHkpKeyserver keyserver, byte[] uncachedKeyringBytes) {
mKeyserver = keyserver; mKeyserver = keyserver;
mMasterKeyId = null; mMasterKeyId = null;
mUncachedKeyringBytes = uncachedKeyringBytes; mUncachedKeyringBytes = uncachedKeyringBytes;
} }
protected UploadKeyringParcel(Parcel in) { protected UploadKeyringParcel(Parcel in) {
mKeyserver = in.readString(); mKeyserver = in.readParcelable(ParcelableHkpKeyserver.class.getClassLoader());
mMasterKeyId = in.readInt() != 0 ? in.readLong() : null; mMasterKeyId = in.readInt() != 0 ? in.readLong() : null;
mUncachedKeyringBytes = in.createByteArray(); mUncachedKeyringBytes = in.createByteArray();
} }
@@ -55,7 +57,7 @@ public class UploadKeyringParcel implements Parcelable {
@Override @Override
public void writeToParcel(Parcel dest, int flags) { public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mKeyserver); dest.writeParcelable(mKeyserver, flags);
if (mMasterKeyId != null) { if (mMasterKeyId != null) {
dest.writeInt(1); dest.writeInt(1);
dest.writeLong(mMasterKeyId); dest.writeLong(mMasterKeyId);

View File

@@ -53,6 +53,7 @@ import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
@@ -501,7 +502,7 @@ public class CreateKeyFinalFragment extends Fragment {
// set data uri as path to keyring // set data uri as path to keyring
final long masterKeyId = saveKeyResult.mMasterKeyId; final long masterKeyId = saveKeyResult.mMasterKeyId;
// upload to favorite keyserver // upload to favorite keyserver
final String keyserver = Preferences.getPreferences(activity).getPreferredKeyserver(); final ParcelableHkpKeyserver keyserver = Preferences.getPreferences(activity).getPreferredKeyserver();
CryptoOperationHelper.Callback<UploadKeyringParcel, UploadResult> callback CryptoOperationHelper.Callback<UploadKeyringParcel, UploadResult> callback
= new CryptoOperationHelper.Callback<UploadKeyringParcel, UploadResult>() { = new CryptoOperationHelper.Callback<UploadKeyringParcel, UploadResult>() {

View File

@@ -45,6 +45,7 @@ import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.SecurityTokenListenerFragment; import org.sufficientlysecure.keychain.ui.CreateKeyActivity.SecurityTokenListenerFragment;
import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment; import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
@@ -73,7 +74,7 @@ public class CreateSecurityTokenImportResetFragment
private View mResetWarning; private View mResetWarning;
// for CryptoOperationFragment key import // for CryptoOperationFragment key import
private String mKeyserver; private ParcelableHkpKeyserver mKeyserver;
private ArrayList<ParcelableKeyRing> mKeyList; private ArrayList<ParcelableKeyRing> mKeyList;
public static Fragment newInstance(byte[] scannedFingerprints, byte[] nfcAid, String userId) { public static Fragment newInstance(byte[] scannedFingerprints, byte[] nfcAid, String userId) {

View File

@@ -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.KeyFormattingUtils.State;
import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
public abstract class DecryptFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> { public abstract class DecryptFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
@@ -137,7 +138,7 @@ public abstract class DecryptFragment extends Fragment implements LoaderManager.
private void lookupUnknownKey(long unknownKeyId) { private void lookupUnknownKey(long unknownKeyId) {
final ArrayList<ParcelableKeyRing> keyList; final ArrayList<ParcelableKeyRing> keyList;
final String keyserver; final ParcelableHkpKeyserver keyserver;
// search config // search config
keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver(); keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();

View File

@@ -94,6 +94,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.FileHelper;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableHashMap; import org.sufficientlysecure.keychain.util.ParcelableHashMap;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
@@ -759,7 +760,7 @@ public class DecryptListFragment
private void lookupUnknownKey(final Uri inputUri, long unknownKeyId) { private void lookupUnknownKey(final Uri inputUri, long unknownKeyId) {
final ArrayList<ParcelableKeyRing> keyList; final ArrayList<ParcelableKeyRing> keyList;
final String keyserver; final ParcelableHkpKeyserver keyserver;
// search config // search config
keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver(); keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver();

View File

@@ -38,6 +38,7 @@ import android.widget.TextView;
import org.openintents.openpgp.util.OpenPgpUtils; import org.openintents.openpgp.util.OpenPgpUtils;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.operations.results.DeleteResult; import org.sufficientlysecure.keychain.operations.results.DeleteResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.RevokeResult; import org.sufficientlysecure.keychain.operations.results.RevokeResult;
@@ -154,7 +155,7 @@ public class DeleteKeyDialogActivity extends FragmentActivity {
@Override @Override
public RevokeKeyringParcel createOperationInput() { public RevokeKeyringParcel createOperationInput() {
return new RevokeKeyringParcel(mMasterKeyIds[0], true, return new RevokeKeyringParcel(mMasterKeyIds[0], true,
getIntent().getStringExtra(EXTRA_KEYSERVER)); (ParcelableHkpKeyserver) getIntent().getParcelableExtra(EXTRA_KEYSERVER));
} }
@Override @Override

View File

@@ -62,6 +62,7 @@ import org.sufficientlysecure.keychain.ui.dialog.AddUserIdDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.EditUserIdDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
public class EditIdentitiesFragment extends Fragment public class EditIdentitiesFragment extends Fragment
@@ -387,7 +388,7 @@ public class EditIdentitiesFragment extends Fragment
final long masterKeyId = editKeyResult.mMasterKeyId; final long masterKeyId = editKeyResult.mMasterKeyId;
// upload to favorite keyserver // upload to favorite keyserver
final String keyserver = Preferences.getPreferences(activity).getPreferredKeyserver(); final ParcelableHkpKeyserver keyserver = Preferences.getPreferences(activity).getPreferredKeyserver();
CryptoOperationHelper.Callback<UploadKeyringParcel, UploadResult> callback CryptoOperationHelper.Callback<UploadKeyringParcel, UploadResult> callback
= new CryptoOperationHelper.Callback<UploadKeyringParcel, UploadResult>() { = new CryptoOperationHelper.Callback<UploadKeyringParcel, UploadResult>() {

View File

@@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache; import org.sufficientlysecure.keychain.util.ParcelableFileCache;
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import java.io.IOException; import java.io.IOException;
@@ -79,7 +80,7 @@ public class ImportKeysActivity extends BaseActivity
public static final String TAG_FRAG_TOP = "frag_top"; public static final String TAG_FRAG_TOP = "frag_top";
// for CryptoOperationHelper.Callback // for CryptoOperationHelper.Callback
private String mKeyserver; private ParcelableHkpKeyserver mKeyserver;
private ArrayList<ParcelableKeyRing> mKeyList; private ArrayList<ParcelableKeyRing> mKeyList;
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper; private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> 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.create(this, R.string.import_url_warn_no_search_parameter, Notify.LENGTH_INDEFINITE,
Notify.Style.WARN).show(); Notify.Style.WARN).show();
} }
ParcelableHkpKeyserver keyserver = new ParcelableHkpKeyserver(dataUri.getAuthority());
Preferences.CloudSearchPrefs cloudSearchPrefs = new Preferences.CloudSearchPrefs( 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 // we allow our users to edit the query if they wish
startTopCloudFragment(query, false, cloudSearchPrefs); startTopCloudFragment(query, false, cloudSearchPrefs);
// search immediately (if query is not null) // search immediately (if query is not null)

View File

@@ -43,6 +43,7 @@ import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4; import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList; import java.util.ArrayList;
@@ -62,7 +63,7 @@ public class ImportKeysProxyActivity extends FragmentActivity
public static final String EXTRA_FINGERPRINT = "fingerprint"; public static final String EXTRA_FINGERPRINT = "fingerprint";
// for CryptoOperationHelper // for CryptoOperationHelper
private String mKeyserver; private ParcelableHkpKeyserver mKeyserver;
private ArrayList<ParcelableKeyRing> mKeyList; private ArrayList<ParcelableKeyRing> mKeyList;
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper; private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;

View File

@@ -76,6 +76,7 @@ import org.sufficientlysecure.keychain.ui.util.ContentDescriptionHint;
import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.FabContainer; import org.sufficientlysecure.keychain.util.FabContainer;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
import se.emilsjolander.stickylistheaders.StickyListHeadersListView; import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
@@ -111,7 +112,7 @@ public class KeyListFragment extends LoaderFragment
// for CryptoOperationHelper import // for CryptoOperationHelper import
private ArrayList<ParcelableKeyRing> mKeyList; private ArrayList<ParcelableKeyRing> mKeyList;
private String mKeyserver; private ParcelableHkpKeyserver mKeyserver;
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper; private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;
// for ConsolidateOperation // for ConsolidateOperation

View File

@@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableFileCache; import org.sufficientlysecure.keychain.util.ParcelableFileCache;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@@ -61,7 +62,7 @@ public class SafeSlingerActivity extends BaseActivity
// for CryptoOperationHelper // for CryptoOperationHelper
private ArrayList<ParcelableKeyRing> mKeyList; private ArrayList<ParcelableKeyRing> mKeyList;
private String mKeyserver; private ParcelableHkpKeyserver mKeyserver;
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper; private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;

View File

@@ -53,10 +53,12 @@ import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
import org.sufficientlysecure.keychain.ui.base.BaseActivity; import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.ThemeChanger; import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
import java.util.ArrayList;
import java.util.List; import java.util.List;
public class SettingsActivity extends AppCompatPreferenceActivity { public class SettingsActivity extends AppCompatPreferenceActivity {
@@ -180,9 +182,9 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
} }
public static String keyserverSummary(Context context) { public static String keyserverSummary(Context context) {
String[] servers = sPreferences.getKeyServers(); ArrayList<ParcelableHkpKeyserver> servers = sPreferences.getKeyServers();
String serverSummary = context.getResources().getQuantityString( 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 return serverSummary + "; " + context.getString(R.string.label_preferred) + ": " + sPreferences
.getPreferredKeyserver(); .getPreferredKeyserver();
} }

View File

@@ -22,8 +22,11 @@ import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.ui.base.BaseActivity; import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import java.util.ArrayList;
public class SettingsKeyServerActivity extends BaseActivity { public class SettingsKeyServerActivity extends BaseActivity {
public static final String EXTRA_KEY_SERVERS = "key_servers"; public static final String EXTRA_KEY_SERVERS = "key_servers";
@@ -33,7 +36,7 @@ public class SettingsKeyServerActivity extends BaseActivity {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
Intent intent = getIntent(); Intent intent = getIntent();
String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS); ArrayList<ParcelableHkpKeyserver> servers = intent.getParcelableArrayListExtra(EXTRA_KEY_SERVERS);
loadFragment(savedInstanceState, servers); loadFragment(savedInstanceState, servers);
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@@ -55,7 +58,7 @@ public class SettingsKeyServerActivity extends BaseActivity {
setContentView(R.layout.key_server_preference); setContentView(R.layout.key_server_preference);
} }
private void loadFragment(Bundle savedInstanceState, String[] keyservers) { private void loadFragment(Bundle savedInstanceState, ArrayList<ParcelableHkpKeyserver> keyservers) {
// However, if we're being restored from a previous state, // However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else // then we don't need to do anything and should return or else
// we could end up with overlapping fragments. // we could end up with overlapping fragments.

View File

@@ -18,7 +18,6 @@
package org.sufficientlysecure.keychain.ui; package org.sufficientlysecure.keychain.ui;
import android.graphics.Color;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
@@ -41,29 +40,28 @@ import android.widget.TextView;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.ui.dialog.AddEditKeyserverDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.AddEditKeyserverDialogFragment;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils; 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.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.ui.util.recyclerview.RecyclerItemClickListener;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List;
public class SettingsKeyserverFragment extends Fragment implements RecyclerItemClickListener.OnItemClickListener { public class SettingsKeyserverFragment extends Fragment implements RecyclerItemClickListener.OnItemClickListener {
private static final String ARG_KEYSERVER_ARRAY = "arg_keyserver_array"; private static final String ARG_KEYSERVER_ARRAY = "arg_keyserver_array";
private ItemTouchHelper mItemTouchHelper; private ItemTouchHelper mItemTouchHelper;
private ArrayList<String> mKeyservers; private ArrayList<ParcelableHkpKeyserver> mKeyservers;
private KeyserverListAdapter mAdapter; private KeyserverListAdapter mAdapter;
public static SettingsKeyserverFragment newInstance(String[] keyservers) { public static SettingsKeyserverFragment newInstance(ArrayList<ParcelableHkpKeyserver> keyservers) {
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putStringArray(ARG_KEYSERVER_ARRAY, keyservers); args.putParcelableArrayList(ARG_KEYSERVER_ARRAY, keyservers);
SettingsKeyserverFragment fragment = new SettingsKeyserverFragment(); SettingsKeyserverFragment fragment = new SettingsKeyserverFragment();
fragment.setArguments(args); fragment.setArguments(args);
@@ -82,8 +80,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC
public void onViewCreated(View view, Bundle savedInstanceState) { public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState); super.onViewCreated(view, savedInstanceState);
String keyservers[] = getArguments().getStringArray(ARG_KEYSERVER_ARRAY); mKeyservers = getArguments().getParcelableArrayList(ARG_KEYSERVER_ARRAY);
mKeyservers = new ArrayList<>(Arrays.asList(keyservers));
mAdapter = new KeyserverListAdapter(mKeyservers); mAdapter = new KeyserverListAdapter(mKeyservers);
@@ -133,7 +130,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC
} }
private void startEditKeyserverDialog(AddEditKeyserverDialogFragment.DialogAction action, private void startEditKeyserverDialog(AddEditKeyserverDialogFragment.DialogAction action,
String keyserver, final int position) { ParcelableHkpKeyserver keyserver, final int position) {
Handler returnHandler = new Handler() { Handler returnHandler = new Handler() {
@Override @Override
public void handleMessage(Message message) { public void handleMessage(Message message) {
@@ -162,7 +159,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC
R.string.add_keyserver_without_verification, R.string.add_keyserver_without_verification,
Notify.Style.WARN).show(); Notify.Style.WARN).show();
} }
String keyserver = data.getString( ParcelableHkpKeyserver keyserver = data.getParcelable(
AddEditKeyserverDialogFragment.MESSAGE_KEYSERVER); AddEditKeyserverDialogFragment.MESSAGE_KEYSERVER);
AddEditKeyserverDialogFragment.DialogAction dialogAction AddEditKeyserverDialogFragment.DialogAction dialogAction
@@ -189,13 +186,13 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC
dialogFragment.show(getFragmentManager(), "addKeyserverDialog"); dialogFragment.show(getFragmentManager(), "addKeyserverDialog");
} }
private void addKeyserver(String keyserver) { private void addKeyserver(ParcelableHkpKeyserver keyserver) {
mKeyservers.add(keyserver); mKeyservers.add(keyserver);
mAdapter.notifyItemInserted(mKeyservers.size() - 1); mAdapter.notifyItemInserted(mKeyservers.size() - 1);
saveKeyserverList(); saveKeyserverList();
} }
private void editKeyserver(String newKeyserver, int position) { private void editKeyserver(ParcelableHkpKeyserver newKeyserver, int position) {
mKeyservers.set(position, newKeyserver); mKeyservers.set(position, newKeyserver);
mAdapter.notifyItemChanged(position); mAdapter.notifyItemChanged(position);
saveKeyserverList(); saveKeyserverList();
@@ -218,8 +215,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC
} }
private void saveKeyserverList() { private void saveKeyserverList() {
String servers[] = mKeyservers.toArray(new String[mKeyservers.size()]); Preferences.getPreferences(getActivity()).setKeyServers(mKeyservers);
Preferences.getPreferences(getActivity()).setKeyServers(servers);
} }
@Override @Override
@@ -231,9 +227,9 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC
public class KeyserverListAdapter extends RecyclerView.Adapter<KeyserverListAdapter.ViewHolder> public class KeyserverListAdapter extends RecyclerView.Adapter<KeyserverListAdapter.ViewHolder>
implements ItemTouchHelperAdapter { implements ItemTouchHelperAdapter {
private final List<String> mKeyservers; private final ArrayList<ParcelableHkpKeyserver> mKeyservers;
public KeyserverListAdapter(List<String> keyservers) { public KeyserverListAdapter(ArrayList<ParcelableHkpKeyserver> keyservers) {
mKeyservers = keyservers; mKeyservers = keyservers;
} }
@@ -246,7 +242,7 @@ public class SettingsKeyserverFragment extends Fragment implements RecyclerItemC
@Override @Override
public void onBindViewHolder(final ViewHolder holder, int position) { 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 // Start a drag whenever the handle view it touched
holder.dragHandleView.setOnTouchListener(new View.OnTouchListener() { holder.dragHandleView.setOnTouchListener(new View.OnTouchListener() {

View File

@@ -35,9 +35,12 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.service.UploadKeyringParcel; import org.sufficientlysecure.keychain.service.UploadKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.BaseActivity; import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import java.util.ArrayList;
/** /**
* Sends the selected public key to a keyserver * Sends the selected public key to a keyserver
*/ */
@@ -49,7 +52,7 @@ public class UploadKeyActivity extends BaseActivity
private Uri mDataUri; private Uri mDataUri;
// CryptoOperationHelper.Callback vars // CryptoOperationHelper.Callback vars
private String mKeyserver; private ParcelableHkpKeyserver mKeyserver;
private CryptoOperationHelper<UploadKeyringParcel, UploadResult> mUploadOpHelper; private CryptoOperationHelper<UploadKeyringParcel, UploadResult> mUploadOpHelper;
@Override @Override
@@ -64,8 +67,7 @@ public class UploadKeyActivity extends BaseActivity
mMultiUserIdsFragment.setCheckboxVisibility(false); mMultiUserIdsFragment.setCheckboxVisibility(false);
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
android.R.layout.simple_spinner_item, Preferences.getPreferences(this) android.R.layout.simple_spinner_item, getKeyserversArray()
.getKeyServers()
); );
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mKeyServerSpinner.setAdapter(adapter); mKeyServerSpinner.setAdapter(adapter);
@@ -91,6 +93,18 @@ public class UploadKeyActivity extends BaseActivity
} }
private String[] getKeyserversArray() {
ArrayList<ParcelableHkpKeyserver> 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 @Override
protected void initLayout() { protected void initLayout() {
setContentView(R.layout.upload_key_activity); setContentView(R.layout.upload_key_activity);
@@ -105,8 +119,7 @@ public class UploadKeyActivity extends BaseActivity
} }
private void uploadKey() { private void uploadKey() {
String server = (String) mKeyServerSpinner.getSelectedItem(); mKeyserver = (ParcelableHkpKeyserver) mKeyServerSpinner.getSelectedItem();
mKeyserver = server;
mUploadOpHelper = new CryptoOperationHelper<>(1, this, this, R.string.progress_uploading); mUploadOpHelper = new CryptoOperationHelper<>(1, this, this, R.string.progress_uploading);
mUploadOpHelper.cryptoOperation(); mUploadOpHelper.cryptoOperation();

View File

@@ -100,6 +100,7 @@ import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
import org.sufficientlysecure.keychain.util.ContactHelper; import org.sufficientlysecure.keychain.util.ContactHelper;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.NfcHelper; import org.sufficientlysecure.keychain.util.NfcHelper;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.util.Passphrase; import org.sufficientlysecure.keychain.util.Passphrase;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
@@ -128,7 +129,7 @@ public class ViewKeyActivity extends BaseSecurityTokenActivity implements
protected Uri mDataUri; protected Uri mDataUri;
// For CryptoOperationHelper.Callback // For CryptoOperationHelper.Callback
private String mKeyserver; private ParcelableHkpKeyserver mKeyserver;
private ArrayList<ParcelableKeyRing> mKeyList; private ArrayList<ParcelableKeyRing> mKeyList;
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper; private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mImportOpHelper;
private CryptoOperationHelper<ChangeUnlockParcel, EditKeyResult> mEditOpHelper; private CryptoOperationHelper<ChangeUnlockParcel, EditKeyResult> mEditOpHelper;

View File

@@ -182,13 +182,13 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements
} }
private void startSearch(final String fingerprint) { private void startSearch(final String fingerprint) {
final Preferences.ProxyPrefs proxyPrefs = final ParcelableProxy parcelableProxy =
Preferences.getPreferences(getActivity()).getProxyPrefs(); Preferences.getPreferences(getActivity()).getParcelableProxy();
OrbotHelper.DialogActions dialogActions = new OrbotHelper.DialogActions() { OrbotHelper.DialogActions dialogActions = new OrbotHelper.DialogActions() {
@Override @Override
public void onOrbotStarted() { public void onOrbotStarted() {
new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint); new DescribeKey(parcelableProxy).execute(fingerprint);
} }
@Override @Override
@@ -204,7 +204,7 @@ public class ViewKeyKeybaseFragment extends LoaderFragment implements
}; };
if (OrbotHelper.putOrbotInRequiredState(dialogActions, getActivity())) { if (OrbotHelper.putOrbotInRequiredState(dialogActions, getActivity())) {
new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint); new DescribeKey(parcelableProxy).execute(fingerprint);
} }
} }

View File

@@ -112,13 +112,12 @@ public class ImportKeysListCloudLoader
* Query keyserver * Query keyserver
*/ */
private void queryServer(boolean enforceFingerprint) { private void queryServer(boolean enforceFingerprint) {
ParcelableProxy parcelableProxy; ParcelableProxy proxy;
if (mParcelableProxy == null) { if (mParcelableProxy == null) {
// no explicit proxy specified, fetch from preferences // no explicit proxy specified, fetch from preferences
if (OrbotHelper.isOrbotInRequiredState(mContext)) { if (OrbotHelper.isOrbotInRequiredState(mContext)) {
parcelableProxy = Preferences.getPreferences(mContext).getProxyPrefs() proxy = Preferences.getPreferences(mContext).getParcelableProxy();
.parcelableProxy;
} else { } else {
// user needs to enable/install orbot // user needs to enable/install orbot
mEntryList.clear(); mEntryList.clear();
@@ -129,14 +128,14 @@ public class ImportKeysListCloudLoader
return; return;
} }
} else { } else {
parcelableProxy = mParcelableProxy; proxy = mParcelableProxy;
} }
try { try {
ArrayList<ImportKeysListEntry> searchResult = CloudSearch.search( ArrayList<ImportKeysListEntry> searchResult = CloudSearch.search(
mServerQuery, mServerQuery,
mCloudPrefs, mCloudPrefs,
parcelableProxy.getProxy() proxy
); );
mEntryList.clear(); mEntryList.clear();

View File

@@ -18,26 +18,20 @@
package org.sufficientlysecure.keychain.ui.dialog; 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.app.Activity;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AlertDialog;
import android.app.Dialog; import android.app.Dialog;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.net.Uri;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.os.Message; import android.os.Message;
import android.os.Messenger; import android.os.Messenger;
import android.os.RemoteException; import android.os.RemoteException;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.design.widget.TextInputLayout;
import android.support.v4.app.DialogFragment; import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@@ -50,18 +44,23 @@ import android.widget.EditText;
import android.widget.TextView; import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener; import android.widget.TextView.OnEditorActionListener;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.OkHttpClientFactory; import org.sufficientlysecure.keychain.util.OkHttpClientFactory;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import org.sufficientlysecure.keychain.util.TlsHelper; import org.sufficientlysecure.keychain.util.TlsHelper;
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; 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 { public class AddEditKeyserverDialogFragment extends DialogFragment implements OnEditorActionListener {
private static final String ARG_MESSENGER = "arg_messenger"; private static final String ARG_MESSENGER = "arg_messenger";
@@ -91,21 +90,22 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
EDIT EDIT
} }
public enum FailureReason { public enum VerifyReturn {
INVALID_URL, INVALID_URL,
CONNECTION_FAILED, CONNECTION_FAILED,
NO_PINNED_CERTIFICATE NO_PINNED_CERTIFICATE,
GOOD
} }
public static AddEditKeyserverDialogFragment newInstance(Messenger messenger, public static AddEditKeyserverDialogFragment newInstance(Messenger messenger,
DialogAction action, DialogAction action,
String keyserver, ParcelableHkpKeyserver keyserver,
int position) { int position) {
AddEditKeyserverDialogFragment frag = new AddEditKeyserverDialogFragment(); AddEditKeyserverDialogFragment frag = new AddEditKeyserverDialogFragment();
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putParcelable(ARG_MESSENGER, messenger); args.putParcelable(ARG_MESSENGER, messenger);
args.putSerializable(ARG_ACTION, action); args.putSerializable(ARG_ACTION, action);
args.putString(ARG_KEYSERVER, keyserver); args.putParcelable(ARG_KEYSERVER, keyserver);
args.putInt(ARG_POSITION, position); args.putInt(ARG_POSITION, position);
frag.setArguments(args); frag.setArguments(args);
@@ -146,7 +146,8 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
} }
case EDIT: { case EDIT: {
alert.setTitle(R.string.edit_keyserver_dialog_title); 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; break;
} }
} }
@@ -226,16 +227,19 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
mKeyserverEditTextLayout.setErrorEnabled(false); mKeyserverEditTextLayout.setErrorEnabled(false);
// behaviour same for edit and add // 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()) { if (mVerifyKeyserverCheckBox.isChecked()) {
final Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(getActivity()) final ParcelableProxy proxy = Preferences.getPreferences(getActivity())
.getProxyPrefs(); .getParcelableProxy();
OrbotHelper.DialogActions dialogActions = new OrbotHelper.DialogActions() { OrbotHelper.DialogActions dialogActions = new OrbotHelper.DialogActions() {
@Override @Override
public void onOrbotStarted() { public void onOrbotStarted() {
verifyConnection( verifyConnection(
keyserverUrl, keyserver,
proxyPrefs.parcelableProxy.getProxy(), proxy,
mOnlyTrustedKeyserverCheckBox.isChecked() mOnlyTrustedKeyserverCheckBox.isChecked()
); );
} }
@@ -243,7 +247,7 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
@Override @Override
public void onNeutralButton() { public void onNeutralButton() {
verifyConnection( verifyConnection(
keyserverUrl, keyserver,
null, null,
mOnlyTrustedKeyserverCheckBox.isChecked() mOnlyTrustedKeyserverCheckBox.isChecked()
); );
@@ -257,26 +261,26 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
if (OrbotHelper.putOrbotInRequiredState(dialogActions, getActivity())) { if (OrbotHelper.putOrbotInRequiredState(dialogActions, getActivity())) {
verifyConnection( verifyConnection(
keyserverUrl, keyserver,
proxyPrefs.parcelableProxy.getProxy(), proxy,
mOnlyTrustedKeyserverCheckBox.isChecked() mOnlyTrustedKeyserverCheckBox.isChecked()
); );
} }
} else { } else {
dismiss(); dismiss();
// return unverified keyserver back to activity // 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(); dismiss();
Bundle data = new Bundle(); Bundle data = new Bundle();
data.putSerializable(MESSAGE_DIALOG_ACTION, mDialogAction); data.putSerializable(MESSAGE_DIALOG_ACTION, mDialogAction);
data.putString(MESSAGE_KEYSERVER, keyserver); data.putParcelable(MESSAGE_KEYSERVER, keyserver);
data.putBoolean(MESSAGE_VERIFIED, verified); data.putBoolean(MESSAGE_VERIFIED, verified);
if (mDialogAction == DialogAction.EDIT) { if (mDialogAction == DialogAction.EDIT) {
@@ -296,8 +300,8 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
sendMessageToHandler(MESSAGE_OKAY, data); sendMessageToHandler(MESSAGE_OKAY, data);
} }
public void verificationFailed(FailureReason failureReason) { public void verificationFailed(VerifyReturn verifyReturn) {
switch (failureReason) { switch (verifyReturn) {
case CONNECTION_FAILED: { case CONNECTION_FAILED: {
mKeyserverEditTextLayout.setError( mKeyserverEditTextLayout.setError(
getString(R.string.add_keyserver_connection_failed)); 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<String, Void, FailureReason>() { new AsyncTask<ParcelableHkpKeyserver, Void, VerifyReturn>() {
ProgressDialog mProgressDialog; ProgressDialog mProgressDialog;
String mKeyserver; ParcelableHkpKeyserver mKeyserver;
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
@@ -332,61 +336,62 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
} }
@Override @Override
protected FailureReason doInBackground(String... keyservers) { protected VerifyReturn doInBackground(ParcelableHkpKeyserver... keyservers) {
mKeyserver = keyservers[0]; 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()); return verifyKeyserver(mKeyserver, proxy, onlyTrustedKeyserver);
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;
} }
@Override @Override
protected void onPostExecute(FailureReason failureReason) { protected void onPostExecute(VerifyReturn verifyReturn) {
mProgressDialog.dismiss(); mProgressDialog.dismiss();
if (failureReason == null) { if (verifyReturn == VerifyReturn.GOOD) {
keyserverEdited(mKeyserver, true); keyserverEdited(mKeyserver, true);
} else { } else {
verificationFailed(failureReason); verificationFailed(verifyReturn);
} }
} }
}.execute(keyserver); }.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 @Override
public void onDismiss(DialogInterface dialog) { public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog); super.onDismiss(dialog);

View File

@@ -19,15 +19,14 @@ package org.sufficientlysecure.keychain.util;
import android.content.Context; import android.content.Context;
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry; import org.sufficientlysecure.keychain.keyimport.ImportKeysListEntry;
import org.sufficientlysecure.keychain.keyimport.Keyserver; import org.sufficientlysecure.keychain.keyimport.Keyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import java.net.Proxy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@@ -41,14 +40,15 @@ public class EmailKeyHelper {
implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> { implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
private ArrayList<ParcelableKeyRing> mKeyList; private ArrayList<ParcelableKeyRing> 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); this(context, new ContactHelper(context).getContactMails(), keyserver, proxy);
} }
public ImportContactKeysCallback(Context context, List<String> mails, String keyserver, public ImportContactKeysCallback(Context context, List<String> mails,
Proxy proxy) { ParcelableHkpKeyserver keyserver, ParcelableProxy proxy) {
Set<ImportKeysListEntry> entries = new HashSet<>(); Set<ImportKeysListEntry> entries = new HashSet<>();
for (String mail : mails) { for (String mail : mails) {
entries.addAll(getEmailKeys(context, mail, proxy)); entries.addAll(getEmailKeys(context, mail, proxy));
@@ -62,39 +62,41 @@ public class EmailKeyHelper {
mKeyList = keys; mKeyList = keys;
mKeyserver = keyserver; mKeyserver = keyserver;
} }
@Override @Override
public ImportKeyringParcel createOperationInput() { public ImportKeyringParcel createOperationInput() {
return new ImportKeyringParcel(mKeyList, mKeyserver); return new ImportKeyringParcel(mKeyList, mKeyserver);
} }
} }
public static Set<ImportKeysListEntry> getEmailKeys(Context context, String mail, Proxy proxy) { public static Set<ImportKeysListEntry> getEmailKeys(Context context, String mail,
ParcelableProxy proxy) {
Set<ImportKeysListEntry> keys = new HashSet<>(); Set<ImportKeysListEntry> keys = new HashSet<>();
// Try _hkp._tcp SRV record first // Try _hkp._tcp SRV record first
String[] mailparts = mail.split("@"); String[] mailparts = mail.split("@");
if (mailparts.length == 2) { if (mailparts.length == 2) {
HkpKeyserver hkp = HkpKeyserver.resolve(mailparts[1], proxy); ParcelableHkpKeyserver hkp = ParcelableHkpKeyserver.resolve(mailparts[1]);
if (hkp != null) { if (hkp != null) {
keys.addAll(getEmailKeys(mail, hkp)); keys.addAll(getEmailKeys(mail, hkp, proxy));
} }
} }
if (keys.isEmpty()) { if (keys.isEmpty()) {
// Most users don't have the SRV record, so ask a default server as well // 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) { if (server != null) {
HkpKeyserver hkp = new HkpKeyserver(server, proxy); keys.addAll(getEmailKeys(mail, server, proxy));
keys.addAll(getEmailKeys(mail, hkp));
} }
} }
return keys; return keys;
} }
public static List<ImportKeysListEntry> getEmailKeys(String mail, Keyserver keyServer) { public static List<ImportKeysListEntry> getEmailKeys(String mail, Keyserver keyServer,
ParcelableProxy proxy) {
Set<ImportKeysListEntry> keys = new HashSet<>(); Set<ImportKeysListEntry> keys = new HashSet<>();
try { try {
for (ImportKeysListEntry key : keyServer.search(mail)) { for (ImportKeysListEntry key : keyServer.search(mail, proxy)) {
if (key.isRevoked() || key.isExpired()) continue; if (key.isRevoked() || key.isExpired()) continue;
for (String userId : key.getUserIds()) { for (String userId : key.getUserIds()) {
if (userId.toLowerCase().contains(mail.toLowerCase(Locale.ENGLISH))) { if (userId.toLowerCase().contains(mail.toLowerCase(Locale.ENGLISH))) {

View File

@@ -32,21 +32,28 @@ public class ParcelableProxy implements Parcelable {
private String mProxyHost; private String mProxyHost;
private int mProxyPort; private int mProxyPort;
private Proxy.Type mProxyType; 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; mProxyHost = hostName;
if (mProxyHost == null) {
if (hostName == null) {
return; // represents a null proxy return; // represents a null proxy
} }
mProxyPort = port; mProxyPort = port;
mProxyType = type; mProxyType = type;
mProxyMode = proxyMode;
} }
public static ParcelableProxy getForNoProxy() { 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 @NonNull
@@ -65,6 +72,7 @@ public class ParcelableProxy implements Parcelable {
mProxyHost = in.readString(); mProxyHost = in.readString();
mProxyPort = in.readInt(); mProxyPort = in.readInt();
mProxyType = (Proxy.Type) in.readSerializable(); mProxyType = (Proxy.Type) in.readSerializable();
mProxyMode = in.readInt();
} }
@Override @Override
@@ -77,6 +85,7 @@ public class ParcelableProxy implements Parcelable {
dest.writeString(mProxyHost); dest.writeString(mProxyHost);
dest.writeInt(mProxyPort); dest.writeInt(mProxyPort);
dest.writeSerializable(mProxyType); dest.writeSerializable(mProxyType);
dest.writeInt(mProxyMode);
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")

View File

@@ -27,19 +27,17 @@ import android.content.SharedPreferences;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Constants.Pref; import org.sufficientlysecure.keychain.Constants.Pref;
import org.sufficientlysecure.keychain.KeychainApplication; import org.sufficientlysecure.keychain.KeychainApplication;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.keyimport.ParcelableHkpKeyserver;
import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService; import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService;
import org.sufficientlysecure.keychain.util.orbot.OrbotStatusReceiver;
import java.io.Serializable; import java.io.Serializable;
import java.net.Proxy; import java.net.Proxy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@@ -47,7 +45,6 @@ import java.util.HashSet;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.Vector;
/** /**
* Singleton Implementation of a Preference Helper * Singleton Implementation of a Preference Helper
@@ -131,40 +128,50 @@ public class Preferences {
editor.commit(); editor.commit();
} }
public String[] getKeyServers() { public ArrayList<ParcelableHkpKeyserver> getKeyServers() {
String rawData = mSharedPreferences.getString(Constants.Pref.KEY_SERVERS, String rawData = mSharedPreferences.getString(Constants.Pref.KEY_SERVERS,
Constants.Defaults.KEY_SERVERS); Constants.Defaults.KEY_SERVERS);
if ("".equals(rawData)) { if ("".equals(rawData)) {
return new String[0]; return new ArrayList<>();
} }
Vector<String> servers = new Vector<>(); ArrayList<ParcelableHkpKeyserver> servers = new ArrayList<>();
String chunks[] = rawData.split(","); String[] entries = rawData.split(",");
for (String c : chunks) { for (String entry : entries) {
String tmp = c.trim();
if (tmp.length() > 0) {
servers.add(tmp);
}
}
return servers.toArray(chunks);
}
public String getPreferredKeyserver() { String[] addresses = entry.trim().split(";");
String[] keyservers = getKeyServers(); String url = addresses[0];
return keyservers.length == 0 ? null : keyservers[0]; String onion = addresses.length == 1 ? null : addresses[1];
}
public void setKeyServers(String[] value) { if (url.isEmpty()) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
String rawData = "";
for (String v : value) {
String tmp = v.trim();
if (tmp.length() == 0) {
continue; continue;
} }
servers.add(new ParcelableHkpKeyserver(url, onion));
}
return servers;
}
public ParcelableHkpKeyserver getPreferredKeyserver() {
ArrayList<ParcelableHkpKeyserver> keyservers = getKeyServers();
return keyservers.size() == 0 ? null : keyservers.get(0);
}
public void setKeyServers(ArrayList<ParcelableHkpKeyserver> 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)) { if (!"".equals(rawData)) {
rawData += ","; rawData += ",";
} }
rawData += tmp;
} }
editor.putString(Constants.Pref.KEY_SERVERS, rawData); editor.putString(Constants.Pref.KEY_SERVERS, rawData);
editor.commit(); editor.commit();
@@ -249,7 +256,7 @@ public class Preferences {
return Integer.parseInt(mSharedPreferences.getString(Pref.PROXY_PORT, "-1")); 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 typeHttp = Pref.ProxyType.TYPE_HTTP;
final String typeSocks = Pref.ProxyType.TYPE_SOCKS; final String typeSocks = Pref.ProxyType.TYPE_SOCKS;
@@ -266,47 +273,24 @@ public class Preferences {
} }
} }
public ProxyPrefs getProxyPrefs() { public ParcelableProxy getParcelableProxy() {
boolean useTor = getUseTorProxy(); boolean useTor = getUseTorProxy();
boolean useNormalProxy = getUseNormalProxy(); boolean useNormalProxy = getUseNormalProxy();
if (useTor) { if (useTor) {
//TODO: Replace Constants.Orbot.PROXY_PORT with OrbotStatusReceiver.getProxyPortHttp() //TODO: Replace Constants.Orbot.PROXY_PORT with OrbotStatusReceiver.getProxyPortHttp()
//TODO: in order to always have the actual port Orbot is offering? //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) { } else if (useNormalProxy) {
return new ProxyPrefs(false, true, getProxyHost(), getProxyPort(), getProxyType()); return new ParcelableProxy(getProxyHost(), getProxyPort(), getProxyType(),
ParcelableProxy.PROXY_MODE_NORMAL);
} else { } 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 * @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 class CacheTTLPrefs implements Serializable {
public static final Map<Integer,Integer> CACHE_TTL_NAMES; public static final Map<Integer, Integer> CACHE_TTL_NAMES;
public static final ArrayList<Integer> CACHE_TTLS; public static final ArrayList<Integer> CACHE_TTLS;
static { static {
HashMap<Integer,Integer> cacheTtlNames = new HashMap<>(); HashMap<Integer, Integer> cacheTtlNames = new HashMap<>();
cacheTtlNames.put(0, R.string.cache_ttl_lock_screen); cacheTtlNames.put(0, R.string.cache_ttl_lock_screen);
cacheTtlNames.put(60 * 10, R.string.cache_ttl_ten_minutes); cacheTtlNames.put(60 * 10, R.string.cache_ttl_ten_minutes);
cacheTtlNames.put(60 * 30, R.string.cache_ttl_thirty_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 searchKeyserver;
public final boolean searchKeybase; public final boolean searchKeybase;
public final boolean searchFacebook; public final boolean searchFacebook;
public final String keyserver; public final ParcelableHkpKeyserver keyserver;
/** /**
* @param searchKeyserver should passed keyserver be searched * @param searchKeyserver should passed keyserver be searched
@@ -405,7 +390,7 @@ public class Preferences {
* @param keyserver the keyserver url authority to search on * @param keyserver the keyserver url authority to search on
*/ */
public CloudSearchPrefs(boolean searchKeyserver, boolean searchKeybase, public CloudSearchPrefs(boolean searchKeyserver, boolean searchKeybase,
boolean searchFacebook, String keyserver) { boolean searchFacebook, ParcelableHkpKeyserver keyserver) {
this.searchKeyserver = searchKeyserver; this.searchKeyserver = searchKeyserver;
this.searchKeybase = searchKeybase; this.searchKeybase = searchKeybase;
this.searchFacebook = searchFacebook; this.searchFacebook = searchFacebook;
@@ -416,7 +401,7 @@ public class Preferences {
searchKeyserver = in.readByte() != 0x00; searchKeyserver = in.readByte() != 0x00;
searchKeybase = in.readByte() != 0x00; searchKeybase = in.readByte() != 0x00;
searchFacebook = in.readByte() != 0x00; searchFacebook = in.readByte() != 0x00;
keyserver = in.readString(); keyserver = in.readParcelable(ParcelableHkpKeyserver.class.getClassLoader());
} }
@Override @Override
@@ -429,7 +414,7 @@ public class Preferences {
dest.writeByte((byte) (searchKeyserver ? 0x01 : 0x00)); dest.writeByte((byte) (searchKeyserver ? 0x01 : 0x00));
dest.writeByte((byte) (searchKeybase ? 0x01 : 0x00)); dest.writeByte((byte) (searchKeybase ? 0x01 : 0x00));
dest.writeByte((byte) (searchFacebook ? 0x01 : 0x00)); dest.writeByte((byte) (searchFacebook ? 0x01 : 0x00));
dest.writeString(keyserver); dest.writeParcelable(keyserver, flags);
} }
public static final Parcelable.Creator<CloudSearchPrefs> CREATOR public static final Parcelable.Creator<CloudSearchPrefs> CREATOR
@@ -476,22 +461,21 @@ public class Preferences {
// fall through // fall through
case 3: { case 3: {
// migrate keyserver to hkps // migrate keyserver to hkps
String[] serversArray = getKeyServers(); ArrayList<ParcelableHkpKeyserver> servers = getKeyServers();
ArrayList<String> servers = new ArrayList<>(Arrays.asList(serversArray)); ListIterator<ParcelableHkpKeyserver> it = servers.listIterator();
ListIterator<String> it = servers.listIterator();
while (it.hasNext()) { while (it.hasNext()) {
String server = it.next(); ParcelableHkpKeyserver server = it.next();
if (server == null) { if (server == null) {
continue; continue;
} }
switch (server) { switch (server.getUrl()) {
case "pool.sks-keyservers.net": case "pool.sks-keyservers.net":
// use HKPS! // use HKPS!
it.set("hkps://hkps.pool.sks-keyservers.net"); it.set(new ParcelableHkpKeyserver("hkps://hkps.pool.sks-keyservers.net", null));
break; break;
case "pgp.mit.edu": case "pgp.mit.edu":
// use HKPS! // use HKPS!
it.set("hkps://pgp.mit.edu"); it.set(new ParcelableHkpKeyserver("hkps://pgp.mit.edu", null));
break; break;
case "subkeys.pgp.net": case "subkeys.pgp.net":
// remove, because often down and no HKPS! // 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 // fall through
case 4: { case 4: {
@@ -513,6 +497,27 @@ public class Preferences {
// fall through // fall through
case 6: { case 6: {
} }
// fall through
case 7: {
// add onion address to sks-keyservers.net
ArrayList<ParcelableHkpKeyserver> servers = getKeyServers();
ListIterator<ParcelableHkpKeyserver> 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 // write new preference version

View File

@@ -72,6 +72,7 @@ import org.sufficientlysecure.keychain.ui.dialog.OrbotStartDialogFragment;
import org.sufficientlysecure.keychain.ui.dialog.PreferenceInstallDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.PreferenceInstallDialogFragment;
import org.sufficientlysecure.keychain.ui.util.ThemeChanger; import org.sufficientlysecure.keychain.ui.util.ThemeChanger;
import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Log;
import org.sufficientlysecure.keychain.util.ParcelableProxy;
import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.Preferences;
import java.util.List; import java.util.List;
@@ -153,6 +154,7 @@ public class OrbotHelper {
/** /**
* Initialize the OrbotStatusReceiver (if not already happened) and check, whether Orbot is * Initialize the OrbotStatusReceiver (if not already happened) and check, whether Orbot is
* running or not. * running or not.
*
* @param context context * @param context context
* @return if Orbot is running * @return if Orbot is running
*/ */
@@ -285,8 +287,8 @@ public class OrbotHelper {
* otherwise * otherwise
*/ */
public static boolean isOrbotInRequiredState(Context context) { public static boolean isOrbotInRequiredState(Context context) {
Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(context).getProxyPrefs(); ParcelableProxy proxy = Preferences.getPreferences(context).getParcelableProxy();
if (!proxyPrefs.torEnabled) { if (!proxy.isTorEnabled()) {
return true; return true;
} else if (!OrbotHelper.isOrbotInstalled(context) || !OrbotHelper.isOrbotRunning(context)) { } else if (!OrbotHelper.isOrbotInstalled(context) || !OrbotHelper.isOrbotRunning(context)) {
return false; 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. * 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 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 * @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, public static boolean putOrbotInRequiredState(final int middleButton,
final DialogActions dialogActions, final DialogActions dialogActions,
Preferences.ProxyPrefs proxyPrefs, ParcelableProxy proxy,
final FragmentActivity fragmentActivity) { final FragmentActivity fragmentActivity) {
if (!proxyPrefs.torEnabled) { if (!proxy.isTorEnabled()) {
return true; return true;
} }
@@ -377,7 +379,7 @@ public class OrbotHelper {
FragmentActivity fragmentActivity) { FragmentActivity fragmentActivity) {
return putOrbotInRequiredState(R.string.orbot_ignore_tor, return putOrbotInRequiredState(R.string.orbot_ignore_tor,
dialogActions, dialogActions,
Preferences.getPreferences(fragmentActivity).getProxyPrefs(), Preferences.getPreferences(fragmentActivity).getParcelableProxy(),
fragmentActivity); fragmentActivity);
} }