From 2cc22c6b6583403354448443014fc73dc0e5eb0c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 22 Oct 2018 12:49:52 +0200 Subject: [PATCH] Remove experimental Linked Identities feature --- OpenKeychain/src/main/AndroidManifest.xml | 5 - .../keychain/Constants.java | 14 +- .../keychain/linked/LinkedAttribute.java | 49 -- .../keychain/linked/LinkedResource.java | 42 -- .../keychain/linked/LinkedTokenResource.java | 302 -------- .../keychain/linked/UriAttribute.java | 95 --- .../linked/resources/DnsResource.java | 147 ---- .../resources/GenericHttpsResource.java | 114 --- .../linked/resources/GithubResource.java | 240 ------- .../linked/resources/TwitterResource.java | 276 ------- .../results/LinkedVerifyResult.java | 48 -- .../keychain/ui/adapter/IdentityAdapter.java | 86 +-- .../ui/keyview/KeyFragmentViewModel.java | 7 +- .../ui/keyview/LinkedIdViewFragment.java | 529 -------------- .../keychain/ui/keyview/ViewKeyFragment.java | 33 +- .../ui/keyview/loader/IdentityDao.java | 69 +- .../ui/keyview/view/IdentitiesCardView.java | 12 - .../linked/LinkedIdCreateFinalFragment.java | 221 ------ .../linked/LinkedIdCreateGithubFragment.java | 671 ------------------ .../LinkedIdCreateHttpsStep1Fragment.java | 104 --- .../LinkedIdCreateHttpsStep2Fragment.java | 153 ---- .../LinkedIdCreateTwitterStep1Fragment.java | 113 --- .../LinkedIdCreateTwitterStep2Fragment.java | 106 --- .../ui/linked/LinkedIdSelectFragment.java | 59 -- .../keychain/ui/linked/LinkedIdWizard.java | 118 --- .../keychain/util/Preferences.java | 4 - .../src/main/res/drawable-hdpi/linked_dns.png | Bin 667 -> 0 bytes .../main/res/drawable-hdpi/linked_github.png | Bin 1437 -> 0 bytes .../main/res/drawable-hdpi/linked_https.png | Bin 1345 -> 0 bytes .../main/res/drawable-hdpi/linked_twitter.png | Bin 1265 -> 0 bytes .../src/main/res/drawable-mdpi/linked_dns.png | Bin 371 -> 0 bytes .../main/res/drawable-mdpi/linked_github.png | Bin 999 -> 0 bytes .../main/res/drawable-mdpi/linked_https.png | Bin 1010 -> 0 bytes .../main/res/drawable-mdpi/linked_twitter.png | Bin 880 -> 0 bytes .../main/res/drawable-xhdpi/linked_dns.png | Bin 736 -> 0 bytes .../main/res/drawable-xhdpi/linked_github.png | Bin 2153 -> 0 bytes .../main/res/drawable-xhdpi/linked_https.png | Bin 1955 -> 0 bytes .../res/drawable-xhdpi/linked_twitter.png | Bin 1916 -> 0 bytes .../main/res/drawable-xxhdpi/linked_dns.png | Bin 1498 -> 0 bytes .../res/drawable-xxhdpi/linked_github.png | Bin 3179 -> 0 bytes .../main/res/drawable-xxhdpi/linked_https.png | Bin 3222 -> 0 bytes .../res/drawable-xxhdpi/linked_twitter.png | Bin 2666 -> 0 bytes .../src/main/res/layout/identities_card.xml | 26 - .../layout/linked_create_github_fragment.xml | 235 ------ .../linked_create_https_fragment_step1.xml | 129 ---- .../linked_create_https_fragment_step2.xml | 165 ----- .../linked_create_twitter_fragment_step1.xml | 123 ---- .../linked_create_twitter_fragment_step2.xml | 154 ---- .../main/res/layout/linked_create_verify.xml | 78 -- .../src/main/res/layout/linked_id_item.xml | 75 -- .../res/layout/linked_id_view_fragment.xml | 193 ----- .../res/layout/linked_select_fragment.xml | 188 ----- .../res/transition/linked_id_card_trans.xml | 4 - OpenKeychain/src/main/res/values/strings.xml | 68 -- graphics/drawables/linked_dns.svg | 4 - graphics/drawables/linked_github.svg | 4 - graphics/drawables/linked_https.svg | 4 - graphics/drawables/linked_twitter.svg | 4 - graphics/update-drawables.sh | 2 +- 59 files changed, 19 insertions(+), 5054 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedAttribute.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/UriAttribute.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/LinkedVerifyResult.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/keyview/LinkedIdViewFragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep2Fragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdSelectFragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdWizard.java delete mode 100644 OpenKeychain/src/main/res/drawable-hdpi/linked_dns.png delete mode 100644 OpenKeychain/src/main/res/drawable-hdpi/linked_github.png delete mode 100644 OpenKeychain/src/main/res/drawable-hdpi/linked_https.png delete mode 100644 OpenKeychain/src/main/res/drawable-hdpi/linked_twitter.png delete mode 100644 OpenKeychain/src/main/res/drawable-mdpi/linked_dns.png delete mode 100644 OpenKeychain/src/main/res/drawable-mdpi/linked_github.png delete mode 100644 OpenKeychain/src/main/res/drawable-mdpi/linked_https.png delete mode 100644 OpenKeychain/src/main/res/drawable-mdpi/linked_twitter.png delete mode 100644 OpenKeychain/src/main/res/drawable-xhdpi/linked_dns.png delete mode 100644 OpenKeychain/src/main/res/drawable-xhdpi/linked_github.png delete mode 100644 OpenKeychain/src/main/res/drawable-xhdpi/linked_https.png delete mode 100644 OpenKeychain/src/main/res/drawable-xhdpi/linked_twitter.png delete mode 100644 OpenKeychain/src/main/res/drawable-xxhdpi/linked_dns.png delete mode 100644 OpenKeychain/src/main/res/drawable-xxhdpi/linked_github.png delete mode 100644 OpenKeychain/src/main/res/drawable-xxhdpi/linked_https.png delete mode 100644 OpenKeychain/src/main/res/drawable-xxhdpi/linked_twitter.png delete mode 100644 OpenKeychain/src/main/res/layout/linked_create_github_fragment.xml delete mode 100644 OpenKeychain/src/main/res/layout/linked_create_https_fragment_step1.xml delete mode 100644 OpenKeychain/src/main/res/layout/linked_create_https_fragment_step2.xml delete mode 100644 OpenKeychain/src/main/res/layout/linked_create_twitter_fragment_step1.xml delete mode 100644 OpenKeychain/src/main/res/layout/linked_create_twitter_fragment_step2.xml delete mode 100644 OpenKeychain/src/main/res/layout/linked_create_verify.xml delete mode 100644 OpenKeychain/src/main/res/layout/linked_id_item.xml delete mode 100644 OpenKeychain/src/main/res/layout/linked_id_view_fragment.xml delete mode 100644 OpenKeychain/src/main/res/layout/linked_select_fragment.xml delete mode 100644 OpenKeychain/src/main/res/transition/linked_id_card_trans.xml delete mode 100644 graphics/drawables/linked_dns.svg delete mode 100644 graphics/drawables/linked_github.svg delete mode 100644 graphics/drawables/linked_https.svg delete mode 100644 graphics/drawables/linked_twitter.svg 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 41719efd6b0b54bc5152ebee08ed68a7823a8cac..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 667 zcmV;M0%ZM(P);c?r7iDX4L7#=%tx3#e^5RTBNHy<;KmWjclT3_=&}Qbe1!z+Y&=#Og zF+f{@HpKvK0qTf|bpXpi4;TURPsZq5^Rjb&9tQL>SWxKbxz=Y~UxLP%5wLCz%9pW- zY}f*P4EWdI$|7QH&s$#v=OfH91nz)7unFt~dlBYMgGUkkt-yQ$=fKj8oGvi2)PViS zae7qf0$oSm8t`n%&!PxcF<=68EQ^28UVPmK2w#2v0yqR#fNkJ3hd1wQK-mnX5|S%E z225*LQ6En2J_g)G5Tb=!yGIe+0tRGI(Ed0Ez*glV_=7R#0I(h}WyDBC?tKEi4QMK* z3up__ru7DRABr}$2)4cr`WSGX!D2$kb&So>8?ayv%9p8#EZPFfZZMUQT&Zfn2XF}- z0jt0ca0a|ooHrfiNG#ldRv(H!y7InrPjfdQv<5szj#DGlfMMh~H9`&8jU1;&r~${u zm?e+6lfe1YQK1X8`htYekSj7V_5xX7l5*_@%4R5)kX-RGU|hS3`fzIZG2kkK5G~x= zJ-R@nugjM|L)rqgDF$c@(54umEkK)MfVKc_J^||_9Ik4Aew_dS002ovPDHLkV1l#a B7WV)E 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 2a1ae67d7a70b075009610127f4a7e5239957e8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1437 zcmV;O1!DS%P){sm&8DrV&(1`Co_iVGV}t&$(6mil8MdG%=}a$MZ| z@G+U0d+xlMH<9EEhX-%YoO8Zs?!9yGxp$0+u$i%~>;THfRszZnplodV1hluxkujzl z*bnTE2*8bqd@mx^mc&)$Tfi8z52!d90Xmhp{ta9Jrhq9CSu7?(KtysCU_0})L zV5UU!b(sNnwKM_1L11A6kgv-EaIhr_aO77v0O_c#W=TSpX1|f1d|XQi>JFg;-pmFd z>5~}(2F8#Cq>c7f;3RMg_!L+xfY_R|eaiW&rsz)QngHy3J}tPZh<-nC7O3Swyyk56 zM|@0q?o-VZylVktOdED8`ap`>OCs`C%)NVn3E)}aHn4>4r;F})!KvQwWS{%p3)}&` z54iKe;$c44EL z+mD2dym<(Ab?J+11<@ zz!fuwl}`GUdU->O>uc=n!tn;lwB9}Z8a9TvJD|K^&^LbjSp3Eg- zgzd(@w|%4R9645JGQ0`ua}-~kw}I}Ex4?dM1J^D^m3oCFphKxwB($3?K?!J68ZBc26h*&MFP2kM0)A5( zEn^K3g*E}PoRSjotI}v%ji6P~#t_S?*%t7GQm;rnrql~+0qZI-33%L9j7ov?{isqs zmQzv!E@6w1KJ)_5DFY}1W58Vsy)`VS^+=g#Uqn~5ybkOrVI9x~{G^QWVwQ@~1x027 z?gid8#u%l3({R2f(a%?sxuj(HFk$8EcYyX5Jo$F#D0tgHBu_lK8NEm1}_1x03QIqCpeu29s#!HK)OECe;G);F5{{8JKK1*}EOI4sJa{^pI6ETK=0EXT59l%$LVBQOO>gSpu z-6#`YUy5M5uv=PDY0w!2eo4@q z(vokmH{E0cDk?t#0iBO=@)t1JpmPA*-vkyQ~njy*)x$_>&L;7JjADrCTmLoT1ph{(sl5cb{uXC5{aPy=2AhD79(Tr3vw z{_exB7MJ+1pAKcPGuUwdva^|ba4L%w_Q{MZSdIY8BC_DqPq48Udu;A=DQFkE&~JwR r57_jd&21&1>;THfRszZnpltjL0?T8iy~ki{00000NkvXXu0mjf^o)U| 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 278fac4f868152754b8903028dedd920a1a3e0eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1345 zcmV-H1-|-;P)EL%|;Y3rZqVMe&ko-=pu z?!CYJ`{RMb4`$Ap^PTT+?%ervuCdnA&C*jhfV$-|0d)hYTe_ZrzGek7#tZ^OzzVM` z{jUXX2Ktk=_I6WnRrwY$#ykt`_WBa(BW@P>92f`2t+l^YgAlORma4!aU@Blo<{*MAgf z)ItET9QY!!lUm(-UZuK+!2N{wF93s0B>>nAOy&Ul6L=SRRjHo_p7uJe*z5&P1Apbj zx(;k?3IV_t;O_{SJHSz3vA6$JvD=?%bH8Hqv9Es$I0pP1iF<24BoyuZGr)}qh_8U> zGxi(&P!8?B#;-xVo$+O%-2-5K#%>evZO%w2^f(lq@GeGz9|!sZpE1RDo6!C?#rA{1 zt^u5i;J846iLnR)A4Y;6QAW$GV0$qVLn8PNutcyu7Qr)GDgnTrNU%2*pEnfyeUUi( z6ra}=`;iE)q;*0{=lg&kl^`b~e&+@I+xW>*@mcI`LcdQUKBpCIS4mhgrVwyQ32<3^ z)_`Tm@Bgz2@y-f<{{V{=`+oF^@xX!z0G_~YPbEOlMf_e@0PRR1V29$nC*t#h#}xWZ zk+Xq{ZikIAZvpQJRX2c3sv~R#)&*q`fsZ0SS=r0nt@AbDyNJ(rV6{+w#9Dhw^~(7- zzDJ*A+C?YkH3Ez=>wuqC=Gse!ta7LlJE7TK5tuq;msZjB1c;A-F=i<+!(4(c;dX&B z*$%7;%BF$O@=yly3T`!-l}X^s9KY2bLsn*SdxW_Mt;*#0BJzZ?GvPIMPVhUI=T}xc zz)l7G7Rg?F1Xd{J*JIRsSt^wMp5T|_BeflhhH<4-s1YC;-@`2|@@yx0Uo0?Pc3)^qzr$ke*}PxwGaXN`nbbHBjBb`Hk8=rA41t+ zqHms$)V3rNR;f>g2v}o`=}ibOfn1j0m*SI4fX5iFL!bl#8o;VN0MtJMR^hOQvS0!r z?J=lz1fXLS`=l*}pTy0={XtcCEQLKO(wSbn>Upi7vEAT!q^{jW?Mv)HWG(57pH`RR z2O{3#N@b9mCs(x2Lw=If7;sW3Z>K}iv1p{6z4ZWYcNI=59dj(6axkG?U3dP)h!4<< zyJnICbpF+t4>GI-vA|))r!y``Gr>k9!A1o8&bc1<0UNmF11;!Df;oXsN|5lu2XTud zE%-OUhK#+IB6At&$=LPyve2$|Qe-k9HsTVJG!S=zx5-bEI+$tmpkmWnNm2m`v5{cO zPm`MgmUx{}Y+5f(E_fVbbHXEyB}%^URjO;_1sg}tsV}?00000NkvXXu0mjf D>1t{= 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 dd0fce197d7dfc239acd49e1f1e33668d6134014..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1265 zcmVt{()4)%_q^h3vV!Ii)!XnZIJO&H`tAHzKshk4Ffi0@K!@=HF zz_hBKnDbCoGX*n6=Gk<73vfFy1*n6}051azjx$5RSREt)mjJIM zLAeCqC|?506FWeSOz=?++T!%O&NyAfrTCk*cu@CBf#Up zD6kK+u6#hsLTGbr#L~cVV6BJX+mpE)V)!@8H^Aj-KG=4GzgnJk1Mi5)n27XR^zyJA z=&10%3nqazs(LEzp^bna9E=YEhec$#jAy_=$@g6G1u&?p-)3s$d zgTU3m^OfDHAGS^0e_$H{hdekE3lpx2HUcIvXK?>VeC(>2Nr0L|$CtdgQ!mZ|pE+yi z?9@7eV}#Gve9F!u8L4$AsH)SzGtO)_2R1UMvTE?ETK$HI zh+GOh4BU*V^jrsARLN*Q-o_LWaNyuy^x0uv z2*3>cH!%khA%lO~ckrbNz*K+k#vDC`UhGZ-!y$rKP00mBWHIJ)(Vxkj?gkbmzXQOX zm`YLTg$~T6h9^{YDp(6Cz1xea35FihHu(;?D^#yLO#$Er%vG~G1E0p+$)!ssE?R0abM_Uxh?dUY4#C7ySPVvn@KOQ~NH?yDfUh`vs%wN)N`#kJNuoikP_n$n>9nE*Ues zzcK7jV%lQJ_^QKWQ-hoYW8gZ$7n_U4SY#4^^mp8uqqm)XPH}sbfYPI&=Vz-ERyK$m zFxXuaIR8bXBH^E`9pn5LpFidW%U_vM@y|o!3!5&Zx6d2bClg=3ZK(dVaH@eY^Yv4^ zb1f%#stYo{NNTlh)92jayT;e^K<;+UeUs-e{d)i98uqA%>uoQvoaN5AcVt@U_eWD^ zTePM=y0yutC-qV81|J`1wRtWPYU>_OQQPLTCn!?Q&P2^YT6n=(>DwQIYS&C=Jq+|a NgQu&X%Q~loCIE5(lSKdk 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 ba3d2e4b7aaa5cabb42832bdb541b9b878486019..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 999 zcmVyX;O$ENFZQqB>@X73++T=V+y&#*jWh17%ZZJ5HLcF7%^(0W-RVKJa=bjXLk2; z1oFV!%)9fv&odu8J9kQJ%~nRP-U8BM+d#e1kQI?qstxD?jsdL_1xYUhA0%D?Pqfxc znH1JqCk~(mxCzVv+B0UPZ%Zl-357QUW58Mh;lWrFuI5cK0QAa|NGm)T%Rq0<3;+rk z2R3RTz7iW!LuG0J$ODr#6dsI8AfJf=8Tri=Uu`0GCR_}R*Fk(G##0%fykSji{vI$0 z+yPcn2wV}4LE$v*3G2p=dsRlgZMv(TYm!-7ugd=cmVjxAC26bV*QHNW6;F3Wtz|5) zSet<{%gD7Go-Y7hKoMv(e*7|cW5%n=Qidj5Bvlz@2!f6tZXc;vnq70mjsaJH=<%2j*vnIn@Y{UNM zD7l`Jz5$eCYPSXZm}a2IG73L|k(iN6gAu6_g^)IrfnyeelGb{`SCuP-yJUGJq|IcY z)j}}qD=GorSaKn4z6NaMS;00)P6i5wwhF?BM)t7P2x&7Ju-%gnd&}|1Axkc#&17IT zA$eA?4N^#(uYt{&fz$>TEd)iS)Gl8|t`Kh9iM$wWFd3M(5bOXZyj1yNLTW@|I@Dq~ z&O3;|hz!)=Gh)D@Zb$5Wfbt!9K8ENE(3zq5PT{oGe23wV97T9G(0L8`XdrzE6jKO4 z2t0Bac?}F1IU~15V<-3`pVYw!#@1a2D@vK#KQMzL{lhU=HZ_){}4z=O~}n z^BEX;51b?7psI<1Dj^NPFy)^ir#$*SrTl4p31~>8!T8(gR>GG5qkG#xy|LvD`~_Fa VY8pc?U(Emj002ovPDHLkV1mNqq|E>T 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 54b085aaf2d19576f79e6a3d896588cbae53749f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1010 zcmVqCoef#!iHZsQ0%I!>}1E{v03TSjYn-)&yTB~aQ5OOBH7TYLxU?cLSyKVPec-Pr zfJ0^Mm9;onW4Uucl&S!r9eCz(S^#E%hhqEyxN7Y{%ssHr6g-@(Kxdi)o_l`9$3UCK zyc6@w&hyJ+E^o)%fJM)Wtj1Z1IKk%>@{hoE2XjNre+Rmp`7S%=Vn&?#QD95)ldDPq za7TIPKLGs#_f%jO6|O~rSrPMD`@_gR5!~3J;5&hDf^bU-{v7Z};D!{wA%Xb?v3g)==i2jcN)`yWCA>Vxx(DDMbO)Qg??!b3_Utmv6 zN+=~m?lb!VY`4Jf#o5Fr@QXBE(*zVne=zX;PtiXg!>7bZj*xE|n<}7i0!heutp&)~ zVhOkuaIho#nE=*@ksKl4WdgK~Gfm@9T%S$H`74oaf*=E^NHNI^crJCof zjlQUHFiDyolji`*P)Z`)W3A+VS&+-`T^_1#U)GXE#V~PWtA;OgeTjRi2#ZPsGElVCzD)UM5Bj5@C(&CE4u_ET& z(&Ccx&KuGyEm}vN9uWqAarY3oW=+<4>Ra4(RwAeD2{&Llwq`93_V}Gozr3z&?AE;C za71)aIw#~|m&EFO#UzAeC|%U$69k^hC0}F%sk%i`l%-U+8Ksnb_wm0#P_t{+)C4rD gwfs%}seneef08%d7!`*CZU6uP07*qoM6N<$f|=&T5dZ)H 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 86ca9d46e49f1fe744a317c62d49c4644da21d22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 880 zcmV-$1CRWPP)0!uQ_1#H3hcxz7FoX+0E)vion zB_>ORXE}-YaS%7tYd;K(ozvm@^r22n;M*GF2k~#bT**K>W%n`pIt)=A={6@vbZkFcV^z@84opTUB>KJwng2Xn4xu@0YMDunP=2w`goVRZ;0WZK-R z5T^_`V~qdi-pv`gF`UzE#O~n=j^LYyF|wQ}T&4xn|E%^qNEiI1$iSQ>6he4Dgm40{ zOZMU#iyGwZLWjH8(g;^#jJJy# zU2XYhsM|OIuc$fgcTj`RuMT`c;?EmLZG(U7c@^N{!ez zB=OVOSXF#w1F4BmhhSh&9r3jcWVY4w^CbQ(#^%*P#>hJC&|QN6gyoI2G&Jyn&ZVMpS>#uH7&S2BQc?8G7b(~3;S69Cj(05l8n$*NywVcFi;_aF>_|M1*Cj8(4P6VLVPP>hgAl@#k^|O_>F+q6 zPPs^O1U{AW&y;6kjFY9oTcVPIM;VX(e`F&AePd*xZ~PCl2gS&iUQj;(00004nJ zn8$)Jqhx~{kij5X;u=vBoS#-wo>-L1P+nfHmzkGcoSayYs+V7sKKq@G6axcOtfz}( zNX4ADvp4!4aS(9bFDN=kPoa^Iqho>OsRpqXj4oxo6D~5ju#^<2uylyEJ6#^=7{`0gdWOVU04|K8VAnd`l4N{PuK<1O9?RdkF! zD<^FE_dM$LKbh|-t8Ps^_q>5=ozV3?W$Jl(OmbZ7th>zf8o`+n{oDpEs^Vv8#g@f_= zGN6FL79(9A=YEyD;z#agKRT)36m-PKTkyUrXJC)?UXhQpU8gSIe4pW3i}vM1?h--@ z2Y8syOyQ_l{ZHhx;@sInjQy;J-imXj&7NONo^y8RU*2;ncRS}RXHIXn7FCJYs$gEA z*VA+ctP4)uNeM6bRmog?$ez9SP_M)M4U)fFq#NuK&DU`H^MJ$uSIgZErWbdcPCUEQ zSkz(uhlZscwTvbiDhvE?@YEhkdm(Ig*yRk21@XX3;d2g}N%NL=yr0yfajZA4>&Mwe zr!L;iXUIM@HS?h|4<|UlUkI<`h8W;9e*@>QmgPK)6^uCyni;SX@$PQUD>sJ3H3L&4 NgQu&X%Q~loCIH2JC#e7c 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 0934f184032cd88be6b4a6ec636a6e12d8071268..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2153 zcmV-v2$uJWP)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H12j)pcK~#90?VW3g7F8I>e~-J_ns$-0(!wB;!q5#{+%lqMi!3Tj ztgtIaAEGZAK@rr~s0a))iZs#+D=1^hBFZ!?FNu{Em}n5)%q1hct!1rGAI=CbZ|ghf zHgjgz`NLrs-uF4@|NQ^&oH;Xd&KXfvYNW%6)C3N7)CMF*Z9rnw1|&vpKw{JeBt~sO zVzj3w&1egHPB>fUmh+e4~$AtoZ@fXji|z{S83 zfmQYc?*P5PN>%+Su)e6MgaINl2_yJyUSpiHQb_QTZD@%ca3W%In1~RS_%Fw3p zldy=)!2GgKD72yY=mX}c>iQxZDfB{2MCJo)7<%G^;k+ixp*R%O<+K3{N#C8sDhq+O zA#n}>#sY6wK(yH9ZD4Gr&jBKG8t^7CDO#n~!VcgvRsAYfh0zQUk+Hx>3}5jNUe)rWZHo{CmO3VUPm8)HVeZkFC4Mh3sRf^V9P3+J zY8NCTlYuXAJBfFy>gPH2_F%3$r!;-k4{QOx0rmjDW`Fl&pC6NzqqD!K0B7<4r)ggR zcd6>TIXtEUpXKD!i2cA6Ro&_kW7_gGUCqc&f{(vg1G|L@^W<#>w_{~!O1n5$q`yCa4hzRCZ z$TW}ee&D#gp|{Nu(1rgL@g+l5e1MGm63_4>Fxs_*jRCIrLmKf`Kx|bIl0&X`HU><{ z|H`1)uBt<6=s75|9JYIg=W17^0oy#|stDg)k{ZzG8COO4<`Tt#@t%#>23TlUR5rh9@7?Ei{6miO~B6)^XrUmKt^A5W$-q7$p!OSpTX-T>dcp9@dtP^N2Nbqjp4&WKgZGxD>A1HH+G@u>$u0`Wd zkj=Jhh`9r^NZTcee&9pQipd!PqF)L;i&>!O%E706=lbLENoqk3iIp_%s{`EWnb02K z)Bppf0DqSt{DHE-nWINi?U*&Rt%z*JEJ~8wG4?L3V(MeR>59*aVDwj+f{W<%(h#h)OSg$>U)?+ zj`vZmRg}Ad2Y{(%iVqZj4rtLQj|6UD@E5slDWJVAgS7K8<{Wi+G96Mi+$2sH<{YsX zvw`+C1+;N3W@GMsn3-?h&zfVFO1w+*P)7Ye;l!v7NQ~Nm#HbBOjM{+2s0~Pr+JMBU f4M>dIfW-I@3IzL3vvlu100000NkvXXu0mjfzLx5O 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 f627d2b3bbc84de3c5ac7030d260b8d20b9b2ebc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1955 zcmV;U2VD4xP)t<88FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H12OvpAK~#90?VVeQRaG3ue{04I`A}J2a=Z+h6=-IPh#2{h=z|DG z4~h`QKoLYlW*HfhQ4c|cNDo37%sgsjXfeuY7*dvEmzNAi2zyYwn72wZnpr*UHFP+g zbJn-^+57C{IqQQ3!<@ao_5Ihk_u99${@YkoCoBBW8@50jvQY z29{C(PI47E2TG<24X*mOcX+R$^)C8UE zD?>mp(A9{($^9g3j9CVJ08GfWq3rQ9@QAhcXr7JadLw3xd4gno&8zdja3v2+1`Zoz zdUE1VkkgRU32Y<%FmPGeW(Zj1iZZk*oYl%Up!0?}2LQJK`%?rt1bhfQ2pkr+-&xY; zPGS24z(YVk$^Ag6z5!r-QyT!x1WpD9_6P6|Fxzn~66#z6CY7{1$^Fk)Z=TzJF7O`k zSD@Cx5uHtEfZOxG4-DviU}721E}`Cr3hi$Y>h+b?nMU|!@nDumLROvMN!YM+0a#yw zXBO~}r|wB$bcMR3@gIGE1Jf$heG2$JK>q+4ofEP$;OzkM{ZzTeCK-x+^;g!=u~Ml2 zR+YN5fFl8MY-9r)8zf1jXdpNHczaC4Qq9l+}W@$?jG0Pp~CT@?GQ zhwBxA!~5Wjz7zNlxX0%n9^#-RW~Hi)aM0KJ zpx+7aty4@pLh3VhTFOL{l_2rag@$aWr8uHolUB4Cjw+oGVS98OhIGh%6%EJ-*&EqKG;&%Y<3t=1L_8M2x7EY-8!$G7&atj;R!fwWi~#vA zQF1~S3qmdfGetgSk>0u#I@ftPLl)`XiI&VG-$i;Qg7yevK10kXf`6InKnjfm9!_L`-@qP}<=vrN^AnuE4bd#lK&EY$7}(AO>KQx|H#Da6^>h1yP7uOQ3<&kI~isshPO z$M>qh=QR{~S&XqabwAc&Ko`m52}R%w0-rBg$7zI>_av9rr#)OsvJN$4N-@bgoXDl< z>1wLx3JM5#g`jDLz~f6-qa^#V6q&B3L~KaHrY%%SIfIt4sbA33@8R$zYQ-$_73H4etZDmP7vs0LG4R{y0$8ji$Td$NFM=}juNhi~~ ziCfE#gplEAqzsc}JY0sBy11{TO(lI=>!&U*>5xVw?;Zx0xT17udnE5JGolCmJ|Rsx z?n++#*U8{gvsP)u2OO4?JiMEnYeU)N#{Yl=fD2$D$v8cncY7INm_+NsygMJrcL=I5 zB+Ws4lwDM5&5!t0Fk&&YUzf)NcU7}0=&5e+C9{{nGWEe91`;AQ{-002ovPDHLkV1ig?e?b5M 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 35bb1fd3b78bb1f3821c665ee35b6a1602d2a976..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1916 zcmaJ?`8U*y0{_m8VT@szrV%X|Tgtxg4Q4Xd>tZHF^_^=e6&Xf&S!R$uVbI*e(uCBvvEBLKkc^xuRju#fKsK!HW^VR>B+Vns2+1A(Zh zDBa-DixK{eut43*;pd7LtQ7%3eA9_+=N(=Awb(a}PQ!O5Tzm}vnG&O-qpBkab3OPR z$L)ErJWL_pIb-ac-leNZ`R?&oK3>He+nsC^Ap7nvW+~4;pwM_sxl}7x#_PRdq;VuG zh>qRLiqE#KEtJVR$LIFEVc>U17Dr4+H{0?qt`<_#m}uLkNnbEA9Ch zTMqgJ4IF;WoHwtB^JXX337~80$TfG5jPw@#hU|GEHTJ3yy^k>&g{vo=2mTSg7idH` z>aoK8+hQc@#|@UQ4P!*dGFw8SpCmWCqNqRGFU0su-6^%UW#zrVeefc2JE!Hx?D@oH zBUv6IR(gA6rb!&+pMbF!AWm0nJl~M@;7vO7jB~o)LlaVn#E2;SprF0CH~2%@p+;@i zOR1@%+JeD01IvnmVgMNgph`7j{==}W4HvA9_%GqaUjL_R-qmKWb z{Vxu;1sA=?h|&nUVBY#`Upn5n5UfjKg`=Fv8YOvrTngc?S7bpQ4 zl%g_M@g{uh4IMWKQFUr$;2IM-8md520xQ$JGpT2v3r$^J?k|=Z16*39TTQjUOk`^< zk9K~G^N3V|FXu~3`a3_LWlz%AL8hR^O1wd`@tpv;YM8T1uZI2b9ZzcSrC-IYGWePP z%gwl*CBe`WWRqqSHgo@zfr$R^&9G0(&|54GNSIs)SW_L z+-CdpfFU$u_M7!hMh@^jLJ(H ze&gvKiTL&h?p(d_`b~`^^rf zBSe?i40QhizdI`f7G>TsoYB15VfvfhQNIHPTAKP>$vA&iXp495eX&;!ZRnZq!8QVk zX2zH`v8eD_7~_QK+%=nN;NPtNV8~#3D^RnBoPB8Pu(~F5ZPhhk|7j~H&x~m7==^LW z#-~zPAi6^J%(@6KMM@HyB~U%GZC`toDFvMv`)&)@GRS6_o>s3-6y|Llb)vmX z+LW0+I-=~H({7uGl~J7tkRa~mEwB2Cc1pu5B=_B}dK1T*+5TsJmGJKDAar=bOGM8N z4@$3_PD+&vJC#aI;xuzAq2BzFhAtgQpZ0F5+M!}Ptt)Io-IWq%7Q`_{t+e+jkL(zA z$=hJtKrACDH&bO(4O~}m=|^;;j^#7 z7T@B)K`tawvW4lMH|AnDiW1S~x6z`x=lx!()wBweuVtply+oov+f4tgTcKhRXU_XO zbu8P{%E+BIQavjsQc-Lv|EP&n3>hoM18VF+3$eF%m@SLn$pbg;;d9Y)F)|z1=GA^Ff%f;gSV}EDqss#{ z&=>OJt@wv{pPPFWZr;uU)PR(~@r)9xQkU=?rIhdHjqbQ5Q zq{-9l^9^kwyv2AylekYx%&SYNgJ}YQ_)=FGEU1ku?_z)@RY5^F<`j^!xy~Y| zbTGTx8n$}9=4?Qmf#YEQQCSS*ml6>(FSJEV-%5j9 zW9|Qq+Bn`TK9vV`x@;cFlEuR0H&I74+;|Swg^a!#QBSuPJ^E-zI#m84xq(>u{cZS!70u>z*{yVK43u@Hxve!eq?LH;oL~$qA+JlMz0le{IRsaA1 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 9b3ee857292349751b65645d921b27c0020f19af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1498 zcma)+dpML?9LL{x#*8sogB3fjgIKKVRFXS0n36QKZe7M;WY=ZZ(5PI-P>s~E(Pc0BknfA}aQ0R#+%A!Xhp{vT()$4*;kD3dz+!>GJ!c&TJjBMz3?8msiP& zdHxn|pdp&?Lds+wJH;&`@d*1|=-SFLYAxf=wUn?hthtKyCjCH+j>qc-cWNd_JsVeR z_b|6O9oZtOd+w;p&0JYNIx1$bd`P8LhIJA}$+7G)fq0nC;I&0hwSuJ~hA#16iB2}w zw9FeR4{2E;qjUN<0nxy-ba}{x7;+Y4uB?=qPkqC&{c zJgVMmFnfc>;M45&)>fHlz&JgKxR#iY6iu^e&U0do7w7rBtYRb3_`$S)*WX6~^>pNJ zzAm2eqeCy707X=OT2J&WB0_4k$iXuuuv+o^T@ZfmLgXAMn%*(z>YW!q*aVb&w!g`N zdZDw$RgI125Zym_-?N&lr3>`~O~5YID;N3OP=Tq;bH*4s`1jplET145(y;M+ECeji z5F{V0!-b{0LE%)(N3U8*s$(hN1a51naose#nwTh;bV<`+-hNkN3wdiFtJXsD?&NIR z#FQfkQ+i_spu6S%mq8W|KIn4t@X6b=1~SbmxrBn3Giw;|q3lM0bRC-JoMUi7Orb6( z;K8)+9^RUQuPtoFd|0^I1AebTtwd03ai$7gr62Nl?Um%(c-W<^@HESe$ zcV-`7-|;PMYSqxw%15Rh5l`g#8-3-St^EWtyC!1h>kN@n+E5Kq3#nIO{i77?R&-) z;s=Dlb;fcCQz*lfE<2?@Om3+0r_>U`h8kDQ${f@#UyDQAMXecG9fJ%xc}=LK&88Lk zRL;c>%ZzuLta0l{k;~|ava;-+;`>mQpxS=&a!D|5WOz&1_lq$`IG7A~31u%*j{sLo z?kJU9_q^+5TJ}RewP}$=HF+HcYr;@c!TDUN9<4IdnsrygAnnMM46~FJy>Zte&LE^; z8_u|;dvDZzsr{t^M2K|E=svDwC5Lbj*)a1aS(3p!!x>>b1ke0Y?;()R-S8y~M>EiK z-_j*sQAX@n+K3_(P)FF1h<~N%&J&sVX7u#(Hjn5mHCm^j`k0%(6aFSvt6I}=BowuM zQac&5&hSk!3WWdhDS6?}?J?4PVt)cru8-DTd|a`3SVzxJ<*WIGxR$)r#=_P3y7V6g MC}bZ}jT?>oFE#Cv%>V!Z 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 19a3421bf856bc04b79db4cd942e1b4395d25685..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3179 zcmZ`+X*kqv`~J;hW{hPFvOTtlY)NDpdl_WSHqsNBFjR(yvGv$ygcu@)lBJL^_H1SS zA(>Jr%Zy0lNqUm)$u_+uujhULpWfs6-^X#?*KuAS?)$vY>-uos=`PN;LTDK@002UE zr|@q3nf6bh5c~Up`Gw*A1gD;~^FZydI8;E|KIW&KI!grrySM)Yq{^DX+b>E-SbIgd zUkQwez7S3VqNAg=FNFqE{Vz~R+E>DZ3Rg{K06^d`JNyZcn4*>9tF;P*149;-xk!?_ z_(gbG$Tim6GE2cr5CL^jOM{fOBnWyad~)Zi#POi)ZBMYH+u2aY=Pm1o$6ZlP9b%)`}2=ZYGO1_nQ3nuE1ma_ zp4Acoy@)FYes?}h%OB@|@acbGrt^v=A&nIXMuD6JN$_O?dxqIzoN}L_tQ?}@g*QMx zg1Xb*548TKle26#hv4dSRk(u`GogG?gzEL{3vp^6{^o6vo*RoMjwNn^Evk)Y&lV>h#Cpt5F(k?VZu04>cC779^Sp(-88 zpx^e0JfVTSbpwK5FeT@js^?+!iK043TdYMj)*$$R<5QnTYg@SgqQbUMd7UAZ=Ism6 z286=0n6+bpVVHFlLVPb1swqZGBV%3-N(37tf*_O@rglSFqdi1MwTViblX z987wPq?gXzcaZ!kr3#~3&E(lvLs77vnZG)AP4^iIgm}r2b^!JpAs&MwK9ySGdJpKo zGE+dBzJk>oE;_765|IkzExsvps#IwfzI*<8W@#tTKl{slp>W=v-0)tFXiH{~dWMcv z%=?hr#|k-t#&cV(O1wBDoetmP z5-s9R)J_ZznHboY+8KbFF$0^tFgeEHH6ILIAvU#YNQH0s?pX&2@R8Pz_FUy=lH`zG z-q&b5=a0gMNSnLdBvLnD<_4p1MoMvc0as2-CxF|`4g>$IX5ci)2O3z#dC=MP_NMyp z#R-X!wn6yp&RP^WBDmZ23$w1r)gbuXezWF8|0F*1GNF}Oa+dq+hEe>Fojx0;b#DBb zk;g+Y_NMwCf&A@@H&=vM2;F+8$}uwJ#OdcYY3Jh1E8lmb+a`^P3U@d-equr&TAe~V zp`Z#eiHW>nmD>!{seW)nQ|?`Gk8M2qG(4qlM*cS=3$^fOrncuKNjO39%!n?*$9v4N zv;!fG3h_l{7g#o&WxkvOgK1}c1lr>dLXyQy&R;h$?K$AoS5rT3B^d7Bf?O{uca8-0 z_(~#=h+mt9qLBL8>TWR>QPdVfwCUGB9Mi0|8Jj>QH`aw3ZF??qsJT9AQ0c?#`5?Pl zrzrD1{kZIl4Sfk`8X{WGn&1PpqKvDC-Ykm}%NALuZ?+@v()Ygql;@&?3L_U#an~>s z8PZonUun{AQ|AoDuK<`lVtc4*^5kTp_p$T@cyf`c!{2f*>XE{(lh@@!rX;X96i>4; zy?mO$Q9iB_)Bh&GBhwAd9nEUHmlbp_R|6O&)DFpo=*x|^r7>IsA<5EU`kPh3k>3B-?n?RP zrQek&cCtn>wufmqV%i4Pv^)zeit?HU5)U!ji&pfd*Vw_fxbM1iqW^tZiE>=}KzXhi(DG4{MTu!YQEdhm?n{&94CLF7-2RU$u|j(fG<5yg2e8uD zNJ1NjG;Ern>*ifeI7(LYYt=9bo#)em6MuJbLgt}b%6{EAm?qt4rp>|y|J?sOpBdXL z-+yR87pc!gqEbH({-H1EPggLRAww+&6X_x6mkoZHI@}}`&HuMEl$#? z1-_x#B-`(5l2H~55PUGEqOrae32*Np+t)IcdiaOJ^di5sAS?_{paHzsUR*UCXChb-5?+V{w{2(AdBG zap2(^arm}Ux-@9b=@gyvl-=xM|9Sxzk@;AisU)>tY|Lp+$g#4j<&XN6tU`SE_84~t z#)e}2VD%n-tBFUnybZ5E$KlGGzc#*_(xu(-X-pApNd115|HHo&CKS254Y!vRfrG18 zf7v2p>SPQ~xNUY@p?}J8Ce8tUL%kDp!8jiI-uPG_HK@AH~ah7{%__Wt@pv%Q`{Rhve9RYp9sWZnCg28NB zF>4R{a#%wIY!^fB5r~;*;%6m%JhoumPHa0l{WWs4eFL$*@vTBObhh|oiPviNgUnJR zy_QytN}N^b!OnXhkWn(vMvTa1>R@4uenjyp{DcMcvUB`?;I@1*h9m@ z_c2X*wHHpeq_NcMPWWBIK2_k6Q>^O|!UV!jyi^6b%Vlzi-f-*4+UrFSqxa59{wMg2 zF>F$o$+fbB0xpv#(!`sFs369vRYPADuy;nbNPw`l_&D=U$Cf>z0bCqwri5@z-!$bE z61EPLBwPmPI-6Pf0`15r?)a|o&xXaejb?lmZXJL}q#OmhJxnx;0`@J4wbLx$0xN^F zdT}&a7UuS1^;~P_0i=J1`aqL91+soC&09+;HgCy$HwJFxLeP}HnEy?WGS-i`@jutu zgT4AyX<7Dt;r#E6x;Lkv-O_Xf7HgK+yIpd!*za=dpF|x;`=(CW?=9}!inO>dFds(g zNb5>EB2S9nS(l%eVWe2c)j%Yxm$I4K$6r7M3EkpG{Dtt#Cc#ZBYk5coQGURZaAh!X zJip>wDWVG|`-o}yC|vOiR*MiX&XMF<*;|N)VHz8l=6BzwF9fZ7M5Z(4F|rxO1~Ol| zgo_?9ZEKc-tBdWlY3Dt*LW#QD@@8w#CL)P29RZFzVeQn4KKwg4|9`mQ%TkFi-dT%W+y58I2K~#90?VW9`6~!6He-BW3DOXW3QbGesjh9L=+8TK)B4}y^ z--2js8x15?(-MtFYpB%HPYwMnccIy=kENI$-T*$dEVx~vpc&p&xop$P+trw+kk{}kqA(8Bm&eN zi2yZ6B0$ZN2vBn*0@NId05wMQlt0A0Yj{%@E6`J;a2n5v#E!_J~1 zgY8wYh@1~h2POmEeg2L!h&c-!0QLg!__7DsrK+FgYH-Qucmjw>7cd)`3(NzqpnbQt zlfX`3n=iXMeWd6}0*J^6;1=K>;0DUOm7M@K0nY;4RP}60c0{A22p}RefW^R_xchmP za0qxF=uy?T%Qhwy9YFvQSpX~tW|VD902J5)tW?z>@mH2?PAs3I03z}Q;1877`2gSbr7TPWwu{KJ(kxKb!V9nn z_!n-@oJTndoHn-E4Gi^Vx3TRf!0{aISD*(NR(M|&-0=dezZ}g$E z{ss&mz}Dfuj%74yc%=g80ncRgeJgE#Fl=|{FCu<8JOq5Xh5VI9nYRYme5+CB6D{N| z#J&BCB|%R|5&-uFFLtW;Ghj>$`CZtiVy_x(f7KxOFfg)(yo-UCGmv0eM-jj~{C=df z4+ZLY#31twFxg=LWZx!K{*x+^o>YI9}Lv-DQpi}nMiBxFt{4`Ti@By4D+XtjfW+ zRR(=4BXzuDjNNJAib@awx2C}QIrRuIE<%@e_|H6b!fi7P)wefN$2jaSi(an^0Tvkz zSKzitU6$d0KgN4<6P@B^h3cIespqzcIL^=NUmh!hmK*D@Rn?awb=_&GyFq*+QFH@QteW^@z2AMgZliHKYhqi6t`T`grxFjs09Sz2EOPba+d zcL1x6@*80)@OWU|U*Hliv=fVPdjUi61K_P3bUsK}hP2_h=YZda3bqp9^M<;=Qq`Xu z<^LZ<Z&nVD&s6e0ouP z0(gYRbfW)Q7X-@)VEOp-Pea{SNCa>Qjp-1JUJC(2R>Sn-SnDdm2zBNoRsBzndK<#) z2wm-yCjl%mJvX#sy9n^1p>8XL2C;~%->NH50$5_&76JqdrJNC9uoIx8Mu0>Bhe1Vv zgh2=k0W8a$%V7RRIqJ+yR~aKA82Ty^AP$KDEs*De(L?|TOH4~AKp7*zxgyf`rD|SB z=tI_^8Uc>sasY(jVuRcey`d;#6tL*XlK`PHJ!{b$OaQ;y^gv+ssiATMkO<%q8q))o zmBH3&;NHM`V@2e%M%m3!djj}ajt#6H+DCw$0Ph&;=5^Lz?FoP_mXM+#3Qbh7N*!l)TT}vOCmxYhOC1?&_aF9?a%*(<} zw*W7=v?I z*zWrv9ywec)+5{$xV->f?~~tw5tc(RV-JkDJtB^o4~)obh_y@rF2tC%@z|0(c zn`zJ&dt}Ub>?f|}a%4@H1;Eo{JUBq@w}G!%6Up~rF?I7*9yom=P{>1BlKi#ay#@rbxp>)1H&$obgL*y}A18}(Rk z1NUa|$#N2E%)ybC1F(BbH4l#T4lRywNP$O;de$3czm>tCw+wo$N5?)E8LP#)_F107 zyki?89Sc516#LBMx(Gk6GwQh3AUpO1@oFC)?VTHgJL){f!N&r1SdWk29O1`igM7>J z@yjD)vyVp)y}H*#4S9?qOQYG;sU+Ud|AYpo^kX1n`dcWN`W)z|Ae>uQbYR z`34gE8@VT1$Xkg0T;RT?r>%X|W&#WYwq|f%amT54HIY9aw-!EB*8YL6R@}UQzd_!| zz=cg@od-OV(f6&uu#P2w55y>3>i8^y*aysMB7dDx)`|gaU15~8>}D12DHI5xD{vteIuok`vwlma6LR zvJL5v%8USgp{l!8br!Gy+rERUVLPxuRcBSP^P!?6K!1qH4BWKwW~#pzQGqSMO3k`y zqX2XS0eay#q%HxzM)kI)AHwaJ?$IoJ^$JEu5ug`7Kfzq!Zru0wveMO@!0iQi7TBhd zTeQnWM-refe9oe2I6}bHkG-xHVxfJ)g zH}s+REG`4eUR>(ccW|F*cd0QCU)3?_2+$0Eq__Zg*hAj}ZZCh|a;(#@>`>H(011sz z$@c&W#UT-(=12snIT8VCjzoZ(BN3qHNCc=k5&>$CM1Y#(zrJ=t|6^{rC;$Ke07*qo IM6N<$f^b&a=>Px# 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 0710a12f1c5101686e2c6096fe2d0256db133738..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2666 zcmb7G`9IT-AAfJgW;rTniZXbzpuyR`Fg%y&*$Uy+v|sChVyAVLAVqg002RZy|v3e6aE0k zx8Hls0vP)QxnhNJgY6d{7IW&E+<+YA6dxvvce3wF|EOtgzqW> zU>*T#_pZnGyCAPHqJ~hC2%h`&iv9Q9Vu93so6|cRv2F=C8DCcit8lz~c@e+U{4vm%$Vs6XGZe zOFJavxy0}BKY60kN$9yA zapGyf47!XWL#3w2hAupECGgYqSbR)*E-^c&DshdcBYE$?XI3Qa;TA7ut4pyLggFZC28SQ+|{(B#Kf$aV_!XA^591>+m{@>_eQ_pk%Mlbq$EnfpzQ~y2}RE3_}NRTel;PQxsBSpgDS^p#|x8Gm8&XS zbM=27Z$#!hO^PHJCE;R1k7JS`YGd~)~q}1Z@(g=0jMLD*IY-Dm;chA_EITJ4&fQW#e8vMaor^J%z}_-HOTrGeWhXP zxPL~iVcJm?DkY~6qYVjxO~yIrdLjOGwVE*#t9_qqWL5{XkQIg3o3l5E zKW{y0)&|r0p6{)2aYq##p{vCWZ26_I+56esSK07c8%(V7<~%bW2S?Nn86TKEhu^2Xa-+Q-C;`n6FGreX2OTDm!g-!UkldFQ zNh>@zN^uWr$I&0m$H-l6Wnoy|ZT%9qenPjd>LLOnvCSpc_A8yA55)&$L8TQI*yA3u zAk&O2AN4E{b(0GvoF^tKggOZ6UFNdA9vP$qL=NkT?BAGPU{;?Mx21#0QXVc^KZLm{ z-9A{~7O-{#JwK|K>Q-#Q9lG?Qbgem-~fG5_*B* z(WDoJ{BSH3z8Y2@eD@K7OpFOH)vfJPx?ulGuSZmyme~Bx^y;H&%$Ckry=Zgq;)}$N z86YOs!CLC&k{B%u7+QwuIIk~N@`z)&zAm~ zGdEw9G=LwPe^&cgv4N#9I>#D73QI{6=W+0|YR z&}F9DWg2i$=bzWUpxvaMoz8Zz*r0jZpn+tz{j`$7vMcrd3p@N|lNveeGF~mJ2ZmwK zE8e7wx>#=ifFoWFJ*NZL9<+EQBE54RpPPzIxVoXkhj#8t%Q<-I+cGiK)fU~kwGTGe z`w5*S(&qO)PSI1WGx7KQ6s?a|NA&>;F2=tcGIO`JndX%bg*Ey4Ywq1nek)Pxx0PK! zsux%Nby7t#DuUVLej?Qeq@>M5PcZZml+`CcX4E%4OsBOG?b@Z&la2(rdlUiUvuz$W zOuhbdoZGhQ)&OQp zop`{<8P^$kMzs9xvH0Sj_=2nLAZXHPS_ocEp)rdz%=6+L4y`>Tfb^EAUu%1sygkdL z`FaPB*S8)ma}K20p&c;Gfmj(V_Xz8w)1iI_HT8C~O1%b(TuPea^T~U*a+Y+Drdrsb zVX2PpsCaT#dxTzB@>s(UNe;F$0V(J4U3)Z_e){_OGQwPQcz38d~0$ z4}YXnG2Y|{7}1(QXXZmBEVPMfJcR_|kK(1*(Fnvt*If@m{GD6s)A1o1pmpmu%IG)R zaLwO?J;to-i62gyFsttS>YihU%-T^metQ+?P}iGI{k!$rl)pO9lDR>3nrUQhRYd2^ zv}d^*Oo>@AC0SBWBvQG`s$of46?5mi4a>j4bQ6P-s840ZZh~ z$Ek3{qBI(x<-2N|Ln0io7!B&0 zxiBK2O-OQyTAS{@V9 zm974sX9&tMB)_exQqQ`3KV7&t=wMZ5)?S#q5a1YPBqXOdsu|M(PKBn}Uzcb?hTvUn zMZE%6BsrK}bymIVXvenl3)wn5h%#d&r&*otJoN1@&qT_BJ} - - - - -