Merge branch 'master' of github.com:open-keychain/open-keychain
This commit is contained in:
@@ -26,6 +26,8 @@ import org.spongycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.sufficientlysecure.keychain.BuildConfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
|
||||
public final class Constants {
|
||||
|
||||
@@ -44,6 +46,8 @@ public final class Constants {
|
||||
public static final String PROVIDER_AUTHORITY = BuildConfig.APPLICATION_ID + ".provider";
|
||||
public static final String TEMPSTORAGE_AUTHORITY = BuildConfig.APPLICATION_ID + ".tempstorage";
|
||||
|
||||
public static final String CLIPBOARD_LABEL = "Keychain";
|
||||
|
||||
// as defined in http://tools.ietf.org/html/rfc3156, section 7
|
||||
public static final String NFC_MIME = "application/pgp-keys";
|
||||
|
||||
@@ -93,6 +97,21 @@ public final class Constants {
|
||||
public static final String FILE_USE_COMPRESSION = "useFileCompression";
|
||||
public static final String TEXT_USE_COMPRESSION = "useTextCompression";
|
||||
public static final String USE_ARMOR = "useArmor";
|
||||
// proxy settings
|
||||
public static final String USE_NORMAL_PROXY = "useNormalProxy";
|
||||
public static final String USE_TOR_PROXY = "useTorProxy";
|
||||
public static final String PROXY_HOST = "proxyHost";
|
||||
public static final String PROXY_PORT = "proxyPort";
|
||||
public static final String PROXY_TYPE = "proxyType";
|
||||
}
|
||||
|
||||
/**
|
||||
* information to connect to Orbot's localhost HTTP proxy
|
||||
*/
|
||||
public static final class Orbot {
|
||||
public static final String PROXY_HOST = "127.0.0.1";
|
||||
public static final int PROXY_PORT = 8118;
|
||||
public static final Proxy.Type PROXY_TYPE = Proxy.Type.HTTP;
|
||||
}
|
||||
|
||||
public static final class Defaults {
|
||||
|
||||
@@ -17,28 +17,22 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.compatibility;
|
||||
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class ClipboardReflection {
|
||||
|
||||
private static final String clipboardLabel = "Keychain";
|
||||
|
||||
public static void copyToClipboard(Context context, String text) {
|
||||
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
|
||||
ClipData clip = ClipData.newPlainText(clipboardLabel, text);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
|
||||
}
|
||||
|
||||
public static String getClipboardText(Context context) {
|
||||
@Nullable
|
||||
public static String getClipboardText(@Nullable Context context) {
|
||||
if (context == null) {
|
||||
return null;
|
||||
}
|
||||
ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
|
||||
ClipData clip = clipboard.getPrimaryClip();
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.net.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Vector;
|
||||
|
||||
@@ -30,7 +31,8 @@ public class CloudSearch {
|
||||
|
||||
private final static long SECONDS = 1000;
|
||||
|
||||
public static ArrayList<ImportKeysListEntry> search(final String query, Preferences.CloudSearchPrefs cloudPrefs)
|
||||
public static ArrayList<ImportKeysListEntry> search(final String query, Preferences.CloudSearchPrefs cloudPrefs,
|
||||
final Proxy proxy)
|
||||
throws Keyserver.CloudSearchFailureException {
|
||||
final ArrayList<Keyserver> servers = new ArrayList<>();
|
||||
|
||||
@@ -51,7 +53,7 @@ public class CloudSearch {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
results.addAll(keyserver.search(query));
|
||||
results.addAll(keyserver.search(query, proxy));
|
||||
} catch (Keyserver.CloudSearchFailureException e) {
|
||||
problems.add(e);
|
||||
}
|
||||
@@ -63,20 +65,24 @@ public class CloudSearch {
|
||||
searchThread.start();
|
||||
}
|
||||
|
||||
// wait for either all the searches to come back, or 10 seconds
|
||||
// wait for either all the searches to come back, or 10 seconds. If using proxy, wait 30 seconds.
|
||||
synchronized (results) {
|
||||
try {
|
||||
results.wait(10 * SECONDS);
|
||||
if (proxy != null) {
|
||||
results.wait(30 * SECONDS);
|
||||
} else {
|
||||
results.wait(10 * SECONDS);
|
||||
}
|
||||
for (Thread thread : searchThreads) {
|
||||
// kill threads that haven't returned yet
|
||||
thread.interrupt();
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
if (results.outstandingSuppliers() > 0) {
|
||||
String message = "Launched " + servers.size() + " cloud searchers, but" +
|
||||
String message = "Launched " + servers.size() + " cloud searchers, but " +
|
||||
results.outstandingSuppliers() + "failed to complete.";
|
||||
problems.add(new Keyserver.QueryFailedException(message));
|
||||
}
|
||||
|
||||
@@ -18,18 +18,20 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.keyimport;
|
||||
|
||||
import com.squareup.okhttp.MediaType;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.RequestBody;
|
||||
import com.squareup.okhttp.Response;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.TlsHelper;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.Proxy;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
@@ -39,6 +41,7 @@ import java.util.Comparator;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -190,36 +193,47 @@ public class HkpKeyserver extends Keyserver {
|
||||
return mSecure ? "https://" : "http://";
|
||||
}
|
||||
|
||||
private HttpURLConnection openConnection(URL url) throws IOException {
|
||||
HttpURLConnection conn = null;
|
||||
/**
|
||||
* returns a client with pinned certificate if necessary
|
||||
*
|
||||
* @param url
|
||||
* @param proxy
|
||||
* @return
|
||||
*/
|
||||
public static OkHttpClient getClient(URL url, Proxy proxy) throws IOException {
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
|
||||
try {
|
||||
conn = (HttpURLConnection) TlsHelper.openConnection(url);
|
||||
TlsHelper.pinCertificateIfNecessary(client, url);
|
||||
} catch (TlsHelper.TlsHelperException e) {
|
||||
Log.w(Constants.TAG, e);
|
||||
}
|
||||
if (conn == null) {
|
||||
conn = (HttpURLConnection) url.openConnection();
|
||||
}
|
||||
conn.setConnectTimeout(5000);
|
||||
conn.setReadTimeout(25000);
|
||||
return conn;
|
||||
|
||||
client.setProxy(proxy);
|
||||
client.setConnectTimeout(proxy != null ? 30000 : 5000, TimeUnit.MILLISECONDS);
|
||||
client.setReadTimeout(45000, TimeUnit.MILLISECONDS);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private String query(String request) throws QueryFailedException, HttpError {
|
||||
private String query(String request, Proxy proxy) throws QueryFailedException, HttpError {
|
||||
try {
|
||||
URL url = new URL(getUrlPrefix() + mHost + ":" + mPort + request);
|
||||
Log.d(Constants.TAG, "hkp keyserver query: " + url);
|
||||
HttpURLConnection conn = openConnection(url);
|
||||
conn.connect();
|
||||
int response = conn.getResponseCode();
|
||||
if (response >= 200 && response < 300) {
|
||||
return readAll(conn.getInputStream(), conn.getContentEncoding());
|
||||
Log.d(Constants.TAG, "hkp keyserver query: " + url + " Proxy: " + proxy);
|
||||
OkHttpClient client = getClient(url, proxy);
|
||||
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
|
||||
|
||||
if (response.isSuccessful()) {
|
||||
return responseBody;
|
||||
} else {
|
||||
String data = readAll(conn.getErrorStream(), conn.getContentEncoding());
|
||||
throw new HttpError(response, data);
|
||||
throw new HttpError(response.code(), responseBody);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new QueryFailedException("Keyserver '" + mHost + "' is unavailable. Check your Internet connection!");
|
||||
Log.e(Constants.TAG, "IOException at HkpKeyserver", e);
|
||||
throw new QueryFailedException("Keyserver '" + mHost + "' is unavailable. Check your Internet connection!" +
|
||||
proxy == null?"":" Using proxy " + proxy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +246,7 @@ public class HkpKeyserver extends Keyserver {
|
||||
* @throws QueryNeedsRepairException
|
||||
*/
|
||||
@Override
|
||||
public ArrayList<ImportKeysListEntry> search(String query) throws QueryFailedException,
|
||||
public ArrayList<ImportKeysListEntry> search(String query, Proxy proxy) throws QueryFailedException,
|
||||
QueryNeedsRepairException {
|
||||
ArrayList<ImportKeysListEntry> results = new ArrayList<>();
|
||||
|
||||
@@ -250,7 +264,7 @@ public class HkpKeyserver extends Keyserver {
|
||||
|
||||
String data;
|
||||
try {
|
||||
data = query(request);
|
||||
data = query(request, proxy);
|
||||
} catch (HttpError e) {
|
||||
if (e.getData() != null) {
|
||||
Log.d(Constants.TAG, "returned error data: " + e.getData().toLowerCase(Locale.ENGLISH));
|
||||
@@ -334,13 +348,14 @@ public class HkpKeyserver extends Keyserver {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String keyIdHex) throws QueryFailedException {
|
||||
public String get(String keyIdHex, Proxy proxy) throws QueryFailedException {
|
||||
String request = "/pks/lookup?op=get&options=mr&search=" + keyIdHex;
|
||||
Log.d(Constants.TAG, "hkp keyserver get: " + request);
|
||||
Log.d(Constants.TAG, "hkp keyserver get: " + request + " using Proxy: " + proxy);
|
||||
String data;
|
||||
try {
|
||||
data = query(request);
|
||||
data = query(request, proxy);
|
||||
} catch (HttpError httpError) {
|
||||
Log.e(Constants.TAG, "Failed to get key at HkpKeyserver", httpError);
|
||||
throw new QueryFailedException("not found");
|
||||
}
|
||||
Matcher matcher = PgpHelper.PGP_PUBLIC_KEY.matcher(data);
|
||||
@@ -351,38 +366,34 @@ public class HkpKeyserver extends Keyserver {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String armoredKey) throws AddKeyException {
|
||||
public void add(String armoredKey, Proxy proxy) throws AddKeyException {
|
||||
try {
|
||||
String request = "/pks/add";
|
||||
String path = "/pks/add";
|
||||
String params;
|
||||
try {
|
||||
params = "keytext=" + URLEncoder.encode(armoredKey, "UTF-8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new AddKeyException();
|
||||
}
|
||||
URL url = new URL(getUrlPrefix() + mHost + ":" + mPort + request);
|
||||
URL url = new URL(getUrlPrefix() + mHost + ":" + mPort + path);
|
||||
|
||||
Log.d(Constants.TAG, "hkp keyserver add: " + url.toString());
|
||||
Log.d(Constants.TAG, "params: " + params);
|
||||
|
||||
HttpURLConnection conn = openConnection(url);
|
||||
conn.setRequestMethod("POST");
|
||||
conn.addRequestProperty("Content-Type", "application/x-www-form-urlencoded");
|
||||
conn.setRequestProperty("Content-Length", Integer.toString(params.getBytes().length));
|
||||
conn.setDoInput(true);
|
||||
conn.setDoOutput(true);
|
||||
RequestBody body = RequestBody.create(MediaType.parse("application/x-www-form-urlencoded"), params);
|
||||
|
||||
OutputStream os = conn.getOutputStream();
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
|
||||
writer.write(params);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
os.close();
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.addHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
.addHeader("Content-Length", Integer.toString(params.getBytes().length))
|
||||
.post(body)
|
||||
.build();
|
||||
|
||||
conn.connect();
|
||||
Response response = getClient(url, proxy).newCall(request).execute();
|
||||
|
||||
Log.d(Constants.TAG, "response code: " + response.code());
|
||||
Log.d(Constants.TAG, "answer: " + response.body().string());
|
||||
|
||||
Log.d(Constants.TAG, "response code: " + conn.getResponseCode());
|
||||
Log.d(Constants.TAG, "answer: " + readAll(conn.getInputStream(), conn.getContentEncoding()));
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "IOException", e);
|
||||
throw new AddKeyException();
|
||||
@@ -398,6 +409,7 @@ public class HkpKeyserver extends Keyserver {
|
||||
* Tries to find a server responsible for a given domain
|
||||
*
|
||||
* @return A responsible Keyserver or null if not found.
|
||||
* TODO: PHILIP Add proxy functionality
|
||||
*/
|
||||
public static HkpKeyserver resolve(String domain) {
|
||||
try {
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.net.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -34,7 +35,7 @@ public class KeybaseKeyserver extends Keyserver {
|
||||
private String mQuery;
|
||||
|
||||
@Override
|
||||
public ArrayList<ImportKeysListEntry> search(String query) throws QueryFailedException,
|
||||
public ArrayList<ImportKeysListEntry> search(String query, Proxy proxy) throws QueryFailedException,
|
||||
QueryNeedsRepairException {
|
||||
ArrayList<ImportKeysListEntry> results = new ArrayList<>();
|
||||
|
||||
@@ -48,7 +49,7 @@ public class KeybaseKeyserver extends Keyserver {
|
||||
mQuery = query;
|
||||
|
||||
try {
|
||||
Iterable<Match> matches = Search.search(query);
|
||||
Iterable<Match> matches = Search.search(query, proxy);
|
||||
for (Match match : matches) {
|
||||
results.add(makeEntry(match));
|
||||
}
|
||||
@@ -98,16 +99,16 @@ public class KeybaseKeyserver extends Keyserver {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(String id) throws QueryFailedException {
|
||||
public String get(String id, Proxy proxy) throws QueryFailedException {
|
||||
try {
|
||||
return User.keyForUsername(id);
|
||||
return User.keyForUsername(id, proxy);
|
||||
} catch (KeybaseException e) {
|
||||
throw new QueryFailedException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(String armoredKey) throws AddKeyException {
|
||||
public void add(String armoredKey, Proxy proxy) throws AddKeyException {
|
||||
throw new AddKeyException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.keyimport;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.Proxy;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Keyserver {
|
||||
@@ -31,6 +32,7 @@ public abstract class Keyserver {
|
||||
public CloudSearchFailureException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CloudSearchFailureException() {
|
||||
super();
|
||||
}
|
||||
@@ -67,12 +69,12 @@ public abstract class Keyserver {
|
||||
private static final long serialVersionUID = -507574859137295530L;
|
||||
}
|
||||
|
||||
public abstract List<ImportKeysListEntry> search(String query) throws QueryFailedException,
|
||||
public abstract List<ImportKeysListEntry> search(String query, Proxy proxy) throws QueryFailedException,
|
||||
QueryNeedsRepairException;
|
||||
|
||||
public abstract String get(String keyIdHex) throws QueryFailedException;
|
||||
public abstract String get(String keyIdHex, Proxy proxy) throws QueryFailedException;
|
||||
|
||||
public abstract void add(String armoredKey) throws AddKeyException;
|
||||
public abstract void add(String armoredKey, Proxy proxy) throws AddKeyException;
|
||||
|
||||
public static String readAll(InputStream in, String encoding) throws IOException {
|
||||
ByteArrayOutputStream raw = new ByteArrayOutputStream();
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.operations;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.pgp.PassphraseCacheInterface;
|
||||
@@ -76,9 +77,8 @@ public abstract class BaseOperation <T extends Parcelable> implements Passphrase
|
||||
mCancelled = cancelled;
|
||||
}
|
||||
|
||||
public OperationResult execute(T input, CryptoInputParcel cryptoInput) {
|
||||
return null;
|
||||
}
|
||||
@NonNull
|
||||
public abstract OperationResult execute(T input, CryptoInputParcel cryptoInput);
|
||||
|
||||
public void updateProgress(int message, int current, int total) {
|
||||
if (mProgressable != null) {
|
||||
|
||||
@@ -18,11 +18,11 @@
|
||||
package org.sufficientlysecure.keychain.operations;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
|
||||
import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
|
||||
import org.sufficientlysecure.keychain.operations.results.CertifyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.ExportResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||
import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult;
|
||||
@@ -43,27 +43,31 @@ import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSignOperationsBuilder;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
import java.net.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/** An operation which implements a high level user id certification operation.
|
||||
*
|
||||
/**
|
||||
* An operation which implements a high level user id certification operation.
|
||||
* <p/>
|
||||
* This operation takes a specific CertifyActionsParcel as its input. These
|
||||
* contain a masterKeyId to be used for certification, and a list of
|
||||
* masterKeyIds and related user ids to certify.
|
||||
*
|
||||
* @see CertifyActionsParcel
|
||||
*
|
||||
*/
|
||||
public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
|
||||
|
||||
public CertifyOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean cancelled) {
|
||||
public CertifyOperation(Context context, ProviderHelper providerHelper, Progressable progressable, AtomicBoolean
|
||||
cancelled) {
|
||||
super(context, providerHelper, progressable, cancelled);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public CertifyResult execute(CertifyActionsParcel parcel, CryptoInputParcel cryptoInput) {
|
||||
|
||||
@@ -174,7 +178,7 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
|
||||
|
||||
}
|
||||
|
||||
if ( ! allRequiredInput.isEmpty()) {
|
||||
if (!allRequiredInput.isEmpty()) {
|
||||
log.add(LogType.MSG_CRT_NFC_RETURN, 1);
|
||||
return new CertifyResult(log, allRequiredInput.build());
|
||||
}
|
||||
@@ -187,11 +191,24 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
|
||||
return new CertifyResult(CertifyResult.RESULT_CANCELLED, log);
|
||||
}
|
||||
|
||||
// these variables are used inside the following loop, but they need to be created only once
|
||||
HkpKeyserver keyServer = null;
|
||||
ExportOperation exportOperation = null;
|
||||
Proxy proxy = null;
|
||||
if (parcel.keyServerUri != null) {
|
||||
keyServer = new HkpKeyserver(parcel.keyServerUri);
|
||||
exportOperation = new ExportOperation(mContext, mProviderHelper, mProgressable);
|
||||
if (cryptoInput.getParcelableProxy() == null) {
|
||||
// explicit proxy not set
|
||||
if (!OrbotHelper.isOrbotInRequiredState(mContext)) {
|
||||
return new CertifyResult(null,
|
||||
RequiredInputParcel.createOrbotRequiredOperation());
|
||||
}
|
||||
proxy = Preferences.getPreferences(mContext).getProxyPrefs()
|
||||
.parcelableProxy.getProxy();
|
||||
} else {
|
||||
proxy = cryptoInput.getParcelableProxy().getProxy();
|
||||
}
|
||||
}
|
||||
|
||||
// Write all certified keys into the database
|
||||
@@ -200,7 +217,8 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
|
||||
// Check if we were cancelled
|
||||
if (checkCancelled()) {
|
||||
log.add(LogType.MSG_OPERATION_CANCELLED, 0);
|
||||
return new CertifyResult(CertifyResult.RESULT_CANCELLED, log, certifyOk, certifyError, uploadOk, uploadError);
|
||||
return new CertifyResult(CertifyResult.RESULT_CANCELLED, log, certifyOk, certifyError, uploadOk,
|
||||
uploadError);
|
||||
}
|
||||
|
||||
log.add(LogType.MSG_CRT_SAVE, 2,
|
||||
@@ -210,12 +228,15 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
|
||||
SaveKeyringResult result = mProviderHelper.savePublicKeyRing(certifiedKey);
|
||||
|
||||
if (exportOperation != null) {
|
||||
// TODO use subresult, get rid of try/catch!
|
||||
try {
|
||||
exportOperation.uploadKeyRingToServer(keyServer, certifiedKey);
|
||||
ExportResult uploadResult = exportOperation.uploadKeyRingToServer(
|
||||
keyServer,
|
||||
certifiedKey,
|
||||
proxy);
|
||||
log.add(uploadResult, 2);
|
||||
|
||||
if (uploadResult.success()) {
|
||||
uploadOk += 1;
|
||||
} catch (AddKeyException e) {
|
||||
Log.e(Constants.TAG, "error uploading key", e);
|
||||
} else {
|
||||
uploadError += 1;
|
||||
}
|
||||
}
|
||||
@@ -227,19 +248,24 @@ public class CertifyOperation extends BaseOperation<CertifyActionsParcel> {
|
||||
}
|
||||
|
||||
log.add(result, 2);
|
||||
|
||||
}
|
||||
|
||||
if (certifyOk == 0) {
|
||||
log.add(LogType.MSG_CRT_ERROR_NOTHING, 0);
|
||||
return new CertifyResult(CertifyResult.RESULT_ERROR, log, certifyOk, certifyError, uploadOk, uploadError);
|
||||
return new CertifyResult(CertifyResult.RESULT_ERROR, log, certifyOk, certifyError,
|
||||
uploadOk, uploadError);
|
||||
}
|
||||
|
||||
log.add(LogType.MSG_CRT_SUCCESS, 0);
|
||||
//since only verified keys are synced to contacts, we need to initiate a sync now
|
||||
// since only verified keys are synced to contacts, we need to initiate a sync now
|
||||
ContactSyncAdapterService.requestSync();
|
||||
|
||||
return new CertifyResult(CertifyResult.RESULT_OK, log, certifyOk, certifyError, uploadOk, uploadError);
|
||||
|
||||
log.add(LogType.MSG_CRT_SUCCESS, 0);
|
||||
if (uploadError != 0) {
|
||||
return new CertifyResult(CertifyResult.RESULT_WARNINGS, log, certifyOk, certifyError, uploadOk,
|
||||
uploadError);
|
||||
} else {
|
||||
return new CertifyResult(CertifyResult.RESULT_OK, log, certifyOk, certifyError, uploadOk, uploadError);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
package org.sufficientlysecure.keychain.operations;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
|
||||
import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||
@@ -34,6 +35,7 @@ public class ConsolidateOperation extends BaseOperation<ConsolidateInputParcel>
|
||||
super(context, providerHelper, progressable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ConsolidateResult execute(ConsolidateInputParcel consolidateInputParcel,
|
||||
CryptoInputParcel cryptoInputParcel) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.sufficientlysecure.keychain.operations;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.sufficientlysecure.keychain.operations.results.ConsolidateResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.DeleteResult;
|
||||
@@ -45,6 +46,7 @@ public class DeleteOperation extends BaseOperation<DeleteKeyringParcel> {
|
||||
super(context, providerHelper, progressable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public DeleteResult execute(DeleteKeyringParcel deleteKeyringParcel,
|
||||
CryptoInputParcel cryptoInputParcel) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package org.sufficientlysecure.keychain.operations;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.EditKeyResult;
|
||||
@@ -57,6 +58,7 @@ public class EditKeyOperation extends BaseOperation<SaveKeyringParcel> {
|
||||
super(context, providerHelper, progressable, cancelled);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public OperationResult execute(SaveKeyringParcel saveParcel, CryptoInputParcel cryptoInput) {
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
|
||||
@@ -21,12 +21,14 @@ package org.sufficientlysecure.keychain.operations;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.spongycastle.bcpg.ArmoredOutputStream;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
|
||||
import org.sufficientlysecure.keychain.keyimport.Keyserver.AddKeyException;
|
||||
import org.sufficientlysecure.keychain.operations.results.CertifyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.ExportResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||
@@ -40,9 +42,12 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.ExportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.FileHelper;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -51,6 +56,7 @@ import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Proxy;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
@@ -62,7 +68,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
* @see org.sufficientlysecure.keychain.ui.adapter.ImportKeysAdapter#getSelectedEntries()
|
||||
* For the export operation, the input consists of a set of key ids and
|
||||
* either the name of a file or an output uri to write to.
|
||||
* TODO rework uploadKeyRingToServer
|
||||
*/
|
||||
public class ExportOperation extends BaseOperation<ExportKeyringParcel> {
|
||||
|
||||
@@ -76,25 +81,39 @@ public class ExportOperation extends BaseOperation<ExportKeyringParcel> {
|
||||
super(context, providerHelper, progressable, cancelled);
|
||||
}
|
||||
|
||||
public void uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring)
|
||||
throws AddKeyException {
|
||||
uploadKeyRingToServer(server, keyring.getUncachedKeyRing());
|
||||
public ExportResult uploadKeyRingToServer(HkpKeyserver server, CanonicalizedPublicKeyRing keyring,
|
||||
Proxy proxy) {
|
||||
return uploadKeyRingToServer(server, keyring.getUncachedKeyRing(), proxy);
|
||||
}
|
||||
|
||||
public void uploadKeyRingToServer(HkpKeyserver server, UncachedKeyRing keyring) throws
|
||||
AddKeyException {
|
||||
public ExportResult uploadKeyRingToServer(HkpKeyserver server, UncachedKeyRing keyring, Proxy proxy) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ArmoredOutputStream aos = null;
|
||||
OperationLog log = new OperationLog();
|
||||
log.add(LogType.MSG_EXPORT_UPLOAD_PUBLIC, 0, KeyFormattingUtils.convertKeyIdToHex(
|
||||
keyring.getPublicKey().getKeyId()
|
||||
));
|
||||
|
||||
try {
|
||||
aos = new ArmoredOutputStream(bos);
|
||||
keyring.encode(aos);
|
||||
aos.close();
|
||||
|
||||
String armoredKey = bos.toString("UTF-8");
|
||||
server.add(armoredKey);
|
||||
server.add(armoredKey, proxy);
|
||||
|
||||
log.add(LogType.MSG_EXPORT_UPLOAD_SUCCESS, 1);
|
||||
return new ExportResult(ExportResult.RESULT_OK, log);
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "IOException", e);
|
||||
throw new AddKeyException();
|
||||
|
||||
log.add(LogType.MSG_EXPORT_ERROR_KEY, 1);
|
||||
return new ExportResult(ExportResult.RESULT_ERROR, log);
|
||||
} catch (AddKeyException e) {
|
||||
Log.e(Constants.TAG, "AddKeyException", e);
|
||||
|
||||
log.add(LogType.MSG_EXPORT_ERROR_UPLOAD, 1);
|
||||
return new ExportResult(ExportResult.RESULT_ERROR, log);
|
||||
} finally {
|
||||
try {
|
||||
if (aos != null) {
|
||||
@@ -311,20 +330,32 @@ public class ExportOperation extends BaseOperation<ExportKeyringParcel> {
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ExportResult execute(ExportKeyringParcel exportInput, CryptoInputParcel cryptoInput) {
|
||||
switch (exportInput.mExportType) {
|
||||
case UPLOAD_KEYSERVER: {
|
||||
Proxy proxy;
|
||||
if (cryptoInput.getParcelableProxy() == null) {
|
||||
// explicit proxy not set
|
||||
if (!OrbotHelper.isOrbotInRequiredState(mContext)) {
|
||||
return new ExportResult(null,
|
||||
RequiredInputParcel.createOrbotRequiredOperation());
|
||||
}
|
||||
proxy = Preferences.getPreferences(mContext).getProxyPrefs()
|
||||
.parcelableProxy.getProxy();
|
||||
} else {
|
||||
proxy = cryptoInput.getParcelableProxy().getProxy();
|
||||
}
|
||||
|
||||
HkpKeyserver hkpKeyserver = new HkpKeyserver(exportInput.mKeyserver);
|
||||
try {
|
||||
CanonicalizedPublicKeyRing keyring
|
||||
= mProviderHelper.getCanonicalizedPublicKeyRing(
|
||||
exportInput.mCanonicalizedPublicKeyringUri);
|
||||
uploadKeyRingToServer(hkpKeyserver, keyring);
|
||||
// TODO: replace with proper log
|
||||
return new ExportResult(ExportResult.RESULT_OK, new OperationLog());
|
||||
} catch (Exception e) {
|
||||
return uploadKeyRingToServer(hkpKeyserver, keyring, proxy);
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
Log.e(Constants.TAG, "error uploading key", e);
|
||||
return new ExportResult(ExportResult.RESULT_ERROR, new OperationLog());
|
||||
// TODO: Implement better exception handling, replace with log
|
||||
}
|
||||
}
|
||||
case EXPORT_FILE: {
|
||||
@@ -335,8 +366,8 @@ public class ExportOperation extends BaseOperation<ExportKeyringParcel> {
|
||||
return exportToUri(exportInput.mMasterKeyIds, exportInput.mExportSecret,
|
||||
exportInput.mOutputUri);
|
||||
}
|
||||
default: { // can't happen
|
||||
return null;
|
||||
default: { // can never happen, all enum types must be handled above
|
||||
throw new AssertionError("must not happen, this is a bug!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,24 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.operations;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
@@ -39,24 +56,13 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.ContactSyncAdapterService;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorCompletionService;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
/**
|
||||
* An operation class which implements high level import
|
||||
@@ -89,39 +95,40 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
||||
|
||||
// Overloaded functions for using progressable supplied in constructor during import
|
||||
public ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
|
||||
String keyServerUri) {
|
||||
return serialKeyRingImport(entries, num, keyServerUri, mProgressable);
|
||||
String keyServerUri, Proxy proxy) {
|
||||
return serialKeyRingImport(entries, num, keyServerUri, mProgressable, proxy);
|
||||
}
|
||||
|
||||
public ImportKeyResult serialKeyRingImport(List<ParcelableKeyRing> entries,
|
||||
String keyServerUri) {
|
||||
String keyServerUri, Proxy proxy) {
|
||||
|
||||
Iterator<ParcelableKeyRing> it = entries.iterator();
|
||||
int numEntries = entries.size();
|
||||
|
||||
return serialKeyRingImport(it, numEntries, keyServerUri, mProgressable);
|
||||
return serialKeyRingImport(it, numEntries, keyServerUri, mProgressable, proxy);
|
||||
|
||||
}
|
||||
|
||||
public ImportKeyResult serialKeyRingImport(List<ParcelableKeyRing> entries, String keyServerUri,
|
||||
Progressable progressable) {
|
||||
Progressable progressable, Proxy proxy) {
|
||||
|
||||
Iterator<ParcelableKeyRing> it = entries.iterator();
|
||||
int numEntries = entries.size();
|
||||
|
||||
return serialKeyRingImport(it, numEntries, keyServerUri, progressable);
|
||||
return serialKeyRingImport(it, numEntries, keyServerUri, progressable, proxy);
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ImportKeyResult serialKeyRingImport(ParcelableFileCache<ParcelableKeyRing> cache,
|
||||
String keyServerUri) {
|
||||
String keyServerUri, Proxy proxy) {
|
||||
|
||||
// get entries from cached file
|
||||
try {
|
||||
IteratorWithSize<ParcelableKeyRing> it = cache.readCache();
|
||||
int numEntries = it.getSize();
|
||||
|
||||
return serialKeyRingImport(it, numEntries, keyServerUri, mProgressable);
|
||||
return serialKeyRingImport(it, numEntries, keyServerUri, mProgressable, proxy);
|
||||
} catch (IOException e) {
|
||||
|
||||
// Special treatment here, we need a lot
|
||||
@@ -143,10 +150,11 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
||||
* @param keyServerUri contains uri of keyserver to import from, if it is an import from cloud
|
||||
* @param progressable Allows multi-threaded import to supply a progressable that ignores the
|
||||
* progress of a single key being imported
|
||||
* @return
|
||||
*/
|
||||
@NonNull
|
||||
public ImportKeyResult serialKeyRingImport(Iterator<ParcelableKeyRing> entries, int num,
|
||||
String keyServerUri, Progressable progressable) {
|
||||
String keyServerUri, Progressable progressable,
|
||||
Proxy proxy) {
|
||||
updateProgress(R.string.progress_importing, 0, 100);
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
@@ -208,10 +216,11 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
||||
if (entry.mExpectedFingerprint != null) {
|
||||
log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, "0x" +
|
||||
entry.mExpectedFingerprint.substring(24));
|
||||
data = keyServer.get("0x" + entry.mExpectedFingerprint).getBytes();
|
||||
data = keyServer.get("0x" + entry.mExpectedFingerprint, proxy)
|
||||
.getBytes();
|
||||
} else {
|
||||
log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER, 2, entry.mKeyIdHex);
|
||||
data = keyServer.get(entry.mKeyIdHex).getBytes();
|
||||
data = keyServer.get(entry.mKeyIdHex, proxy).getBytes();
|
||||
}
|
||||
key = UncachedKeyRing.decodeFromData(data);
|
||||
if (key != null) {
|
||||
@@ -234,7 +243,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
||||
|
||||
try {
|
||||
log.add(LogType.MSG_IMPORT_FETCH_KEYBASE, 2, entry.mKeybaseName);
|
||||
byte[] data = keybaseServer.get(entry.mKeybaseName).getBytes();
|
||||
byte[] data = keybaseServer.get(entry.mKeybaseName, proxy).getBytes();
|
||||
UncachedKeyRing keybaseKey = UncachedKeyRing.decodeFromData(data);
|
||||
|
||||
// If there already is a key, merge the two
|
||||
@@ -373,12 +382,11 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
||||
importedMasterKeyIdsArray);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) {
|
||||
return importKeys(importInput.mKeyList, importInput.mKeyserver);
|
||||
}
|
||||
|
||||
public ImportKeyResult importKeys(ArrayList<ParcelableKeyRing> keyList, String keyServer) {
|
||||
public OperationResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) {
|
||||
ArrayList<ParcelableKeyRing> keyList = importInput.mKeyList;
|
||||
String keyServer = importInput.mKeyserver;
|
||||
|
||||
ImportKeyResult result;
|
||||
|
||||
@@ -386,8 +394,21 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
||||
ParcelableFileCache<ParcelableKeyRing> cache = new ParcelableFileCache<>(mContext,
|
||||
"key_import.pcl");
|
||||
|
||||
result = serialKeyRingImport(cache, keyServer);
|
||||
result = serialKeyRingImport(cache, null, null);
|
||||
} else {
|
||||
Proxy proxy;
|
||||
if (cryptoInput.getParcelableProxy() == null) {
|
||||
// explicit proxy not set
|
||||
if(!OrbotHelper.isOrbotInRequiredState(mContext)) {
|
||||
// show dialog to enable/install dialog
|
||||
return new ImportKeyResult(null,
|
||||
RequiredInputParcel.createOrbotRequiredOperation());
|
||||
}
|
||||
proxy = Preferences.getPreferences(mContext).getProxyPrefs().parcelableProxy
|
||||
.getProxy();
|
||||
} else {
|
||||
proxy = cryptoInput.getParcelableProxy().getProxy();
|
||||
}
|
||||
// if there is more than one key with the same fingerprint, we do a serial import to
|
||||
// prevent
|
||||
// https://github.com/open-keychain/open-keychain/issues/1221
|
||||
@@ -397,9 +418,10 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
||||
}
|
||||
if (keyFingerprintSet.size() == keyList.size()) {
|
||||
// all keys have unique fingerprints
|
||||
result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer);
|
||||
result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer,
|
||||
proxy);
|
||||
} else {
|
||||
result = serialKeyRingImport(keyList, keyServer);
|
||||
result = serialKeyRingImport(keyList, keyServer, proxy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,8 +429,10 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ImportKeyResult multiThreadedKeyImport(Iterator<ParcelableKeyRing> keyListIterator,
|
||||
int totKeys, final String keyServer) {
|
||||
int totKeys, final String keyServer,
|
||||
final Proxy proxy) {
|
||||
Log.d(Constants.TAG, "Multi-threaded key import starting");
|
||||
if (keyListIterator != null) {
|
||||
KeyImportAccumulator accumulator = new KeyImportAccumulator(totKeys, mProgressable);
|
||||
@@ -421,7 +445,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
||||
new SynchronousQueue<Runnable>());
|
||||
|
||||
ExecutorCompletionService<ImportKeyResult> importCompletionService =
|
||||
new ExecutorCompletionService(importExecutor);
|
||||
new ExecutorCompletionService<>(importExecutor);
|
||||
|
||||
while (keyListIterator.hasNext()) { // submit all key rings to be imported
|
||||
|
||||
@@ -436,7 +460,7 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
||||
ArrayList<ParcelableKeyRing> list = new ArrayList<>();
|
||||
list.add(pkRing);
|
||||
|
||||
return serialKeyRingImport(list, keyServer, ignoreProgressable);
|
||||
return serialKeyRingImport(list, keyServer, ignoreProgressable, proxy);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -562,4 +586,4 @@ public class ImportOperation extends BaseOperation<ImportKeyringParcel> {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,15 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.operations;
|
||||
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.textuality.keybase.lib.Proof;
|
||||
import com.textuality.keybase.lib.prover.Prover;
|
||||
@@ -41,11 +49,9 @@ import org.sufficientlysecure.keychain.pgp.Progressable;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificationParcel> {
|
||||
|
||||
@@ -54,9 +60,22 @@ public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificat
|
||||
super(context, providerHelper, progressable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public KeybaseVerificationResult execute(KeybaseVerificationParcel keybaseInput,
|
||||
CryptoInputParcel cryptoInput) {
|
||||
Proxy proxy;
|
||||
if (cryptoInput.getParcelableProxy() == null) {
|
||||
// explicit proxy not set
|
||||
if (!OrbotHelper.isOrbotInRequiredState(mContext)) {
|
||||
return new KeybaseVerificationResult(null,
|
||||
RequiredInputParcel.createOrbotRequiredOperation());
|
||||
}
|
||||
proxy = Preferences.getPreferences(mContext).getProxyPrefs()
|
||||
.parcelableProxy.getProxy();
|
||||
} else {
|
||||
proxy = cryptoInput.getParcelableProxy().getProxy();
|
||||
}
|
||||
|
||||
String requiredFingerprint = keybaseInput.mRequiredFingerprint;
|
||||
|
||||
@@ -76,7 +95,7 @@ public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificat
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
|
||||
if (!prover.fetchProofData()) {
|
||||
if (!prover.fetchProofData(proxy)) {
|
||||
log.add(OperationResult.LogType.MSG_KEYBASE_ERROR_FETCH_PROOF, 1);
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
@@ -96,7 +115,7 @@ public class KeybaseVerificationOperation extends BaseOperation<KeybaseVerificat
|
||||
return new KeybaseVerificationResult(OperationResult.RESULT_ERROR, log);
|
||||
}
|
||||
Record[] records = dnsQuery.getAnswers();
|
||||
List<List<byte[]>> extents = new ArrayList<List<byte[]>>();
|
||||
List<List<byte[]>> extents = new ArrayList<>();
|
||||
for (Record r : records) {
|
||||
Data d = r.getPayload();
|
||||
if (d instanceof TXT) {
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.operations;
|
||||
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||
@@ -36,8 +40,6 @@ import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/** An operation which promotes a public key ring to a secret one.
|
||||
*
|
||||
* This operation can only be applied to public key rings where no secret key
|
||||
@@ -52,18 +54,10 @@ public class PromoteKeyOperation extends BaseOperation<PromoteKeyringParcel> {
|
||||
super(context, providerHelper, progressable, cancelled);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PromoteKeyResult execute(PromoteKeyringParcel promoteKeyringParcel,
|
||||
CryptoInputParcel cryptoInputParcel) {
|
||||
// Input
|
||||
long masterKeyId = promoteKeyringParcel.mKeyRingId;
|
||||
byte[] cardAid = promoteKeyringParcel.mCardAid;
|
||||
long[] subKeyIds = promoteKeyringParcel.mSubKeyIds;
|
||||
|
||||
return execute(masterKeyId, cardAid, subKeyIds);
|
||||
}
|
||||
|
||||
public PromoteKeyResult execute(long masterKeyId, byte[] cardAid, long[] subKeyIds) {
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
log.add(LogType.MSG_PR, 0);
|
||||
@@ -74,17 +68,17 @@ public class PromoteKeyOperation extends BaseOperation<PromoteKeyringParcel> {
|
||||
try {
|
||||
|
||||
log.add(LogType.MSG_PR_FETCHING, 1,
|
||||
KeyFormattingUtils.convertKeyIdToHex(masterKeyId));
|
||||
KeyFormattingUtils.convertKeyIdToHex(promoteKeyringParcel.mKeyRingId));
|
||||
CanonicalizedPublicKeyRing pubRing =
|
||||
mProviderHelper.getCanonicalizedPublicKeyRing(masterKeyId);
|
||||
mProviderHelper.getCanonicalizedPublicKeyRing(promoteKeyringParcel.mKeyRingId);
|
||||
|
||||
if (subKeyIds == null) {
|
||||
if (promoteKeyringParcel.mSubKeyIds == null) {
|
||||
log.add(LogType.MSG_PR_ALL, 1);
|
||||
} else {
|
||||
// sort for binary search
|
||||
for (CanonicalizedPublicKey key : pubRing.publicKeyIterator()) {
|
||||
long subKeyId = key.getKeyId();
|
||||
if (naiveIndexOf(subKeyIds, subKeyId) != null) {
|
||||
if (naiveIndexOf(promoteKeyringParcel.mSubKeyIds, subKeyId) != null) {
|
||||
log.add(LogType.MSG_PR_SUBKEY_MATCH, 1,
|
||||
KeyFormattingUtils.convertKeyIdToHex(subKeyId));
|
||||
} else {
|
||||
@@ -95,7 +89,8 @@ public class PromoteKeyOperation extends BaseOperation<PromoteKeyringParcel> {
|
||||
}
|
||||
|
||||
// create divert-to-card secret key from public key
|
||||
promotedRing = pubRing.createDivertSecretRing(cardAid, subKeyIds);
|
||||
promotedRing = pubRing.createDivertSecretRing(promoteKeyringParcel.mCardAid,
|
||||
promoteKeyringParcel.mSubKeyIds);
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.add(LogType.MSG_PR_ERROR_KEY_NOT_FOUND, 2);
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.operations;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||
@@ -36,6 +37,7 @@ import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.NfcSign
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequiredInputType;
|
||||
import org.sufficientlysecure.keychain.util.FileHelper;
|
||||
import org.sufficientlysecure.keychain.util.InputData;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.ProgressScaler;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -61,6 +63,7 @@ public class SignEncryptOperation extends BaseOperation<SignEncryptParcel> {
|
||||
super(context, providerHelper, progressable, cancelled);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public SignEncryptResult execute(SignEncryptParcel input, CryptoInputParcel cryptoInput) {
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
@@ -84,7 +87,7 @@ public class SignEncryptOperation extends BaseOperation<SignEncryptParcel> {
|
||||
input.getSignatureMasterKeyId()).getSecretSignId();
|
||||
input.setSignatureSubKeyId(signKeyId);
|
||||
} catch (PgpKeyNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(Constants.TAG, "Key not found", e);
|
||||
return new SignEncryptResult(SignEncryptResult.RESULT_ERROR, log, results);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,9 @@ package org.sufficientlysecure.keychain.operations.results;
|
||||
|
||||
import android.os.Parcel;
|
||||
|
||||
public class ExportResult extends OperationResult {
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
|
||||
public class ExportResult extends InputPendingResult {
|
||||
|
||||
final int mOkPublic, mOkSecret;
|
||||
|
||||
@@ -33,6 +35,14 @@ public class ExportResult extends OperationResult {
|
||||
mOkSecret = okSecret;
|
||||
}
|
||||
|
||||
|
||||
public ExportResult(OperationLog log, RequiredInputParcel requiredInputParcel) {
|
||||
super(log, requiredInputParcel);
|
||||
// we won't use these values
|
||||
mOkPublic = -1;
|
||||
mOkSecret = -1;
|
||||
}
|
||||
|
||||
/** Construct from a parcel - trivial because we have no extra data. */
|
||||
public ExportResult(Parcel source) {
|
||||
super(source);
|
||||
|
||||
@@ -23,6 +23,8 @@ import android.content.Intent;
|
||||
import android.os.Parcel;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
import org.sufficientlysecure.keychain.ui.LogDisplayActivity;
|
||||
import org.sufficientlysecure.keychain.ui.LogDisplayFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
@@ -30,7 +32,7 @@ import org.sufficientlysecure.keychain.ui.util.Notify.ActionListener;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify.Showable;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||
|
||||
public class ImportKeyResult extends OperationResult {
|
||||
public class ImportKeyResult extends InputPendingResult {
|
||||
|
||||
public final int mNewKeys, mUpdatedKeys, mBadKeys, mSecret;
|
||||
public final long[] mImportedMasterKeyIds;
|
||||
@@ -80,7 +82,7 @@ public class ImportKeyResult extends OperationResult {
|
||||
}
|
||||
|
||||
public ImportKeyResult(int result, OperationLog log) {
|
||||
this(result, log, 0, 0, 0, 0, new long[] { });
|
||||
this(result, log, 0, 0, 0, 0, new long[]{});
|
||||
}
|
||||
|
||||
public ImportKeyResult(int result, OperationLog log,
|
||||
@@ -94,6 +96,16 @@ public class ImportKeyResult extends OperationResult {
|
||||
mImportedMasterKeyIds = importedMasterKeyIds;
|
||||
}
|
||||
|
||||
public ImportKeyResult(OperationLog log, RequiredInputParcel requiredInputParcel) {
|
||||
super(log, requiredInputParcel);
|
||||
// just assign default values, we won't use them anyway
|
||||
mNewKeys = 0;
|
||||
mUpdatedKeys = 0;
|
||||
mBadKeys = 0;
|
||||
mSecret = 0;
|
||||
mImportedMasterKeyIds = new long[]{};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
|
||||
@@ -24,7 +24,9 @@ import android.os.Parcelable;
|
||||
import com.textuality.keybase.lib.KeybaseException;
|
||||
import com.textuality.keybase.lib.prover.Prover;
|
||||
|
||||
public class KeybaseVerificationResult extends OperationResult implements Parcelable {
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
|
||||
public class KeybaseVerificationResult extends InputPendingResult {
|
||||
public final String mProofUrl;
|
||||
public final String mPresenceUrl;
|
||||
public final String mPresenceLabel;
|
||||
@@ -44,6 +46,13 @@ public class KeybaseVerificationResult extends OperationResult implements Parcel
|
||||
mPresenceLabel = prover.getPresenceLabel();
|
||||
}
|
||||
|
||||
public KeybaseVerificationResult(OperationLog log, RequiredInputParcel requiredInputParcel) {
|
||||
super(log, requiredInputParcel);
|
||||
mProofUrl = null;
|
||||
mPresenceUrl = null;
|
||||
mPresenceLabel = null;
|
||||
}
|
||||
|
||||
protected KeybaseVerificationResult(Parcel in) {
|
||||
super(in);
|
||||
mProofUrl = in.readString();
|
||||
|
||||
@@ -609,6 +609,7 @@ public abstract class OperationResult implements Parcelable {
|
||||
MSG_DC_ERROR_INTEGRITY_MISSING (LogLevel.ERROR, R.string.msg_dc_error_integrity_missing),
|
||||
MSG_DC_ERROR_INVALID_DATA (LogLevel.ERROR, R.string.msg_dc_error_invalid_data),
|
||||
MSG_DC_ERROR_IO (LogLevel.ERROR, R.string.msg_dc_error_io),
|
||||
MSG_DC_ERROR_INPUT (LogLevel.ERROR, R.string.msg_dc_error_input),
|
||||
MSG_DC_ERROR_NO_DATA (LogLevel.ERROR, R.string.msg_dc_error_no_data),
|
||||
MSG_DC_ERROR_NO_KEY (LogLevel.ERROR, R.string.msg_dc_error_no_key),
|
||||
MSG_DC_ERROR_PGP_EXCEPTION (LogLevel.ERROR, R.string.msg_dc_error_pgp_exception),
|
||||
@@ -692,6 +693,7 @@ public abstract class OperationResult implements Parcelable {
|
||||
MSG_CRT_WARN_NOT_FOUND (LogLevel.WARN, R.string.msg_crt_warn_not_found),
|
||||
MSG_CRT_WARN_CERT_FAILED (LogLevel.WARN, R.string.msg_crt_warn_cert_failed),
|
||||
MSG_CRT_WARN_SAVE_FAILED (LogLevel.WARN, R.string.msg_crt_warn_save_failed),
|
||||
MSG_CRT_WARN_UPLOAD_FAILED (LogLevel.WARN, R.string.msg_crt_warn_upload_failed),
|
||||
|
||||
MSG_IMPORT (LogLevel.START, R.plurals.msg_import),
|
||||
|
||||
@@ -712,6 +714,7 @@ public abstract class OperationResult implements Parcelable {
|
||||
MSG_IMPORT_SUCCESS (LogLevel.OK, R.string.msg_import_success),
|
||||
|
||||
MSG_EXPORT (LogLevel.START, R.plurals.msg_export),
|
||||
MSG_EXPORT_UPLOAD_PUBLIC (LogLevel.START, R.string.msg_export_upload_public),
|
||||
MSG_EXPORT_PUBLIC (LogLevel.DEBUG, R.string.msg_export_public),
|
||||
MSG_EXPORT_SECRET (LogLevel.DEBUG, R.string.msg_export_secret),
|
||||
MSG_EXPORT_ALL (LogLevel.START, R.string.msg_export_all),
|
||||
@@ -723,7 +726,9 @@ public abstract class OperationResult implements Parcelable {
|
||||
MSG_EXPORT_ERROR_DB (LogLevel.ERROR, R.string.msg_export_error_db),
|
||||
MSG_EXPORT_ERROR_IO (LogLevel.ERROR, R.string.msg_export_error_io),
|
||||
MSG_EXPORT_ERROR_KEY (LogLevel.ERROR, R.string.msg_export_error_key),
|
||||
MSG_EXPORT_ERROR_UPLOAD (LogLevel.ERROR, R.string.msg_export_error_upload),
|
||||
MSG_EXPORT_SUCCESS (LogLevel.OK, R.string.msg_export_success),
|
||||
MSG_EXPORT_UPLOAD_SUCCESS (LogLevel.OK, R.string.msg_export_upload_success),
|
||||
|
||||
MSG_CRT_UPLOAD_SUCCESS (LogLevel.OK, R.string.msg_crt_upload_success),
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/** A generic wrapped PGPKeyRing object.
|
||||
*
|
||||
@@ -91,6 +94,16 @@ public abstract class CanonicalizedKeyRing extends KeyRing {
|
||||
return getRing().getPublicKey().isEncryptionKey();
|
||||
}
|
||||
|
||||
public Set<Long> getEncryptIds() {
|
||||
HashSet<Long> result = new HashSet<>();
|
||||
for(CanonicalizedPublicKey key : publicKeyIterator()) {
|
||||
if (key.canEncrypt() && key.isValid()) {
|
||||
result.add(key.getKeyId());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public long getEncryptId() throws PgpKeyNotFoundException {
|
||||
for(CanonicalizedPublicKey key : publicKeyIterator()) {
|
||||
if (key.canEncrypt() && key.isValid()) {
|
||||
|
||||
@@ -62,19 +62,6 @@ public class CanonicalizedPublicKeyRing extends CanonicalizedKeyRing {
|
||||
return mRing;
|
||||
}
|
||||
|
||||
/** Getter that returns the subkey that should be used for signing. */
|
||||
CanonicalizedPublicKey getEncryptionSubKey() throws PgpKeyNotFoundException {
|
||||
PGPPublicKey key = getRing().getPublicKey(getEncryptId());
|
||||
if(key != null) {
|
||||
CanonicalizedPublicKey cKey = new CanonicalizedPublicKey(this, key);
|
||||
if(!cKey.canEncrypt()) {
|
||||
throw new PgpKeyNotFoundException("key error");
|
||||
}
|
||||
return cKey;
|
||||
}
|
||||
throw new PgpKeyNotFoundException("no encryption key available");
|
||||
}
|
||||
|
||||
public IterableIterator<CanonicalizedPublicKey> publicKeyIterator() {
|
||||
@SuppressWarnings("unchecked")
|
||||
final Iterator<PGPPublicKey> it = getRing().getPublicKeys();
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import org.openintents.openpgp.OpenPgpMetadata;
|
||||
@@ -80,9 +81,8 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
|
||||
super(context, providerHelper, progressable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypts and/or verifies data based on parameters of class
|
||||
*/
|
||||
/** Decrypts and/or verifies data based on parameters of PgpDecryptVerifyInputParcel. */
|
||||
@NonNull
|
||||
public DecryptVerifyResult execute(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput) {
|
||||
InputData inputData;
|
||||
OutputStream outputStream;
|
||||
@@ -96,8 +96,10 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
|
||||
long inputSize = FileHelper.getFileSize(mContext, input.getInputUri(), 0);
|
||||
inputData = new InputData(inputStream, inputSize);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
Log.e(Constants.TAG, "Input URI could not be opened: " + input.getInputUri(), e);
|
||||
OperationLog log = new OperationLog();
|
||||
log.add(LogType.MSG_DC_ERROR_INPUT, 1);
|
||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,8 +109,10 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
|
||||
try {
|
||||
outputStream = mContext.getContentResolver().openOutputStream(input.getOutputUri());
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
Log.e(Constants.TAG, "Output URI could not be opened: " + input.getOutputUri(), e);
|
||||
OperationLog log = new OperationLog();
|
||||
log.add(LogType.MSG_DC_ERROR_IO, 1);
|
||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,11 +126,13 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public DecryptVerifyResult execute(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput,
|
||||
InputData inputData, OutputStream outputStream) {
|
||||
return executeInternal(input, cryptoInput, inputData, outputStream);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private DecryptVerifyResult executeInternal(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput,
|
||||
InputData inputData, OutputStream outputStream) {
|
||||
try {
|
||||
@@ -161,6 +167,13 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
|
||||
OperationLog log = new OperationLog();
|
||||
log.add(LogType.MSG_DC_ERROR_PGP_EXCEPTION, 1);
|
||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
// these can happen if assumptions in JcaPGPObjectFactory.nextObject() aren't
|
||||
// fulfilled, so we need to catch them here to handle this gracefully
|
||||
Log.d(Constants.TAG, "array index out of bounds", e);
|
||||
OperationLog log = new OperationLog();
|
||||
log.add(LogType.MSG_DC_ERROR_IO, 1);
|
||||
return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log);
|
||||
} catch (IOException e) {
|
||||
Log.d(Constants.TAG, "IOException", e);
|
||||
OperationLog log = new OperationLog();
|
||||
@@ -169,9 +182,8 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify Keybase.io style signed literal data
|
||||
*/
|
||||
/**Verify signed plaintext data (PGP/INLINE). */
|
||||
@NonNull
|
||||
private DecryptVerifyResult verifySignedLiteralData(
|
||||
PgpDecryptVerifyInputParcel input, InputStream in, OutputStream out, int indent)
|
||||
throws IOException, PGPException {
|
||||
@@ -301,9 +313,8 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decrypt and/or verifies binary or ascii armored pgp
|
||||
*/
|
||||
/** Decrypt and/or verify binary or ascii armored pgp data. */
|
||||
@NonNull
|
||||
private DecryptVerifyResult decryptVerify(
|
||||
PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput,
|
||||
InputStream in, OutputStream out, int indent) throws IOException, PGPException {
|
||||
@@ -843,6 +854,7 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
|
||||
* The method is heavily based on
|
||||
* pg/src/main/java/org/spongycastle/openpgp/examples/ClearSignedFileProcessor.java
|
||||
*/
|
||||
@NonNull
|
||||
private DecryptVerifyResult verifyCleartextSignature(
|
||||
ArmoredInputStream aIn, OutputStream outputStream, int indent) throws IOException, PGPException {
|
||||
|
||||
@@ -950,6 +962,7 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
|
||||
return result;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private DecryptVerifyResult verifyDetachedSignature(
|
||||
PgpDecryptVerifyInputParcel input, InputData inputData, OutputStream out, int indent)
|
||||
throws IOException, PGPException {
|
||||
@@ -1042,7 +1055,9 @@ public class PgpDecryptVerify extends BaseOperation<PgpDecryptVerifyInputParcel>
|
||||
return result;
|
||||
}
|
||||
|
||||
private PGPSignature processPGPSignatureList(PGPSignatureList sigList, OpenPgpSignatureResultBuilder signatureResultBuilder) throws PGPException {
|
||||
private PGPSignature processPGPSignatureList(
|
||||
PGPSignatureList sigList, OpenPgpSignatureResultBuilder signatureResultBuilder)
|
||||
throws PGPException {
|
||||
CanonicalizedPublicKeyRing signingRing = null;
|
||||
CanonicalizedPublicKey signingKey = null;
|
||||
int signatureIndex = -1;
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import org.spongycastle.bcpg.ArmoredOutputStream;
|
||||
import org.spongycastle.bcpg.BCPGOutputStream;
|
||||
@@ -36,6 +38,7 @@ import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.BaseOperation;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog;
|
||||
import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult;
|
||||
@@ -63,6 +66,7 @@ import java.security.SignatureException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
@@ -99,6 +103,13 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
||||
super(context, providerHelper, progressable);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
// TODO this is horrible, refactor ASAP!!
|
||||
public OperationResult execute(Parcelable input, CryptoInputParcel cryptoInput) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signs and/or encrypts data based on parameters of class
|
||||
*/
|
||||
@@ -263,15 +274,19 @@ public class PgpSignEncryptOperation extends BaseOperation {
|
||||
try {
|
||||
CanonicalizedPublicKeyRing keyRing = mProviderHelper.getCanonicalizedPublicKeyRing(
|
||||
KeyRings.buildUnifiedKeyRingUri(id));
|
||||
CanonicalizedPublicKey key = keyRing.getEncryptionSubKey();
|
||||
cPk.addMethod(key.getPubKeyEncryptionGenerator(input.isHiddenRecipients()));
|
||||
log.add(LogType.MSG_PSE_KEY_OK, indent + 1,
|
||||
KeyFormattingUtils.convertKeyIdToHex(id));
|
||||
} catch (PgpKeyNotFoundException e) {
|
||||
log.add(LogType.MSG_PSE_KEY_WARN, indent + 1,
|
||||
KeyFormattingUtils.convertKeyIdToHex(id));
|
||||
if (input.isFailOnMissingEncryptionKeyIds()) {
|
||||
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
|
||||
Set<Long> encryptSubKeyIds = keyRing.getEncryptIds();
|
||||
for (Long subKeyId : encryptSubKeyIds) {
|
||||
CanonicalizedPublicKey key = keyRing.getPublicKey(subKeyId);
|
||||
cPk.addMethod(key.getPubKeyEncryptionGenerator(input.isHiddenRecipients()));
|
||||
log.add(LogType.MSG_PSE_KEY_OK, indent + 1,
|
||||
KeyFormattingUtils.convertKeyIdToHex(id));
|
||||
}
|
||||
if (encryptSubKeyIds.isEmpty()) {
|
||||
log.add(LogType.MSG_PSE_KEY_WARN, indent + 1,
|
||||
KeyFormattingUtils.convertKeyIdToHex(id));
|
||||
if (input.isFailOnMissingEncryptionKeyIds()) {
|
||||
return new PgpSignEncryptResult(PgpSignEncryptResult.RESULT_ERROR, log);
|
||||
}
|
||||
}
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
log.add(LogType.MSG_PSE_KEY_UNKNOWN, indent + 1,
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.content.OperationApplicationException;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.util.LongSparseArray;
|
||||
|
||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||
@@ -725,7 +726,7 @@ public class ProviderHelper {
|
||||
LongSparseArray<WrappedSignature> trustedCerts = new LongSparseArray<>();
|
||||
|
||||
@Override
|
||||
public int compareTo(UserPacketItem o) {
|
||||
public int compareTo(@NonNull UserPacketItem o) {
|
||||
// revoked keys always come last!
|
||||
//noinspection DoubleNegation
|
||||
if ((selfRevocation != null) != (o.selfRevocation != null)) {
|
||||
@@ -906,7 +907,8 @@ public class ProviderHelper {
|
||||
// If there is a secret key, merge new data (if any) and save the key for later
|
||||
CanonicalizedSecretKeyRing canSecretRing;
|
||||
try {
|
||||
UncachedKeyRing secretRing = getCanonicalizedSecretKeyRing(publicRing.getMasterKeyId()).getUncachedKeyRing();
|
||||
UncachedKeyRing secretRing = getCanonicalizedSecretKeyRing(publicRing.getMasterKeyId())
|
||||
.getUncachedKeyRing();
|
||||
|
||||
// Merge data from new public ring into secret one
|
||||
log(LogType.MSG_IP_MERGE_SECRET);
|
||||
@@ -1031,7 +1033,8 @@ public class ProviderHelper {
|
||||
publicRing = secretRing.extractPublicKeyRing();
|
||||
}
|
||||
|
||||
CanonicalizedPublicKeyRing canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog, mIndent);
|
||||
CanonicalizedPublicKeyRing canPublicRing = (CanonicalizedPublicKeyRing) publicRing.canonicalize(mLog,
|
||||
mIndent);
|
||||
if (canPublicRing == null) {
|
||||
return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null);
|
||||
}
|
||||
@@ -1057,6 +1060,7 @@ public class ProviderHelper {
|
||||
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ConsolidateResult consolidateDatabaseStep1(Progressable progress) {
|
||||
|
||||
OperationLog log = new OperationLog();
|
||||
@@ -1082,7 +1086,7 @@ public class ProviderHelper {
|
||||
indent += 1;
|
||||
|
||||
final Cursor cursor = mContentResolver.query(KeyRingData.buildSecretKeyRingUri(),
|
||||
new String[]{ KeyRingData.KEY_RING_DATA }, null, null, null);
|
||||
new String[]{KeyRingData.KEY_RING_DATA}, null, null, null);
|
||||
|
||||
if (cursor == null) {
|
||||
log.add(LogType.MSG_CON_ERROR_DB, indent);
|
||||
@@ -1124,6 +1128,7 @@ public class ProviderHelper {
|
||||
}
|
||||
|
||||
});
|
||||
cursor.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "error saving secret", e);
|
||||
@@ -1143,7 +1148,7 @@ public class ProviderHelper {
|
||||
|
||||
final Cursor cursor = mContentResolver.query(
|
||||
KeyRingData.buildPublicKeyRingUri(),
|
||||
new String[]{ KeyRingData.KEY_RING_DATA }, null, null, null);
|
||||
new String[]{KeyRingData.KEY_RING_DATA}, null, null, null);
|
||||
|
||||
if (cursor == null) {
|
||||
log.add(LogType.MSG_CON_ERROR_DB, indent);
|
||||
@@ -1185,6 +1190,7 @@ public class ProviderHelper {
|
||||
}
|
||||
|
||||
});
|
||||
cursor.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.e(Constants.TAG, "error saving public", e);
|
||||
@@ -1200,12 +1206,14 @@ public class ProviderHelper {
|
||||
return consolidateDatabaseStep2(log, indent, progress, false);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ConsolidateResult consolidateDatabaseStep2(Progressable progress) {
|
||||
return consolidateDatabaseStep2(new OperationLog(), 0, progress, true);
|
||||
}
|
||||
|
||||
private static boolean mConsolidateCritical = false;
|
||||
|
||||
@NonNull
|
||||
private ConsolidateResult consolidateDatabaseStep2(
|
||||
OperationLog log, int indent, Progressable progress, boolean recovery) {
|
||||
|
||||
@@ -1250,7 +1258,7 @@ public class ProviderHelper {
|
||||
|
||||
ImportKeyResult result = new ImportOperation(mContext, this,
|
||||
new ProgressFixedScaler(progress, 10, 25, 100, R.string.progress_con_reimport))
|
||||
.serialKeyRingImport(itSecrets, numSecrets, null);
|
||||
.serialKeyRingImport(itSecrets, numSecrets, null, null);
|
||||
log.add(result, indent);
|
||||
} else {
|
||||
log.add(LogType.MSG_CON_REIMPORT_SECRET_SKIP, indent);
|
||||
@@ -1278,7 +1286,7 @@ public class ProviderHelper {
|
||||
|
||||
ImportKeyResult result = new ImportOperation(mContext, this,
|
||||
new ProgressFixedScaler(progress, 25, 99, 100, R.string.progress_con_reimport))
|
||||
.serialKeyRingImport(itPublics, numPublics, null);
|
||||
.serialKeyRingImport(itPublics, numPublics, null, null);
|
||||
log.add(result, indent);
|
||||
} else {
|
||||
log.add(LogType.MSG_CON_REIMPORT_PUBLIC_SKIP, indent);
|
||||
|
||||
@@ -22,13 +22,10 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||
|
||||
|
||||
/**
|
||||
@@ -54,6 +51,7 @@ public class CertifyActionsParcel implements Parcelable {
|
||||
mMasterKeyId = source.readLong();
|
||||
// just like parcelables, this is meant for ad-hoc IPC only and is NOT portable!
|
||||
mLevel = CertifyLevel.values()[source.readInt()];
|
||||
keyServerUri = source.readString();
|
||||
|
||||
mCertifyActions = (ArrayList<CertifyAction>) source.readSerializable();
|
||||
}
|
||||
@@ -66,6 +64,7 @@ public class CertifyActionsParcel implements Parcelable {
|
||||
public void writeToParcel(Parcel destination, int flags) {
|
||||
destination.writeLong(mMasterKeyId);
|
||||
destination.writeInt(mLevel.ordinal());
|
||||
destination.writeString(keyServerUri);
|
||||
|
||||
destination.writeSerializable(mCertifyActions);
|
||||
}
|
||||
@@ -94,7 +93,7 @@ public class CertifyActionsParcel implements Parcelable {
|
||||
}
|
||||
|
||||
public CertifyAction(long masterKeyId, ArrayList<String> userIds,
|
||||
ArrayList<WrappedUserAttribute> attributes) {
|
||||
ArrayList<WrappedUserAttribute> attributes) {
|
||||
mMasterKeyId = masterKeyId;
|
||||
mUserIds = userIds;
|
||||
mUserAttributes = attributes;
|
||||
|
||||
@@ -17,17 +17,17 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.service.input;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||
import org.sufficientlysecure.keychain.util.Passphrase;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This is a base class for the input of crypto operations.
|
||||
*/
|
||||
@@ -35,6 +35,8 @@ public class CryptoInputParcel implements Parcelable {
|
||||
|
||||
final Date mSignatureTime;
|
||||
final Passphrase mPassphrase;
|
||||
// used to supply an explicit proxy to operations that require it
|
||||
private ParcelableProxy mParcelableProxy;
|
||||
|
||||
// this map contains both decrypted session keys and signed hashes to be
|
||||
// used in the crypto operation described by this parcel.
|
||||
@@ -43,26 +45,37 @@ public class CryptoInputParcel implements Parcelable {
|
||||
public CryptoInputParcel() {
|
||||
mSignatureTime = new Date();
|
||||
mPassphrase = null;
|
||||
mParcelableProxy = null;
|
||||
}
|
||||
|
||||
public CryptoInputParcel(Date signatureTime, Passphrase passphrase) {
|
||||
mSignatureTime = signatureTime == null ? new Date() : signatureTime;
|
||||
mPassphrase = passphrase;
|
||||
mParcelableProxy = null;
|
||||
}
|
||||
|
||||
public CryptoInputParcel(Passphrase passphrase) {
|
||||
mSignatureTime = new Date();
|
||||
mPassphrase = passphrase;
|
||||
mParcelableProxy = null;
|
||||
}
|
||||
|
||||
public CryptoInputParcel(Date signatureTime) {
|
||||
mSignatureTime = signatureTime == null ? new Date() : signatureTime;
|
||||
mPassphrase = null;
|
||||
mParcelableProxy = null;
|
||||
}
|
||||
|
||||
public CryptoInputParcel(ParcelableProxy parcelableProxy) {
|
||||
mSignatureTime = new Date(); // just for compatibility with parcel-ing
|
||||
mPassphrase = null;
|
||||
mParcelableProxy = parcelableProxy;
|
||||
}
|
||||
|
||||
protected CryptoInputParcel(Parcel source) {
|
||||
mSignatureTime = new Date(source.readLong());
|
||||
mPassphrase = source.readParcelable(getClass().getClassLoader());
|
||||
mParcelableProxy = source.readParcelable(getClass().getClassLoader());
|
||||
|
||||
{
|
||||
int count = source.readInt();
|
||||
@@ -85,6 +98,7 @@ public class CryptoInputParcel implements Parcelable {
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeLong(mSignatureTime.getTime());
|
||||
dest.writeParcelable(mPassphrase, 0);
|
||||
dest.writeParcelable(mParcelableProxy, 0);
|
||||
|
||||
dest.writeInt(mCryptoData.size());
|
||||
for (HashMap.Entry<ByteBuffer, byte[]> entry : mCryptoData.entrySet()) {
|
||||
@@ -93,6 +107,10 @@ public class CryptoInputParcel implements Parcelable {
|
||||
}
|
||||
}
|
||||
|
||||
public void addParcelableProxy(ParcelableProxy parcelableProxy) {
|
||||
mParcelableProxy = parcelableProxy;
|
||||
}
|
||||
|
||||
public void addCryptoData(byte[] hash, byte[] signedHash) {
|
||||
mCryptoData.put(ByteBuffer.wrap(hash), signedHash);
|
||||
}
|
||||
@@ -101,6 +119,10 @@ public class CryptoInputParcel implements Parcelable {
|
||||
mCryptoData.putAll(cachedSessionKeys);
|
||||
}
|
||||
|
||||
public ParcelableProxy getParcelableProxy() {
|
||||
return mParcelableProxy;
|
||||
}
|
||||
|
||||
public Map<ByteBuffer, byte[]> getCryptoData() {
|
||||
return mCryptoData;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.util.Date;
|
||||
public class RequiredInputParcel implements Parcelable {
|
||||
|
||||
public enum RequiredInputType {
|
||||
PASSPHRASE, PASSPHRASE_SYMMETRIC, NFC_SIGN, NFC_DECRYPT, NFC_MOVE_KEY_TO_CARD
|
||||
PASSPHRASE, PASSPHRASE_SYMMETRIC, NFC_SIGN, NFC_DECRYPT, NFC_MOVE_KEY_TO_CARD, ENABLE_ORBOT
|
||||
}
|
||||
|
||||
public Date mSignatureTime;
|
||||
@@ -77,6 +77,10 @@ public class RequiredInputParcel implements Parcelable {
|
||||
return mSubKeyId;
|
||||
}
|
||||
|
||||
public static RequiredInputParcel createOrbotRequiredOperation() {
|
||||
return new RequiredInputParcel(RequiredInputType.ENABLE_ORBOT, null, null, null, 0L, 0L);
|
||||
}
|
||||
|
||||
public static RequiredInputParcel createNfcSignOperation(
|
||||
long masterKeyId, long subKeyId,
|
||||
byte[] inputHash, int signAlgo, Date signatureTime) {
|
||||
|
||||
@@ -54,6 +54,7 @@ import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -311,6 +312,11 @@ public class CertifyKeyFragment
|
||||
CertifyActionsParcel actionsParcel = new CertifyActionsParcel(selectedKeyId);
|
||||
actionsParcel.mCertifyActions.addAll(certifyActions);
|
||||
|
||||
if (mUploadKeyCheckbox.isChecked()) {
|
||||
actionsParcel.keyServerUri = Preferences.getPreferences(getActivity())
|
||||
.getPreferredKeyserver();
|
||||
}
|
||||
|
||||
// cached for next cryptoOperation loop
|
||||
cacheActionsParcel(actionsParcel);
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -385,14 +386,18 @@ public class CreateKeyFinalFragment extends Fragment {
|
||||
}
|
||||
|
||||
public void handleResult(ExportResult result) {
|
||||
// TODO: ExportOperation UPLOAD_KEYSERVER needs logs!
|
||||
// TODO: then merge logs here!
|
||||
//saveKeyResult.getLog().add(result, 0);
|
||||
Activity activity = getActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
saveKeyResult.getLog().add(result, 0);
|
||||
|
||||
Intent data = new Intent();
|
||||
data.putExtra(OperationResult.EXTRA_RESULT, saveKeyResult);
|
||||
getActivity().setResult(Activity.RESULT_OK, data);
|
||||
getActivity().finish();
|
||||
activity.setResult(Activity.RESULT_OK, data);
|
||||
activity.finish();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -41,7 +41,9 @@ import org.sufficientlysecure.keychain.ui.CreateKeyActivity.FragAction;
|
||||
import org.sufficientlysecure.keychain.ui.CreateKeyActivity.NfcListenerFragment;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
|
||||
public class CreateYubiKeyImportFragment
|
||||
@@ -131,7 +133,18 @@ public class CreateYubiKeyImportFragment
|
||||
view.findViewById(R.id.button_search).setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
refreshSearch();
|
||||
final Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(getActivity()).getProxyPrefs();
|
||||
Runnable ignoreTor = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
refreshSearch(new ParcelableProxy(null, -1, null));
|
||||
}
|
||||
};
|
||||
|
||||
if (OrbotHelper.putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTor, proxyPrefs,
|
||||
getActivity())) {
|
||||
refreshSearch(proxyPrefs.parcelableProxy);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -170,9 +183,10 @@ public class CreateYubiKeyImportFragment
|
||||
}
|
||||
}
|
||||
|
||||
public void refreshSearch() {
|
||||
public void refreshSearch(ParcelableProxy parcelableProxy) {
|
||||
// TODO: PHILIP verify proxy implementation in YubiKey parts
|
||||
mListFragment.loadNew(new ImportKeysListFragment.CloudLoaderState("0x" + mNfcFingerprint,
|
||||
Preferences.getPreferences(getActivity()).getCloudSearchPrefs()));
|
||||
Preferences.getPreferences(getActivity()).getCloudSearchPrefs()), parcelableProxy);
|
||||
}
|
||||
|
||||
public void importKey() {
|
||||
@@ -210,7 +224,20 @@ public class CreateYubiKeyImportFragment
|
||||
public void onNfcPostExecute() throws IOException {
|
||||
|
||||
setData();
|
||||
refreshSearch();
|
||||
|
||||
Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(getActivity()).getProxyPrefs();
|
||||
Runnable ignoreTor = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
refreshSearch(new ParcelableProxy(null, -1, null));
|
||||
}
|
||||
};
|
||||
|
||||
if (OrbotHelper.putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTor, proxyPrefs,
|
||||
getActivity())) {
|
||||
refreshSearch(proxyPrefs.parcelableProxy);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,15 +19,11 @@ package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.CursorLoader;
|
||||
import android.support.v4.content.Loader;
|
||||
@@ -52,13 +48,14 @@ import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.base.CachingCryptoOperationFragment;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
public abstract class DecryptFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
|
||||
|
||||
|
||||
@@ -304,7 +304,7 @@ public class DecryptListFragment
|
||||
}
|
||||
|
||||
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setType(type);
|
||||
intent.setDataAndType(outputUri, type);
|
||||
|
||||
final List<ResolveInfo> matches =
|
||||
context.getPackageManager().queryIntentActivities(intent, 0);
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -29,9 +33,9 @@ import android.widget.TextView;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
||||
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||
import org.sufficientlysecure.keychain.util.ShareHelper;
|
||||
|
||||
public class DisplayTextFragment extends DecryptFragment {
|
||||
@@ -80,8 +84,19 @@ public class DisplayTextFragment extends DecryptFragment {
|
||||
}
|
||||
|
||||
private void copyToClipboard(String text) {
|
||||
ClipboardReflection.copyToClipboard(getActivity(), text);
|
||||
Notify.create(getActivity(), R.string.text_copied_to_clipboard, Notify.Style.OK).show();
|
||||
Activity activity = getActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
if (clipMan == null) {
|
||||
Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR).show();
|
||||
return;
|
||||
}
|
||||
|
||||
clipMan.setPrimaryClip(ClipData.newPlainText(Constants.CLIPBOARD_LABEL, text));
|
||||
Notify.create(activity, R.string.text_copied_to_clipboard, Style.OK).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -124,8 +124,7 @@ public class EncryptDecryptOverviewFragment extends Fragment {
|
||||
super.onResume();
|
||||
|
||||
// get text from clipboard
|
||||
final CharSequence clipboardText =
|
||||
ClipboardReflection.getClipboardText(getActivity());
|
||||
final CharSequence clipboardText = ClipboardReflection.getClipboardText(getActivity());
|
||||
|
||||
// if it's null, nothing to do here /o/
|
||||
if (clipboardText == null) {
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.graphics.Point;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
import android.support.v7.widget.DefaultItemAnimator;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
@@ -388,6 +389,12 @@ public class EncryptFilesFragment
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(final SignEncryptResult result) {
|
||||
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity == null) {
|
||||
// it's gone, there's nothing we can do here
|
||||
return;
|
||||
}
|
||||
|
||||
if (mDeleteAfterEncrypt) {
|
||||
// TODO make behavior coherent here
|
||||
DeleteFileDialogFragment deleteFileDialog =
|
||||
@@ -400,13 +407,18 @@ public class EncryptFilesFragment
|
||||
// Share encrypted message/file
|
||||
startActivity(sendWithChooserExcludingEncrypt());
|
||||
} else {
|
||||
Activity activity = getActivity();
|
||||
if (activity == null) {
|
||||
// it's gone, there's nothing we can do here
|
||||
return;
|
||||
}
|
||||
// Save encrypted file
|
||||
result.createNotify(getActivity()).show();
|
||||
result.createNotify(activity).show();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
deleteFileDialog.show(getActivity().getSupportFragmentManager(), "deleteDialog");
|
||||
deleteFileDialog.show(activity.getSupportFragmentManager(), "deleteDialog");
|
||||
} else {
|
||||
|
||||
switch (mAfterEncryptAction) {
|
||||
@@ -417,25 +429,24 @@ public class EncryptFilesFragment
|
||||
break;
|
||||
|
||||
case COPY:
|
||||
Activity activity = getActivity();
|
||||
if (activity == null) {
|
||||
// it's gone, there's nothing we can do here
|
||||
return;
|
||||
}
|
||||
|
||||
ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
if (clipMan == null) {
|
||||
Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR).show();
|
||||
break;
|
||||
}
|
||||
ClipData clip = new ClipData(getString(R.string.label_clip_title),
|
||||
// make available as application/pgp-encrypted
|
||||
new String[] { "text/plain" },
|
||||
new ClipData.Item(mOutputUris.get(0))
|
||||
);
|
||||
clipMan.setPrimaryClip(clip);
|
||||
result.createNotify(getActivity()).show();
|
||||
result.createNotify(activity).show();
|
||||
break;
|
||||
|
||||
case SAVE:
|
||||
// Encrypted file was saved already, just show notification
|
||||
result.createNotify(getActivity()).show();
|
||||
result.createNotify(activity).show();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
@@ -33,7 +36,6 @@ import android.widget.TextView;
|
||||
import org.spongycastle.bcpg.CompressionAlgorithmTags;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
||||
import org.sufficientlysecure.keychain.operations.results.SignEncryptResult;
|
||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpConstants;
|
||||
@@ -265,8 +267,21 @@ public class EncryptTextFragment
|
||||
return data;
|
||||
}
|
||||
|
||||
private void copyToClipboard(byte[] resultBytes) {
|
||||
ClipboardReflection.copyToClipboard(getActivity(), new String(resultBytes));
|
||||
private void copyToClipboard(SignEncryptResult result) {
|
||||
Activity activity = getActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
if (clipMan == null) {
|
||||
Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR).show();
|
||||
return;
|
||||
}
|
||||
|
||||
ClipData clip = ClipData.newPlainText(Constants.CLIPBOARD_LABEL, new String(result.getResultBytes()));
|
||||
clipMan.setPrimaryClip(clip);
|
||||
result.createNotify(activity).show();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -323,11 +338,7 @@ public class EncryptTextFragment
|
||||
startActivity(sendWithChooserExcludingEncrypt(result.getResultBytes()));
|
||||
} else {
|
||||
// Copy to clipboard
|
||||
copyToClipboard(result.getResultBytes());
|
||||
result.createNotify(getActivity()).show();
|
||||
// Notify.create(EncryptTextActivity.this,
|
||||
// R.string.encrypt_sign_clipboard_successful, Notify.Style.OK)
|
||||
// .show(getSupportFragmentManager().findFragmentById(R.id.encrypt_text_fragment));
|
||||
copyToClipboard(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,6 +42,8 @@ import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@@ -87,11 +89,14 @@ public class ImportKeysActivity extends BaseNfcActivity
|
||||
private ArrayList<ParcelableKeyRing> mKeyList;
|
||||
|
||||
private CryptoOperationHelper<ImportKeyringParcel, ImportKeyResult> mOperationHelper;
|
||||
private Preferences.ProxyPrefs mProxyPrefs;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
mProxyPrefs = Preferences.getPreferences(this).getProxyPrefs();
|
||||
|
||||
mImportButton = findViewById(R.id.import_import);
|
||||
mImportButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
@@ -224,7 +229,7 @@ public class ImportKeysActivity extends BaseNfcActivity
|
||||
Notify.Style.WARN).show(mTopFragment);
|
||||
// we just set the keyserver
|
||||
startCloudFragment(savedInstanceState, null, false, keyserver);
|
||||
// it's not necessary to set the keyserver for ImportKeysListFragment since
|
||||
// we don't set the keyserver for ImportKeysListFragment since
|
||||
// it'll be taken care of by ImportKeysCloudFragment when the user clicks
|
||||
// the search button
|
||||
startListFragment(savedInstanceState, null, null, null, null);
|
||||
@@ -316,7 +321,8 @@ public class ImportKeysActivity extends BaseNfcActivity
|
||||
* specified in user preferences
|
||||
*/
|
||||
|
||||
private void startCloudFragment(Bundle savedInstanceState, String query, boolean disableQueryEdit, String keyserver) {
|
||||
private void startCloudFragment(Bundle savedInstanceState, String query, boolean disableQueryEdit, String
|
||||
keyserver) {
|
||||
// However, if we're being restored from a previous state,
|
||||
// then we don't need to do anything and should return or else
|
||||
// we could end up with overlapping fragments.
|
||||
@@ -346,8 +352,24 @@ public class ImportKeysActivity extends BaseNfcActivity
|
||||
}
|
||||
}
|
||||
|
||||
public void loadCallback(ImportKeysListFragment.LoaderState loaderState) {
|
||||
mListFragment.loadNew(loaderState);
|
||||
public void loadCallback(final ImportKeysListFragment.LoaderState loaderState) {
|
||||
if (loaderState instanceof ImportKeysListFragment.CloudLoaderState) {
|
||||
// do the tor check
|
||||
// this handle will set tor to be ignored whenever a message is received
|
||||
Runnable ignoreTor = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// disables Tor until Activity is recreated
|
||||
mProxyPrefs = new Preferences.ProxyPrefs(false, false, null, -1, null);
|
||||
mListFragment.loadNew(loaderState, mProxyPrefs.parcelableProxy);
|
||||
}
|
||||
};
|
||||
if (OrbotHelper.putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTor, mProxyPrefs, this)) {
|
||||
mListFragment.loadNew(loaderState, mProxyPrefs.parcelableProxy);
|
||||
}
|
||||
} else if (loaderState instanceof ImportKeysListFragment.BytesLoaderState) { // must always be true
|
||||
mListFragment.loadNew(loaderState, mProxyPrefs.parcelableProxy);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMessage(Message message) {
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListLoader;
|
||||
import org.sufficientlysecure.keychain.util.InputData;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -64,6 +65,7 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
private ImportKeysAdapter mAdapter;
|
||||
|
||||
private LoaderState mLoaderState;
|
||||
private ParcelableProxy mProxy;
|
||||
|
||||
private static final int LOADER_ID_BYTES = 0;
|
||||
private static final int LOADER_ID_CLOUD = 1;
|
||||
@@ -126,6 +128,7 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
/**
|
||||
* Creates an interactive ImportKeyListFragment which reads keyrings from bytes, or file specified
|
||||
* by dataUri, or searches a keyserver for serverQuery, if parameter is not null, in that order
|
||||
* Will immediately load data if non-null bytes/dataUri/serverQuery
|
||||
*
|
||||
* @param bytes byte data containing list of keyrings to be imported
|
||||
* @param dataUri file from which keyrings are to be imported
|
||||
@@ -141,7 +144,7 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
|
||||
/**
|
||||
* Visually consists of a list of keyrings with checkboxes to specify which are to be imported
|
||||
* Can immediately load keyrings specified by any of its parameters
|
||||
* Will immediately load data if non-null bytes/dataUri/serverQuery is supplied
|
||||
*
|
||||
* @param bytes byte data containing list of keyrings to be imported
|
||||
* @param dataUri file from which keyrings are to be imported
|
||||
@@ -258,7 +261,9 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void loadNew(LoaderState loaderState) {
|
||||
public void loadNew(LoaderState loaderState, ParcelableProxy proxy) {
|
||||
mProxy = proxy;
|
||||
|
||||
mLoaderState = loaderState;
|
||||
|
||||
restartLoaders();
|
||||
@@ -301,7 +306,7 @@ public class ImportKeysListFragment extends ListFragment implements
|
||||
}
|
||||
case LOADER_ID_CLOUD: {
|
||||
CloudLoaderState ls = (CloudLoaderState) mLoaderState;
|
||||
return new ImportKeysListCloudLoader(getActivity(), ls.mServerQuery, ls.mCloudPrefs);
|
||||
return new ImportKeysListCloudLoader(getActivity(), ls.mServerQuery, ls.mCloudPrefs, mProxy);
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -44,7 +44,9 @@ import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.util.IntentIntegratorSupportV4;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
@@ -157,8 +159,7 @@ public class ImportKeysProxyActivity extends FragmentActivity
|
||||
returnResult(intent);
|
||||
return;
|
||||
}
|
||||
|
||||
String fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH);
|
||||
final String fingerprint = uri.getEncodedSchemeSpecificPart().toLowerCase(Locale.ENGLISH);
|
||||
if (!fingerprint.matches("[a-fA-F0-9]{40}")) {
|
||||
SingletonResult result = new SingletonResult(
|
||||
SingletonResult.RESULT_ERROR, LogType.MSG_WRONG_QR_CODE_FP);
|
||||
@@ -273,7 +274,8 @@ public class ImportKeysProxyActivity extends FragmentActivity
|
||||
// only one message sent during the beam
|
||||
NdefMessage msg = (NdefMessage) rawMsgs[0];
|
||||
// record 0 contains the MIME type, record 1 is the AAR, if present
|
||||
byte[] receivedKeyringBytes = msg.getRecords()[0].getPayload();
|
||||
final byte[] receivedKeyringBytes = msg.getRecords()[0].getPayload();
|
||||
|
||||
importKeys(receivedKeyringBytes);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,9 @@ import android.widget.TextView;
|
||||
import com.getbase.floatingactionbutton.FloatingActionButton;
|
||||
import com.getbase.floatingactionbutton.FloatingActionsMenu;
|
||||
|
||||
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
|
||||
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing;
|
||||
@@ -71,19 +74,16 @@ import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||
import org.sufficientlysecure.keychain.util.ExportHelper;
|
||||
import org.sufficientlysecure.keychain.util.FabContainer;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter;
|
||||
import se.emilsjolander.stickylistheaders.StickyListHeadersListView;
|
||||
|
||||
/**
|
||||
* Public key list with sticky list headers. It does _not_ extend ListFragment because it uses
|
||||
* StickyListHeaders library which does not extend upon ListView.
|
||||
@@ -478,10 +478,6 @@ public class KeyListFragment extends LoaderFragment
|
||||
updateAllKeys();
|
||||
return true;
|
||||
|
||||
case R.id.menu_key_list_debug_cons:
|
||||
consolidate();
|
||||
return true;
|
||||
|
||||
case R.id.menu_key_list_debug_read:
|
||||
try {
|
||||
KeychainDatabase.debugBackup(getActivity(), true);
|
||||
|
||||
@@ -128,6 +128,11 @@ public class MainActivity extends BaseNfcActivity implements FabContainer, OnBac
|
||||
|
||||
getSupportFragmentManager().addOnBackStackChangedListener(this);
|
||||
|
||||
// all further initialization steps are saved as instance state
|
||||
if (savedInstanceState != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent data = getIntent();
|
||||
// If we got an EXTRA_RESULT in the intent, show the notification
|
||||
if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) {
|
||||
@@ -135,20 +140,18 @@ public class MainActivity extends BaseNfcActivity implements FabContainer, OnBac
|
||||
result.createNotify(this).show();
|
||||
}
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
// always initialize keys fragment to the bottom of the backstack
|
||||
onKeysSelected();
|
||||
// always initialize keys fragment to the bottom of the backstack
|
||||
onKeysSelected();
|
||||
|
||||
if (data != null && data.hasExtra(EXTRA_INIT_FRAG)) {
|
||||
// initialize FragmentLayout with KeyListFragment at first
|
||||
switch (data.getIntExtra(EXTRA_INIT_FRAG, -1)) {
|
||||
case ID_ENCRYPT_DECRYPT:
|
||||
onEnDecryptSelected();
|
||||
break;
|
||||
case ID_APPS:
|
||||
onAppsSelected();
|
||||
break;
|
||||
}
|
||||
if (data != null && data.hasExtra(EXTRA_INIT_FRAG)) {
|
||||
// initialize FragmentLayout with KeyListFragment at first
|
||||
switch (data.getIntExtra(EXTRA_INIT_FRAG, -1)) {
|
||||
case ID_ENCRYPT_DECRYPT:
|
||||
onEnDecryptSelected();
|
||||
break;
|
||||
case ID_APPS:
|
||||
onAppsSelected();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
* Copyright (C) 2015 Adithya Abraham Philip <adithyaphilip@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
/**
|
||||
* Simply encapsulates a dialog. If orbot is not installed, it shows an install dialog, else a
|
||||
* dialog to enable orbot.
|
||||
*/
|
||||
public class OrbotRequiredDialogActivity extends FragmentActivity {
|
||||
|
||||
// to provide any previous crypto input into which proxy preference is merged
|
||||
public static final String EXTRA_CRYPTO_INPUT = "extra_crypto_input";
|
||||
|
||||
public static final String RESULT_CRYPTO_INPUT = "result_crypto_input";
|
||||
|
||||
private CryptoInputParcel mCryptoInputParcel;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mCryptoInputParcel = getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT);
|
||||
if (mCryptoInputParcel == null) {
|
||||
mCryptoInputParcel = new CryptoInputParcel();
|
||||
}
|
||||
showDialog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays an install or start orbot dialog depending on orbot's presence and state
|
||||
*/
|
||||
public void showDialog() {
|
||||
DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() {
|
||||
public void run() {
|
||||
Runnable ignoreTor = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Intent intent = new Intent();
|
||||
mCryptoInputParcel.addParcelableProxy(ParcelableProxy.getForNoProxy());
|
||||
intent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
};
|
||||
|
||||
Runnable dialogDismissed = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finish();
|
||||
}
|
||||
};
|
||||
|
||||
if (OrbotHelper.putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTor, dialogDismissed,
|
||||
Preferences.getPreferences(OrbotRequiredDialogActivity.this)
|
||||
.getProxyPrefs(),
|
||||
OrbotRequiredDialogActivity.this)) {
|
||||
// no action required after all
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel);
|
||||
setResult(RESULT_OK, intent);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -43,7 +43,9 @@ import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
@@ -207,7 +209,7 @@ public class PassphraseWizardActivity extends FragmentActivity {
|
||||
// transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit();
|
||||
}
|
||||
} catch (IOException | FormatException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(Constants.TAG, "Failed to write on NFC tag", e);
|
||||
}
|
||||
|
||||
} else if (readNFC && AUTHENTICATION.equals(selectedAction)) {
|
||||
@@ -232,7 +234,7 @@ public class PassphraseWizardActivity extends FragmentActivity {
|
||||
}
|
||||
}
|
||||
} catch (IOException | FormatException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(Constants.TAG, "Failed to read NFC tag", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -263,7 +265,7 @@ public class PassphraseWizardActivity extends FragmentActivity {
|
||||
try {
|
||||
password = readText(ndefRecord);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
e.printStackTrace();
|
||||
Log.e(Constants.TAG, "Failed to read password from tag", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,21 @@
|
||||
|
||||
package org.sufficientlysecure.keychain.ui;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
@@ -33,9 +40,11 @@ import android.widget.LinearLayout;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.compatibility.AppCompatPreferenceActivity;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.widget.IntegerListPreference;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -43,6 +52,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
|
||||
public static final String ACTION_PREFS_CLOUD = "org.sufficientlysecure.keychain.ui.PREFS_CLOUD";
|
||||
public static final String ACTION_PREFS_ADV = "org.sufficientlysecure.keychain.ui.PREFS_ADV";
|
||||
public static final String ACTION_PREFS_PROXY = "org.sufficientlysecure.keychain.ui.PREFS_PROXY";
|
||||
|
||||
public static final int REQUEST_CODE_KEYSERVER_PREF = 0x00007005;
|
||||
|
||||
@@ -209,9 +219,205 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
}
|
||||
}
|
||||
|
||||
public static class ProxyPrefsFragment extends PreferenceFragment {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
new Initializer(this).initialize();
|
||||
|
||||
}
|
||||
|
||||
public static class Initializer {
|
||||
private CheckBoxPreference mUseTor;
|
||||
private CheckBoxPreference mUseNormalProxy;
|
||||
private EditTextPreference mProxyHost;
|
||||
private EditTextPreference mProxyPort;
|
||||
private ListPreference mProxyType;
|
||||
private PreferenceActivity mActivity;
|
||||
private PreferenceFragment mFragment;
|
||||
|
||||
public Initializer(PreferenceFragment fragment) {
|
||||
mFragment = fragment;
|
||||
}
|
||||
|
||||
public Initializer(PreferenceActivity activity) {
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
public Preference automaticallyFindPreference(String key) {
|
||||
if (mFragment != null) {
|
||||
return mFragment.findPreference(key);
|
||||
} else {
|
||||
return mActivity.findPreference(key);
|
||||
}
|
||||
}
|
||||
|
||||
public void initialize() {
|
||||
// makes android's preference framework write to our file instead of default
|
||||
// This allows us to use the "persistent" attribute to simplify code
|
||||
if (mFragment != null) {
|
||||
Preferences.setPreferenceManagerFileAndMode(mFragment.getPreferenceManager());
|
||||
// Load the preferences from an XML resource
|
||||
mFragment.addPreferencesFromResource(R.xml.proxy_prefs);
|
||||
} else {
|
||||
Preferences.setPreferenceManagerFileAndMode(mActivity.getPreferenceManager());
|
||||
// Load the preferences from an XML resource
|
||||
mActivity.addPreferencesFromResource(R.xml.proxy_prefs);
|
||||
}
|
||||
|
||||
mUseTor = (CheckBoxPreference) automaticallyFindPreference(Constants.Pref.USE_TOR_PROXY);
|
||||
mUseNormalProxy = (CheckBoxPreference) automaticallyFindPreference(Constants.Pref.USE_NORMAL_PROXY);
|
||||
mProxyHost = (EditTextPreference) automaticallyFindPreference(Constants.Pref.PROXY_HOST);
|
||||
mProxyPort = (EditTextPreference) automaticallyFindPreference(Constants.Pref.PROXY_PORT);
|
||||
mProxyType = (ListPreference) automaticallyFindPreference(Constants.Pref.PROXY_TYPE);
|
||||
initializeUseTorPref();
|
||||
initializeUseNormalProxyPref();
|
||||
initializeEditTextPreferences();
|
||||
initializeProxyTypePreference();
|
||||
|
||||
if (mUseTor.isChecked()) disableNormalProxyPrefs();
|
||||
else if (mUseNormalProxy.isChecked()) disableUseTorPrefs();
|
||||
}
|
||||
|
||||
private void initializeUseTorPref() {
|
||||
mUseTor.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
Activity activity = mFragment != null ? mFragment.getActivity() : mActivity;
|
||||
if ((Boolean) newValue) {
|
||||
boolean installed = OrbotHelper.isOrbotInstalled(activity);
|
||||
if (!installed) {
|
||||
Log.d(Constants.TAG, "Prompting to install Tor");
|
||||
OrbotHelper.getPreferenceInstallDialogFragment().show(activity.getFragmentManager(),
|
||||
"installDialog");
|
||||
// don't let the user check the box until he's installed orbot
|
||||
return false;
|
||||
} else {
|
||||
disableNormalProxyPrefs();
|
||||
// let the enable tor box be checked
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
// we're unchecking Tor, so enable other proxy
|
||||
enableNormalProxyPrefs();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initializeUseNormalProxyPref() {
|
||||
mUseNormalProxy.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
if ((Boolean) newValue) {
|
||||
disableUseTorPrefs();
|
||||
} else {
|
||||
enableUseTorPrefs();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initializeEditTextPreferences() {
|
||||
mProxyHost.setSummary(mProxyHost.getText());
|
||||
mProxyPort.setSummary(mProxyPort.getText());
|
||||
|
||||
mProxyHost.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
Activity activity = mFragment != null ? mFragment.getActivity() : mActivity;
|
||||
if (TextUtils.isEmpty((String) newValue)) {
|
||||
Notify.create(
|
||||
activity,
|
||||
R.string.pref_proxy_host_err_invalid,
|
||||
Notify.Style.ERROR
|
||||
).show();
|
||||
return false;
|
||||
} else {
|
||||
mProxyHost.setSummary((CharSequence) newValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
mProxyPort.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
Activity activity = mFragment != null ? mFragment.getActivity() : mActivity;
|
||||
try {
|
||||
int port = Integer.parseInt((String) newValue);
|
||||
if (port < 0 || port > 65535) {
|
||||
Notify.create(
|
||||
activity,
|
||||
R.string.pref_proxy_port_err_invalid,
|
||||
Notify.Style.ERROR
|
||||
).show();
|
||||
return false;
|
||||
}
|
||||
// no issues, save port
|
||||
mProxyPort.setSummary("" + port);
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
Notify.create(
|
||||
activity,
|
||||
R.string.pref_proxy_port_err_invalid,
|
||||
Notify.Style.ERROR
|
||||
).show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initializeProxyTypePreference() {
|
||||
mProxyType.setSummary(mProxyType.getEntry());
|
||||
|
||||
mProxyType.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
CharSequence entry = mProxyType.getEntries()[mProxyType.findIndexOfValue((String) newValue)];
|
||||
mProxyType.setSummary(entry);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void disableNormalProxyPrefs() {
|
||||
mUseNormalProxy.setChecked(false);
|
||||
mUseNormalProxy.setEnabled(false);
|
||||
mProxyHost.setEnabled(false);
|
||||
mProxyPort.setEnabled(false);
|
||||
mProxyType.setEnabled(false);
|
||||
}
|
||||
|
||||
private void enableNormalProxyPrefs() {
|
||||
mUseNormalProxy.setEnabled(true);
|
||||
mProxyHost.setEnabled(true);
|
||||
mProxyPort.setEnabled(true);
|
||||
mProxyType.setEnabled(true);
|
||||
}
|
||||
|
||||
private void disableUseTorPrefs() {
|
||||
mUseTor.setChecked(false);
|
||||
mUseTor.setEnabled(false);
|
||||
}
|
||||
|
||||
private void enableUseTorPrefs() {
|
||||
mUseTor.setEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
protected boolean isValidFragment(String fragmentName) {
|
||||
return AdvancedPrefsFragment.class.getName().equals(fragmentName)
|
||||
|| CloudSearchPrefsFragment.class.getName().equals(fragmentName)
|
||||
|| ProxyPrefsFragment.class.getName().equals(fragmentName)
|
||||
|| super.isValidFragment(fragmentName);
|
||||
}
|
||||
|
||||
@@ -270,7 +476,8 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
String[] servers = sPreferences.getKeyServers();
|
||||
String serverSummary = context.getResources().getQuantityString(
|
||||
R.plurals.n_keyservers, servers.length, servers.length);
|
||||
return serverSummary + "; " + context.getString(R.string.label_preferred) + ": " + sPreferences.getPreferredKeyserver();
|
||||
return serverSummary + "; " + context.getString(R.string.label_preferred) + ": " + sPreferences
|
||||
.getPreferredKeyserver();
|
||||
}
|
||||
|
||||
private static void initializeUseDefaultYubiKeyPin(final CheckBoxPreference mUseDefaultYubiKeyPin) {
|
||||
|
||||
@@ -26,7 +26,6 @@ import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
@@ -38,6 +37,7 @@ import org.sufficientlysecure.keychain.ui.base.BaseActivity;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
/**
|
||||
* Sends the selected public key to a keyserver
|
||||
@@ -132,8 +132,7 @@ public class UploadKeyActivity extends BaseActivity
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(ExportResult result) {
|
||||
Toast.makeText(UploadKeyActivity.this, R.string.msg_crt_upload_success,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
result.createNotify(this).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -143,7 +142,7 @@ public class UploadKeyActivity extends BaseActivity
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(ExportResult result) {
|
||||
// TODO: Implement proper log for key upload then show error
|
||||
result.createNotify(this).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -84,7 +84,9 @@ import org.sufficientlysecure.keychain.util.ContactHelper;
|
||||
import org.sufficientlysecure.keychain.util.ExportHelper;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.NfcHelper;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
@@ -464,11 +466,11 @@ public class ViewKeyActivity extends BaseNfcActivity implements
|
||||
|
||||
HashMap<String, Object> data = providerHelper.getGenericData(
|
||||
baseUri,
|
||||
new String[] {KeychainContract.Keys.MASTER_KEY_ID, KeychainContract.KeyRings.HAS_SECRET},
|
||||
new int[] {ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_INTEGER});
|
||||
new String[]{KeychainContract.Keys.MASTER_KEY_ID, KeychainContract.KeyRings.HAS_SECRET},
|
||||
new int[]{ProviderHelper.FIELD_TYPE_INTEGER, ProviderHelper.FIELD_TYPE_INTEGER});
|
||||
|
||||
exportHelper.showExportKeysDialog(
|
||||
new long[] {(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)},
|
||||
new long[]{(Long) data.get(KeychainContract.KeyRings.MASTER_KEY_ID)},
|
||||
Constants.Path.APP_DIR_FILE, ((Long) data.get(KeychainContract.KeyRings.HAS_SECRET) != 0)
|
||||
);
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
@@ -492,7 +494,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
|
||||
// Create a new Messenger for the communication back
|
||||
Messenger messenger = new Messenger(returnHandler);
|
||||
DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger,
|
||||
new long[] {mMasterKeyId});
|
||||
new long[]{mMasterKeyId});
|
||||
deleteKeyDialog.show(getSupportFragmentManager(), "deleteKeyDialog");
|
||||
}
|
||||
|
||||
@@ -636,7 +638,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
|
||||
long keyId = new ProviderHelper(this)
|
||||
.getCachedPublicKeyRing(dataUri)
|
||||
.extractOrGetMasterKeyId();
|
||||
long[] encryptionKeyIds = new long[] {keyId};
|
||||
long[] encryptionKeyIds = new long[]{keyId};
|
||||
Intent intent;
|
||||
if (text) {
|
||||
intent = new Intent(this, EncryptTextActivity.class);
|
||||
@@ -742,7 +744,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements
|
||||
|
||||
|
||||
// These are the rows that we will retrieve.
|
||||
static final String[] PROJECTION = new String[] {
|
||||
static final String[] PROJECTION = new String[]{
|
||||
KeychainContract.KeyRings._ID,
|
||||
KeychainContract.KeyRings.MASTER_KEY_ID,
|
||||
KeychainContract.KeyRings.USER_ID,
|
||||
@@ -830,7 +832,8 @@ public class ViewKeyActivity extends BaseNfcActivity implements
|
||||
AsyncTask<Long, Void, Bitmap> photoTask =
|
||||
new AsyncTask<Long, Void, Bitmap>() {
|
||||
protected Bitmap doInBackground(Long... mMasterKeyId) {
|
||||
return ContactHelper.loadPhotoByMasterKeyId(getContentResolver(), mMasterKeyId[0], true);
|
||||
return ContactHelper.loadPhotoByMasterKeyId(getContentResolver(),
|
||||
mMasterKeyId[0], true);
|
||||
}
|
||||
|
||||
protected void onPostExecute(Bitmap photo) {
|
||||
|
||||
@@ -25,6 +25,9 @@ import java.io.OutputStreamWriter;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
@@ -49,7 +52,6 @@ import android.widget.TextView;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
@@ -58,6 +60,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify;
|
||||
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
|
||||
import org.sufficientlysecure.keychain.ui.util.QrCodeUtils;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.NfcHelper;
|
||||
@@ -197,7 +200,15 @@ public class ViewKeyAdvShareFragment extends LoaderFragment implements
|
||||
}
|
||||
|
||||
if (toClipboard) {
|
||||
ClipboardReflection.copyToClipboard(activity, content);
|
||||
ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
if (clipMan == null) {
|
||||
Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
ClipData clip = ClipData.newPlainText(Constants.CLIPBOARD_LABEL, content);
|
||||
clipMan.setPrimaryClip(clip);
|
||||
|
||||
Notify.create(activity, fingerprintOnly ? R.string.fingerprint_copied_to_clipboard
|
||||
: R.string.key_copied_to_clipboard, Notify.Style.OK).show();
|
||||
return;
|
||||
|
||||
@@ -51,6 +51,9 @@ import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Hashtable;
|
||||
@@ -197,8 +200,22 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
|
||||
mStartSearch.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mStartSearch.setEnabled(false);
|
||||
new DescribeKey().execute(fingerprint);
|
||||
final Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(getActivity())
|
||||
.getProxyPrefs();
|
||||
|
||||
Runnable ignoreTor = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mStartSearch.setEnabled(false);
|
||||
new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint);
|
||||
}
|
||||
};
|
||||
|
||||
if (OrbotHelper.putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTor, proxyPrefs,
|
||||
getActivity())) {
|
||||
mStartSearch.setEnabled(false);
|
||||
new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -229,6 +246,11 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
|
||||
// look for evidence from keybase in the background, make tabular version of result
|
||||
//
|
||||
private class DescribeKey extends AsyncTask<String, Void, ResultPage> {
|
||||
ParcelableProxy mParcelableProxy;
|
||||
|
||||
public DescribeKey(ParcelableProxy parcelableProxy) {
|
||||
mParcelableProxy = parcelableProxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResultPage doInBackground(String... args) {
|
||||
@@ -237,7 +259,7 @@ public class ViewKeyTrustFragment extends LoaderFragment implements
|
||||
final ArrayList<CharSequence> proofList = new ArrayList<CharSequence>();
|
||||
final Hashtable<Integer, ArrayList<Proof>> proofs = new Hashtable<Integer, ArrayList<Proof>>();
|
||||
try {
|
||||
User keybaseUser = User.findByFingerprint(fingerprint);
|
||||
User keybaseUser = User.findByFingerprint(fingerprint, mParcelableProxy.getProxy());
|
||||
for (Proof proof : keybaseUser.getProofs()) {
|
||||
Integer proofType = proof.getType();
|
||||
appendIfOK(proofs, proofType, proof);
|
||||
|
||||
@@ -210,7 +210,7 @@ public class ViewKeyYubiKeyFragment
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCryptoOperationResult(PromoteKeyResult result) {
|
||||
public void onCryptoOperationSuccess(PromoteKeyResult result) {
|
||||
result.createNotify(getActivity()).show();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.sufficientlysecure.keychain.keyimport.Keyserver;
|
||||
import org.sufficientlysecure.keychain.operations.results.GetKeyResult;
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.ParcelableProxy;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -38,15 +39,18 @@ public class ImportKeysListCloudLoader
|
||||
|
||||
Preferences.CloudSearchPrefs mCloudPrefs;
|
||||
String mServerQuery;
|
||||
private ParcelableProxy mParcelableProxy;
|
||||
|
||||
private ArrayList<ImportKeysListEntry> mEntryList = new ArrayList<>();
|
||||
private AsyncTaskResultWrapper<ArrayList<ImportKeysListEntry>> mEntryListWrapper;
|
||||
|
||||
public ImportKeysListCloudLoader(Context context, String serverQuery, Preferences.CloudSearchPrefs cloudPrefs) {
|
||||
public ImportKeysListCloudLoader(Context context, String serverQuery, Preferences.CloudSearchPrefs cloudPrefs,
|
||||
ParcelableProxy proxy) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
mServerQuery = serverQuery;
|
||||
mCloudPrefs = cloudPrefs;
|
||||
mParcelableProxy = proxy;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -96,8 +100,11 @@ public class ImportKeysListCloudLoader
|
||||
*/
|
||||
private void queryServer(boolean enforceFingerprint) {
|
||||
try {
|
||||
ArrayList<ImportKeysListEntry> searchResult
|
||||
= CloudSearch.search(mServerQuery, mCloudPrefs);
|
||||
ArrayList<ImportKeysListEntry> searchResult = CloudSearch.search(
|
||||
mServerQuery,
|
||||
mCloudPrefs,
|
||||
mParcelableProxy.getProxy()
|
||||
);
|
||||
|
||||
mEntryList.clear();
|
||||
// add result to data
|
||||
|
||||
@@ -31,8 +31,12 @@ public abstract class CachingCryptoOperationFragment <T extends Parcelable, S ex
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCryptoOperationResult(S result) {
|
||||
super.onCryptoOperationResult(result);
|
||||
public void onCryptoOperationSuccess(S result) {
|
||||
mCachedActionsParcel = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(S result) {
|
||||
mCachedActionsParcel = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,56 +20,88 @@ package org.sufficientlysecure.keychain.ui.base;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Parcelable;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
|
||||
import org.sufficientlysecure.keychain.operations.results.OperationResult;
|
||||
import org.sufficientlysecure.keychain.service.KeychainService;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
|
||||
/**
|
||||
* All fragments executing crypto operations need to extend this class.
|
||||
/** This is a base class for fragments which implement a cryptoOperation.
|
||||
*
|
||||
* Subclasses of this class can call the cryptoOperation method to execute an
|
||||
* operation in KeychainService which takes a parcelable of type T as its input
|
||||
* and returns an OperationResult of type S as a result.
|
||||
*
|
||||
* The input (of type T) is not given directly to the cryptoOperation method,
|
||||
* but must be provided by the overriden createOperationInput method to be
|
||||
* available upon request during execution of the cryptoOperation.
|
||||
*
|
||||
* After running cryptoOperation, one of the onCryptoOperation*() methods will
|
||||
* be called, depending on the success status of the operation. The subclass
|
||||
* must override at least onCryptoOperationSuccess to proceed after a
|
||||
* successful operation.
|
||||
*
|
||||
* @see KeychainService
|
||||
*
|
||||
*/
|
||||
public abstract class CryptoOperationFragment<T extends Parcelable, S extends OperationResult>
|
||||
extends Fragment implements CryptoOperationHelper.Callback<T, S> {
|
||||
|
||||
private CryptoOperationHelper<T, S> mOperationHelper;
|
||||
final private CryptoOperationHelper<T, S> mOperationHelper;
|
||||
|
||||
public CryptoOperationFragment() {
|
||||
|
||||
mOperationHelper = new CryptoOperationHelper<>(this, this);
|
||||
}
|
||||
|
||||
public void setProgressMessageResource(int id) {
|
||||
mOperationHelper.setProgressMessageResource(id);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
mOperationHelper.handleActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract T createOperationInput();
|
||||
|
||||
/** Starts execution of the cryptographic operation.
|
||||
*
|
||||
* During this process, the createOperationInput() method will be called,
|
||||
* this input will be handed to KeychainService, where it is executed in
|
||||
* the appropriate *Operation class. If the result is a PendingInputResult,
|
||||
* it is handled accordingly. Otherwise, it is returned in one of the
|
||||
* onCryptoOperation* callbacks.
|
||||
*/
|
||||
protected void cryptoOperation() {
|
||||
cryptoOperation(new CryptoInputParcel());
|
||||
mOperationHelper.cryptoOperation();
|
||||
}
|
||||
|
||||
protected void cryptoOperation(CryptoInputParcel cryptoInput) {
|
||||
cryptoOperation(cryptoInput, true);
|
||||
mOperationHelper.cryptoOperation(cryptoInput);
|
||||
}
|
||||
|
||||
protected void cryptoOperation(CryptoInputParcel cryptoInput, boolean showProgress) {
|
||||
mOperationHelper.cryptoOperation(cryptoInput, showProgress);
|
||||
}
|
||||
|
||||
@Override @Nullable
|
||||
/** Creates input for the crypto operation. Called internally after the
|
||||
* crypto operation is started by a call to cryptoOperation(). Silently
|
||||
* cancels operation if this method returns null. */
|
||||
public abstract T createOperationInput();
|
||||
|
||||
/** Returns false, indicating that we did not handle progress ourselves. */
|
||||
public boolean onCryptoSetProgress(String msg, int progress, int max) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setProgressMessageResource(int id) {
|
||||
mOperationHelper.setProgressMessageResource(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
/** Called when the cryptoOperation() was successful. No default behavior
|
||||
* here, this should always be implemented by a subclass! */
|
||||
abstract public void onCryptoOperationSuccess(S result);
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationError(S result) {
|
||||
onCryptoOperationResult(result);
|
||||
result.createNotify(getActivity()).show();
|
||||
}
|
||||
|
||||
@@ -77,19 +109,4 @@ public abstract class CryptoOperationFragment<T extends Parcelable, S extends Op
|
||||
public void onCryptoOperationCancelled() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCryptoOperationSuccess(S result) {
|
||||
onCryptoOperationResult(result);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* To be overriden by subclasses, if desired. Provides a way to access the method by the
|
||||
* same name in CryptoOperationHelper, if super.onCryptoOperationSuccess and
|
||||
* super.onCryptoOperationError are called at the start of the respective functions in the
|
||||
* subclass overriding them
|
||||
* @param result
|
||||
*/
|
||||
protected void onCryptoOperationResult(S result) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ import android.os.Messenger;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import android.support.v4.app.FragmentManager;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.operations.results.InputPendingResult;
|
||||
@@ -39,6 +39,7 @@ import org.sufficientlysecure.keychain.service.ServiceProgressHandler;
|
||||
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
|
||||
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
|
||||
import org.sufficientlysecure.keychain.ui.NfcOperationActivity;
|
||||
import org.sufficientlysecure.keychain.ui.OrbotRequiredDialogActivity;
|
||||
import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
@@ -52,16 +53,21 @@ import org.sufficientlysecure.keychain.util.Log;
|
||||
*/
|
||||
public class CryptoOperationHelper<T extends Parcelable, S extends OperationResult> {
|
||||
|
||||
public interface Callback <T extends Parcelable, S extends OperationResult> {
|
||||
public interface Callback<T extends Parcelable, S extends OperationResult> {
|
||||
T createOperationInput();
|
||||
|
||||
void onCryptoOperationSuccess(S result);
|
||||
|
||||
void onCryptoOperationCancelled();
|
||||
|
||||
void onCryptoOperationError(S result);
|
||||
|
||||
boolean onCryptoSetProgress(String msg, int progress, int max);
|
||||
}
|
||||
|
||||
public static final int REQUEST_CODE_PASSPHRASE = 0x00008001;
|
||||
public static final int REQUEST_CODE_NFC = 0x00008002;
|
||||
public static final int REQUEST_CODE_ENABLE_ORBOT = 0x00008004;
|
||||
|
||||
// keeps track of request code used to start an activity from this CryptoOperationHelper.
|
||||
// this is necessary when multiple CryptoOperationHelpers are used in the same fragment/activity
|
||||
@@ -78,8 +84,6 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
||||
|
||||
/**
|
||||
* If OperationHelper is being integrated into an activity
|
||||
*
|
||||
* @param activity
|
||||
*/
|
||||
public CryptoOperationHelper(FragmentActivity activity, Callback<T, S> callback, int progressMessageString) {
|
||||
mActivity = activity;
|
||||
@@ -90,8 +94,6 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
||||
|
||||
/**
|
||||
* if OperationHelper is being integrated into a fragment
|
||||
*
|
||||
* @param fragment
|
||||
*/
|
||||
public CryptoOperationHelper(Fragment fragment, Callback<T, S> callback, int progressMessageString) {
|
||||
mFragment = fragment;
|
||||
@@ -102,8 +104,6 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
||||
|
||||
/**
|
||||
* if OperationHelper is being integrated into a fragment with default message for the progress dialog
|
||||
*
|
||||
* @param fragment
|
||||
*/
|
||||
public CryptoOperationHelper(Fragment fragment, Callback<T, S> callback) {
|
||||
mFragment = fragment;
|
||||
@@ -116,11 +116,14 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
||||
mProgressMessageResource = id;
|
||||
}
|
||||
|
||||
private void initiateInputActivity(RequiredInputParcel requiredInput) {
|
||||
private void initiateInputActivity(RequiredInputParcel requiredInput,
|
||||
CryptoInputParcel cryptoInputParcel) {
|
||||
|
||||
Activity activity = mUseFragment ? mFragment.getActivity() : mActivity;
|
||||
|
||||
switch (requiredInput.mType) {
|
||||
// TODO: Verify that all started activities add to cryptoInputParcel if necessary (like OrbotRequiredDialogActivity)
|
||||
// don't forget to set mRequestedCode!
|
||||
case NFC_MOVE_KEY_TO_CARD:
|
||||
case NFC_DECRYPT:
|
||||
case NFC_SIGN: {
|
||||
@@ -130,7 +133,7 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
||||
if (mUseFragment) {
|
||||
mFragment.startActivityForResult(intent, mRequestedCode);
|
||||
} else {
|
||||
mActivity.startActivityForResult(intent, mRequestedCode);
|
||||
activity.startActivityForResult(intent, mRequestedCode);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -143,22 +146,32 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
||||
if (mUseFragment) {
|
||||
mFragment.startActivityForResult(intent, mRequestedCode);
|
||||
} else {
|
||||
mActivity.startActivityForResult(intent, mRequestedCode);
|
||||
activity.startActivityForResult(intent, mRequestedCode);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unhandled pending result!");
|
||||
case ENABLE_ORBOT: {
|
||||
Intent intent = new Intent(activity, OrbotRequiredDialogActivity.class);
|
||||
intent.putExtra(OrbotRequiredDialogActivity.EXTRA_CRYPTO_INPUT, cryptoInputParcel);
|
||||
mRequestedCode = REQUEST_CODE_ENABLE_ORBOT;
|
||||
if (mUseFragment) {
|
||||
mFragment.startActivityForResult(intent, mRequestedCode);
|
||||
} else {
|
||||
activity.startActivityForResult(intent, mRequestedCode);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
throw new RuntimeException("Unhandled pending result!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts the result of an activity started by this helper. Returns true if requestCode is recognized,
|
||||
* false otherwise.
|
||||
*
|
||||
* @param requestCode
|
||||
* @param resultCode
|
||||
* @param data
|
||||
* Attempts the result of an activity started by this helper. Returns true if requestCode is
|
||||
* recognized, false otherwise.
|
||||
* @return true if requestCode was recognized, false otherwise
|
||||
*/
|
||||
public boolean handleActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
@@ -196,6 +209,16 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
||||
break;
|
||||
}
|
||||
|
||||
case REQUEST_CODE_ENABLE_ORBOT: {
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
CryptoInputParcel cryptoInput =
|
||||
data.getParcelableExtra(
|
||||
OrbotRequiredDialogActivity.RESULT_CRYPTO_INPUT);
|
||||
cryptoOperation(cryptoInput);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
@@ -225,7 +248,7 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
||||
|
||||
}
|
||||
|
||||
public void cryptoOperation(CryptoInputParcel cryptoInput, boolean showProgress) {
|
||||
public void cryptoOperation(final CryptoInputParcel cryptoInput, boolean showProgress) {
|
||||
|
||||
FragmentActivity activity = mUseFragment ? mFragment.getActivity() : mActivity;
|
||||
|
||||
@@ -257,14 +280,14 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
||||
final OperationResult result =
|
||||
returnData.getParcelable(OperationResult.EXTRA_RESULT);
|
||||
|
||||
onHandleResult(result);
|
||||
onHandleResult(result, cryptoInput);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetProgress(String msg, int progress, int max) {
|
||||
// allow handling of progress in fragment, or delegate upwards
|
||||
if ( ! mCallback.onCryptoSetProgress(msg, progress, max)) {
|
||||
if (!mCallback.onCryptoSetProgress(msg, progress, max)) {
|
||||
super.onSetProgress(msg, progress, max);
|
||||
}
|
||||
}
|
||||
@@ -291,15 +314,7 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
||||
cryptoOperation(new CryptoInputParcel());
|
||||
}
|
||||
|
||||
protected void onCryptoOperationResult(S result) {
|
||||
if (result.success()) {
|
||||
mCallback.onCryptoOperationSuccess(result);
|
||||
} else {
|
||||
mCallback.onCryptoOperationError(result);
|
||||
}
|
||||
}
|
||||
|
||||
public void onHandleResult(OperationResult result) {
|
||||
public void onHandleResult(OperationResult result, CryptoInputParcel oldCryptoInput) {
|
||||
Log.d(Constants.TAG, "Handling result in OperationHelper success: " + result.success());
|
||||
|
||||
if (result instanceof InputPendingResult) {
|
||||
@@ -307,7 +322,7 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
||||
if (pendingResult.isPending()) {
|
||||
|
||||
RequiredInputParcel requiredInput = pendingResult.getRequiredInputParcel();
|
||||
initiateInputActivity(requiredInput);
|
||||
initiateInputActivity(requiredInput, oldCryptoInput);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -315,8 +330,13 @@ public class CryptoOperationHelper<T extends Parcelable, S extends OperationResu
|
||||
dismissProgress();
|
||||
|
||||
try {
|
||||
// noinspection unchecked, because type erasure :(
|
||||
onCryptoOperationResult((S) result);
|
||||
if (result.success()) {
|
||||
// noinspection unchecked, because type erasure :(
|
||||
mCallback.onCryptoOperationSuccess((S) result);
|
||||
} else {
|
||||
// noinspection unchecked, because type erasure :(
|
||||
mCallback.onCryptoOperationError((S) result);
|
||||
}
|
||||
} catch (ClassCastException e) {
|
||||
throw new AssertionError("bad return class ("
|
||||
+ result.getClass().getSimpleName() + "), this is a programming error!");
|
||||
|
||||
@@ -48,10 +48,17 @@ import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TextView.OnEditorActionListener;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
import org.sufficientlysecure.keychain.util.TlsHelper;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
import java.net.Proxy;
|
||||
|
||||
public class AddEditKeyserverDialogFragment extends DialogFragment implements OnEditorActionListener {
|
||||
private static final String ARG_MESSENGER = "arg_messenger";
|
||||
@@ -205,9 +212,21 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// behaviour same for edit and add
|
||||
String keyserverUrl = mKeyserverEditText.getText().toString();
|
||||
final String keyserverUrl = mKeyserverEditText.getText().toString();
|
||||
if (mVerifyKeyserverCheckBox.isChecked()) {
|
||||
verifyConnection(keyserverUrl);
|
||||
final Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(getActivity())
|
||||
.getProxyPrefs();
|
||||
Runnable ignoreTor = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
verifyConnection(keyserverUrl, null);
|
||||
}
|
||||
};
|
||||
|
||||
if (OrbotHelper.putOrbotInRequiredState(R.string.orbot_ignore_tor, ignoreTor, proxyPrefs,
|
||||
getActivity())) {
|
||||
verifyConnection(keyserverUrl, proxyPrefs.parcelableProxy.getProxy());
|
||||
}
|
||||
} else {
|
||||
dismiss();
|
||||
// return unverified keyserver back to activity
|
||||
@@ -249,7 +268,7 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
|
||||
sendMessageToHandler(MESSAGE_VERIFICATION_FAILED, data);
|
||||
}
|
||||
|
||||
public void verifyConnection(String keyserver) {
|
||||
public void verifyConnection(String keyserver, final Proxy proxy) {
|
||||
|
||||
new AsyncTask<String, Void, FailureReason>() {
|
||||
ProgressDialog mProgressDialog;
|
||||
@@ -283,10 +302,11 @@ public class AddEditKeyserverDialogFragment extends DialogFragment implements On
|
||||
}
|
||||
URI newKeyserver = new URI(scheme, schemeSpecificPart, fragment);
|
||||
|
||||
Log.d(Constants.TAG, "Converted URL" + newKeyserver);
|
||||
Log.d("Converted URL", newKeyserver.toString());
|
||||
|
||||
// just see if we can get a connection, then immediately close
|
||||
TlsHelper.openConnection(newKeyserver.toURL()).getInputStream().close();
|
||||
OkHttpClient client = HkpKeyserver.getClient(newKeyserver.toURL(), proxy);
|
||||
TlsHelper.pinCertificateIfNecessary(client, newKeyserver.toURL());
|
||||
client.newCall(new Request.Builder().url(newKeyserver.toURL()).build()).execute();
|
||||
} catch (TlsHelper.TlsHelperException e) {
|
||||
reason = FailureReason.CONNECTION_FAILED;
|
||||
} catch (MalformedURLException | URISyntaxException e) {
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ui.dialog;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.view.ContextThemeWrapper;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
import org.sufficientlysecure.keychain.util.orbot.OrbotHelper;
|
||||
|
||||
/**
|
||||
* displays a dialog asking the user to enable Tor
|
||||
*/
|
||||
public class OrbotStartDialogFragment extends DialogFragment {
|
||||
private static final String ARG_MESSENGER = "messenger";
|
||||
private static final String ARG_TITLE = "title";
|
||||
private static final String ARG_MESSAGE = "message";
|
||||
private static final String ARG_MIDDLE_BUTTON = "middleButton";
|
||||
|
||||
public static final int MESSAGE_MIDDLE_BUTTON = 1;
|
||||
public static final int MESSAGE_DIALOG_DISMISSED = 2; // for either cancel or enable pressed
|
||||
|
||||
public static OrbotStartDialogFragment newInstance(Messenger messenger, int title, int message, int middleButton) {
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelable(ARG_MESSENGER, messenger);
|
||||
args.putInt(ARG_TITLE, title);
|
||||
args.putInt(ARG_MESSAGE, message);
|
||||
args.putInt(ARG_MIDDLE_BUTTON, middleButton);
|
||||
|
||||
OrbotStartDialogFragment fragment = new OrbotStartDialogFragment();
|
||||
fragment.setArguments(args);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
final Messenger messenger = getArguments().getParcelable(ARG_MESSENGER);
|
||||
int title = getArguments().getInt(ARG_TITLE);
|
||||
final int message = getArguments().getInt(ARG_MESSAGE);
|
||||
int middleButton = getArguments().getInt(ARG_MIDDLE_BUTTON);
|
||||
final Activity activity = getActivity();
|
||||
|
||||
// if the dialog is displayed from the application class, design is missing.
|
||||
// hack to get holo design (which is not automatically applied due to activity's
|
||||
// Theme.NoDisplay)
|
||||
ContextThemeWrapper theme = new ContextThemeWrapper(activity,
|
||||
R.style.Theme_AppCompat_Light_Dialog);
|
||||
|
||||
CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(theme);
|
||||
builder.setTitle(title).setMessage(message);
|
||||
|
||||
builder.setNegativeButton(R.string.orbot_start_dialog_cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Message msg = Message.obtain();
|
||||
msg.what = MESSAGE_DIALOG_DISMISSED;
|
||||
try {
|
||||
messenger.send(msg);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
|
||||
} catch (NullPointerException e) {
|
||||
Log.w(Constants.TAG, "Messenger is null!", e);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
builder.setPositiveButton(R.string.orbot_start_dialog_start, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
getActivity().startActivityForResult(OrbotHelper.getOrbotStartIntent(), 1);
|
||||
|
||||
Message msg = Message.obtain();
|
||||
msg.what = MESSAGE_DIALOG_DISMISSED;
|
||||
try {
|
||||
messenger.send(msg);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
|
||||
} catch (NullPointerException e) {
|
||||
Log.w(Constants.TAG, "Messenger is null!", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
builder.setNeutralButton(middleButton, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Message msg = new Message();
|
||||
msg.what = MESSAGE_MIDDLE_BUTTON;
|
||||
try {
|
||||
messenger.send(msg);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
|
||||
} catch (NullPointerException e) {
|
||||
Log.w(Constants.TAG, "Messenger is null!", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return builder.show();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ui.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.os.Messenger;
|
||||
import android.app.DialogFragment;
|
||||
|
||||
import org.sufficientlysecure.keychain.ui.util.InstallDialogFragmentHelper;
|
||||
|
||||
public class PreferenceInstallDialogFragment extends DialogFragment {
|
||||
|
||||
public static final int MESSAGE_MIDDLE_CLICKED = 1;
|
||||
public static final int MESSAGE_DIALOG_DISMISSED = 2;
|
||||
|
||||
/**
|
||||
* Creates a dialog which prompts the user to install an application. Consists of two default buttons ("Install"
|
||||
* and "Cancel") and an optional third button. Callbacks are provided only for the middle button, if set.
|
||||
*
|
||||
* @param messenger required only for callback from middle button if it has been set
|
||||
* @param title
|
||||
* @param message content of dialog
|
||||
* @param packageToInstall package name of application to install
|
||||
* @param middleButton if not null, adds a third button to the app with a call back
|
||||
* @return The dialog to display
|
||||
*/
|
||||
public static PreferenceInstallDialogFragment newInstance(Messenger messenger, int title, int message,
|
||||
String packageToInstall, int middleButton, boolean
|
||||
useMiddleButton) {
|
||||
PreferenceInstallDialogFragment frag = new PreferenceInstallDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
|
||||
InstallDialogFragmentHelper.wrapIntoArgs(messenger, title, message, packageToInstall, middleButton,
|
||||
useMiddleButton, args);
|
||||
|
||||
frag.setArguments(args);
|
||||
|
||||
return frag;
|
||||
}
|
||||
|
||||
/**
|
||||
* To create a DialogFragment with only two buttons
|
||||
*
|
||||
* @param title
|
||||
* @param message
|
||||
* @param packageToInstall
|
||||
* @return
|
||||
*/
|
||||
public static PreferenceInstallDialogFragment newInstance(int title, int message,
|
||||
String packageToInstall) {
|
||||
return newInstance(null, title, message, packageToInstall, -1, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
return InstallDialogFragmentHelper.getInstallDialogFromArgs(getArguments(), getActivity(),
|
||||
MESSAGE_MIDDLE_CLICKED, MESSAGE_DIALOG_DISMISSED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ui.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
|
||||
import org.sufficientlysecure.keychain.ui.util.InstallDialogFragmentHelper;
|
||||
|
||||
public class SupportInstallDialogFragment extends DialogFragment {
|
||||
|
||||
public static final int MESSAGE_MIDDLE_CLICKED = 1;
|
||||
public static final int MESSAGE_DIALOG_DISMISSED = 2;
|
||||
|
||||
/**
|
||||
* Creates a dialog which prompts the user to install an application. Consists of two default buttons ("Install"
|
||||
* and "Cancel") and an optional third button. Callbacks are provided only for the middle button, if set.
|
||||
*
|
||||
* @param messenger required only for callback from middle button if it has been set
|
||||
* @param title
|
||||
* @param message content of dialog
|
||||
* @param packageToInstall package name of application to install
|
||||
* @param middleButton if not null, adds a third button to the app with a call back
|
||||
* @return The dialog to display
|
||||
*/
|
||||
public static SupportInstallDialogFragment newInstance(Messenger messenger, int title, int message,
|
||||
String packageToInstall, int middleButton, boolean
|
||||
useMiddleButton) {
|
||||
SupportInstallDialogFragment frag = new SupportInstallDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
|
||||
InstallDialogFragmentHelper.wrapIntoArgs(messenger, title, message, packageToInstall, middleButton,
|
||||
useMiddleButton, args);
|
||||
|
||||
frag.setArguments(args);
|
||||
|
||||
return frag;
|
||||
}
|
||||
|
||||
/**
|
||||
* To create a DialogFragment with only two buttons
|
||||
*
|
||||
* @param title
|
||||
* @param message
|
||||
* @param packageToInstall
|
||||
* @return
|
||||
*/
|
||||
public static SupportInstallDialogFragment newInstance(int title, int message,
|
||||
String packageToInstall) {
|
||||
return newInstance(null, title, message, packageToInstall, -1, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
return InstallDialogFragmentHelper.getInstallDialogFromArgs(getArguments(), getActivity(),
|
||||
MESSAGE_MIDDLE_CLICKED, MESSAGE_DIALOG_DISMISSED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2012-2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.ui.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
import android.view.ContextThemeWrapper;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
public class InstallDialogFragmentHelper {
|
||||
private static final String ARG_MESSENGER = "messenger";
|
||||
private static final String ARG_TITLE = "title";
|
||||
private static final String ARG_MESSAGE = "message";
|
||||
private static final String ARG_MIDDLE_BUTTON = "middleButton";
|
||||
private static final String ARG_INSTALL_PATH = "installPath";
|
||||
private static final String ARG_USE_MIDDLE_BUTTON = "useMiddleButton";
|
||||
|
||||
private static final String PLAY_STORE_PATH = "market://search?q=pname:";
|
||||
|
||||
public static void wrapIntoArgs(Messenger messenger, int title, int message, String packageToInstall,
|
||||
int middleButton, boolean useMiddleButton, Bundle args) {
|
||||
args.putParcelable(ARG_MESSENGER, messenger);
|
||||
|
||||
args.putInt(ARG_TITLE, title);
|
||||
args.putInt(ARG_MESSAGE, message);
|
||||
args.putInt(ARG_MIDDLE_BUTTON, middleButton);
|
||||
args.putString(ARG_INSTALL_PATH, PLAY_STORE_PATH + packageToInstall);
|
||||
args.putBoolean(ARG_USE_MIDDLE_BUTTON, useMiddleButton);
|
||||
}
|
||||
|
||||
public static AlertDialog getInstallDialogFromArgs(Bundle args, final Activity activity,
|
||||
final int messengerMiddleButtonClicked,
|
||||
final int messengerDialogDimissed) {
|
||||
final Messenger messenger = args.getParcelable(ARG_MESSENGER);
|
||||
|
||||
final int title = args.getInt(ARG_TITLE);
|
||||
final int message = args.getInt(ARG_MESSAGE);
|
||||
final int middleButton = args.getInt(ARG_MIDDLE_BUTTON);
|
||||
final String installPath = args.getString(ARG_INSTALL_PATH);
|
||||
final boolean useMiddleButton = args.getBoolean(ARG_USE_MIDDLE_BUTTON);
|
||||
|
||||
// if the dialog is displayed from the application class, design is missing.
|
||||
// hack to get holo design (which is not automatically applied due to activity's
|
||||
// Theme.NoDisplay)
|
||||
ContextThemeWrapper theme = new ContextThemeWrapper(activity,
|
||||
R.style.Theme_AppCompat_Light_Dialog);
|
||||
CustomAlertDialogBuilder builder = new CustomAlertDialogBuilder(theme);
|
||||
|
||||
builder.setTitle(title).setMessage(message);
|
||||
|
||||
builder.setNegativeButton(R.string.orbot_install_dialog_cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Message msg = Message.obtain();
|
||||
msg.what = messengerDialogDimissed;
|
||||
try {
|
||||
messenger.send(msg);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
|
||||
} catch (NullPointerException e) {
|
||||
Log.w(Constants.TAG, "Messenger is null!", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
builder.setPositiveButton(R.string.orbot_install_dialog_install,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Uri uri = Uri.parse(installPath);
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
|
||||
activity.startActivity(intent);
|
||||
|
||||
Message msg = Message.obtain();
|
||||
msg.what = messengerDialogDimissed;
|
||||
try {
|
||||
messenger.send(msg);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
|
||||
} catch (NullPointerException e) {
|
||||
Log.w(Constants.TAG, "Messenger is null!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (useMiddleButton) {
|
||||
builder.setNeutralButton(middleButton,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
Message msg = Message.obtain();
|
||||
msg.what = messengerMiddleButtonClicked;
|
||||
try {
|
||||
messenger.send(msg);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(Constants.TAG, "Exception sending message, Is handler present?", e);
|
||||
} catch (NullPointerException e) {
|
||||
Log.w(Constants.TAG, "Messenger is null!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return builder.show();
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,9 @@ import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
/**
|
||||
* Created by Matt Allen
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult;
|
||||
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
|
||||
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
|
||||
|
||||
import java.net.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -34,6 +35,7 @@ import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
public class EmailKeyHelper {
|
||||
// TODO: Make this not require a proxy in it's constructor, redesign when it is to be used
|
||||
// to import keys, simply use CryptoOperationHelper with this callback
|
||||
public abstract class ImportContactKeysCallback
|
||||
implements CryptoOperationHelper.Callback<ImportKeyringParcel, ImportKeyResult> {
|
||||
@@ -41,14 +43,15 @@ public class EmailKeyHelper {
|
||||
private ArrayList<ParcelableKeyRing> mKeyList;
|
||||
private String mKeyserver;
|
||||
|
||||
public ImportContactKeysCallback(Context context, String keyserver) {
|
||||
this(context, ContactHelper.getContactMails(context), keyserver);
|
||||
public ImportContactKeysCallback(Context context, String keyserver, Proxy proxy) {
|
||||
this(context, ContactHelper.getContactMails(context), keyserver, proxy);
|
||||
}
|
||||
|
||||
public ImportContactKeysCallback(Context context, List<String> mails, String keyserver) {
|
||||
public ImportContactKeysCallback(Context context, List<String> mails, String keyserver,
|
||||
Proxy proxy) {
|
||||
Set<ImportKeysListEntry> entries = new HashSet<>();
|
||||
for (String mail : mails) {
|
||||
entries.addAll(getEmailKeys(context, mail));
|
||||
entries.addAll(getEmailKeys(context, mail, proxy));
|
||||
}
|
||||
|
||||
// Put them in a list and import
|
||||
@@ -65,7 +68,7 @@ public class EmailKeyHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<ImportKeysListEntry> getEmailKeys(Context context, String mail) {
|
||||
public static Set<ImportKeysListEntry> getEmailKeys(Context context, String mail, Proxy proxy) {
|
||||
Set<ImportKeysListEntry> keys = new HashSet<>();
|
||||
|
||||
// Try _hkp._tcp SRV record first
|
||||
@@ -73,7 +76,7 @@ public class EmailKeyHelper {
|
||||
if (mailparts.length == 2) {
|
||||
HkpKeyserver hkp = HkpKeyserver.resolve(mailparts[1]);
|
||||
if (hkp != null) {
|
||||
keys.addAll(getEmailKeys(mail, hkp));
|
||||
keys.addAll(getEmailKeys(mail, hkp, proxy));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,16 +85,17 @@ public class EmailKeyHelper {
|
||||
String server = Preferences.getPreferences(context).getPreferredKeyserver();
|
||||
if (server != null) {
|
||||
HkpKeyserver hkp = new HkpKeyserver(server);
|
||||
keys.addAll(getEmailKeys(mail, hkp));
|
||||
keys.addAll(getEmailKeys(mail, hkp, proxy));
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
public static List<ImportKeysListEntry> getEmailKeys(String mail, Keyserver keyServer) {
|
||||
public static List<ImportKeysListEntry> getEmailKeys(String mail, Keyserver keyServer,
|
||||
Proxy proxy) {
|
||||
Set<ImportKeysListEntry> keys = new HashSet<>();
|
||||
try {
|
||||
for (ImportKeysListEntry key : keyServer.search(mail)) {
|
||||
for (ImportKeysListEntry key : keyServer.search(mail, proxy)) {
|
||||
if (key.isRevoked() || key.isExpired()) continue;
|
||||
for (String userId : key.getUserIds()) {
|
||||
if (userId.toLowerCase().contains(mail.toLowerCase(Locale.ENGLISH))) {
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.util;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
|
||||
/**
|
||||
* used to simply transport java.net.Proxy objects created using InetSockets between services/activities
|
||||
*/
|
||||
public class ParcelableProxy implements Parcelable {
|
||||
private String mProxyHost;
|
||||
private int mProxyPort;
|
||||
private Proxy.Type mProxyType;
|
||||
|
||||
public ParcelableProxy(String hostName, int port, Proxy.Type type) {
|
||||
mProxyHost = hostName;
|
||||
|
||||
if (hostName == null) {
|
||||
return; // represents a null proxy
|
||||
}
|
||||
|
||||
mProxyPort = port;
|
||||
|
||||
mProxyType = type;
|
||||
}
|
||||
|
||||
public static ParcelableProxy getForNoProxy() {
|
||||
return new ParcelableProxy(null, -1, null);
|
||||
}
|
||||
|
||||
public Proxy getProxy() {
|
||||
if (mProxyHost == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Proxy(mProxyType, new InetSocketAddress(mProxyHost, mProxyPort));
|
||||
}
|
||||
|
||||
protected ParcelableProxy(Parcel in) {
|
||||
mProxyHost = in.readString();
|
||||
mProxyPort = in.readInt();
|
||||
mProxyType = (Proxy.Type) in.readSerializable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(mProxyHost);
|
||||
dest.writeInt(mProxyPort);
|
||||
dest.writeSerializable(mProxyType);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static final Parcelable.Creator<ParcelableProxy> CREATOR = new Parcelable.Creator<ParcelableProxy>() {
|
||||
@Override
|
||||
public ParcelableProxy createFromParcel(Parcel in) {
|
||||
return new ParcelableProxy(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelableProxy[] newArray(int size) {
|
||||
return new ParcelableProxy[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -21,9 +21,13 @@ package org.sufficientlysecure.keychain.util;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.preference.PreferenceManager;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.Constants.Pref;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
|
||||
import java.net.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.ListIterator;
|
||||
@@ -35,6 +39,10 @@ import java.util.Vector;
|
||||
public class Preferences {
|
||||
private static Preferences sPreferences;
|
||||
private SharedPreferences mSharedPreferences;
|
||||
private Resources mResources;
|
||||
|
||||
private static String PREF_FILE_NAME = "APG.main";
|
||||
private static int PREF_FILE_MODE = Context.MODE_MULTI_PROCESS;
|
||||
|
||||
public static synchronized Preferences getPreferences(Context context) {
|
||||
return getPreferences(context, false);
|
||||
@@ -51,12 +59,18 @@ public class Preferences {
|
||||
}
|
||||
|
||||
private Preferences(Context context) {
|
||||
mResources = context.getResources();
|
||||
updateSharedPreferences(context);
|
||||
}
|
||||
|
||||
public static void setPreferenceManagerFileAndMode(PreferenceManager manager) {
|
||||
manager.setSharedPreferencesName(PREF_FILE_NAME);
|
||||
manager.setSharedPreferencesMode(PREF_FILE_MODE);
|
||||
}
|
||||
|
||||
public void updateSharedPreferences(Context context) {
|
||||
// multi-process safe preferences
|
||||
mSharedPreferences = context.getSharedPreferences("APG.main", Context.MODE_MULTI_PROCESS);
|
||||
mSharedPreferences = context.getSharedPreferences(PREF_FILE_NAME, PREF_FILE_MODE);
|
||||
}
|
||||
|
||||
public String getLanguage() {
|
||||
@@ -207,7 +221,6 @@ public class Preferences {
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void setUseArmor(boolean useArmor) {
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
editor.putBoolean(Pref.USE_ARMOR, useArmor);
|
||||
@@ -228,6 +241,89 @@ public class Preferences {
|
||||
return mSharedPreferences.getBoolean(Pref.ENCRYPT_FILENAMES, true);
|
||||
}
|
||||
|
||||
// proxy preference functions start here
|
||||
|
||||
public boolean getUseNormalProxy() {
|
||||
return mSharedPreferences.getBoolean(Constants.Pref.USE_NORMAL_PROXY, false);
|
||||
}
|
||||
|
||||
public boolean getUseTorProxy() {
|
||||
return mSharedPreferences.getBoolean(Constants.Pref.USE_TOR_PROXY, false);
|
||||
}
|
||||
|
||||
public String getProxyHost() {
|
||||
return mSharedPreferences.getString(Constants.Pref.PROXY_HOST, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* we store port as String for easy interfacing with EditTextPreference, but return it as an integer
|
||||
*
|
||||
* @return port number of proxy
|
||||
*/
|
||||
public int getProxyPort() {
|
||||
return Integer.parseInt(mSharedPreferences.getString(Pref.PROXY_PORT, "-1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* we store port as String for easy interfacing with EditTextPreference, but return it as an integer
|
||||
*
|
||||
* @param port proxy port
|
||||
*/
|
||||
public void setProxyPort(String port) {
|
||||
SharedPreferences.Editor editor = mSharedPreferences.edit();
|
||||
editor.putString(Pref.PROXY_PORT, port);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public Proxy.Type getProxyType() {
|
||||
final String typeHttp = mResources.getString(R.string.pref_proxy_type_value_http);
|
||||
final String typeSocks = mResources.getString(R.string.pref_proxy_type_value_socks);
|
||||
|
||||
String type = mSharedPreferences.getString(Pref.PROXY_TYPE, typeHttp);
|
||||
|
||||
if (type.equals(typeHttp)) return Proxy.Type.HTTP;
|
||||
else if (type.equals(typeSocks)) return Proxy.Type.SOCKS;
|
||||
else { // shouldn't happen
|
||||
Log.e(Constants.TAG, "Invalid Proxy Type in preferences");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ProxyPrefs getProxyPrefs() {
|
||||
boolean useTor = getUseTorProxy();
|
||||
boolean useNormalProxy = getUseNormalProxy();
|
||||
|
||||
if (useTor) {
|
||||
return new ProxyPrefs(true, false, Constants.Orbot.PROXY_HOST, Constants.Orbot.PROXY_PORT,
|
||||
Constants.Orbot.PROXY_TYPE);
|
||||
} else if (useNormalProxy) {
|
||||
return new ProxyPrefs(useTor, useNormalProxy, getProxyHost(), getProxyPort(), getProxyType());
|
||||
} else {
|
||||
return new ProxyPrefs(false, false, null, -1, null);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// proxy preference functions ends here
|
||||
|
||||
public CloudSearchPrefs getCloudSearchPrefs() {
|
||||
return new CloudSearchPrefs(mSharedPreferences.getBoolean(Pref.SEARCH_KEYSERVER, true),
|
||||
mSharedPreferences.getBoolean(Pref.SEARCH_KEYBASE, true),
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.util;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -26,7 +27,6 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
@@ -61,7 +61,7 @@ public class TlsHelper {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int reads = is.read();
|
||||
|
||||
while(reads != -1){
|
||||
while (reads != -1) {
|
||||
baos.write(reads);
|
||||
reads = is.read();
|
||||
}
|
||||
@@ -74,15 +74,56 @@ public class TlsHelper {
|
||||
}
|
||||
}
|
||||
|
||||
public static URLConnection openConnection(URL url) throws IOException, TlsHelperException {
|
||||
public static void pinCertificateIfNecessary(OkHttpClient client, URL url) throws TlsHelperException, IOException {
|
||||
if (url.getProtocol().equals("https")) {
|
||||
for (String domain : sStaticCA.keySet()) {
|
||||
if (url.getHost().endsWith(domain)) {
|
||||
return openCAConnection(sStaticCA.get(domain), url);
|
||||
pinCertificate(sStaticCA.get(domain), client);
|
||||
}
|
||||
}
|
||||
}
|
||||
return url.openConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies the client to accept only requests with a given certificate. Applies to all URLs requested by the
|
||||
* client.
|
||||
* Therefore a client that is pinned this way should be used to only make requests to URLs with passed certificate.
|
||||
* TODO: Refactor - More like SSH StrictHostKeyChecking than pinning?
|
||||
*
|
||||
* @param certificate certificate to pin
|
||||
* @param client OkHttpClient to enforce pinning on
|
||||
* @throws TlsHelperException
|
||||
* @throws IOException
|
||||
*/
|
||||
private static void pinCertificate(byte[] certificate, OkHttpClient client)
|
||||
throws TlsHelperException, IOException {
|
||||
// We don't use OkHttp's CertificatePinner since it depends on a TrustManager to verify it too. Refer to
|
||||
// note at end of description: http://square.github.io/okhttp/javadoc/com/squareup/okhttp/CertificatePinner.html
|
||||
// Creating our own TrustManager that trusts only our certificate eliminates the need for certificate pinning
|
||||
try {
|
||||
// Load CA
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
Certificate ca = cf.generateCertificate(new ByteArrayInputStream(certificate));
|
||||
|
||||
// Create a KeyStore containing our trusted CAs
|
||||
String keyStoreType = KeyStore.getDefaultType();
|
||||
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
|
||||
keyStore.load(null, null);
|
||||
keyStore.setCertificateEntry("ca", ca);
|
||||
|
||||
// Create a TrustManager that trusts the CAs in our KeyStore
|
||||
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
|
||||
tmf.init(keyStore);
|
||||
|
||||
// Create an SSLContext that uses our TrustManager
|
||||
SSLContext context = SSLContext.getInstance("TLS");
|
||||
context.init(null, tmf.getTrustManagers(), null);
|
||||
|
||||
client.setSslSocketFactory(context.getSocketFactory());
|
||||
} catch (CertificateException | KeyStoreException | KeyManagementException | NoSuchAlgorithmException e) {
|
||||
throw new TlsHelperException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,222 @@
|
||||
/* This is the license for Orlib, a free software project to
|
||||
provide anonymity on the Internet from a Google Android smartphone.
|
||||
|
||||
For more information about Orlib, see https://guardianproject.info/
|
||||
|
||||
If you got this file as a part of a larger bundle, there may be other
|
||||
license terms that you should be aware of.
|
||||
===============================================================================
|
||||
Orlib is distributed under this license (aka the 3-clause BSD license)
|
||||
|
||||
Copyright (c) 2009-2010, Nathan Freitas, The Guardian Project
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the names of the copyright owners nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*****
|
||||
Orlib contains a binary distribution of the JSocks library:
|
||||
http://code.google.com/p/jsocks-mirror/
|
||||
which is licensed under the GNU Lesser General Public License:
|
||||
http://www.gnu.org/licenses/lgpl.html
|
||||
|
||||
*****
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.util.orbot;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v4.app.FragmentActivity;
|
||||
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.SupportInstallDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.OrbotStartDialogFragment;
|
||||
import org.sufficientlysecure.keychain.ui.dialog.PreferenceInstallDialogFragment;
|
||||
import org.sufficientlysecure.keychain.util.Preferences;
|
||||
|
||||
/**
|
||||
* This class is taken from the NetCipher library: https://github.com/guardianproject/NetCipher/
|
||||
*/
|
||||
public class OrbotHelper {
|
||||
|
||||
public final static String ORBOT_PACKAGE_NAME = "org.torproject.android";
|
||||
public final static String TOR_BIN_PATH = "/data/data/org.torproject.android/app_bin/tor";
|
||||
|
||||
public final static String ACTION_START_TOR = "org.torproject.android.START_TOR";
|
||||
|
||||
public static boolean isOrbotRunning() {
|
||||
int procId = TorServiceUtils.findProcessId(TOR_BIN_PATH);
|
||||
|
||||
return (procId != -1);
|
||||
}
|
||||
|
||||
public static boolean isOrbotInstalled(Context context) {
|
||||
return isAppInstalled(ORBOT_PACKAGE_NAME, context);
|
||||
}
|
||||
|
||||
private static boolean isAppInstalled(String uri, Context context) {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
|
||||
boolean installed;
|
||||
try {
|
||||
pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES);
|
||||
installed = true;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
installed = false;
|
||||
}
|
||||
return installed;
|
||||
}
|
||||
|
||||
/**
|
||||
* hack to get around the fact that PreferenceActivity still supports only android.app.DialogFragment
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static android.app.DialogFragment getPreferenceInstallDialogFragment() {
|
||||
return PreferenceInstallDialogFragment.newInstance(R.string.orbot_install_dialog_title,
|
||||
R.string.orbot_install_dialog_content, ORBOT_PACKAGE_NAME);
|
||||
}
|
||||
|
||||
public static DialogFragment getInstallDialogFragment() {
|
||||
return SupportInstallDialogFragment.newInstance(R.string.orbot_install_dialog_title,
|
||||
R.string.orbot_install_dialog_content, ORBOT_PACKAGE_NAME);
|
||||
}
|
||||
|
||||
public static DialogFragment getInstallDialogFragmentWithThirdButton(Messenger messenger, int middleButton) {
|
||||
return SupportInstallDialogFragment.newInstance(messenger, R.string.orbot_install_dialog_title,
|
||||
R.string.orbot_install_dialog_content, ORBOT_PACKAGE_NAME, middleButton, true);
|
||||
}
|
||||
|
||||
public static DialogFragment getOrbotStartDialogFragment(Messenger messenger, int middleButton) {
|
||||
return OrbotStartDialogFragment.newInstance(messenger, R.string.orbot_start_dialog_title, R.string
|
||||
.orbot_start_dialog_content,
|
||||
middleButton);
|
||||
}
|
||||
|
||||
public static Intent getOrbotStartIntent() {
|
||||
Intent intent = new Intent(ACTION_START_TOR);
|
||||
intent.setPackage(ORBOT_PACKAGE_NAME);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static boolean isOrbotInRequiredState(Context context) {
|
||||
Preferences.ProxyPrefs proxyPrefs = Preferences.getPreferences(context).getProxyPrefs();
|
||||
if (!proxyPrefs.torEnabled) {
|
||||
return true;
|
||||
} else if (!OrbotHelper.isOrbotInstalled(context) || !OrbotHelper.isOrbotRunning()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if Tor is enabled and if it is, that Orbot is installed and runnign. Generates appropriate dialogs.
|
||||
*
|
||||
* @param middleButton resourceId of string to display as the middle button of install and enable dialogs
|
||||
* @param middleButtonRunnable runnable to be executed if the user clicks on the middle button
|
||||
* @param proxyPrefs
|
||||
* @param fragmentActivity
|
||||
* @return true if Tor is not enabled or Tor is enabled and Orbot is installed and running, else false
|
||||
*/
|
||||
public static boolean putOrbotInRequiredState(final int middleButton,
|
||||
final Runnable middleButtonRunnable,
|
||||
final Runnable dialogDismissRunnable,
|
||||
Preferences.ProxyPrefs proxyPrefs,
|
||||
FragmentActivity fragmentActivity) {
|
||||
|
||||
if (!proxyPrefs.torEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!OrbotHelper.isOrbotInstalled(fragmentActivity)) {
|
||||
Handler ignoreTorHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case SupportInstallDialogFragment.MESSAGE_MIDDLE_CLICKED:
|
||||
middleButtonRunnable.run();
|
||||
break;
|
||||
case SupportInstallDialogFragment.MESSAGE_DIALOG_DISMISSED:
|
||||
dialogDismissRunnable.run();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OrbotHelper.getInstallDialogFragmentWithThirdButton(
|
||||
new Messenger(ignoreTorHandler),
|
||||
middleButton
|
||||
).show(fragmentActivity.getSupportFragmentManager(), "OrbotHelperOrbotInstallDialog");
|
||||
|
||||
return false;
|
||||
} else if (!OrbotHelper.isOrbotRunning()) {
|
||||
|
||||
Handler ignoreTorHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case OrbotStartDialogFragment.MESSAGE_MIDDLE_BUTTON:
|
||||
middleButtonRunnable.run();
|
||||
break;
|
||||
case OrbotStartDialogFragment.MESSAGE_DIALOG_DISMISSED:
|
||||
dialogDismissRunnable.run();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
OrbotHelper.getOrbotStartDialogFragment(new Messenger(ignoreTorHandler),
|
||||
middleButton)
|
||||
.show(fragmentActivity.getSupportFragmentManager(), "OrbotHelperOrbotStartDialog");
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean putOrbotInRequiredState(final int middleButton,
|
||||
final Runnable middleButtonRunnable,
|
||||
Preferences.ProxyPrefs proxyPrefs,
|
||||
FragmentActivity fragmentActivity) {
|
||||
Runnable emptyRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
}
|
||||
};
|
||||
return putOrbotInRequiredState(middleButton, middleButtonRunnable, emptyRunnable,
|
||||
proxyPrefs, fragmentActivity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
/* This is the license for Orlib, a free software project to
|
||||
provide anonymity on the Internet from a Google Android smartphone.
|
||||
|
||||
For more information about Orlib, see https://guardianproject.info/
|
||||
|
||||
If you got this file as a part of a larger bundle, there may be other
|
||||
license terms that you should be aware of.
|
||||
===============================================================================
|
||||
Orlib is distributed under this license (aka the 3-clause BSD license)
|
||||
|
||||
Copyright (c) 2009-2010, Nathan Freitas, The Guardian Project
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the names of the copyright owners nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*****
|
||||
Orlib contains a binary distribution of the JSocks library:
|
||||
http://code.google.com/p/jsocks-mirror/
|
||||
which is licensed under the GNU Lesser General Public License:
|
||||
http://www.gnu.org/licenses/lgpl.html
|
||||
|
||||
*****
|
||||
*/
|
||||
|
||||
package org.sufficientlysecure.keychain.util.orbot;
|
||||
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* This class is taken from the NetCipher library: https://github.com/guardianproject/NetCipher/
|
||||
*/
|
||||
public class TorServiceUtils {
|
||||
// various console cmds
|
||||
public final static String SHELL_CMD_PS = "ps";
|
||||
public final static String SHELL_CMD_PIDOF = "pidof";
|
||||
|
||||
public static int findProcessId(String command) {
|
||||
int procId = -1;
|
||||
|
||||
try {
|
||||
procId = findProcessIdWithPidOf(command);
|
||||
|
||||
if (procId == -1) {
|
||||
procId = findProcessIdWithPS(command);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
procId = findProcessIdWithPS(command);
|
||||
} catch (Exception e2) {
|
||||
Log.e(Constants.TAG, "Unable to get proc id for command: " + URLEncoder.encode(command), e2);
|
||||
}
|
||||
}
|
||||
|
||||
return procId;
|
||||
}
|
||||
|
||||
// use 'pidof' command
|
||||
public static int findProcessIdWithPidOf(String command) throws Exception {
|
||||
|
||||
int procId = -1;
|
||||
Runtime r = Runtime.getRuntime();
|
||||
Process procPs;
|
||||
|
||||
String baseName = new File(command).getName();
|
||||
// fix contributed my mikos on 2010.12.10
|
||||
procPs = r.exec(new String[]{
|
||||
SHELL_CMD_PIDOF, baseName
|
||||
});
|
||||
// procPs = r.exec(SHELL_CMD_PIDOF);
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(procPs.getInputStream()));
|
||||
String line;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
|
||||
try {
|
||||
// this line should just be the process id
|
||||
procId = Integer.parseInt(line.trim());
|
||||
break;
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e("TorServiceUtils", "unable to parse process pid: " + line, e);
|
||||
}
|
||||
}
|
||||
|
||||
return procId;
|
||||
}
|
||||
|
||||
// use 'ps' command
|
||||
public static int findProcessIdWithPS(String command) throws Exception {
|
||||
int procId = -1;
|
||||
Runtime r = Runtime.getRuntime();
|
||||
Process procPs;
|
||||
|
||||
procPs = r.exec(SHELL_CMD_PS);
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(procPs.getInputStream()));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.contains(' ' + command)) {
|
||||
|
||||
StringTokenizer st = new StringTokenizer(line, " ");
|
||||
st.nextToken(); // proc owner
|
||||
|
||||
procId = Integer.parseInt(st.nextToken().trim());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return procId;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user