diff --git a/OpenKeychain/src/main/AndroidManifest.xml b/OpenKeychain/src/main/AndroidManifest.xml index 7c604611b..94d17b8d6 100644 --- a/OpenKeychain/src/main/AndroidManifest.xml +++ b/OpenKeychain/src/main/AndroidManifest.xml @@ -148,11 +148,6 @@ android:name=".ui.EditKeyActivity" android:configChanges="orientation|screenSize|keyboardHidden|keyboard" android:label="@string/title_edit_key" /> - ANALYTICS_PREFS = Arrays.asList(USE_NORMAL_PROXY, USE_TOR_PROXY, SYNC_CONTACTS, SYNC_KEYSERVER, ENABLE_WIFI_SYNC_ONLY, EXPERIMENTAL_ENABLE_KEYBASE, - EXPERIMENTAL_ENABLE_LINKED_IDENTITIES, EXPERIMENTAL_USB_ALLOW_UNTESTED, + EXPERIMENTAL_USB_ALLOW_UNTESTED, PASSPHRASE_CACHE_SUBS, SEARCH_KEYSERVER, SEARCH_KEYBASE, SEARCH_WEB_KEY_DIRECTORY, TEXT_USE_COMPRESSION, TEXT_SELF_ENCRYPT, FILE_USE_COMPRESSION, FILE_SELF_ENCRYPT, USE_ARMOR, USE_NUMKEYPAD_FOR_SECURITY_TOKEN_PIN, ENCRYPT_FILENAMES); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedAttribute.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedAttribute.java deleted file mode 100644 index cceb66438..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedAttribute.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.linked; - -import java.net.URI; - -import android.content.Context; -import android.support.annotation.DrawableRes; - -public class LinkedAttribute extends UriAttribute { - - public final LinkedResource mResource; - - protected LinkedAttribute(URI uri, LinkedResource resource) { - super(uri); - if (resource == null) { - throw new AssertionError("resource must not be null in a LinkedIdentity!"); - } - mResource = resource; - } - - public @DrawableRes int getDisplayIcon() { - return mResource.getDisplayIcon(); - } - - public String getDisplayTitle(Context context) { - return mResource.getDisplayTitle(context); - } - - public String getDisplayComment(Context context) { - return mResource.getDisplayComment(context); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedResource.java deleted file mode 100644 index ba2d88e11..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedResource.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.linked; - -import java.net.URI; - -import android.content.Context; -import android.content.Intent; -import android.support.annotation.DrawableRes; -import android.support.annotation.StringRes; - -public abstract class LinkedResource { - - public abstract URI toUri(); - - public abstract @DrawableRes int getDisplayIcon(); - public abstract @StringRes int getVerifiedText(boolean isSecret); - public abstract String getDisplayTitle(Context context); - public abstract String getDisplayComment(Context context); - public boolean isViewable() { - return false; - } - public Intent getViewIntent() { - return null; - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java deleted file mode 100644 index b7ce26072..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java +++ /dev/null @@ -1,302 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.linked; - -import android.content.Context; - -import okhttp3.CertificatePinner; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import org.json.JSONException; -import org.sufficientlysecure.keychain.linked.resources.GenericHttpsResource; -import org.sufficientlysecure.keychain.linked.resources.GithubResource; -import org.sufficientlysecure.keychain.linked.resources.TwitterResource; -import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; -import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; -import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.network.OkHttpClientFactory; -import timber.log.Timber; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URI; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map.Entry; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - - -public abstract class LinkedTokenResource extends LinkedResource { - - protected final URI mSubUri; - protected final Set mFlags; - protected final HashMap mParams; - - public static Pattern magicPattern = - Pattern.compile("\\[Verifying my (?:Open|)?PGP key(?::|) openpgp4fpr:([a-zA-Z0-9]+)]"); - - protected LinkedTokenResource(Set flags, HashMap params, URI uri) { - mFlags = flags; - mParams = params; - mSubUri = uri; - } - - @SuppressWarnings("unused") - public URI getSubUri () { - return mSubUri; - } - - public Set getFlags () { - return new HashSet<>(mFlags); - } - - public HashMap getParams () { - return new HashMap<>(mParams); - } - - public static String generate (byte[] fingerprint) { - return String.format("[Verifying my OpenPGP key: openpgp4fpr:%s]", - KeyFormattingUtils.convertFingerprintToHex(fingerprint)); - } - - protected static LinkedTokenResource fromUri (URI uri) { - - if (!"openpgpid+token".equals(uri.getScheme()) - && !"openpgpid+cookie".equals(uri.getScheme())) { - Timber.e("unknown uri scheme in (suspected) linked id packet"); - return null; - } - - if (!uri.isOpaque()) { - Timber.e("non-opaque uri in (suspected) linked id packet"); - return null; - } - - String specific = uri.getSchemeSpecificPart(); - if (!specific.contains("@")) { - Timber.e("unknown uri scheme in linked id packet"); - return null; - } - - String[] pieces = specific.split("@", 2); - URI subUri = URI.create(pieces[1]); - - Set flags = new HashSet<>(); - HashMap params = new HashMap<>(); - if (!pieces[0].isEmpty()) { - String[] rawParams = pieces[0].split(";"); - for (String param : rawParams) { - String[] p = param.split("=", 2); - if (p.length == 1) { - flags.add(param); - } else { - params.put(p[0], p[1]); - } - } - } - - return findResourceType(flags, params, subUri); - - } - - protected static LinkedTokenResource findResourceType (Set flags, - HashMap params, URI subUri) { - - LinkedTokenResource res; - - res = GenericHttpsResource.create(flags, params, subUri); - if (res != null) { - return res; - } - // res = DnsResource.create(flags, params, subUri); - // if (res != null) { - // return res; - // } - res = TwitterResource.create(flags, params, subUri); - if (res != null) { - return res; - } - res = GithubResource.create(flags, params, subUri); - if (res != null) { - return res; - } - - return null; - - } - - public URI toUri () { - - StringBuilder b = new StringBuilder(); - b.append("openpgpid+token:"); - - // add flags - if (mFlags != null) { - boolean first = true; - for (String flag : mFlags) { - if (!first) { - b.append(";"); - } - first = false; - b.append(flag); - } - } - - // add parameters - if (mParams != null) { - boolean first = true; - for (Entry stringStringEntry : mParams.entrySet()) { - if (!first) { - b.append(";"); - } - first = false; - b.append(stringStringEntry.getKey()).append("=").append(stringStringEntry.getValue()); - } - } - - b.append("@"); - b.append(mSubUri); - - return URI.create(b.toString()); - - } - - public LinkedVerifyResult verify(Context context, byte[] fingerprint) { - - OperationLog log = new OperationLog(); - log.add(LogType.MSG_LV, 0); - - // Try to fetch resource. Logs for itself - String res = null; - try { - res = fetchResource(context, log, 1); - } catch (HttpStatusException e) { - // log verbose output to logcat - Timber.e("http error (" + e.getStatus() + "): " + e.getReason()); - log.add(LogType.MSG_LV_FETCH_ERROR, 2, Integer.toString(e.getStatus())); - } catch (MalformedURLException e) { - log.add(LogType.MSG_LV_FETCH_ERROR_URL, 2); - } catch (IOException e) { - Timber.e(e, "io error"); - log.add(LogType.MSG_LV_FETCH_ERROR_IO, 2); - } catch (JSONException e) { - Timber.e(e, "json error"); - log.add(LogType.MSG_LV_FETCH_ERROR_FORMAT, 2); - } - - if (res == null) { - // if this is null, an error was recorded in fetchResource above - return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); - } - - Timber.d("Resource data: '" + res + "'"); - - return verifyString(log, 1, res, fingerprint); - - } - - protected abstract String fetchResource (Context context, OperationLog log, int indent) - throws HttpStatusException, IOException, JSONException; - - protected Matcher matchResource (OperationLog log, int indent, String res) { - return magicPattern.matcher(res); - } - - protected LinkedVerifyResult verifyString (OperationLog log, int indent, - String res, - byte[] fingerprint) { - - log.add(LogType.MSG_LV_MATCH, indent); - Matcher match = matchResource(log, indent+1, res); - if (!match.find()) { - log.add(LogType.MSG_LV_MATCH_ERROR, 2); - return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); - } - - String candidateFp = match.group(1).toLowerCase(); - String fp = KeyFormattingUtils.convertFingerprintToHex(fingerprint); - if (!fp.equals(candidateFp)) { - log.add(LogType.MSG_LV_FP_ERROR, indent); - return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); - } - log.add(LogType.MSG_LV_FP_OK, indent); - - return new LinkedVerifyResult(LinkedVerifyResult.RESULT_OK, log); - - } - - - private static CertificatePinner getCertificatePinner(String hostname, String[] pins){ - CertificatePinner.Builder builder = new CertificatePinner.Builder(); - for(String pin : pins){ - builder.add(hostname,pin); - } - return builder.build(); - } - - - public static String getResponseBody(Request request, String... pins) - throws IOException, HttpStatusException { - - Timber.d(""); - OkHttpClient client; - if (pins != null) { - client = OkHttpClientFactory.getSimpleClientPinned(getCertificatePinner(request.url().url().getHost(), pins)); - } else { - client = OkHttpClientFactory.getSimpleClient(); - } - - Response response = client.newCall(request).execute(); - - - int statusCode = response.code(); - String reason = response.message(); - - if (statusCode != 200) { - throw new HttpStatusException(statusCode, reason); - } - - return response.body().string(); - } - - public static class HttpStatusException extends Throwable { - - private final int mStatusCode; - private final String mReason; - - HttpStatusException(int statusCode, String reason) { - super("http status " + statusCode + ": " + reason); - mStatusCode = statusCode; - mReason = reason; - } - - public int getStatus() { - return mStatusCode; - } - - public String getReason() { - return mReason; - } - - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/UriAttribute.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/UriAttribute.java deleted file mode 100644 index 6187d8ff7..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/UriAttribute.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.linked; - -import org.bouncycastle.util.Strings; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import timber.log.Timber; - -import java.io.IOException; -import java.net.URI; - -import android.content.Context; -import android.support.annotation.DrawableRes; - -/** The RawLinkedIdentity contains raw parsed data from a Linked Identity subpacket. */ -public class UriAttribute { - - public final URI mUri; - - protected UriAttribute(URI uri) { - mUri = uri; - } - - public byte[] getEncoded() { - return Strings.toUTF8ByteArray(mUri.toASCIIString()); - } - - public static UriAttribute fromAttributeData(byte[] data) throws IOException { - WrappedUserAttribute att = WrappedUserAttribute.fromData(data); - - byte[][] subpackets = att.getSubpackets(); - if (subpackets.length >= 1) { - return fromSubpacketData(subpackets[0]); - } - - throw new IOException("no subpacket data"); - } - - static UriAttribute fromSubpacketData(byte[] data) { - - try { - String uriStr = Strings.fromUTF8ByteArray(data); - URI uri = URI.create(uriStr); - - LinkedResource res = LinkedTokenResource.fromUri(uri); - if (res == null) { - return new UriAttribute(uri); - } - - return new LinkedAttribute(uri, res); - - } catch (IllegalArgumentException e) { - Timber.e("error parsing uri in (suspected) linked id packet"); - return null; - } - } - - public static UriAttribute fromResource (LinkedTokenResource res) { - return new UriAttribute(res.toUri()); - } - - - public WrappedUserAttribute toUserAttribute () { - return WrappedUserAttribute.fromSubpacket(WrappedUserAttribute.UAT_URI_ATTRIBUTE, getEncoded()); - } - - public @DrawableRes int getDisplayIcon() { - return R.drawable.ic_warning_grey_24dp; - } - - public String getDisplayTitle(Context context) { - return "Unknown Identity"; - } - - public String getDisplayComment(Context context) { - return null; - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java deleted file mode 100644 index e3f7a9c48..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.linked.resources; - -import android.content.Context; -import android.support.annotation.DrawableRes; -import android.support.annotation.StringRes; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.linked.LinkedTokenResource; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; - -import java.net.URI; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import de.measite.minidns.Client; -import de.measite.minidns.DNSMessage; -import de.measite.minidns.Question; -import de.measite.minidns.Record; -import de.measite.minidns.Record.CLASS; -import de.measite.minidns.Record.TYPE; -import de.measite.minidns.record.TXT; - -public class DnsResource extends LinkedTokenResource { - - final static Pattern magicPattern = - Pattern.compile("openpgpid\\+token=([a-zA-Z0-9]+)(?:#|;)([a-zA-Z0-9]+)"); - - String mFqdn; - CLASS mClass; - TYPE mType; - - DnsResource(Set flags, HashMap params, URI uri, - String fqdn, CLASS clazz, TYPE type) { - super(flags, params, uri); - - mFqdn = fqdn; - mClass = clazz; - mType = type; - } - - public static String generateText(byte[] fingerprint) { - - return String.format("openpgp4fpr=%s", - KeyFormattingUtils.convertFingerprintToHex(fingerprint)); - - } - - public static DnsResource createNew (String domain) { - HashSet flags = new HashSet<>(); - HashMap params = new HashMap<>(); - URI uri = URI.create("dns:" + domain + "?TYPE=TXT"); - return create(flags, params, uri); - } - - public static DnsResource create(Set flags, HashMap params, URI uri) { - if ( ! ("dns".equals(uri.getScheme()) - && (flags == null || flags.isEmpty()) - && (params == null || params.isEmpty()))) { - return null; - } - - // - String spec = uri.getSchemeSpecificPart(); - // If there are // at the beginning, this includes an authority - we don't support those! - if (spec.startsWith("//")) { - return null; - } - - String[] pieces = spec.split("\\?", 2); - // In either case, part before a ? is the fqdn - String fqdn = pieces[0]; - // There may be a query part - /* - if (pieces.length > 1) { - // parse CLASS and TYPE query parameters - } - */ - - CLASS clazz = CLASS.IN; - TYPE type = TYPE.TXT; - - return new DnsResource(flags, params, uri, fqdn, clazz, type); - } - - @SuppressWarnings("unused") - public String getFqdn() { - return mFqdn; - } - - @Override - protected String fetchResource (Context context, OperationLog log, int indent) { - - Client c = new Client(); - DNSMessage msg = c.query(new Question(mFqdn, mType, mClass)); - Record aw = msg.getAnswers()[0]; - TXT txt = (TXT) aw.getPayload(); - return txt.getText().toLowerCase(); - - } - - @Override - protected Matcher matchResource(OperationLog log, int indent, String res) { - return magicPattern.matcher(res); - } - - @Override - public @StringRes - int getVerifiedText(boolean isSecret) { - return isSecret ? R.string.linked_verified_secret_dns : R.string.linked_verified_dns; - } - - @Override - public @DrawableRes int getDisplayIcon() { - return R.drawable.linked_dns; - } - - @Override - public String getDisplayTitle(Context context) { - return context.getString(R.string.linked_title_dns); - } - - @Override - public String getDisplayComment(Context context) { - return mFqdn; - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java deleted file mode 100644 index e9b531ddb..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.linked.resources; - -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.support.annotation.DrawableRes; -import android.support.annotation.StringRes; - -import okhttp3.Request; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; -import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.linked.LinkedTokenResource; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; - -import java.io.IOException; -import java.net.URI; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; - -public class GenericHttpsResource extends LinkedTokenResource { - - GenericHttpsResource(Set flags, HashMap params, URI uri) { - super(flags, params, uri); - } - - public static String generateText (Context context, byte[] fingerprint) { - String token = LinkedTokenResource.generate(fingerprint); - - return String.format(context.getResources().getString(R.string.linked_id_generic_text), - token, "0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprint).substring(24)); - } - - @Override - protected String fetchResource (Context context, OperationLog log, int indent) - throws HttpStatusException, IOException { - - log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); - Request request = new Request.Builder() - .url(mSubUri.toURL()) - .addHeader("User-Agent", "OpenKeychain") - .build(); - return getResponseBody(request); - - } - - public static GenericHttpsResource createNew (URI uri) { - HashSet flags = new HashSet<>(); - flags.add("generic"); - HashMap params = new HashMap<>(); - return create(flags, params, uri); - } - - public static GenericHttpsResource create(Set flags, HashMap params, URI uri) { - if ( ! ("https".equals(uri.getScheme()) - && flags != null && flags.size() == 1 && flags.contains("generic") - && (params == null || params.isEmpty()))) { - return null; - } - return new GenericHttpsResource(flags, params, uri); - } - - @Override - public @DrawableRes - int getDisplayIcon() { - return R.drawable.linked_https; - } - - @Override - public @StringRes - int getVerifiedText(boolean isSecret) { - return isSecret ? R.string.linked_verified_secret_https : R.string.linked_verified_https; - } - - @Override - public String getDisplayTitle(Context context) { - return context.getString(R.string.linked_title_https); - } - - @Override - public String getDisplayComment(Context context) { - return mSubUri.toString(); - } - - @Override - public boolean isViewable() { - return true; - } - - @Override - public Intent getViewIntent() { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(mSubUri.toString())); - return intent; - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java deleted file mode 100644 index 5b32c9c4e..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.linked.resources; - -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.support.annotation.DrawableRes; -import android.support.annotation.StringRes; - -import okhttp3.Request; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; -import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.linked.LinkedTokenResource; -import timber.log.Timber; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URI; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - - -public class GithubResource extends LinkedTokenResource { - - final String mHandle; - final String mGistId; - - GithubResource(Set flags, HashMap params, URI uri, - String handle, String gistId) { - super(flags, params, uri); - - mHandle = handle; - mGistId = gistId; - } - - public static String generate(Context context, byte[] fingerprint) { - String token = LinkedTokenResource.generate(fingerprint); - - return String.format(context.getResources().getString(R.string.linked_id_github_text), token); - } - - - @Override - protected String fetchResource (Context context, OperationLog log, int indent) - throws HttpStatusException, IOException, JSONException { - - log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); - indent += 1; - - Request request = new Request.Builder() - .url("https://api.github.com/gists/" + mGistId) - .addHeader("User-Agent", "OpenKeychain") - .build(); - String response = getResponseBody(request); - - JSONObject obj = new JSONObject(response); - - JSONObject owner = obj.getJSONObject("owner"); - if (!mHandle.equals(owner.getString("login"))) { - log.add(LogType.MSG_LV_ERROR_GITHUB_HANDLE, indent); - return null; - } - - JSONObject files = obj.getJSONObject("files"); - Iterator it = files.keys(); - if (it.hasNext()) { - // TODO can there be multiple candidates? - JSONObject file = files.getJSONObject(it.next()); - return file.getString("content"); - } - - log.add(LogType.MSG_LV_ERROR_GITHUB_NOT_FOUND, indent); - return null; - - } - - - @SuppressWarnings({ "deprecation", "unused" }) - public static GithubResource searchInGithubStream( - Context context, String screenName, String needle, OperationLog log) { - - // narrow the needle down to important part - Matcher matcher = magicPattern.matcher(needle); - if (!matcher.find()) { - throw new AssertionError("Needle must contain token pattern! This is a programming error, please report."); - } - needle = matcher.group(); - - try { - - JSONArray array; { - Request request = new Request.Builder() - .url("https://api.github.com/users/" + screenName + "/gists") - .addHeader("Content-Type", "application/json") - .addHeader("User-Agent", "OpenKeychain") - .build(); - String response = getResponseBody(request); - array = new JSONArray(response); - } - - for (int i = 0, j = Math.min(array.length(), 5); i < j; i++) { - JSONObject obj = array.getJSONObject(i); - - JSONObject files = obj.getJSONObject("files"); - Iterator it = files.keys(); - if (it.hasNext()) { - - JSONObject file = files.getJSONObject(it.next()); - String type = file.getString("type"); - if (!"text/plain".equals(type)) { - continue; - } - String id = obj.getString("id"); - - Request request = new Request.Builder() - .url("https://api.github.com/gists/" + id) - .addHeader("User-Agent", "OpenKeychain") - .build(); - - JSONObject gistObj = new JSONObject(getResponseBody(request)); - JSONObject gistFiles = gistObj.getJSONObject("files"); - Iterator gistIt = gistFiles.keys(); - if (!gistIt.hasNext()) { - continue; - } - // TODO can there be multiple candidates? - JSONObject gistFile = gistFiles.getJSONObject(gistIt.next()); - String content = gistFile.getString("content"); - if (!content.contains(needle)) { - continue; - } - - URI uri = URI.create("https://gist.github.com/" + screenName + "/" + id); - return create(uri); - } - } - - // update the results with the body of the response - log.add(LogType.MSG_LV_FETCH_ERROR_NOTHING, 2); - return null; - - } catch (HttpStatusException e) { - // log verbose output to logcat - Timber.e("http error (" + e.getStatus() + "): " + e.getReason()); - log.add(LogType.MSG_LV_FETCH_ERROR, 2, Integer.toString(e.getStatus())); - } catch (MalformedURLException e) { - log.add(LogType.MSG_LV_FETCH_ERROR_URL, 2); - } catch (IOException e) { - Timber.e(e, "io error"); - log.add(LogType.MSG_LV_FETCH_ERROR_IO, 2); - } catch (JSONException e) { - Timber.e(e, "json error"); - log.add(LogType.MSG_LV_FETCH_ERROR_FORMAT, 2); - } - - return null; - } - - public static GithubResource create(URI uri) { - return create(new HashSet(), new HashMap(), uri); - } - - public static GithubResource create(Set flags, HashMap params, URI uri) { - - // no params or flags - if (!flags.isEmpty() || !params.isEmpty()) { - return null; - } - - Pattern p = Pattern.compile("https://gist\\.github\\.com/([a-zA-Z0-9_-]+)/([0-9a-f]+)"); - Matcher match = p.matcher(uri.toString()); - if (!match.matches()) { - return null; - } - String handle = match.group(1); - String gistId = match.group(2); - - return new GithubResource(flags, params, uri, handle, gistId); - - } - - - @Override - public @DrawableRes - int getDisplayIcon() { - return R.drawable.linked_github; - } - - @Override - public @StringRes - int getVerifiedText(boolean isSecret) { - return isSecret ? R.string.linked_verified_secret_github : R.string.linked_verified_github; - } - - @Override - public String getDisplayTitle(Context context) { - return context.getString(R.string.linked_title_github); - } - - @Override - public String getDisplayComment(Context context) { - return mHandle; - } - - @Override - public boolean isViewable() { - return true; - } - - @Override - public Intent getViewIntent() { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(mSubUri.toString())); - return intent; - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java deleted file mode 100644 index 48e6b4163..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.linked.resources; - -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.support.annotation.DrawableRes; -import android.support.annotation.StringRes; -import android.util.Log; - -import com.textuality.keybase.lib.JWalk; - -import okhttp3.MediaType; -import okhttp3.Request; -import okhttp3.RequestBody; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; -import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.linked.LinkedTokenResource; -import timber.log.Timber; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URI; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class TwitterResource extends LinkedTokenResource { - - public static final String[] CERT_PINS = null; /*(new String[] { - // Symantec Class 3 Secure Server CA - G4 - "513fb9743870b73440418d30930699ff" - };*/ - - final String mHandle; - final String mTweetId; - - TwitterResource(Set flags, HashMap params, - URI uri, String handle, String tweetId) { - super(flags, params, uri); - - mHandle = handle; - mTweetId = tweetId; - } - - public static TwitterResource create(URI uri) { - return create(new HashSet(), new HashMap(), uri); - } - - public static TwitterResource create(Set flags, HashMap params, URI uri) { - - // no params or flags - if (!flags.isEmpty() || !params.isEmpty()) { - return null; - } - - Pattern p = Pattern.compile("https://twitter\\.com/([a-zA-Z0-9_]+)/status/([0-9]+)"); - Matcher match = p.matcher(uri.toString()); - if (!match.matches()) { - return null; - } - String handle = match.group(1); - String tweetId = match.group(2); - - return new TwitterResource(flags, params, uri, handle, tweetId); - - } - - @SuppressWarnings("deprecation") - @Override - protected String fetchResource(Context context, OperationLog log, int indent) - throws IOException, HttpStatusException, JSONException { - - String authToken; - try { - authToken = getAuthToken(context); - } catch (IOException | HttpStatusException | JSONException e) { - log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, indent); - return null; - } - - // construct a normal HTTPS request and include an Authorization - // header with the value of Bearer <> - Request request = new Request.Builder() - .url("https://api.twitter.com/1.1/statuses/show.json" - + "?id=" + mTweetId - + "&include_entities=false") - .addHeader("Authorization", "Bearer " + authToken) - .addHeader("Content-Type", "application/json") - .addHeader("User-Agent", "OpenKeychain") - .build(); - - try { - String response = getResponseBody(request, CERT_PINS); - JSONObject obj = new JSONObject(response); - JSONObject user = obj.getJSONObject("user"); - if (!mHandle.equalsIgnoreCase(user.getString("screen_name"))) { - log.add(LogType.MSG_LV_ERROR_TWITTER_HANDLE, indent); - return null; - } - - // update the results with the body of the response - return obj.getString("text"); - } catch (JSONException e) { - log.add(LogType.MSG_LV_ERROR_TWITTER_RESPONSE, indent); - return null; - } - - } - - @Override - public @DrawableRes int getDisplayIcon() { - return R.drawable.linked_twitter; - } - - @Override - public @StringRes - int getVerifiedText(boolean isSecret) { - return isSecret ? R.string.linked_verified_secret_twitter : R.string.linked_verified_twitter; - } - - @Override - public String getDisplayTitle(Context context) { - return context.getString(R.string.linked_title_twitter); - } - - @Override - public String getDisplayComment(Context context) { - return "@" + mHandle; - } - - @Override - public boolean isViewable() { - return true; - } - - @Override - public Intent getViewIntent() { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(mSubUri.toString())); - return intent; - } - - @SuppressWarnings("deprecation") - public static TwitterResource searchInTwitterStream( - Context context, String screenName, String needle, OperationLog log) { - - String authToken; - try { - authToken = getAuthToken(context); - } catch (IOException | HttpStatusException | JSONException e) { - log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, 1); - return null; - } - - Request request = new Request.Builder() - .url("https://api.twitter.com/1.1/statuses/user_timeline.json" - + "?screen_name=" + screenName - + "&count=15" - + "&include_rts=false" - + "&trim_user=true" - + "&exclude_replies=true") - .addHeader("Authorization", "Bearer " + authToken) - .addHeader("Content-Type", "application/json") - .addHeader("User-Agent", "OpenKeychain") - .build(); - - try { - String response = getResponseBody(request, CERT_PINS); - JSONArray array = new JSONArray(response); - - for (int i = 0; i < array.length(); i++) { - JSONObject obj = array.getJSONObject(i); - String tweet = obj.getString("text"); - if (tweet.contains(needle)) { - String id = obj.getString("id_str"); - URI uri = URI.create("https://twitter.com/" + screenName + "/status/" + id); - return create(uri); - } - } - - // update the results with the body of the response - log.add(LogType.MSG_LV_FETCH_ERROR_NOTHING, 1); - return null; - - } catch (HttpStatusException e) { - // log verbose output to logcat - Timber.e("http error (" + e.getStatus() + "): " + e.getReason()); - log.add(LogType.MSG_LV_FETCH_ERROR, 1, Integer.toString(e.getStatus())); - } catch (MalformedURLException e) { - log.add(LogType.MSG_LV_FETCH_ERROR_URL, 1); - } catch (IOException e) { - Timber.e(e, "io error"); - log.add(LogType.MSG_LV_FETCH_ERROR_IO, 1); - } catch (JSONException e) { - Timber.e(e, "json error"); - log.add(LogType.MSG_LV_FETCH_ERROR_FORMAT, 1); - } - - return null; - } - - private static String cachedAuthToken; - - @SuppressWarnings("deprecation") - private static String getAuthToken(Context context) - throws IOException, HttpStatusException, JSONException { - if (cachedAuthToken != null) { - return cachedAuthToken; - } - String base64Encoded = rot13("D293FQqanH0jH29KIaWJER5DomqSGRE2Ewc1LJACn3cbD1c" - + "Fq1bmqSAQAz5MI2cIHKOuo3cPoRAQI1OyqmIVFJS6LHMXq2g6MRLkIj") + "=="; - - RequestBody requestBody = RequestBody.create( - MediaType.parse("application/x-www-form-urlencoded;charset=UTF-8"), - "grant_type=client_credentials"); - - // Step 2: Obtain a bearer token - Request request = new Request.Builder() - .url("https://api.twitter.com/oauth2/token") - .addHeader("Authorization", "Basic " + base64Encoded) - .addHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8") - .addHeader("User-Agent", "OpenKeychain") - .post(requestBody) - .build(); - - JSONObject rawAuthorization = new JSONObject(getResponseBody(request, CERT_PINS)); - - // Applications should verify that the value associated with the - // token_type key of the returned object is bearer - if (!"bearer".equals(JWalk.getString(rawAuthorization, "token_type"))) { - throw new JSONException("Expected bearer token in response!"); - } - - cachedAuthToken = rawAuthorization.getString("access_token"); - return cachedAuthToken; - - } - - public static String rot13(String input) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < input.length(); i++) { - char c = input.charAt(i); - if (c >= 'a' && c <= 'm') c += 13; - else if (c >= 'A' && c <= 'M') c += 13; - else if (c >= 'n' && c <= 'z') c -= 13; - else if (c >= 'N' && c <= 'Z') c -= 13; - sb.append(c); - } - return sb.toString(); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/LinkedVerifyResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/LinkedVerifyResult.java deleted file mode 100644 index 7dc75dfcc..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/LinkedVerifyResult.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.operations.results; - -import android.os.Parcel; - -public class LinkedVerifyResult extends OperationResult { - - public LinkedVerifyResult(int result, OperationLog log) { - super(result, log); - } - - /** Construct from a parcel - trivial because we have no extra data. */ - public LinkedVerifyResult(Parcel source) { - super(source); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - super.writeToParcel(dest, flags); - } - - public static Creator CREATOR = new Creator() { - public LinkedVerifyResult createFromParcel(final Parcel source) { - return new LinkedVerifyResult(source); - } - - public LinkedVerifyResult[] newArray(final int size) { - return new LinkedVerifyResult[size]; - } - }; - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/IdentityAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/IdentityAdapter.java index b8bbd43c0..44b6ab5b5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/IdentityAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/IdentityAdapter.java @@ -20,11 +20,8 @@ package org.sufficientlysecure.keychain.ui.adapter; import java.util.List; -import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Typeface; -import android.os.Build; -import android.os.Build.VERSION_CODES; import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -34,41 +31,30 @@ import android.widget.ImageView; import android.widget.TextView; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.linked.UriAttribute; import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter.ViewHolder; import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.AutocryptPeerInfo; import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo; -import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.LinkedIdInfo; import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.UserIdInfo; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; -import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker; public class IdentityAdapter extends RecyclerView.Adapter { private static final int VIEW_TYPE_USER_ID = 0; - private static final int VIEW_TYPE_LINKED_ID = 1; - private final Context context; private final LayoutInflater layoutInflater; private final IdentityClickListener identityClickListener; private List data; - private boolean isSecret; public IdentityAdapter(Context context, IdentityClickListener identityClickListener) { super(); this.layoutInflater = LayoutInflater.from(context); - this.context = context; this.identityClickListener = identityClickListener; } - public void setData(List data, boolean isSecret) { + public void setData(List data) { this.data = data; - this.isSecret = isSecret; - notifyDataSetChanged(); } @@ -83,8 +69,6 @@ public class IdentityAdapter extends RecyclerView.Adapter { } else { ((UserIdViewHolder) holder).bind((UserIdInfo) info); } - } else if (viewType == VIEW_TYPE_LINKED_ID) { - ((LinkedIdViewHolder) holder).bind(context, (LinkedIdInfo) info, isSecret); } else { throw new IllegalStateException("unhandled identitytype!"); } @@ -96,9 +80,6 @@ public class IdentityAdapter extends RecyclerView.Adapter { if (viewType == VIEW_TYPE_USER_ID) { return new UserIdViewHolder( layoutInflater.inflate(R.layout.view_key_identity_user_id, parent, false), identityClickListener); - } else if (viewType == VIEW_TYPE_LINKED_ID) { - return new LinkedIdViewHolder(layoutInflater.inflate(R.layout.linked_id_item, parent, false), - identityClickListener); } else { throw new IllegalStateException("unhandled identitytype!"); } @@ -109,8 +90,6 @@ public class IdentityAdapter extends RecyclerView.Adapter { IdentityInfo info = data.get(position); if (info instanceof UserIdInfo || info instanceof AutocryptPeerInfo) { return VIEW_TYPE_USER_ID; - } else if (info instanceof LinkedIdInfo) { - return VIEW_TYPE_LINKED_ID; } else { throw new IllegalStateException("unhandled identitytype!"); } @@ -131,69 +110,6 @@ public class IdentityAdapter extends RecyclerView.Adapter { } } - public static class LinkedIdViewHolder extends ViewHolder { - public final ImageView vVerified; - final private ImageView vIcon; - final private TextView vTitle; - final private TextView vComment; - - public LinkedIdViewHolder(View view, final IdentityClickListener identityClickListener) { - super(view); - - vVerified = view.findViewById(R.id.linked_id_certified_icon); - vIcon = view.findViewById(R.id.linked_id_type_icon); - vTitle = view.findViewById(R.id.linked_id_title); - vComment = view.findViewById(R.id.linked_id_comment); - - view.setOnClickListener(v -> { - if (identityClickListener != null) { - identityClickListener.onClickIdentity(getAdapterPosition()); - } - }); - } - - public void bind(Context context, LinkedIdInfo info, boolean isSecret) { - bindVerified(context, info, isSecret); - - UriAttribute uriAttribute = info.getLinkedAttribute(); - bind(context, uriAttribute); - } - - public void bind(Context context, UriAttribute uriAttribute) { - vTitle.setText(uriAttribute.getDisplayTitle(context)); - - String comment = uriAttribute.getDisplayComment(context); - if (comment != null) { - vComment.setVisibility(View.VISIBLE); - vComment.setText(comment); - } else { - vComment.setVisibility(View.GONE); - } - - vIcon.setImageResource(uriAttribute.getDisplayIcon()); - } - - private void bindVerified(Context context, IdentityInfo info, boolean isSecret) { - if (!isSecret) { - if (info.isVerified()) { - KeyFormattingUtils.setStatusImage(context, vVerified, - null, State.VERIFIED, KeyFormattingUtils.DEFAULT_COLOR); - } else { - KeyFormattingUtils.setStatusImage(context, vVerified, - null, State.UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR); - } - } - } - - public void seekAttention() { - if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { - ObjectAnimator anim = SubtleAttentionSeeker.tintText(vComment, 1000); - anim.setStartDelay(200); - anim.start(); - } - } - } - private static class UserIdViewHolder extends ViewHolder { private final TextView vName; private final TextView vAddress; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/KeyFragmentViewModel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/KeyFragmentViewModel.java index 3e3c58466..1f208349b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/KeyFragmentViewModel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/KeyFragmentViewModel.java @@ -8,10 +8,10 @@ import android.arch.lifecycle.Transformations; import android.arch.lifecycle.ViewModel; import android.content.Context; +import org.sufficientlysecure.keychain.daos.KeyMetadataDao; import org.sufficientlysecure.keychain.livedata.GenericLiveData; import org.sufficientlysecure.keychain.model.KeyMetadata; import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; -import org.sufficientlysecure.keychain.daos.KeyMetadataDao; import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao; import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo; import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao; @@ -26,13 +26,12 @@ public class KeyFragmentViewModel extends ViewModel { private LiveData systemContactInfo; private LiveData keyserverStatus; - LiveData> getIdentityInfo(Context context, LiveData unifiedKeyInfoLiveData, - boolean showLinkedIds) { + LiveData> getIdentityInfo(Context context, LiveData unifiedKeyInfoLiveData) { if (identityInfo == null) { IdentityDao identityDao = IdentityDao.getInstance(context); identityInfo = Transformations.switchMap(unifiedKeyInfoLiveData, (unifiedKeyInfo) -> unifiedKeyInfo == null ? null : new GenericLiveData<>(context, - () -> identityDao.getIdentityInfos(unifiedKeyInfo.master_key_id(), showLinkedIds))); + () -> identityDao.getIdentityInfos(unifiedKeyInfo.master_key_id()))); } return identityInfo; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/LinkedIdViewFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/LinkedIdViewFragment.java deleted file mode 100644 index b30087fd7..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/LinkedIdViewFragment.java +++ /dev/null @@ -1,529 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui.keyview; - - -import java.util.Collections; -import java.util.List; - -import android.arch.lifecycle.LiveData; -import android.arch.lifecycle.ViewModel; -import android.arch.lifecycle.ViewModelProviders; -import android.content.Context; -import android.content.Intent; -import android.graphics.PorterDuff; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.Handler; -import android.os.Parcelable; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentManager.OnBackStackChangedListener; -import android.support.v4.content.ContextCompat; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextSwitcher; -import android.widget.TextView; -import android.widget.ViewAnimator; - -import org.sufficientlysecure.keychain.Constants.key; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.linked.LinkedAttribute; -import org.sufficientlysecure.keychain.linked.LinkedResource; -import org.sufficientlysecure.keychain.linked.LinkedTokenResource; -import org.sufficientlysecure.keychain.linked.UriAttribute; -import org.sufficientlysecure.keychain.daos.CertificationDao; -import org.sufficientlysecure.keychain.livedata.GenericLiveData; -import org.sufficientlysecure.keychain.model.Certification.CertDetails; -import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; -import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; -import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.daos.KeyRepository; -import org.sufficientlysecure.keychain.daos.KeyRepository.NotFoundException; -import org.sufficientlysecure.keychain.service.CertifyActionsParcel; -import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; -import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter; -import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; -import org.sufficientlysecure.keychain.ui.keyview.LinkedIdViewFragment.ViewHolder.VerifyState; -import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao; -import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.LinkedIdInfo; -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.ui.util.SubtleAttentionSeeker; -import org.sufficientlysecure.keychain.ui.widget.CertListWidget; -import org.sufficientlysecure.keychain.ui.widget.KeySpinner; -import timber.log.Timber; - - -public class LinkedIdViewFragment extends CryptoOperationFragment implements OnBackStackChangedListener { - - private static final String ARG_LID_RANK = "rank"; - private static final String ARG_IS_SECRET = "verified"; - private static final String ARG_MASTER_KEY_ID = "master_key_id"; - - private long masterKeyId; - private boolean isSecret; - - private UriAttribute linkedId; - private LinkedTokenResource linkedResource; - - private AsyncTask taskInProgress; - - private ViewHolder viewHolder; - private int lidRank; - private long certifyKeyId; - - public static LinkedIdViewFragment newInstance(long masterKeyId, int rank, boolean isSecret) { - LinkedIdViewFragment frag = new LinkedIdViewFragment(); - - Bundle args = new Bundle(); - args.putInt(ARG_LID_RANK, rank); - args.putBoolean(ARG_IS_SECRET, isSecret); - args.putLong(ARG_MASTER_KEY_ID, masterKeyId); - frag.setArguments(args); - - return frag; - } - - public LinkedIdViewFragment() { - // IMPORTANT: the id must be unique in the ViewKeyActivity CryptoOperationHelper id namespace! - // no initial progress message -> we handle progress ourselves! - super(5, null); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - Bundle args = getArguments(); - lidRank = args.getInt(ARG_LID_RANK); - - isSecret = args.getBoolean(ARG_IS_SECRET); - masterKeyId = args.getLong(ARG_MASTER_KEY_ID); - } - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - LinkedIdViewModel viewModel = ViewModelProviders.of(this).get(LinkedIdViewModel.class); - viewModel.getLinkedIdInfo(requireContext(), masterKeyId, lidRank).observe(this, this::onLinkedIdInfoLoaded); - viewModel.getCertifyingKeys(requireContext()).observe(this, viewHolder.vKeySpinner::setData); - } - - private void onLinkedIdInfoLoaded(LinkedIdInfo linkedIdInfo) { - if (linkedIdInfo == null) { - Timber.e("error loading identity"); - Notify.create(getActivity(), "Error loading linked identity!", - Notify.LENGTH_LONG, Style.ERROR).show(); - finishFragment(); - return; - } - - loadIdentity(linkedIdInfo.getLinkedAttribute(), linkedIdInfo.isVerified()); - } - - public void finishFragment() { - new Handler().post(() -> { - FragmentManager manager = getFragmentManager(); - manager.removeOnBackStackChangedListener(LinkedIdViewFragment.this); - manager.popBackStack("linked_id", FragmentManager.POP_BACK_STACK_INCLUSIVE); - }); - } - - private void loadIdentity(LinkedAttribute linkedId, boolean isVerified) { - this.linkedId = linkedId; - - LinkedResource res = ((LinkedAttribute) this.linkedId).mResource; - linkedResource = (LinkedTokenResource) res; - - if (!isSecret) { - if (isVerified) { - KeyFormattingUtils.setStatusImage(getContext(), viewHolder.mLinkedIdHolder.vVerified, - null, State.VERIFIED, KeyFormattingUtils.DEFAULT_COLOR); - } else { - KeyFormattingUtils.setStatusImage(getContext(), viewHolder.mLinkedIdHolder.vVerified, - null, State.UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR); - } - } else { - viewHolder.mLinkedIdHolder.vVerified.setImageResource(R.drawable.octo_link_24dp); - } - - viewHolder.mLinkedIdHolder.bind(getContext(), this.linkedId); - - setShowVerifying(false); - - if (linkedResource.isViewable()) { - viewHolder.vButtonView.setVisibility(View.VISIBLE); - viewHolder.vButtonView.setOnClickListener(v -> { - Intent intent = linkedResource.getViewIntent(); - if (intent == null) { - return; - } - startActivity(intent); - }); - } else { - viewHolder.vButtonView.setVisibility(View.GONE); - } - - } - - static class ViewHolder { - private final View vButtonView; - private final ViewAnimator vVerifyingContainer; - private final ViewAnimator vItemCertified; - private final View vKeySpinnerContainer; - IdentityAdapter.LinkedIdViewHolder mLinkedIdHolder; - - private ViewAnimator vButtonSwitcher; - private CertListWidget vLinkedCerts; - private KeySpinner vKeySpinner; - private final View vButtonVerify; - private final View vButtonRetry; - private final View vButtonConfirm; - - private final ViewAnimator vProgress; - private final TextSwitcher vText; - - ViewHolder(View root) { - vLinkedCerts = root.findViewById(R.id.linked_id_certs); - vKeySpinner = root.findViewById(R.id.cert_key_spinner); - vKeySpinnerContainer = root.findViewById(R.id.cert_key_spincontainer); - vButtonSwitcher = root.findViewById(R.id.button_animator); - - mLinkedIdHolder = new IdentityAdapter.LinkedIdViewHolder(root, null); - - vButtonVerify = root.findViewById(R.id.button_verify); - vButtonRetry = root.findViewById(R.id.button_retry); - vButtonConfirm = root.findViewById(R.id.button_confirm); - vButtonView = root.findViewById(R.id.button_view); - - vVerifyingContainer = root.findViewById(R.id.linked_verify_container); - vItemCertified = root.findViewById(R.id.linked_id_certified); - - vProgress = root.findViewById(R.id.linked_cert_progress); - vText = root.findViewById(R.id.linked_cert_text); - - vKeySpinner.setShowNone(R.string.choice_select_cert); - } - - enum VerifyState { - VERIFYING, VERIFY_OK, VERIFY_ERROR, CERTIFYING - } - - void setVerifyingState(Context context, VerifyState state, boolean isSecret) { - switch (state) { - case VERIFYING: - vProgress.setDisplayedChild(0); - vText.setText(context.getString(R.string.linked_text_verifying)); - vKeySpinnerContainer.setVisibility(View.GONE); - break; - - case VERIFY_OK: - vProgress.setDisplayedChild(1); - if (!isSecret) { - showButton(2); - if (!vKeySpinner.isSingleEntry()) { - vKeySpinnerContainer.setVisibility(View.VISIBLE); - } - } else { - showButton(1); - vKeySpinnerContainer.setVisibility(View.GONE); - } - break; - - case VERIFY_ERROR: - showButton(1); - vProgress.setDisplayedChild(2); - vText.setText(context.getString(R.string.linked_text_error)); - vKeySpinnerContainer.setVisibility(View.GONE); - break; - - case CERTIFYING: - vProgress.setDisplayedChild(0); - vText.setText(context.getString(R.string.linked_text_confirming)); - vKeySpinnerContainer.setVisibility(View.GONE); - vButtonConfirm.setEnabled(false); - break; - } - } - - void showVerifyingContainer(Context context, boolean show, boolean isSecret) { - if (vVerifyingContainer.getDisplayedChild() == (show ? 1 : 0)) { - return; - } - - vVerifyingContainer.setInAnimation(context, show ? R.anim.fade_in_up : R.anim.fade_in_down); - vVerifyingContainer.setOutAnimation(context, show ? R.anim.fade_out_up : R.anim.fade_out_down); - vVerifyingContainer.setDisplayedChild(show ? 1 : 0); - - vItemCertified.setInAnimation(context, show ? R.anim.fade_in_up : R.anim.fade_in_down); - vItemCertified.setOutAnimation(context, show ? R.anim.fade_out_up : R.anim.fade_out_down); - vItemCertified.setDisplayedChild(show || isSecret ? 1 : 0); - } - - void showButton(int which) { - if (vButtonSwitcher.getDisplayedChild() == which) { - return; - } - vButtonSwitcher.setDisplayedChild(which); - } - - } - - private boolean mVerificationState = false; - /** Switches between the 'verifying' ui bit and certificate status. This method - * must behave correctly in all states, showing or hiding the appropriate views - * and cancelling pending operations where necessary. - * - * This method also handles back button functionality in combination with - * onBackStateChanged. - */ - void setShowVerifying(boolean show) { - if (!show) { - if (taskInProgress != null) { - taskInProgress.cancel(false); - taskInProgress = null; - } - getFragmentManager().removeOnBackStackChangedListener(this); - new Handler().post(() -> getFragmentManager().popBackStack("verification", - FragmentManager.POP_BACK_STACK_INCLUSIVE)); - - if (!mVerificationState) { - return; - } - mVerificationState = false; - - viewHolder.showButton(0); - viewHolder.vKeySpinnerContainer.setVisibility(View.GONE); - viewHolder.showVerifyingContainer(getContext(), false, isSecret); - return; - } - - if (mVerificationState) { - return; - } - mVerificationState = true; - - FragmentManager manager = getFragmentManager(); - manager.beginTransaction().addToBackStack("verification").commit(); - manager.executePendingTransactions(); - manager.addOnBackStackChangedListener(this); - viewHolder.showVerifyingContainer(getContext(), true, isSecret); - - } - - @Override - public void onBackStackChanged() { - setShowVerifying(false); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { - View root = inflater.inflate(R.layout.linked_id_view_fragment, superContainer, false); - Context context = getContext(); - if (context == null) { - throw new NullPointerException(); - } - - viewHolder = new ViewHolder(root); - root.setTag(viewHolder); - - ((ImageView) root.findViewById(R.id.status_icon_verified)) - .setColorFilter(ContextCompat.getColor(context, R.color.android_green_light), - PorterDuff.Mode.SRC_IN); - ((ImageView) root.findViewById(R.id.status_icon_invalid)) - .setColorFilter(ContextCompat.getColor(context, R.color.android_red_light), - PorterDuff.Mode.SRC_IN); - - viewHolder.vButtonVerify.setOnClickListener(v -> verifyResource()); - viewHolder.vButtonRetry.setOnClickListener(v -> verifyResource()); - viewHolder.vButtonConfirm.setOnClickListener(v -> initiateCertifying()); - - LinkedIdViewModel viewModel = ViewModelProviders.of(this).get(LinkedIdViewModel.class); - viewModel.getCertDetails(context, masterKeyId, lidRank).observe(this, this::onLoadCertDetails); - - return root; - } - - private void onLoadCertDetails(CertDetails certDetails) { - viewHolder.vLinkedCerts.setData(certDetails, isSecret); - } - - void verifyResource() { - - // only one at a time (no sync needed, taskInProgress is only touched in ui thread) - if (taskInProgress != null) { - return; - } - - setShowVerifying(true); - - viewHolder.vKeySpinnerContainer.setVisibility(View.GONE); - viewHolder.setVerifyingState(getContext(), VerifyState.VERIFYING, isSecret); - - taskInProgress = new AsyncTask() { - @Override - protected LinkedVerifyResult doInBackground(Void... params) { - FragmentActivity activity = getActivity(); - - byte[] fingerprint; - try { - fingerprint = KeyRepository.create(activity).getFingerprintByKeyId(masterKeyId); - } catch (NotFoundException e) { - throw new IllegalStateException("Key to verify linked id for must exist in db!"); - } - - long timer = System.currentTimeMillis(); - LinkedVerifyResult result = linkedResource.verify(activity, fingerprint); - - // ux flow: this operation should take at last a second - timer = System.currentTimeMillis() -timer; - if (timer < 1000) try { - Thread.sleep(1000 -timer); - } catch (InterruptedException e) { - // never mind - } - - return result; - } - - @Override - protected void onPostExecute(LinkedVerifyResult result) { - if (isCancelled()) { - return; - } - if (result.success()) { - viewHolder.vText.setText(getString(linkedResource.getVerifiedText(isSecret))); - // hack to preserve bold text - ((TextView) viewHolder.vText.getCurrentView()).setText( - linkedResource.getVerifiedText(isSecret)); - viewHolder.setVerifyingState(getContext(), VerifyState.VERIFY_OK, isSecret); - viewHolder.mLinkedIdHolder.seekAttention(); - } else { - viewHolder.setVerifyingState(getContext(), VerifyState.VERIFY_ERROR, isSecret); - result.createNotify(getActivity()).show(); - } - taskInProgress = null; - } - }.execute(); - - } - - private void initiateCertifying() { - - if (isSecret) { - return; - } - - // get the user's passphrase for this key (if required) - certifyKeyId = viewHolder.vKeySpinner.getSelectedKeyId(); - if (certifyKeyId == key.none || certifyKeyId == key.symmetric) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - SubtleAttentionSeeker.tintBackground(viewHolder.vKeySpinnerContainer, 600).start(); - } else { - Notify.create(getActivity(), R.string.select_key_to_certify, Style.ERROR).show(); - } - return; - } - - viewHolder.setVerifyingState(getContext(), VerifyState.CERTIFYING, false); - cryptoOperation(); - - } - - @Override - public void onCryptoOperationCancelled() { - super.onCryptoOperationCancelled(); - - // go back to 'verified ok' - setShowVerifying(false); - - } - - @Nullable - @Override - public Parcelable createOperationInput() { - CertifyAction action = CertifyAction.createForUserAttributes(masterKeyId, - Collections.singletonList(linkedId.toUserAttribute())); - - // fill values for this action - CertifyActionsParcel.Builder builder = CertifyActionsParcel.builder(certifyKeyId); - builder.addActions(Collections.singletonList(action)); - - return builder.build(); - } - - @Override - public void onCryptoOperationSuccess(OperationResult result) { - result.createNotify(getActivity()).show(); - // no need to do anything else, we will get a loader refresh! - } - - @Override - public void onCryptoOperationError(OperationResult result) { - result.createNotify(getActivity()).show(); - } - - @Override - public boolean onCryptoSetProgress(String msg, int progress, int max) { - return true; - } - - public static class LinkedIdViewModel extends ViewModel { - LiveData> certifyingKeysLiveData; - LiveData certDetailsLiveData; - LiveData linkedIfInfoLiveData; - - LiveData> getCertifyingKeys(Context context) { - if (certifyingKeysLiveData == null) { - certifyingKeysLiveData = new GenericLiveData<>(context, () -> { - KeyRepository keyRepository = KeyRepository.create(context); - return keyRepository.getAllUnifiedKeyInfoWithSecret(); - }); - } - return certifyingKeysLiveData; - } - - LiveData getCertDetails(Context context, long masterKeyId, int lidRank) { - if (certDetailsLiveData == null) { - CertificationDao certificationDao = CertificationDao.getInstance(context); - certDetailsLiveData = new GenericLiveData<>(context, masterKeyId, - () -> certificationDao.getVerifyingCertDetails(masterKeyId, lidRank)); - } - return certDetailsLiveData; - } - - public LiveData getLinkedIdInfo(Context context, long masterKeyId, int lidRank) { - if (linkedIfInfoLiveData == null) { - IdentityDao identityDao = IdentityDao.getInstance(context); - linkedIfInfoLiveData = new GenericLiveData<>(context, masterKeyId, - () -> identityDao.getLinkedIdInfo(masterKeyId, lidRank)); - } - return linkedIfInfoLiveData; - } - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java index 004a7cb0c..4636a827c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/ViewKeyFragment.java @@ -41,17 +41,16 @@ import android.view.ViewGroup; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; +import org.sufficientlysecure.keychain.daos.AutocryptPeerDao; import org.sufficientlysecure.keychain.model.KeyMetadata; import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; -import org.sufficientlysecure.keychain.daos.AutocryptPeerDao; import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter; import org.sufficientlysecure.keychain.ui.adapter.IdentityAdapter.IdentityClickListener; import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.AutocryptPeerInfo; import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.IdentityInfo; -import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.LinkedIdInfo; import org.sufficientlysecure.keychain.ui.keyview.loader.IdentityDao.UserIdInfo; import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeyHealthStatus; import org.sufficientlysecure.keychain.ui.keyview.loader.SubkeyStatusDao.KeySubkeyStatus; @@ -62,8 +61,6 @@ import org.sufficientlysecure.keychain.ui.keyview.view.KeyHealthView; import org.sufficientlysecure.keychain.ui.keyview.view.KeyStatusList.KeyDisplayStatus; import org.sufficientlysecure.keychain.ui.keyview.view.KeyserverStatusView; import org.sufficientlysecure.keychain.ui.keyview.view.SystemContactCardView; -import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard; -import org.sufficientlysecure.keychain.util.Preferences; import timber.log.Timber; @@ -128,8 +125,7 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener KeyFragmentViewModel model = ViewModelProviders.of(this).get(KeyFragmentViewModel.class); - boolean showLinkedIds = Preferences.getPreferences(context).getExperimentalEnableLinkedIdentities(); - model.getIdentityInfo(context, unifiedKeyInfoLiveData, showLinkedIds).observe(this, this::onLoadIdentityInfo); + model.getIdentityInfo(context, unifiedKeyInfoLiveData).observe(this, this::onLoadIdentityInfo); model.getKeyserverStatus(context, unifiedKeyInfoLiveData).observe(this, this::onLoadKeyMetadata); model.getSystemContactInfo(context, unifiedKeyInfoLiveData).observe(this, this::onLoadSystemContact); model.getSubkeyStatus(context, unifiedKeyInfoLiveData).observe(this, this::onLoadSubkeyStatus); @@ -238,21 +234,12 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener return; } - Context context = requireContext(); - this.unifiedKeyInfo = unifiedKeyInfo; - - boolean showLinkedIds = Preferences.getPreferences(context).getExperimentalEnableLinkedIdentities(); - boolean isSecret = unifiedKeyInfo.has_any_secret(); - identitiesCardView.setAddLinkedIdButtonVisible(showLinkedIds && isSecret); - identitiesCardView.setIdentitiesCardListener((v) -> addLinkedIdentity()); } private void showIdentityInfo(final int position) { IdentityInfo info = identitiesAdapter.getInfo(position); - if (info instanceof LinkedIdInfo) { - showLinkedId((LinkedIdInfo) info); - } else if (info instanceof UserIdInfo) { + if (info instanceof UserIdInfo) { showUserIdInfo((UserIdInfo) info); } else if (info instanceof AutocryptPeerInfo) { Intent autocryptPeerIntent = ((AutocryptPeerInfo) info).getAutocryptPeerIntent(); @@ -266,12 +253,6 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener showContextMenu(position, anchor); } - private void showLinkedId(final LinkedIdInfo info) { - LinkedIdViewFragment frag = LinkedIdViewFragment.newInstance(info.getMasterKeyId(), info.getRank(), unifiedKeyInfo.has_any_secret()); - - switchToFragment(frag, "linked_id"); - } - private void showUserIdInfo(UserIdInfo info) { if (!unifiedKeyInfo.has_any_secret()) { UserIdInfoDialogFragment dialogFragment = UserIdInfoDialogFragment.newInstance(false, info.isVerified()); @@ -279,12 +260,6 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener } } - private void addLinkedIdentity() { - Intent intent = new Intent(requireContext(), LinkedIdWizard.class); - intent.putExtra(LinkedIdWizard.EXTRA_MASTER_KEY_ID, unifiedKeyInfo.master_key_id()); - startActivity(intent); - } - public void onClickForgetIdentity(int position) { AutocryptPeerInfo info = (AutocryptPeerInfo) identitiesAdapter.getInfo(position); if (info == null) { @@ -296,7 +271,7 @@ public class ViewKeyFragment extends Fragment implements OnMenuItemClickListener } private void onLoadIdentityInfo(List identityInfos) { - identitiesAdapter.setData(identityInfos, unifiedKeyInfo.has_any_secret()); + identitiesAdapter.setData(identityInfos); } private void onLoadSystemContact(SystemContactInfo systemContactInfo) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityDao.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityDao.java index 9bc8c7398..288d6d18f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityDao.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/loader/IdentityDao.java @@ -18,7 +18,6 @@ package org.sufficientlysecure.keychain.ui.keyview.loader; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -35,17 +34,12 @@ import android.support.annotation.Nullable; import com.google.auto.value.AutoValue; import com.squareup.sqldelight.SqlDelightQuery; import org.openintents.openpgp.util.OpenPgpApi; -import org.sufficientlysecure.keychain.linked.LinkedAttribute; -import org.sufficientlysecure.keychain.linked.UriAttribute; +import org.sufficientlysecure.keychain.KeychainDatabase; +import org.sufficientlysecure.keychain.daos.AutocryptPeerDao; import org.sufficientlysecure.keychain.model.AutocryptPeer; import org.sufficientlysecure.keychain.model.UserPacket; -import org.sufficientlysecure.keychain.model.UserPacket.UserAttribute; import org.sufficientlysecure.keychain.model.UserPacket.UserId; -import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.daos.AutocryptPeerDao; -import org.sufficientlysecure.keychain.KeychainDatabase; import org.sufficientlysecure.keychain.ui.util.PackageIconGetter; -import timber.log.Timber; public class IdentityDao { @@ -71,12 +65,9 @@ public class IdentityDao { this.autocryptPeerDao = autocryptPeerDao; } - public List getIdentityInfos(long masterKeyId, boolean showLinkedIds) { + public List getIdentityInfos(long masterKeyId) { ArrayList identities = new ArrayList<>(); - if (showLinkedIds) { - loadLinkedIds(identities, masterKeyId); - } loadUserIds(identities, masterKeyId); correlateOrAddAutocryptPeers(identities, masterKeyId); @@ -132,46 +123,6 @@ public class IdentityDao { return null; } - private void loadLinkedIds(ArrayList identities, long masterKeyId) { - SqlDelightQuery query = UserPacket.FACTORY.selectUserAttributesByTypeAndMasterKeyId( - (long) WrappedUserAttribute.UAT_URI_ATTRIBUTE, masterKeyId); - try (Cursor cursor = db.query(query)) { - while (cursor.moveToNext()) { - UserAttribute userAttribute = UserPacket.USER_ATTRIBUTE_MAPPER.map(cursor); - - LinkedIdInfo linkedIdInfo = parseLinkedIdInfo(userAttribute); - identities.add(linkedIdInfo); - } - } - } - - public LinkedIdInfo getLinkedIdInfo(long masterKeyId, int rank) { - SqlDelightQuery query = UserPacket.FACTORY.selectSpecificUserAttribute( - (long) WrappedUserAttribute.UAT_URI_ATTRIBUTE, masterKeyId, rank); - try (Cursor cursor = db.query(query)) { - if (cursor.moveToFirst()) { - UserAttribute userAttribute = UserPacket.USER_ATTRIBUTE_MAPPER.map(cursor); - - return parseLinkedIdInfo(userAttribute); - } - } - return null; - } - - @Nullable - private LinkedIdInfo parseLinkedIdInfo(UserAttribute userAttribute) { - try { - UriAttribute uriAttribute = LinkedAttribute.fromAttributeData(userAttribute.attribute_data()); - if (uriAttribute instanceof LinkedAttribute) { - return LinkedIdInfo.create(userAttribute.master_key_id(), userAttribute.rank(), - userAttribute.isVerified(), userAttribute.is_primary(), (LinkedAttribute) uriAttribute); - } - } catch (IOException e) { - Timber.e(e, "Failed parsing uri attribute"); - } - return null; - } - private void loadUserIds(ArrayList identities, long... masterKeyId) { SqlDelightQuery query = UserPacket.FACTORY.selectUserIdsByMasterKeyId(masterKeyId); try (Cursor cursor = db.query(query)) { @@ -214,20 +165,6 @@ public class IdentityDao { } } - @AutoValue - public abstract static class LinkedIdInfo implements IdentityInfo { - public abstract long getMasterKeyId(); - public abstract int getRank(); - public abstract boolean isVerified(); - public abstract boolean isPrimary(); - - public abstract LinkedAttribute getLinkedAttribute(); - - static LinkedIdInfo create(long masterKeyId, int rank, boolean isVerified, boolean isPrimary, LinkedAttribute linkedAttribute) { - return new AutoValue_IdentityDao_LinkedIdInfo(masterKeyId, rank, isVerified, isPrimary, linkedAttribute); - } - } - @AutoValue public abstract static class AutocryptPeerInfo implements IdentityInfo { public abstract long getMasterKeyId(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/IdentitiesCardView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/IdentitiesCardView.java index 9a9e2b5b0..aaedd2daa 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/IdentitiesCardView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/view/IdentitiesCardView.java @@ -36,8 +36,6 @@ import org.sufficientlysecure.keychain.ui.util.recyclerview.DividerItemDecoratio public class IdentitiesCardView extends CardView { private final RecyclerView vIdentities; - private final Button linkedIdsAddButton; - public IdentitiesCardView(Context context, AttributeSet attrs) { super(context, attrs); @@ -46,8 +44,6 @@ public class IdentitiesCardView extends CardView { vIdentities = view.findViewById(R.id.view_key_user_ids); vIdentities.setLayoutManager(new LinearLayoutManager(context)); vIdentities.addItemDecoration(new DividerItemDecoration(context, DividerItemDecoration.VERTICAL_LIST, false)); - - linkedIdsAddButton = view.findViewById(R.id.view_key_card_linked_ids_add); } public void setIdentitiesAdapter(IdentityAdapter identityAdapter) { @@ -59,12 +55,4 @@ public class IdentitiesCardView extends CardView { }); vIdentities.setAdapter(identityAdapter); } - - public void setIdentitiesCardListener(OnClickListener identitiesCardListener) { - linkedIdsAddButton.setOnClickListener(identitiesCardListener); - } - - public void setAddLinkedIdButtonVisible(boolean show) { - linkedIdsAddButton.setVisibility(show ? View.VISIBLE : View.GONE); - } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java deleted file mode 100644 index 55552ae03..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui.linked; - - -import android.arch.lifecycle.ViewModelProviders; -import android.graphics.PorterDuff; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Parcelable; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.ViewAnimator; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.linked.LinkedAttribute; -import org.sufficientlysecure.keychain.linked.LinkedTokenResource; -import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; -import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; -import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.service.SaveKeyringParcel; -import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; -import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; -import org.sufficientlysecure.keychain.ui.keyview.UnifiedKeyInfoViewModel; -import org.sufficientlysecure.keychain.ui.util.Notify; - -public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragment { - private ImageView mVerifyImage; - private TextView mVerifyStatus; - private ViewAnimator mVerifyAnimator; - - private long masterKeyId; - byte[] fingerprint; - - // This is a resource, set AFTER it has been verified - LinkedTokenResource mVerifiedResource = null; - private ViewAnimator mVerifyButtonAnimator; - - protected abstract View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - UnifiedKeyInfoViewModel viewModel = ViewModelProviders.of(requireActivity()).get(UnifiedKeyInfoViewModel.class); - viewModel.getUnifiedKeyInfoLiveData(requireContext()).observe(this, this::onLoadUnifiedKeyInfo); - } - - private void onLoadUnifiedKeyInfo(UnifiedKeyInfo unifiedKeyInfo) { - this.masterKeyId = unifiedKeyInfo.master_key_id(); - this.fingerprint = unifiedKeyInfo.fingerprint(); - } - - @NonNull - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = newView(inflater, container, savedInstanceState); - - View nextButton = view.findViewById(R.id.next_button); - if (nextButton != null) { - nextButton.setOnClickListener(v -> cryptoOperation()); - } - - view.findViewById(R.id.back_button).setOnClickListener( - v -> ((LinkedIdWizard) requireActivity()).loadFragment(null, LinkedIdWizard.FRAG_ACTION_TO_LEFT)); - - mVerifyAnimator = view.findViewById(R.id.verify_progress); - mVerifyImage = view.findViewById(R.id.verify_image); - mVerifyStatus = view.findViewById(R.id.verify_status); - mVerifyButtonAnimator = view.findViewById(R.id.verify_buttons); - - view.findViewById(R.id.button_verify).setOnClickListener(v -> proofVerify()); - - view.findViewById(R.id.button_retry).setOnClickListener(v -> proofVerify()); - - setVerifyProgress(false, null); - mVerifyStatus.setText(R.string.linked_verify_pending); - - return view; - } - - abstract LinkedTokenResource getResource(OperationLog log); - - private void setVerifyProgress(boolean on, Boolean success) { - if (success == null) { - mVerifyStatus.setText(R.string.linked_verifying); - displayButton(on ? 2 : 0); - } else if (success) { - mVerifyStatus.setText(R.string.linked_verify_success); - mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout_24dp); - mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark), - PorterDuff.Mode.SRC_IN); - displayButton(2); - } else { - mVerifyStatus.setText(R.string.linked_verify_error); - mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout_24dp); - mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), - PorterDuff.Mode.SRC_IN); - displayButton(1); - } - mVerifyAnimator.setDisplayedChild(on ? 1 : 0); - } - - public void displayButton(int button) { - if (mVerifyButtonAnimator.getDisplayedChild() == button) { - return; - } - mVerifyButtonAnimator.setDisplayedChild(button); - } - - protected void proofVerify() { - setVerifyProgress(true, null); - - new AsyncTask() { - - @Override - protected LinkedVerifyResult doInBackground(Void... params) { - long timer = System.currentTimeMillis(); - - OperationLog log = new OperationLog(); - LinkedTokenResource resource = getResource(log); - if (resource == null) { - return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); - } - - LinkedVerifyResult result = resource.verify(getActivity(), fingerprint); - - // ux flow: this operation should take at last a second - timer = System.currentTimeMillis() -timer; - if (timer < 1000) try { - Thread.sleep(1000 -timer); - } catch (InterruptedException e) { - // never mind - } - - if (result.success()) { - mVerifiedResource = resource; - } - return result; - } - - @Override - protected void onPostExecute(LinkedVerifyResult result) { - super.onPostExecute(result); - if (result.success()) { - setVerifyProgress(false, true); - } else { - setVerifyProgress(false, false); - // on error, show error message - result.createNotify(getActivity()).show(LinkedIdCreateFinalFragment.this); - } - } - }.execute(); - - } - - @Override - protected void cryptoOperation() { - if (mVerifiedResource == null) { - Notify.create(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR) - .show(LinkedIdCreateFinalFragment.this); - return; - } - - super.cryptoOperation(); - } - - @Override - protected void cryptoOperation(CryptoInputParcel cryptoInput) { - if (mVerifiedResource == null) { - Notify.create(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR) - .show(LinkedIdCreateFinalFragment.this); - return; - } - - super.cryptoOperation(cryptoInput); - } - - @Nullable - @Override - public Parcelable createOperationInput() { - SaveKeyringParcel.Builder builder= - SaveKeyringParcel.buildChangeKeyringParcel(masterKeyId, fingerprint); - WrappedUserAttribute ua = LinkedAttribute.fromResource(mVerifiedResource).toUserAttribute(); - builder.addUserAttribute(ua); - return builder.build(); - } - - @Override - public void onCryptoOperationSuccess(OperationResult result) { - requireActivity().finish(); - } - - @Override - public void onCryptoOperationError(OperationResult result) { - result.createNotify(getActivity()).show(this); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java deleted file mode 100644 index 81594bcb4..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ /dev/null @@ -1,671 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui.linked; - - -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.net.SocketTimeoutException; -import java.net.URI; -import java.net.URL; -import java.util.Random; - -import android.app.Activity; -import android.app.Dialog; -import android.arch.lifecycle.ViewModelProviders; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; -import android.os.Bundle; -import android.os.Handler; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.ActivityOptionsCompat; -import android.util.Base64; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.webkit.CookieManager; -import android.webkit.WebView; -import android.webkit.WebViewClient; -import android.widget.Button; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.ViewAnimator; - -import javax.net.ssl.HttpsURLConnection; -import org.bouncycastle.util.encoders.Hex; -import org.json.JSONException; -import org.json.JSONObject; -import org.sufficientlysecure.keychain.BuildConfig; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.linked.LinkedAttribute; -import org.sufficientlysecure.keychain.linked.resources.GithubResource; -import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; -import org.sufficientlysecure.keychain.operations.results.EditKeyResult; -import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.service.SaveKeyringParcel; -import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; -import org.sufficientlysecure.keychain.ui.keyview.UnifiedKeyInfoViewModel; -import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity; -import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.ui.util.Notify.Style; -import org.sufficientlysecure.keychain.ui.widget.StatusIndicator; -import org.sufficientlysecure.keychain.ui.widget.StatusIndicator.Status; -import timber.log.Timber; - - -public class LinkedIdCreateGithubFragment extends CryptoOperationFragment { - - public static final String ARG_GITHUB_COOKIE = "github_cookie"; - private Button mRetryButton; - - enum State { - IDLE, AUTH_PROCESS, AUTH_ERROR, POST_PROCESS, POST_ERROR, LID_PROCESS, LID_ERROR, DONE - } - - ViewAnimator mButtonContainer; - - StatusIndicator mStatus1, mStatus2, mStatus3; - - byte[] mFingerprint; - long mMasterKeyId; - private SaveKeyringParcel.Builder mSkpBuilder; - private TextView mLinkedIdTitle, mLinkedIdComment; - private boolean mFinishOnStop; - - public static LinkedIdCreateGithubFragment newInstance() { - return new LinkedIdCreateGithubFragment(); - } - - public LinkedIdCreateGithubFragment() { - super(null); - } - - @Override @NonNull - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.linked_create_github_fragment, container, false); - - mButtonContainer = view.findViewById(R.id.button_container); - - mStatus1 = view.findViewById(R.id.linked_status_step1); - mStatus2 = view.findViewById(R.id.linked_status_step2); - mStatus3 = view.findViewById(R.id.linked_status_step3); - - mRetryButton = view.findViewById(R.id.button_retry); - - ((ImageView) view.findViewById(R.id.linked_id_type_icon)).setImageResource(R.drawable.linked_github); - ((ImageView) view.findViewById(R.id.linked_id_certified_icon)).setImageResource(R.drawable.octo_link_24dp); - mLinkedIdTitle = view.findViewById(R.id.linked_id_title); - mLinkedIdComment = view.findViewById(R.id.linked_id_comment); - - view.findViewById(R.id.back_button).setOnClickListener(v -> { - LinkedIdWizard activity = (LinkedIdWizard) requireActivity(); - activity.loadFragment(null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); - }); - - view.findViewById(R.id.button_send).setOnClickListener(v -> { - step1GetOAuthCode(); - // for animation testing - // onCryptoOperationSuccess(null); - }); - - return view; - } - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - UnifiedKeyInfoViewModel viewModel = ViewModelProviders.of(requireActivity()).get(UnifiedKeyInfoViewModel.class); - viewModel.getUnifiedKeyInfoLiveData(requireContext()).observe(this, this::onLoadUnifiedKeyInfo); - } - - private void onLoadUnifiedKeyInfo(UnifiedKeyInfo unifiedKeyInfo) { - this.mMasterKeyId = unifiedKeyInfo.master_key_id(); - this.mFingerprint = unifiedKeyInfo.fingerprint(); - } - - private void step1GetOAuthCode() { - setState(State.AUTH_PROCESS); - - mButtonContainer.setDisplayedChild(1); - - new Handler().postDelayed( - () -> oAuthRequest("github.com/login/oauth/authorize", BuildConfig.GITHUB_CLIENT_ID, "gist"), 300); - - } - - private void showRetryForOAuth() { - mRetryButton.setOnClickListener(v -> { - v.setOnClickListener(null); - step1GetOAuthCode(); - }); - mButtonContainer.setDisplayedChild(3); - - } - - private void step1GetOAuthToken() { - - if (mOAuthCode == null) { - setState(State.AUTH_ERROR); - showRetryForOAuth(); - return; - } - - Activity activity = getActivity(); - if (activity == null) { - return; - } - - final String gistText = GithubResource.generate(activity, mFingerprint); - - new AsyncTask() { - - Exception mException; - - @Override - protected JSONObject doInBackground(Void... dummy) { - try { - - JSONObject params = new JSONObject(); - params.put("client_id", BuildConfig.GITHUB_CLIENT_ID); - params.put("client_secret", BuildConfig.GITHUB_CLIENT_SECRET); - params.put("code", mOAuthCode); - params.put("state", mOAuthState); - - return jsonHttpRequest("https://github.com/login/oauth/access_token", params, null); - - } catch (IOException | HttpResultException e) { - mException = e; - } catch (JSONException e) { - throw new AssertionError("json error, this is a bug!"); - } - return null; - } - - @Override - protected void onPostExecute(JSONObject result) { - super.onPostExecute(result); - - Activity activity = getActivity(); - if (activity == null) { - // we couldn't show an error anyways - return; - } - - Timber.d("response: " + result); - - if (result == null || result.optString("access_token", null) == null) { - setState(State.AUTH_ERROR); - showRetryForOAuth(); - - if (result != null) { - Notify.create(activity, R.string.linked_error_auth_failed, Style.ERROR).show(); - return; - } - - if (mException instanceof SocketTimeoutException) { - Notify.create(activity, R.string.linked_error_timeout, Style.ERROR).show(); - } else if (mException instanceof HttpResultException) { - Notify.create(activity, activity.getString(R.string.linked_error_http, - ((HttpResultException) mException).mResponse), - Style.ERROR).show(); - } else if (mException instanceof IOException) { - Notify.create(activity, R.string.linked_error_network, Style.ERROR).show(); - } - - return; - } - - step2PostGist(result.optString("access_token"), gistText); - - } - }.execute(); - - } - - private void step2PostGist(final String accessToken, final String gistText) { - - setState(State.POST_PROCESS); - - new AsyncTask() { - - Exception mException; - - @Override - protected JSONObject doInBackground(Void... dummy) { - try { - - long timer = System.currentTimeMillis(); - - JSONObject file = new JSONObject(); - file.put("content", gistText); - - JSONObject files = new JSONObject(); - files.put("openpgp.txt", file); - - JSONObject params = new JSONObject(); - params.put("public", true); - params.put("description", getString(R.string.linked_gist_description)); - params.put("files", files); - - JSONObject result = jsonHttpRequest("https://api.github.com/gists", params, accessToken); - - // ux flow: this operation should take at last a second - timer = System.currentTimeMillis() -timer; - if (timer < 1000) try { - Thread.sleep(1000 -timer); - } catch (InterruptedException e) { - // never mind - } - - return result; - - } catch (IOException | HttpResultException e) { - mException = e; - } catch (JSONException e) { - throw new AssertionError("json error, this is a bug!"); - } - return null; - } - - @Override - protected void onPostExecute(JSONObject result) { - super.onPostExecute(result); - - Timber.d("response: " + result); - - Activity activity = getActivity(); - if (activity == null) { - // we couldn't show an error anyways - return; - } - - if (result == null) { - setState(State.POST_ERROR); - showRetryForOAuth(); - - if (mException instanceof SocketTimeoutException) { - Notify.create(activity, R.string.linked_error_timeout, Style.ERROR).show(); - } else if (mException instanceof HttpResultException) { - Notify.create(activity, activity.getString(R.string.linked_error_http, - ((HttpResultException) mException).mResponse), - Style.ERROR).show(); - } else if (mException instanceof IOException) { - Notify.create(activity, R.string.linked_error_network, Style.ERROR).show(); - } - - return; - } - - GithubResource resource; - - try { - String gistId = result.getString("id"); - JSONObject owner = result.getJSONObject("owner"); - String gistLogin = owner.getString("login"); - - URI uri = URI.create("https://gist.github.com/" + gistLogin + "/" + gistId); - resource = GithubResource.create(uri); - } catch (JSONException e) { - setState(State.POST_ERROR); - return; - } - - View linkedItem = mButtonContainer.getChildAt(2); - if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { - linkedItem.setTransitionName(resource.toUri().toString()); - } - - // we only need authorization for this one operation, drop it afterwards - revokeToken(accessToken); - - step3EditKey(resource); - } - - }.execute(); - - } - - private void revokeToken(final String token) { - - new AsyncTask() { - @Override - protected Void doInBackground(Void... dummy) { - try { - HttpsURLConnection nection = (HttpsURLConnection) new URL( - "https://api.github.com/applications/" + BuildConfig.GITHUB_CLIENT_ID + "/tokens/" + token) - .openConnection(); - nection.setRequestMethod("DELETE"); - String encoded = Base64.encodeToString( - (BuildConfig.GITHUB_CLIENT_ID + ":" + BuildConfig.GITHUB_CLIENT_SECRET).getBytes(), Base64.NO_WRAP); - nection.setRequestProperty("Authorization", "Basic " + encoded); - nection.connect(); - } catch (IOException e) { - // nvm - } - return null; - } - }.execute(); - - } - - private void step3EditKey(final GithubResource resource) { - - // set item data while we're there - { - Context context = getActivity(); - mLinkedIdTitle.setText(resource.getDisplayTitle(context)); - mLinkedIdComment.setText(resource.getDisplayComment(context)); - } - - setState(State.LID_PROCESS); - - new Handler().postDelayed(() -> { - WrappedUserAttribute ua = LinkedAttribute.fromResource(resource).toUserAttribute(); - mSkpBuilder = SaveKeyringParcel.buildChangeKeyringParcel(mMasterKeyId, mFingerprint); - mSkpBuilder.addUserAttribute(ua); - cryptoOperation(); - }, 250); - - } - - @Nullable - @Override - public SaveKeyringParcel createOperationInput() { - // if this is null, the cryptoOperation silently aborts - which is what we want in that case - return mSkpBuilder.build(); - } - - @Override - public void onCryptoOperationSuccess(EditKeyResult result) { - - setState(State.DONE); - - mButtonContainer.getInAnimation().setDuration(750); - mButtonContainer.setDisplayedChild(2); - - new Handler().postDelayed(() -> { - Activity activity = requireActivity(); - Intent intent = ViewKeyActivity.getViewKeyActivityIntent(requireActivity(), mMasterKeyId); - // intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - - if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { - intent.putExtra(ViewKeyActivity.EXTRA_LINKED_TRANSITION, true); - View linkedItem = mButtonContainer.getChildAt(2); - - Bundle options = ActivityOptionsCompat.makeSceneTransitionAnimation( - activity, linkedItem, linkedItem.getTransitionName()).toBundle(); - activity.startActivity(intent, options); - mFinishOnStop = true; - } else { - activity.startActivity(intent); - activity.finish(); - } - }, 1000); - } - - @Override - public void onSaveInstanceState(@NonNull Bundle outState) { - super.onSaveInstanceState(outState); - - // cookies are automatically saved, we don't want that - CookieManager cookieManager = CookieManager.getInstance(); - String cookie = cookieManager.getCookie("https://github.com/"); - outState.putString(ARG_GITHUB_COOKIE, cookie); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - if (savedInstanceState != null) { - String cookie = savedInstanceState.getString(ARG_GITHUB_COOKIE); - CookieManager cookieManager = CookieManager.getInstance(); - cookieManager.setCookie("https://github.com/", cookie); - } - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - try { - // cookies are automatically saved, we don't want that - CookieManager cookieManager = CookieManager.getInstance(); - // noinspection deprecation (replacement is api lvl 21) - cookieManager.removeAllCookie(); - } catch (Exception e) { - // no biggie if this fails - } - } - - @Override - public void onStop() { - super.onStop(); - - if (mFinishOnStop) { - Activity activity = requireActivity(); - activity.setResult(Activity.RESULT_OK); - activity.finish(); - } - } - - @Override - public void onCryptoOperationError(EditKeyResult result) { - result.createNotify(getActivity()).show(this); - setState(State.LID_ERROR); - } - - @Override - public void onCryptoOperationCancelled() { - mRetryButton.setOnClickListener(v -> { - v.setOnClickListener(null); - mButtonContainer.setDisplayedChild(1); - setState(State.LID_PROCESS); - cryptoOperation(); - }); - mButtonContainer.setDisplayedChild(3); - setState(State.LID_ERROR); - } - - private String mOAuthCode, mOAuthState; - - public void oAuthRequest(String hostAndPath, String clientId, String scope) { - - Activity activity = getActivity(); - if (activity == null) { - return; - } - - byte[] buf = new byte[16]; - new Random().nextBytes(buf); - mOAuthState = new String(Hex.encode(buf)); - mOAuthCode = null; - - final Dialog auth_dialog = new Dialog(activity); - auth_dialog.setContentView(R.layout.oauth_webview); - WebView web = auth_dialog.findViewById(R.id.web_view); - web.getSettings().setSaveFormData(false); - web.getSettings().setJavaScriptEnabled(true); - web.getSettings().setUserAgentString("OpenKeychain " + BuildConfig.VERSION_NAME); - web.setWebViewClient(new WebViewClient() { - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - Uri uri = Uri.parse(url); - if ("oauth-openkeychain".equals(uri.getScheme())) { - - if (mOAuthCode != null) { - return true; - } - - if (uri.getQueryParameter("error") != null) { - Timber.i("got oauth error: " + uri.getQueryParameter("error")); - auth_dialog.dismiss(); - return true; - } - - // check if mOAuthState == queryParam[state] - mOAuthCode = uri.getQueryParameter("code"); - - auth_dialog.dismiss(); - return true; - } - // don't surf away from github! - if (!"github.com".equals(uri.getHost())) { - auth_dialog.dismiss(); - return true; - } - return false; - } - - }); - - auth_dialog.setTitle(R.string.linked_webview_title_github); - auth_dialog.setCancelable(true); - auth_dialog.setOnDismissListener(dialog -> step1GetOAuthToken()); - auth_dialog.show(); - - web.loadUrl("https://" + hostAndPath + - "?client_id=" + clientId + - "&scope=" + scope + - "&redirect_uri=oauth-openkeychain://linked/" + - "&state=" + mOAuthState); - - } - - public void setState(State state) { - switch (state) { - case IDLE: - mStatus1.setDisplayedChild(Status.IDLE); - mStatus2.setDisplayedChild(Status.IDLE); - mStatus3.setDisplayedChild(Status.IDLE); - break; - case AUTH_PROCESS: - mStatus1.setDisplayedChild(Status.PROGRESS); - mStatus2.setDisplayedChild(Status.IDLE); - mStatus3.setDisplayedChild(Status.IDLE); - break; - case AUTH_ERROR: - mStatus1.setDisplayedChild(Status.ERROR); - mStatus2.setDisplayedChild(Status.IDLE); - mStatus3.setDisplayedChild(Status.IDLE); - break; - case POST_PROCESS: - mStatus1.setDisplayedChild(Status.OK); - mStatus2.setDisplayedChild(Status.PROGRESS); - mStatus3.setDisplayedChild(Status.IDLE); - break; - case POST_ERROR: - mStatus1.setDisplayedChild(Status.OK); - mStatus2.setDisplayedChild(Status.ERROR); - mStatus3.setDisplayedChild(Status.IDLE); - break; - case LID_PROCESS: - mStatus1.setDisplayedChild(Status.OK); - mStatus2.setDisplayedChild(Status.OK); - mStatus3.setDisplayedChild(Status.PROGRESS); - break; - case LID_ERROR: - mStatus1.setDisplayedChild(Status.OK); - mStatus2.setDisplayedChild(Status.OK); - mStatus3.setDisplayedChild(Status.ERROR); - break; - case DONE: - mStatus1.setDisplayedChild(Status.OK); - mStatus2.setDisplayedChild(Status.OK); - mStatus3.setDisplayedChild(Status.OK); - } - } - - private static JSONObject jsonHttpRequest(String url, JSONObject params, String accessToken) - throws IOException, HttpResultException { - - HttpsURLConnection nection = (HttpsURLConnection) new URL(url).openConnection(); - nection.setDoInput(true); - nection.setDoOutput(true); - nection.setConnectTimeout(3000); - nection.setReadTimeout(5000); - nection.setRequestProperty("Content-Type", "application/json"); - nection.setRequestProperty("Accept", "application/json"); - nection.setRequestProperty("User-Agent", "OpenKeychain " + BuildConfig.VERSION_NAME); - if (accessToken != null) { - nection.setRequestProperty("Authorization", "token " + accessToken); - } - - try { - - nection.connect(); - - OutputStream os = nection.getOutputStream(); - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8")); - writer.write(params.toString()); - writer.flush(); - writer.close(); - os.close(); - - int code = nection.getResponseCode(); - if (code != HttpsURLConnection.HTTP_CREATED && code != HttpsURLConnection.HTTP_OK) { - throw new HttpResultException(nection.getResponseCode(), nection.getResponseMessage()); - } - - InputStream in = new BufferedInputStream(nection.getInputStream()); - BufferedReader reader = new BufferedReader(new InputStreamReader(in)); - StringBuilder response = new StringBuilder(); - while (true) { - String line = reader.readLine(); - if (line == null) { - break; - } - response.append(line); - } - - try { - return new JSONObject(response.toString()); - } catch (JSONException e) { - throw new IOException(e); - } - - } finally { - nection.disconnect(); - } - - } - - static class HttpResultException extends Exception { - final int mCode; - final String mResponse; - - HttpResultException(int code, String response) { - mCode = code; - mResponse = response; - } - - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java deleted file mode 100644 index 4ec67e948..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui.linked; - - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.Fragment; -import android.text.Editable; -import android.text.TextWatcher; -import android.util.Patterns; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.EditText; - -import org.sufficientlysecure.keychain.R; - - -public class LinkedIdCreateHttpsStep1Fragment extends Fragment { - EditText mEditUri; - - public static LinkedIdCreateHttpsStep1Fragment newInstance() { - LinkedIdCreateHttpsStep1Fragment frag = new LinkedIdCreateHttpsStep1Fragment(); - - Bundle args = new Bundle(); - frag.setArguments(args); - - return frag; - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.linked_create_https_fragment_step1, container, false); - - view.findViewById(R.id.next_button).setOnClickListener(v -> { - String uri = "https://" + mEditUri.getText(); - - if (!checkUri(uri)) { - return; - } - - LinkedIdCreateHttpsStep2Fragment frag = LinkedIdCreateHttpsStep2Fragment.newInstance(uri); - - ((LinkedIdWizard) requireActivity()).loadFragment(frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); - }); - - view.findViewById(R.id.back_button).setOnClickListener( - v -> ((LinkedIdWizard) requireActivity()).loadFragment(null, LinkedIdWizard.FRAG_ACTION_TO_LEFT)); - - mEditUri = view.findViewById(R.id.linked_create_https_uri); - - mEditUri.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { - } - - @Override - public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { - } - - @Override - public void afterTextChanged(Editable editable) { - String uri = "https://" + editable; - if (uri.length() > 0) { - if (checkUri(uri)) { - mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0, - R.drawable.ic_stat_retyped_ok, 0); - } else { - mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0, - R.drawable.ic_stat_retyped_bad, 0); - } - } else { - // remove drawable if email is empty - mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); - } - } - }); - - // mEditUri.setText("mugenguild.com/pgpkey.txt"); - - return view; - } - - private static boolean checkUri(String uri) { - return Patterns.WEB_URL.matcher(uri).matches(); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java deleted file mode 100644 index f6e90c28c..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui.linked; - - -import java.io.FileNotFoundException; -import java.io.PrintWriter; -import java.net.URI; -import java.net.URISyntaxException; - -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.os.Environment; -import android.support.annotation.NonNull; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.EditText; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.linked.resources.GenericHttpsResource; -import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.ui.util.Notify.Style; -import org.sufficientlysecure.keychain.util.FileHelper; -import timber.log.Timber; - -public class LinkedIdCreateHttpsStep2Fragment extends LinkedIdCreateFinalFragment { - private static final int REQUEST_CODE_OUTPUT = 0x00007007; - - public static final String ARG_URI = "uri"; - - EditText mEditUri; - - URI mResourceUri; - - public static LinkedIdCreateHttpsStep2Fragment newInstance(String uri) { - - LinkedIdCreateHttpsStep2Fragment frag = new LinkedIdCreateHttpsStep2Fragment(); - - Bundle args = new Bundle(); - args.putString(ARG_URI, uri); - frag.setArguments(args); - - return frag; - } - - @Override - GenericHttpsResource getResource(OperationLog log) { - return GenericHttpsResource.createNew(mResourceUri); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - try { - mResourceUri = new URI(getArguments().getString(ARG_URI)); - } catch (URISyntaxException e) { - Timber.e(e); - requireActivity().finish(); - } - } - - @Override - protected View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.linked_create_https_fragment_step2, container, false); - } - - @NonNull - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = super.onCreateView(inflater, container, savedInstanceState); - - view.findViewById(R.id.button_send).setOnClickListener(v -> proofSend()); - view.findViewById(R.id.button_save).setOnClickListener(v -> proofSave()); - - mEditUri = view.findViewById(R.id.linked_create_https_uri); - mEditUri.setText(mResourceUri.toString()); - - return view; - } - - private String getResourceString() { - return GenericHttpsResource.generateText(requireActivity(), fingerprint); - } - - private void proofSend() { - Intent sendIntent = new Intent(); - sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, getResourceString()); - sendIntent.setType("text/plain"); - startActivity(sendIntent); - } - - private void proofSave() { - String state = Environment.getExternalStorageState(); - if (!Environment.MEDIA_MOUNTED.equals(state)) { - Notify.create(getActivity(), "External storage not available!", Style.ERROR).show(); - return; - } - - String targetName = "pgpkey.txt"; - - // TODO: not supported on Android < 4.4 - FileHelper.saveDocument(this, targetName, "text/plain", REQUEST_CODE_OUTPUT); - } - - private void saveFile(Uri uri) { - try { - PrintWriter out = new PrintWriter(requireActivity().getContentResolver().openOutputStream(uri)); - out.print(getResourceString()); - if (out.checkError()) { - Notify.create(getActivity(), "Error writing file!", Style.ERROR).show(); - } - } catch (FileNotFoundException e) { - Notify.create(getActivity(), "File could not be opened for writing!", Style.ERROR).show(); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - // For saving a file - case REQUEST_CODE_OUTPUT: - if (data == null) { - return; - } - Uri uri = data.getData(); - saveFile(uri); - break; - default: - super.onActivityResult(requestCode, resultCode, data); - } - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java deleted file mode 100644 index 41d3f2adb..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui.linked; - -import android.os.AsyncTask; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.EditText; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.util.Notify; - -public class LinkedIdCreateTwitterStep1Fragment extends Fragment { - EditText mEditHandle; - - public static LinkedIdCreateTwitterStep1Fragment newInstance() { - return new LinkedIdCreateTwitterStep1Fragment(); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.linked_create_twitter_fragment_step1, container, false); - - view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - - final String handle = mEditHandle.getText().toString(); - - if ("".equals(handle)) { - mEditHandle.setError("Please input a Twitter handle!"); - return; - } - - new AsyncTask() { - - @Override - protected Boolean doInBackground(Void... params) { - return true; - // return checkHandle(handle); - } - - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - - if (result == null) { - Notify.create(getActivity(), - "Connection error while checking username!", - Notify.Style.ERROR).show(LinkedIdCreateTwitterStep1Fragment.this); - return; - } - - if (!result) { - Notify.create(getActivity(), - "This handle does not exist on Twitter!", - Notify.Style.ERROR).show(LinkedIdCreateTwitterStep1Fragment.this); - return; - } - - LinkedIdCreateTwitterStep2Fragment frag = - LinkedIdCreateTwitterStep2Fragment.newInstance(handle); - - ((LinkedIdWizard) requireActivity()).loadFragment(frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); - } - }.execute(); - - } - }); - - view.findViewById(R.id.back_button).setOnClickListener( - v -> ((LinkedIdWizard) requireActivity()).loadFragment(null, LinkedIdWizard.FRAG_ACTION_TO_LEFT)); - - mEditHandle = view.findViewById(R.id.linked_create_twitter_handle); - - return view; - } - - /* not used at this point, too many problems - private static Boolean checkHandle(String handle) { - try { - HttpURLConnection nection = - (HttpURLConnection) new URL("https://twitter.com/" + handle).getUrlResponse(); - nection.setRequestMethod("HEAD"); - nection.setRequestProperty("User-Agent", "OpenKeychain"); - return nection.getResponseCode() == 200; - } catch (IOException e) { - return null; - } - } - */ - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep2Fragment.java deleted file mode 100644 index 37be1b89c..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep2Fragment.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui.linked; - - -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.text.Html; -import android.text.Spanned; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.linked.LinkedTokenResource; -import org.sufficientlysecure.keychain.linked.resources.TwitterResource; -import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; - -public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragment { - - public static final String ARG_HANDLE = "handle"; - - String mResourceHandle; - - public static LinkedIdCreateTwitterStep2Fragment newInstance(String handle) { - LinkedIdCreateTwitterStep2Fragment frag = new LinkedIdCreateTwitterStep2Fragment(); - - Bundle args = new Bundle(); - args.putString(ARG_HANDLE, handle); - frag.setArguments(args); - - return frag; - } - - @Override - public void onActivityCreated(@Nullable Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mResourceHandle = getArguments().getString(ARG_HANDLE); - } - - private String getResourceString() { - return TwitterResource.generate(fingerprint); - } - - @NonNull - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = super.onCreateView(inflater, container, savedInstanceState); - - view.findViewById(R.id.button_send).setOnClickListener(v -> proofSend()); - view.findViewById(R.id.button_share).setOnClickListener(v -> proofShare()); - - Spanned tweetText = Html.fromHtml(getString(R.string.linked_create_twitter_2_3, mResourceHandle)); - ((TextView) view.findViewById(R.id.linked_tweet_published)).setText(tweetText); - - return view; - } - - @Override - LinkedTokenResource getResource(OperationLog log) { - return TwitterResource.searchInTwitterStream(getActivity(), mResourceHandle, getResourceString(), log); - } - - @Override - protected View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.linked_create_twitter_fragment_step2, container, false); - } - - private void proofShare() { - Intent sendIntent = new Intent(); - sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, getResourceString()); - sendIntent.setType("text/plain"); - startActivity(sendIntent); - } - - private void proofSend() { - Uri.Builder builder = Uri.parse("https://twitter.com/intent/tweet").buildUpon(); - builder.appendQueryParameter("text", getResourceString()); - Uri uri = builder.build(); - - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - startActivity(intent); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdSelectFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdSelectFragment.java deleted file mode 100644 index cd8262aaf..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdSelectFragment.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui.linked; - - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import org.sufficientlysecure.keychain.R; - - -public class LinkedIdSelectFragment extends Fragment { - public static LinkedIdSelectFragment newInstance() { - return new LinkedIdSelectFragment(); - } - - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.linked_select_fragment, container, false); - - view.findViewById(R.id.linked_create_https_button).setOnClickListener(v -> { - LinkedIdCreateHttpsStep1Fragment frag = LinkedIdCreateHttpsStep1Fragment.newInstance(); - ((LinkedIdWizard) requireActivity()).loadFragment(frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); - }); - - view.findViewById(R.id.linked_create_twitter_button).setOnClickListener(v -> { - LinkedIdCreateTwitterStep1Fragment frag = LinkedIdCreateTwitterStep1Fragment.newInstance(); - ((LinkedIdWizard) requireActivity()).loadFragment(frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); - }); - - view.findViewById(R.id.linked_create_github_button).setOnClickListener(v -> { - LinkedIdCreateGithubFragment frag = LinkedIdCreateGithubFragment.newInstance(); - ((LinkedIdWizard) requireActivity()).loadFragment(frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); - }); - - - return view; - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdWizard.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdWizard.java deleted file mode 100644 index bea6df04b..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdWizard.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2017 Schürmann & Breitmoser GbR - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package org.sufficientlysecure.keychain.ui.linked; - - -import android.arch.lifecycle.ViewModelProviders; -import android.content.Context; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentTransaction; -import android.view.View; -import android.view.inputmethod.InputMethodManager; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.model.SubKey.UnifiedKeyInfo; -import org.sufficientlysecure.keychain.ui.base.BaseActivity; -import org.sufficientlysecure.keychain.ui.keyview.UnifiedKeyInfoViewModel; -import timber.log.Timber; - - -public class LinkedIdWizard extends BaseActivity { - public static final String EXTRA_MASTER_KEY_ID = "master_key_id"; - - public static final int FRAG_ACTION_START = 0; - public static final int FRAG_ACTION_TO_RIGHT = 1; - public static final int FRAG_ACTION_TO_LEFT = 2; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setTitle(getString(R.string.title_linked_id_create)); - - Bundle extras = getIntent().getExtras(); - if (extras == null || !extras.containsKey(EXTRA_MASTER_KEY_ID)) { - Timber.e("Missing required extra master_key_id!"); - finish(); - return; - } - - long masterKeyId = extras.getLong(EXTRA_MASTER_KEY_ID); - UnifiedKeyInfoViewModel viewModel = ViewModelProviders.of(this).get(UnifiedKeyInfoViewModel.class); - viewModel.setMasterKeyId(masterKeyId); - viewModel.getUnifiedKeyInfoLiveData(this).observe(this, this::onLoadUnifiedKeyInfo); - - hideKeyboard(); - - // pass extras into fragment - if (savedInstanceState == null) { - LinkedIdSelectFragment frag = LinkedIdSelectFragment.newInstance(); - loadFragment(frag, FRAG_ACTION_START); - } - } - - private void onLoadUnifiedKeyInfo(UnifiedKeyInfo unifiedKeyInfo) { - if (!unifiedKeyInfo.has_any_secret()) { - Timber.e("Linked Identities can only be added to secret keys!"); - finish(); - } - } - - @Override - protected void initLayout() { - setContentView(R.layout.create_key_activity); - } - - public void loadFragment(Fragment fragment, int action) { - // Add the fragment to the 'fragment_container' FrameLayout - // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - - switch (action) { - case FRAG_ACTION_START: - transaction.setCustomAnimations(0, 0); - transaction.replace(R.id.create_key_fragment_container, fragment) - .commitAllowingStateLoss(); - break; - case FRAG_ACTION_TO_LEFT: - getSupportFragmentManager().popBackStackImmediate(); - break; - case FRAG_ACTION_TO_RIGHT: - transaction.setCustomAnimations(R.anim.frag_slide_in_from_right, R.anim.frag_slide_out_to_left, - R.anim.frag_slide_in_from_left, R.anim.frag_slide_out_to_right); - transaction.addToBackStack(null); - transaction.replace(R.id.create_key_fragment_container, fragment) - .commitAllowingStateLoss(); - break; - - } - getSupportFragmentManager().executePendingTransactions(); - } - - private void hideKeyboard() { - InputMethodManager inputManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - - View v = getCurrentFocus(); - if (v == null || inputManager == null) { - return; - } - - inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0); - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java index 8dd09205e..b28385add 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java @@ -432,10 +432,6 @@ public class Preferences { // experimental prefs - public boolean getExperimentalEnableLinkedIdentities() { - return mSharedPreferences.getBoolean(Pref.EXPERIMENTAL_ENABLE_LINKED_IDENTITIES, false); - } - public boolean getExperimentalEnableKeybase() { return mSharedPreferences.getBoolean(Pref.EXPERIMENTAL_ENABLE_KEYBASE, false); } diff --git a/OpenKeychain/src/main/res/drawable-hdpi/linked_dns.png b/OpenKeychain/src/main/res/drawable-hdpi/linked_dns.png deleted file mode 100644 index 41719efd6..000000000 Binary files a/OpenKeychain/src/main/res/drawable-hdpi/linked_dns.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/drawable-hdpi/linked_github.png b/OpenKeychain/src/main/res/drawable-hdpi/linked_github.png deleted file mode 100644 index 2a1ae67d7..000000000 Binary files a/OpenKeychain/src/main/res/drawable-hdpi/linked_github.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/drawable-hdpi/linked_https.png b/OpenKeychain/src/main/res/drawable-hdpi/linked_https.png deleted file mode 100644 index 278fac4f8..000000000 Binary files a/OpenKeychain/src/main/res/drawable-hdpi/linked_https.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/drawable-hdpi/linked_twitter.png b/OpenKeychain/src/main/res/drawable-hdpi/linked_twitter.png deleted file mode 100644 index dd0fce197..000000000 Binary files a/OpenKeychain/src/main/res/drawable-hdpi/linked_twitter.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/drawable-mdpi/linked_dns.png b/OpenKeychain/src/main/res/drawable-mdpi/linked_dns.png deleted file mode 100644 index c6f3e9a06..000000000 Binary files a/OpenKeychain/src/main/res/drawable-mdpi/linked_dns.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/drawable-mdpi/linked_github.png b/OpenKeychain/src/main/res/drawable-mdpi/linked_github.png deleted file mode 100644 index ba3d2e4b7..000000000 Binary files a/OpenKeychain/src/main/res/drawable-mdpi/linked_github.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/drawable-mdpi/linked_https.png b/OpenKeychain/src/main/res/drawable-mdpi/linked_https.png deleted file mode 100644 index 54b085aaf..000000000 Binary files a/OpenKeychain/src/main/res/drawable-mdpi/linked_https.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/drawable-mdpi/linked_twitter.png b/OpenKeychain/src/main/res/drawable-mdpi/linked_twitter.png deleted file mode 100644 index 86ca9d46e..000000000 Binary files a/OpenKeychain/src/main/res/drawable-mdpi/linked_twitter.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/linked_dns.png b/OpenKeychain/src/main/res/drawable-xhdpi/linked_dns.png deleted file mode 100644 index ec87cdd1c..000000000 Binary files a/OpenKeychain/src/main/res/drawable-xhdpi/linked_dns.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/linked_github.png b/OpenKeychain/src/main/res/drawable-xhdpi/linked_github.png deleted file mode 100644 index 0934f1840..000000000 Binary files a/OpenKeychain/src/main/res/drawable-xhdpi/linked_github.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/linked_https.png b/OpenKeychain/src/main/res/drawable-xhdpi/linked_https.png deleted file mode 100644 index f627d2b3b..000000000 Binary files a/OpenKeychain/src/main/res/drawable-xhdpi/linked_https.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/drawable-xhdpi/linked_twitter.png b/OpenKeychain/src/main/res/drawable-xhdpi/linked_twitter.png deleted file mode 100644 index 35bb1fd3b..000000000 Binary files a/OpenKeychain/src/main/res/drawable-xhdpi/linked_twitter.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/linked_dns.png b/OpenKeychain/src/main/res/drawable-xxhdpi/linked_dns.png deleted file mode 100644 index 9b3ee8572..000000000 Binary files a/OpenKeychain/src/main/res/drawable-xxhdpi/linked_dns.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/linked_github.png b/OpenKeychain/src/main/res/drawable-xxhdpi/linked_github.png deleted file mode 100644 index 19a3421bf..000000000 Binary files a/OpenKeychain/src/main/res/drawable-xxhdpi/linked_github.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/linked_https.png b/OpenKeychain/src/main/res/drawable-xxhdpi/linked_https.png deleted file mode 100644 index c41ab6c1c..000000000 Binary files a/OpenKeychain/src/main/res/drawable-xxhdpi/linked_https.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/drawable-xxhdpi/linked_twitter.png b/OpenKeychain/src/main/res/drawable-xxhdpi/linked_twitter.png deleted file mode 100644 index 0710a12f1..000000000 Binary files a/OpenKeychain/src/main/res/drawable-xxhdpi/linked_twitter.png and /dev/null differ diff --git a/OpenKeychain/src/main/res/layout/identities_card.xml b/OpenKeychain/src/main/res/layout/identities_card.xml index a970fd76b..2a5c84db9 100644 --- a/OpenKeychain/src/main/res/layout/identities_card.xml +++ b/OpenKeychain/src/main/res/layout/identities_card.xml @@ -18,30 +18,4 @@ android:layout_height="wrap_content" android:layout_marginBottom="4dp" /> - - - - -