From 4378f8f871f6a47321352f90a59cfaad7f52279b Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 9 May 2015 12:24:48 +0200 Subject: linked-ids: code cleanup, handle all lint errors --- .../keychain/linked/LinkedCookieResource.java | 282 +++++++++++++++++++++ .../keychain/linked/LinkedIdentity.java | 32 +++ .../keychain/linked/LinkedResource.java | 25 ++ .../keychain/linked/RawLinkedIdentity.java | 79 ++++++ .../keychain/linked/resources/DnsResource.java | 130 ++++++++++ .../linked/resources/GenericHttpsResource.java | 94 +++++++ .../keychain/linked/resources/GithubResource.java | 217 ++++++++++++++++ .../keychain/linked/resources/TwitterResource.java | 244 ++++++++++++++++++ .../keychain/pgp/linked/LinkedCookieResource.java | 196 -------------- .../keychain/pgp/linked/LinkedIdentity.java | 88 ------- .../keychain/pgp/linked/LinkedResource.java | 122 --------- .../keychain/pgp/linked/RawLinkedIdentity.java | 41 --- .../keychain/pgp/linked/resources/DnsResource.java | 127 ---------- .../pgp/linked/resources/GenericHttpsResource.java | 98 ------- .../pgp/linked/resources/GithubResource.java | 215 ---------------- .../pgp/linked/resources/TwitterResource.java | 241 ------------------ .../keychain/ui/adapter/LinkedIdsAdapter.java | 4 +- .../ui/linked/LinkedIdCreateDnsStep1Fragment.java | 7 +- .../ui/linked/LinkedIdCreateDnsStep2Fragment.java | 41 ++- .../ui/linked/LinkedIdCreateFinalFragment.java | 4 +- .../linked/LinkedIdCreateGithubStep1Fragment.java | 8 +- .../linked/LinkedIdCreateGithubStep2Fragment.java | 34 +-- .../linked/LinkedIdCreateHttpsStep1Fragment.java | 3 +- .../linked/LinkedIdCreateHttpsStep2Fragment.java | 33 +-- .../linked/LinkedIdCreateTwitterStep1Fragment.java | 6 +- .../linked/LinkedIdCreateTwitterStep2Fragment.java | 43 ++-- .../keychain/ui/linked/LinkedIdViewFragment.java | 8 +- .../keychain/ui/linked/LinkedIdWizard.java | 1 - 28 files changed, 1197 insertions(+), 1226 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedCookieResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedIdentity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/RawLinkedIdentity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/DnsResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/GenericHttpsResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/GithubResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/TwitterResource.java diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedCookieResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedCookieResource.java new file mode 100644 index 000000000..d09854583 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedCookieResource.java @@ -0,0 +1,282 @@ +package org.sufficientlysecure.keychain.linked; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.BasicHttpParams; +import org.json.JSONException; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.linked.resources.DnsResource; +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.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +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 LinkedCookieResource 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 LinkedCookieResource(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 LinkedCookieResource fromUri (URI uri) { + + if (!"openpgpid+cookie".equals(uri.getScheme())) { + Log.e(Constants.TAG, "unknown uri scheme in (suspected) linked id packet"); + return null; + } + + if (!uri.isOpaque()) { + Log.e(Constants.TAG, "non-opaque uri in (suspected) linked id packet"); + return null; + } + + String specific = uri.getSchemeSpecificPart(); + if (!specific.contains("@")) { + Log.e(Constants.TAG, "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 LinkedCookieResource findResourceType (Set flags, + HashMap params, + URI subUri) { + + LinkedCookieResource 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+cookie:"); + + // 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(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(log, 1); + } catch (HttpStatusException e) { + // log verbose output to logcat + Log.e(Constants.TAG, "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) { + Log.e(Constants.TAG, "io error", e); + log.add(LogType.MSG_LV_FETCH_ERROR_IO, 2); + } catch (JSONException e) { + Log.e(Constants.TAG, "json error", e); + 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); + } + + Log.d(Constants.TAG, "Resource data: '" + res + "'"); + + return verifyString(log, 1, res, fingerprint); + + } + + protected abstract String fetchResource (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); + + } + + @SuppressWarnings("deprecation") // HttpRequestBase is deprecated + public static String getResponseBody(HttpRequestBase request) throws IOException, HttpStatusException { + StringBuilder sb = new StringBuilder(); + + request.setHeader("User-Agent", "Open Keychain"); + + DefaultHttpClient httpClient = new DefaultHttpClient(new BasicHttpParams()); + HttpResponse response = httpClient.execute(request); + int statusCode = response.getStatusLine().getStatusCode(); + String reason = response.getStatusLine().getReasonPhrase(); + + if (statusCode != 200) { + throw new HttpStatusException(statusCode, reason); + } + + HttpEntity entity = response.getEntity(); + InputStream inputStream = entity.getContent(); + + BufferedReader bReader = new BufferedReader( + new InputStreamReader(inputStream, "UTF-8"), 8); + String line; + while ((line = bReader.readLine()) != null) { + sb.append(line); + } + + return sb.toString(); + } + + 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/LinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedIdentity.java new file mode 100644 index 000000000..af19aefdd --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedIdentity.java @@ -0,0 +1,32 @@ +package org.sufficientlysecure.keychain.linked; + +import java.net.URI; + +import android.content.Context; +import android.support.annotation.DrawableRes; + +public class LinkedIdentity extends RawLinkedIdentity { + + public final LinkedResource mResource; + + protected LinkedIdentity(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 new file mode 100644 index 000000000..dffeea65e --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedResource.java @@ -0,0 +1,25 @@ +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/RawLinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/RawLinkedIdentity.java new file mode 100644 index 000000000..0bdc1b280 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/RawLinkedIdentity.java @@ -0,0 +1,79 @@ +package org.sufficientlysecure.keychain.linked; + +import org.spongycastle.util.Strings; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.util.Log; + +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 RawLinkedIdentity { + + public final URI mUri; + + protected RawLinkedIdentity(URI uri) { + mUri = uri; + } + + public byte[] getEncoded() { + return Strings.toUTF8ByteArray(mUri.toASCIIString()); + } + + public static RawLinkedIdentity 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 RawLinkedIdentity fromSubpacketData(byte[] data) { + + try { + String uriStr = Strings.fromUTF8ByteArray(data); + URI uri = URI.create(uriStr); + + LinkedResource res = LinkedCookieResource.fromUri(uri); + if (res == null) { + return new RawLinkedIdentity(uri); + } + + return new LinkedIdentity(uri, res); + + } catch (IllegalArgumentException e) { + Log.e(Constants.TAG, "error parsing uri in (suspected) linked id packet"); + return null; + } + } + + public static RawLinkedIdentity fromResource (LinkedCookieResource res) { + return new RawLinkedIdentity(res.toUri()); + } + + + public WrappedUserAttribute toUserAttribute () { + return WrappedUserAttribute.fromSubpacket(WrappedUserAttribute.UAT_LINKED_ID, getEncoded()); + } + + public @DrawableRes int getDisplayIcon() { + return R.drawable.ic_warning_grey_24dp; + } + + public String getDisplayTitle(Context context) { + return "unknown"; + } + + 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 new file mode 100644 index 000000000..d63cafcc2 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java @@ -0,0 +1,130 @@ +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.LinkedCookieResource; +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 LinkedCookieResource { + + final static Pattern magicPattern = + Pattern.compile("openpgpid\\+cookie=([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("openpgpid+cookie=%s", + KeyFormattingUtils.convertFingerprintToHex(fingerprint)); + + } + + public static DnsResource createNew (String domain) { + HashSet flags = new HashSet<>(); + HashMap params = new HashMap<>(); + URI uri = URI.create("dns:" + domain); + 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 paramters + } + */ + + 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 (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 new file mode 100644 index 000000000..55f998952 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java @@ -0,0 +1,94 @@ +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 org.apache.http.client.methods.HttpGet; +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.LinkedCookieResource; +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 LinkedCookieResource { + + GenericHttpsResource(Set flags, HashMap params, URI uri) { + super(flags, params, uri); + } + + public static String generateText (Context context, byte[] fingerprint) { + String cookie = LinkedCookieResource.generate(fingerprint); + + return String.format(context.getResources().getString(R.string.linked_id_generic_text), + cookie, "0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprint).substring(24)); + } + + @SuppressWarnings("deprecation") // HttpGet is deprecated + @Override + protected String fetchResource (OperationLog log, int indent) throws HttpStatusException, IOException { + + log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); + HttpGet httpGet = new HttpGet(mSubUri); + return getResponseBody(httpGet); + + } + + 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 new file mode 100644 index 000000000..078328198 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java @@ -0,0 +1,217 @@ +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 org.apache.http.client.methods.HttpGet; +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.LinkedCookieResource; +import org.sufficientlysecure.keychain.util.Log; + +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 LinkedCookieResource { + + 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 cookie = LinkedCookieResource.generate(fingerprint); + + return String.format(context.getResources().getString(R.string.linked_id_github_text), cookie); + } + + @SuppressWarnings("deprecation") // HttpGet is deprecated + @Override + protected String fetchResource (OperationLog log, int indent) + throws HttpStatusException, IOException, JSONException { + + log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); + indent += 1; + + HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + mGistId); + String response = getResponseBody(httpGet); + + 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") + public static GithubResource searchInGithubStream(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 cookie pattern! This is a programming error, please report."); + } + needle = matcher.group(); + + try { + + JSONArray array; { + HttpGet httpGet = + new HttpGet("https://api.github.com/users/" + screenName + "/gists"); + httpGet.setHeader("Content-Type", "application/json"); + httpGet.setHeader("User-Agent", "OpenKeychain"); + + String response = getResponseBody(httpGet); + 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"); + HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + id); + httpGet.setHeader("User-Agent", "OpenKeychain"); + + JSONObject gistObj = new JSONObject(getResponseBody(httpGet)); + 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 + Log.e(Constants.TAG, "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) { + Log.e(Constants.TAG, "io error", e); + log.add(LogType.MSG_LV_FETCH_ERROR_IO, 2); + } catch (JSONException e) { + Log.e(Constants.TAG, "json error", e); + 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 new file mode 100644 index 000000000..c981e2bd6 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java @@ -0,0 +1,244 @@ +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 org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +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.LinkedCookieResource; + +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 LinkedCookieResource { + + 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(OperationLog log, int indent) throws IOException, HttpStatusException, + JSONException { + + String authToken; + try { + authToken = getAuthToken(); + } catch (IOException | HttpStatusException | JSONException e) { + log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, indent); + return null; + } + + HttpGet httpGet = + new HttpGet("https://api.twitter.com/1.1/statuses/show.json" + + "?id=" + mTweetId + + "&include_entities=false"); + + // construct a normal HTTPS request and include an Authorization + // header with the value of Bearer <> + httpGet.setHeader("Authorization", "Bearer " + authToken); + httpGet.setHeader("Content-Type", "application/json"); + + try { + String response = getResponseBody(httpGet); + 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( + String screenName, String needle, OperationLog log) { + + String authToken; + try { + authToken = getAuthToken(); + } catch (IOException | HttpStatusException | JSONException e) { + log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, 1); + return null; + } + + HttpGet httpGet = + new HttpGet("https://api.twitter.com/1.1/statuses/user_timeline.json" + + "?screen_name=" + screenName + + "&count=15" + + "&include_rts=false" + + "&trim_user=true" + + "&exclude_replies=true"); + + // construct a normal HTTPS request and include an Authorization + // header with the value of Bearer <> + httpGet.setHeader("Authorization", "Bearer " + authToken); + httpGet.setHeader("Content-Type", "application/json"); + + try { + String response = getResponseBody(httpGet); + 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 + Log.e(Constants.TAG, "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) { + Log.e(Constants.TAG, "io error", e); + log.add(LogType.MSG_LV_FETCH_ERROR_IO, 1); + } catch (JSONException e) { + Log.e(Constants.TAG, "json error", e); + log.add(LogType.MSG_LV_FETCH_ERROR_FORMAT, 1); + } + + return null; + } + + private static String cachedAuthToken; + + @SuppressWarnings("deprecation") + private static String getAuthToken() throws IOException, HttpStatusException, JSONException { + if (cachedAuthToken != null) { + return cachedAuthToken; + } + String base64Encoded = rot13("D293FQqanH0jH29KIaWJER5DomqSGRE2Ewc1LJACn3cbD1c" + + "Fq1bmqSAQAz5MI2cIHKOuo3cPoRAQI1OyqmIVFJS6LHMXq2g6MRLkIj") + "=="; + + // Step 2: Obtain a bearer token + HttpPost httpPost = new HttpPost("https://api.twitter.com/oauth2/token"); + httpPost.setHeader("Authorization", "Basic " + base64Encoded); + httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"); + httpPost.setEntity(new StringEntity("grant_type=client_credentials")); + JSONObject rawAuthorization = new JSONObject(getResponseBody(httpPost)); + + // 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/pgp/linked/LinkedCookieResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java deleted file mode 100644 index 37265b2b5..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java +++ /dev/null @@ -1,196 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.linked; - -import android.content.Context; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.params.BasicHttpParams; -import org.json.JSONException; -import org.sufficientlysecure.keychain.Constants; -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.util.Log; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.MalformedURLException; -import java.net.URI; -import java.util.HashMap; -import java.util.Map.Entry; -import java.util.Set; -import java.util.regex.Matcher; - -public abstract class LinkedCookieResource extends LinkedResource { - - protected LinkedCookieResource(Set flags, HashMap params, URI uri) { - super(flags, params, uri); - } - - public URI toUri () { - - StringBuilder b = new StringBuilder(); - b.append("openpgpid+cookie:"); - - // 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 URI getSubUri () { - return mSubUri; - } - - public static String generate (Context context, byte[] fingerprint) { - return String.format("[Verifying my OpenPGP key: openpgp4fpr:%s]", - KeyFormattingUtils.convertFingerprintToHex(fingerprint)); - } - - public static String generatePreview () { - return "[Verifying my OpenPGP key: openpgp4fpr:0x…]"; - } - - public LinkedVerifyResult verify(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(log, 1); - } catch (HttpStatusException e) { - // log verbose output to logcat - Log.e(Constants.TAG, "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) { - Log.e(Constants.TAG, "io error", e); - log.add(LogType.MSG_LV_FETCH_ERROR_IO, 2); - } catch (JSONException e) { - Log.e(Constants.TAG, "json error", e); - 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); - } - - Log.d(Constants.TAG, "Resource data: '" + res + "'"); - - return verifyString(log, 1, res, fingerprint); - - } - - protected abstract String fetchResource (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); - - } - - public static String getResponseBody(HttpRequestBase request) throws IOException, HttpStatusException { - StringBuilder sb = new StringBuilder(); - - request.setHeader("User-Agent", "Open Keychain"); - - DefaultHttpClient httpClient = new DefaultHttpClient(new BasicHttpParams()); - HttpResponse response = httpClient.execute(request); - int statusCode = response.getStatusLine().getStatusCode(); - String reason = response.getStatusLine().getReasonPhrase(); - - if (statusCode != 200) { - throw new HttpStatusException(statusCode, reason); - } - - HttpEntity entity = response.getEntity(); - InputStream inputStream = entity.getContent(); - - BufferedReader bReader = new BufferedReader( - new InputStreamReader(inputStream, "UTF-8"), 8); - String line; - while ((line = bReader.readLine()) != null) { - sb.append(line); - } - - return sb.toString(); - } - - 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/pgp/linked/LinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java deleted file mode 100644 index ed3031b84..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.linked; - -import org.spongycastle.bcpg.UserAttributeSubpacket; -import org.spongycastle.util.Strings; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.util.Log; - -import java.io.IOException; -import java.net.URI; - -import android.content.Context; -import android.support.annotation.DrawableRes; - - -public class LinkedIdentity extends RawLinkedIdentity { - - public final LinkedResource mResource; - - protected LinkedIdentity(URI uri, LinkedResource resource) { - super(uri); - if (resource == null) { - throw new AssertionError("resource must not be null in a LinkedIdentity!"); - } - mResource = resource; - } - - public static RawLinkedIdentity 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"); - } - - /** This method parses a linked id from a UserAttributeSubpacket, or returns null if the - * subpacket can not be parsed as a valid linked id. - */ - static RawLinkedIdentity fromAttributeSubpacket(UserAttributeSubpacket subpacket) { - if (subpacket.getType() != 101) { - return null; - } - - byte[] data = subpacket.getData(); - - return fromSubpacketData(data); - } - - static RawLinkedIdentity fromSubpacketData(byte[] data) { - - try { - String uriStr = Strings.fromUTF8ByteArray(data); - URI uri = URI.create(uriStr); - - LinkedResource res = LinkedResource.fromUri(uri); - if (res == null) { - return new RawLinkedIdentity(uri); - } - - return new LinkedIdentity(uri, res); - - } catch (IllegalArgumentException e) { - Log.e(Constants.TAG, "error parsing uri in (suspected) linked id packet"); - return null; - } - } - - public static RawLinkedIdentity fromResource (LinkedCookieResource res) { - return new RawLinkedIdentity(res.toUri()); - } - - - 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/pgp/linked/LinkedResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java deleted file mode 100644 index 26fc9f4cf..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.linked; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.pgp.linked.resources.DnsResource; -import org.sufficientlysecure.keychain.pgp.linked.resources.GenericHttpsResource; -import org.sufficientlysecure.keychain.pgp.linked.resources.GithubResource; -import org.sufficientlysecure.keychain.pgp.linked.resources.TwitterResource; -import org.sufficientlysecure.keychain.util.Log; - -import java.net.URI; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Pattern; - -import android.content.Context; -import android.content.Intent; -import android.support.annotation.DrawableRes; -import android.support.annotation.StringRes; - - -public abstract class 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 LinkedResource(Set flags, HashMap params, URI uri) { - mFlags = flags; - mParams = params; - mSubUri = uri; - } - - public Set getFlags () { - return new HashSet<>(mFlags); - } - - public HashMap getParams () { - return new HashMap<>(mParams); - } - - protected static LinkedCookieResource fromUri (URI uri) { - - if (!"openpgpid+cookie".equals(uri.getScheme())) { - Log.e(Constants.TAG, "unknown uri scheme in (suspected) linked id packet"); - return null; - } - - if (!uri.isOpaque()) { - Log.e(Constants.TAG, "non-opaque uri in (suspected) linked id packet"); - return null; - } - - String specific = uri.getSchemeSpecificPart(); - if (!specific.contains("@")) { - Log.e(Constants.TAG, "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 LinkedCookieResource findResourceType (Set flags, - HashMap params, - URI subUri) { - - LinkedCookieResource 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 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/pgp/linked/RawLinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java deleted file mode 100644 index b3acc6790..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.linked; - -import org.spongycastle.util.Strings; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; - -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 RawLinkedIdentity { - - public final URI mUri; - - protected RawLinkedIdentity(URI uri) { - mUri = uri; - } - - public byte[] getEncoded() { - return Strings.toUTF8ByteArray(mUri.toASCIIString()); - } - - public WrappedUserAttribute toUserAttribute () { - return WrappedUserAttribute.fromSubpacket(WrappedUserAttribute.UAT_LINKED_ID, getEncoded()); - } - - public @DrawableRes int getDisplayIcon() { - return R.drawable.ic_warning_grey_24dp; - } - - public String getDisplayTitle(Context context) { - return "unknown"; - } - - public String getDisplayComment(Context context) { - return null; - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/DnsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/DnsResource.java deleted file mode 100644 index 3ca71c74b..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/DnsResource.java +++ /dev/null @@ -1,127 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.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.pgp.linked.LinkedCookieResource; -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 LinkedCookieResource { - - final static Pattern magicPattern = - Pattern.compile("openpgpid\\+cookie=([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 (Context context, byte[] fingerprint) { - - return String.format("openpgpid+cookie=%s", - KeyFormattingUtils.convertFingerprintToHex(fingerprint)); - - } - - public static DnsResource createNew (String domain) { - HashSet flags = new HashSet(); - HashMap params = new HashMap(); - URI uri = URI.create("dns:" + domain); - 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) { - // TODO parse CLASS and TYPE query paramters - } - - CLASS clazz = CLASS.IN; - TYPE type = TYPE.TXT; - - return new DnsResource(flags, params, uri, fqdn, clazz, type); - } - - public String getFqdn() { - return mFqdn; - } - - @Override - protected String fetchResource (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/pgp/linked/resources/GenericHttpsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/GenericHttpsResource.java deleted file mode 100644 index 8f5c0f8c2..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/GenericHttpsResource.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.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 org.apache.http.client.methods.HttpGet; -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.pgp.linked.LinkedCookieResource; -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 LinkedCookieResource { - - GenericHttpsResource(Set flags, HashMap params, URI uri) { - super(flags, params, uri); - } - - public static String generateText (Context context, byte[] fingerprint) { - String cookie = LinkedCookieResource.generate(context, fingerprint); - - return String.format(context.getResources().getString(R.string.linked_id_generic_text), - cookie, "0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprint).substring(24)); - } - - @Override - protected String fetchResource (OperationLog log, int indent) throws HttpStatusException, IOException { - - log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); - indent += 1; - - HttpGet httpGet = new HttpGet(mSubUri); - return getResponseBody(httpGet); - - // log.add(LogType.MSG_LV_FETCH_REDIR, indent, url.toString()); - - } - - 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/pgp/linked/resources/GithubResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/GithubResource.java deleted file mode 100644 index d411395a3..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/GithubResource.java +++ /dev/null @@ -1,215 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.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 org.apache.http.client.methods.HttpGet; -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.pgp.linked.LinkedCookieResource; -import org.sufficientlysecure.keychain.util.Log; - -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 LinkedCookieResource { - - 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 cookie = LinkedCookieResource.generate(context, fingerprint); - - return String.format(context.getResources().getString(R.string.linked_id_github_text), cookie); - } - - @Override - protected String fetchResource (OperationLog log, int indent) - throws HttpStatusException, IOException, JSONException { - - log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); - indent += 1; - - HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + mGistId); - String response = getResponseBody(httpGet); - - 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; - - } - - public static GithubResource searchInGithubStream(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 cookie pattern! This is a programming error, please report."); - } - needle = matcher.group(); - - try { - - JSONArray array; { - HttpGet httpGet = - new HttpGet("https://api.github.com/users/" + screenName + "/gists"); - httpGet.setHeader("Content-Type", "application/json"); - httpGet.setHeader("User-Agent", "OpenKeychain"); - - String response = getResponseBody(httpGet); - 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"); - HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + id); - httpGet.setHeader("User-Agent", "OpenKeychain"); - - JSONObject gistObj = new JSONObject(getResponseBody(httpGet)); - 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 - Log.e(Constants.TAG, "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) { - Log.e(Constants.TAG, "io error", e); - log.add(LogType.MSG_LV_FETCH_ERROR_IO, 2); - } catch (JSONException e) { - Log.e(Constants.TAG, "json error", e); - 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/pgp/linked/resources/TwitterResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/TwitterResource.java deleted file mode 100644 index 935268da6..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/TwitterResource.java +++ /dev/null @@ -1,241 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.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 org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.entity.StringEntity; -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.pgp.linked.LinkedCookieResource; - -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 LinkedCookieResource { - - 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); - - } - - @Override - protected String fetchResource(OperationLog log, int indent) throws IOException, HttpStatusException, - JSONException { - - String authToken; - try { - authToken = getAuthToken(); - } catch (IOException | HttpStatusException | JSONException e) { - log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, indent); - return null; - } - - HttpGet httpGet = - new HttpGet("https://api.twitter.com/1.1/statuses/show.json" - + "?id=" + mTweetId - + "&include_entities=false"); - - // construct a normal HTTPS request and include an Authorization - // header with the value of Bearer <> - httpGet.setHeader("Authorization", "Bearer " + authToken); - httpGet.setHeader("Content-Type", "application/json"); - - try { - String response = getResponseBody(httpGet); - 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; - } - - public static TwitterResource searchInTwitterStream( - String screenName, String needle, OperationLog log) { - - String authToken; - try { - authToken = getAuthToken(); - } catch (IOException | HttpStatusException | JSONException e) { - log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, 1); - return null; - } - - HttpGet httpGet = - new HttpGet("https://api.twitter.com/1.1/statuses/user_timeline.json" - + "?screen_name=" + screenName - + "&count=15" - + "&include_rts=false" - + "&trim_user=true" - + "&exclude_replies=true"); - - // construct a normal HTTPS request and include an Authorization - // header with the value of Bearer <> - httpGet.setHeader("Authorization", "Bearer " + authToken); - httpGet.setHeader("Content-Type", "application/json"); - - try { - String response = getResponseBody(httpGet); - 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 - Log.e(Constants.TAG, "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) { - Log.e(Constants.TAG, "io error", e); - log.add(LogType.MSG_LV_FETCH_ERROR_IO, 1); - } catch (JSONException e) { - Log.e(Constants.TAG, "json error", e); - log.add(LogType.MSG_LV_FETCH_ERROR_FORMAT, 1); - } - - return null; - } - - private static String cachedAuthToken; - - private static String getAuthToken() throws IOException, HttpStatusException, JSONException { - if (cachedAuthToken != null) { - return cachedAuthToken; - } - String base64Encoded = rot13("D293FQqanH0jH29KIaWJER5DomqSGRE2Ewc1LJACn3cbD1c" - + "Fq1bmqSAQAz5MI2cIHKOuo3cPoRAQI1OyqmIVFJS6LHMXq2g6MRLkIj") + "=="; - - // Step 2: Obtain a bearer token - HttpPost httpPost = new HttpPost("https://api.twitter.com/oauth2/token"); - httpPost.setHeader("Authorization", "Basic " + base64Encoded); - httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"); - httpPost.setEntity(new StringEntity("grant_type=client_credentials")); - JSONObject rawAuthorization = new JSONObject(getResponseBody(httpPost)); - - // 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/ui/adapter/LinkedIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsAdapter.java index 9455f9489..bc49e9a78 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsAdapter.java @@ -33,8 +33,8 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; -import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; +import org.sufficientlysecure.keychain.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.linked.RawLinkedIdentity; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java index 6b3eef26a..8062428e3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java @@ -29,8 +29,7 @@ import android.view.ViewGroup; import android.widget.EditText; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; -import org.sufficientlysecure.keychain.pgp.linked.resources.DnsResource; +import org.sufficientlysecure.keychain.linked.resources.DnsResource; public class LinkedIdCreateDnsStep1Fragment extends Fragment { @@ -73,7 +72,7 @@ public class LinkedIdCreateDnsStep1Fragment extends Fragment { return; } - String proofText = DnsResource.generateText(getActivity(), + String proofText = DnsResource.generateText( mLinkedIdWizard.mFingerprint); LinkedIdCreateDnsStep2Fragment frag = @@ -120,8 +119,6 @@ public class LinkedIdCreateDnsStep1Fragment extends Fragment { } }); - mEditDns.setText("test.mugenguild.com"); - return view; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java index e8668dc90..e72366fe1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java @@ -19,26 +19,21 @@ package org.sufficientlysecure.keychain.ui.linked; import android.content.Intent; import android.net.Uri; -import android.os.Build; import android.os.Bundle; -import android.os.Environment; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.TextView; -import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; -import org.sufficientlysecure.keychain.pgp.linked.resources.DnsResource; +import org.sufficientlysecure.keychain.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.linked.resources.DnsResource; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; -import org.sufficientlysecure.keychain.util.FileHelper; -import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; @@ -84,22 +79,26 @@ public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); - view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofSend(); - } - }); + if (view != null) { - view.findViewById(R.id.button_save).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofToClipboard(); - } - }); + view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSend(); + } + }); - mTextView = (TextView) view.findViewById(R.id.linked_create_dns_text); - mTextView.setText(mResourceString); + view.findViewById(R.id.button_save).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofToClipboard(); + } + }); + + mTextView = (TextView) view.findViewById(R.id.linked_create_dns_text); + mTextView.setText(mResourceString); + + } return view; } 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 index ef9ae9ac4..5b3250644 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java @@ -20,8 +20,8 @@ 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.pgp.linked.LinkedCookieResource; -import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.linked.LinkedIdentity; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep1Fragment.java index 77eccf3be..b166b3e4f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep1Fragment.java @@ -17,11 +17,6 @@ package org.sufficientlysecure.keychain.ui.linked; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URL; - import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -109,11 +104,11 @@ public class LinkedIdCreateGithubStep1Fragment extends Fragment { }); mEditHandle = (EditText) view.findViewById(R.id.linked_create_github_handle); - mEditHandle.setText("Valodim"); return view; } + /* not used at this point, too much hassle private static Boolean checkHandle(String handle) { try { HttpURLConnection nection = @@ -125,5 +120,6 @@ public class LinkedIdCreateGithubStep1Fragment extends Fragment { return null; } } + */ } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep2Fragment.java index cc1052b1f..95c029738 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep2Fragment.java @@ -27,8 +27,8 @@ import android.view.ViewGroup; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; -import org.sufficientlysecure.keychain.pgp.linked.resources.GithubResource; +import org.sufficientlysecure.keychain.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.linked.resources.GithubResource; public class LinkedIdCreateGithubStep2Fragment extends LinkedIdCreateFinalFragment { @@ -65,19 +65,23 @@ public class LinkedIdCreateGithubStep2Fragment extends LinkedIdCreateFinalFragme public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); - view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofSend(); - } - }); - - view.findViewById(R.id.button_share).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofShare(); - } - }); + if (view != null) { + + view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSend(); + } + }); + + view.findViewById(R.id.button_share).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofShare(); + } + }); + + } return view; } 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 index 72669142b..5a8b4a72a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java @@ -29,8 +29,7 @@ import android.view.ViewGroup; import android.widget.EditText; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; -import org.sufficientlysecure.keychain.pgp.linked.resources.GenericHttpsResource; +import org.sufficientlysecure.keychain.linked.resources.GenericHttpsResource; public class LinkedIdCreateHttpsStep1Fragment extends Fragment { 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 index 6bb555105..2af97fe36 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java @@ -31,7 +31,7 @@ import android.widget.EditText; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.pgp.linked.resources.GenericHttpsResource; +import org.sufficientlysecure.keychain.linked.resources.GenericHttpsResource; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.util.FileHelper; @@ -95,22 +95,25 @@ public class LinkedIdCreateHttpsStep2Fragment extends LinkedIdCreateFinalFragmen public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); - view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofSend(); - } - }); + if (view != null) { - view.findViewById(R.id.button_save).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofSave(); - } - }); + view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSend(); + } + }); - mEditUri = (EditText) view.findViewById(R.id.linked_create_https_uri); - mEditUri.setText(mResourceUri.toString()); + view.findViewById(R.id.button_save).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSave(); + } + }); + + mEditUri = (EditText) view.findViewById(R.id.linked_create_https_uri); + mEditUri.setText(mResourceUri.toString()); + } return view; } 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 index 0d6d36ca4..c25f775b0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java @@ -29,10 +29,6 @@ import android.widget.EditText; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.util.Notify; -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URL; - public class LinkedIdCreateTwitterStep1Fragment extends Fragment { LinkedIdWizard mLinkedIdWizard; @@ -119,6 +115,7 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { return view; } + /* not used at this point, too many problems private static Boolean checkHandle(String handle) { try { HttpURLConnection nection = @@ -130,5 +127,6 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { 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 index 89d72faf3..ebfbfd16b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep2Fragment.java @@ -25,13 +25,12 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; -import android.widget.EditText; import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; -import org.sufficientlysecure.keychain.pgp.linked.resources.TwitterResource; +import org.sufficientlysecure.keychain.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.linked.resources.TwitterResource; public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragment { @@ -57,7 +56,7 @@ public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragm super.onCreate(savedInstanceState); mResourceString = - TwitterResource.generate(getActivity(), mLinkedIdWizard.mFingerprint); + TwitterResource.generate(mLinkedIdWizard.mFingerprint); mResourceHandle = getArguments().getString(ARG_HANDLE); @@ -67,23 +66,25 @@ public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragm public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); - view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofSend(); - } - }); - - view.findViewById(R.id.button_share).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofShare(); - } - }); - - ((TextView) view.findViewById(R.id.linked_tweet_published)).setText( - Html.fromHtml(getString(R.string.linked_create_twitter_2_3, mResourceHandle)) - ); + if (view != null) { + view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSend(); + } + }); + + view.findViewById(R.id.button_share).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofShare(); + } + }); + + ((TextView) view.findViewById(R.id.linked_tweet_published)).setText( + Html.fromHtml(getString(R.string.linked_create_twitter_2_3, mResourceHandle)) + ); + } return view; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java index f4987d137..d2ae69a24 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java @@ -33,10 +33,10 @@ import org.sufficientlysecure.keychain.Constants.key; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.CertifyResult; import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; -import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; -import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; -import org.sufficientlysecure.keychain.pgp.linked.LinkedResource; -import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; +import org.sufficientlysecure.keychain.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.linked.LinkedResource; +import org.sufficientlysecure.keychain.linked.RawLinkedIdentity; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; 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 index 6165efd90..a29f175c0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdWizard.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdWizard.java @@ -22,7 +22,6 @@ import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; -import android.support.v7.app.ActionBarActivity; import android.view.View; import android.view.inputmethod.InputMethodManager; -- cgit v1.2.3