From 5faeb5f5f060e049000e804deca5445d281f8611 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 12 Jan 2015 13:17:18 +0100 Subject: intermediate state, nothing really working yet --- .../keychain/pgp/affirmation/Affirmation.java | 157 ++++++++++++ .../pgp/affirmation/AffirmationResource.java | 43 ++++ .../pgp/affirmation/resources/DnsResouce.java | 4 + .../resources/GenericHttpsResource.java | 130 ++++++++++ .../pgp/affirmation/resources/TwitterResource.java | 4 + .../pgp/affirmation/resources/UnknownResource.java | 20 ++ .../keychain/ui/ViewKeyMainFragment.java | 19 ++ .../AffirmationCreateHttpsStep1Fragment.java | 275 +++++++++++++++++++++ .../AffirmationCreateHttpsStep2Fragment.java | 256 +++++++++++++++++++ .../ui/affirmations/AffirmationSelectFragment.java | 69 ++++++ .../ui/affirmations/AffirmationWizard.java | 98 ++++++++ .../keychain/ui/widget/HttpsPrefixedText.java | 38 +++ 12 files changed, 1113 insertions(+) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationSelectFragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationWizard.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/HttpsPrefixedText.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java new file mode 100644 index 000000000..f12ebd481 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java @@ -0,0 +1,157 @@ +package org.sufficientlysecure.keychain.pgp.affirmation; + +import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.util.Strings; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.util.Log; + +import java.net.URI; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Set; + +public class Affirmation { + + protected byte[] mData; + public final long mNonce; + public final URI mSubUri; + final Set mFlags; + final HashMap mParams; + + protected Affirmation(byte[] data, long nonce, Set flags, + HashMap params, URI subUri) { + mData = data; + mNonce = nonce; + mFlags = flags; + mParams = params; + mSubUri = subUri; + } + + Affirmation(long nonce, Set flags, + HashMap params, URI subUri) { + this(null, nonce, flags, params, subUri); + } + + public byte[] encode() { + if (mData != null) { + return mData; + } + + StringBuilder b = new StringBuilder(); + b.append("pgpid:"); + + // 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; + Iterator> it = mParams.entrySet().iterator(); + while (it.hasNext()) { + if (!first) { + b.append(";"); + } + first = false; + Entry entry = it.next(); + b.append(entry.getKey()).append("=").append(entry.getValue()); + } + } + + b.append("@"); + b.append(mSubUri); + + byte[] data = Strings.toUTF8ByteArray(b.toString()); + + byte[] result = new byte[data.length+4]; + result[0] = (byte) (mNonce >> 24 & 255); + result[1] = (byte) (mNonce >> 16 & 255); + result[2] = (byte) (mNonce >> 8 & 255); + result[3] = (byte) (mNonce & 255); + + System.arraycopy(data, 0, result, 4, result.length); + + return result; + } + + /** This method parses an affirmation from a UserAttributeSubpacket, or returns null if the + * subpacket can not be parsed as a valid affirmation. + */ + public static Affirmation parseAffirmation(UserAttributeSubpacket subpacket) { + if (subpacket.getType() != 100) { + return null; + } + + byte[] data = subpacket.getData(); + + long nonce = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + + try { + return parseUri(nonce, Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 4, data.length))); + + } catch (IllegalArgumentException e) { + Log.e(Constants.TAG, "error parsing uri in (suspected) affirmation packet"); + return null; + } + } + + public static Affirmation generateForUri(String uri) { + return parseUri(generateNonce(), uri); + } + + protected static Affirmation parseUri (long nonce, String uriString) { + URI uri = URI.create(uriString); + + if ("pgpid".equals(uri.getScheme())) { + Log.e(Constants.TAG, "unknown uri scheme in (suspected) affirmation packet"); + return null; + } + + if (!uri.isOpaque()) { + Log.e(Constants.TAG, "non-opaque uri in (suspected) affirmation packet"); + return null; + } + + String specific = uri.getSchemeSpecificPart(); + if (!specific.contains("@")) { + Log.e(Constants.TAG, "unknown uri scheme in affirmation packet"); + return null; + } + + String[] pieces = specific.split("@", 2); + URI subUri = URI.create(pieces[1]); + + Set flags = new HashSet(); + HashMap params = new HashMap(); + { + 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 new Affirmation(null, nonce, flags, params, subUri); + + } + + public static long generateNonce() { + return 1234567890L; // new SecureRandom().nextLong(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java new file mode 100644 index 000000000..e356ccb8e --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java @@ -0,0 +1,43 @@ +package org.sufficientlysecure.keychain.pgp.affirmation; + +import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; +import org.sufficientlysecure.keychain.pgp.affirmation.resources.UnknownResource; + +import java.net.URI; +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.Set; + +public abstract class AffirmationResource { + + protected final URI mUri; + protected final Set mFlags; + protected final HashMap mParams; + + protected AffirmationResource(Set flags, HashMap params, URI uri) { + mFlags = flags; + mParams = params; + mUri = uri; + } + + public abstract boolean verify(); + + public static AffirmationResource findResourceType + (Set flags, HashMap params, URI uri) { + + AffirmationResource res; + + res = GenericHttpsResource.create(flags, params, uri); + if (res != null) { + return res; + } + + return new UnknownResource(flags, params, uri); + + } + + public static long generateNonce() { + return 1234567890L; // new SecureRandom().nextLong(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java new file mode 100644 index 000000000..3e39a695d --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java @@ -0,0 +1,4 @@ +package org.sufficientlysecure.keychain.pgp.affirmation.resources; + +public class DnsResouce { +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java new file mode 100644 index 000000000..42615d105 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java @@ -0,0 +1,130 @@ +package org.sufficientlysecure.keychain.pgp.affirmation.resources; + +import android.content.Context; + +import com.textuality.keybase.lib.Search; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; +import org.sufficientlysecure.keychain.pgp.Progressable; +import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.Log; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import javax.net.ssl.HttpsURLConnection; + +public class GenericHttpsResource extends AffirmationResource { + + GenericHttpsResource(Set flags, HashMap params, URI uri) { + super(flags, params, uri); + } + + @Override + public boolean verify() { + return false; + } + + public static String generate (byte[] fingerprint, String uri) { + long nonce = generateNonce(); + + StringBuilder b = new StringBuilder(); + b.append("---\r\n"); + + b.append("fingerprint="); + b.append(KeyFormattingUtils.convertFingerprintToHex(fingerprint)); + b.append('\r').append('\n'); + + b.append("nonce="); + b.append(nonce); + b.append('\r').append('\n'); + + if (uri != null) { + b.append("uri="); + b.append(uri); + b.append('\r').append('\n'); + } + b.append("---\r\n"); + + return b.toString(); + } + + public DecryptVerifyResult verify + (Context context, ProviderHelper providerHelper, Progressable progress) + throws IOException { + + byte[] data = fetchResource(mUri).getBytes(); + InputData input = new InputData(new ByteArrayInputStream(data), data.length); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + PgpDecryptVerify.Builder b = + new PgpDecryptVerify.Builder(context, providerHelper, progress, input, out); + PgpDecryptVerify op = b.build(); + + Log.d(Constants.TAG, new String(out.toByteArray())); + + return op.execute(); + } + + protected static String fetchResource (URI uri) throws IOException { + + try { + HttpsURLConnection conn = null; + URL url = uri.toURL(); + int status = 0; + int redirects = 0; + while (redirects < 5) { + conn = (HttpsURLConnection) url.openConnection(); + conn.addRequestProperty("User-Agent", "OpenKeychain"); + conn.setConnectTimeout(5000); + conn.setReadTimeout(25000); + conn.connect(); + status = conn.getResponseCode(); + if (status == 301) { + redirects++; + url = new URL(conn.getHeaderFields().get("Location").get(0)); + } else { + break; + } + } + if (status >= 200 && status < 300) { + return Search.snarf(conn.getInputStream()); + } else { + throw new IOException("Fetch failed, status " + status + ": " + Search.snarf(conn.getErrorStream())); + } + + } catch (MalformedURLException e) { + throw new IOException(e); + } + + } + + 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); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java new file mode 100644 index 000000000..4fc3590f8 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java @@ -0,0 +1,4 @@ +package org.sufficientlysecure.keychain.pgp.affirmation.resources; + +public class TwitterResource { +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java new file mode 100644 index 000000000..e2d050eb4 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java @@ -0,0 +1,20 @@ +package org.sufficientlysecure.keychain.pgp.affirmation.resources; + +import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; + +import java.net.URI; +import java.util.HashMap; +import java.util.Set; + +public class UnknownResource extends AffirmationResource { + + public UnknownResource(Set flags, HashMap params, URI uri) { + super(flags, params, uri); + } + + @Override + public boolean verify() { + return false; + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index f1453c40c..3dd4258e7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -37,6 +37,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; +import org.sufficientlysecure.keychain.ui.affirmations.AffirmationWizard; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; @@ -55,6 +56,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements public static final String ARG_DATA_URI = "uri"; + private View mActionLink, mActionLinkDivider; private View mActionEdit; private View mActionEditDivider; private View mActionEncryptFiles; @@ -83,6 +85,8 @@ public class ViewKeyMainFragment extends LoaderFragment implements View view = inflater.inflate(R.layout.view_key_main_fragment, getContainer()); mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids); + mActionLink = view.findViewById(R.id.view_key_action_link); + mActionLinkDivider = view.findViewById(R.id.view_key_action_link); mActionEdit = view.findViewById(R.id.view_key_action_edit); mActionEditDivider = view.findViewById(R.id.view_key_action_edit_divider); mActionEncryptText = view.findViewById(R.id.view_key_action_encrypt_text); @@ -156,6 +160,11 @@ public class ViewKeyMainFragment extends LoaderFragment implements certify(mDataUri); } }); + mActionLink.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + linkKey(mDataUri); + } + }); mActionEdit.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { editKey(mDataUri); @@ -224,10 +233,14 @@ public class ViewKeyMainFragment extends LoaderFragment implements if (data.moveToFirst()) { if (data.getInt(INDEX_UNIFIED_HAS_ANY_SECRET) != 0) { // edit button + mActionLink.setVisibility(View.VISIBLE); + mActionLinkDivider.setVisibility(View.VISIBLE); mActionEdit.setVisibility(View.VISIBLE); mActionEditDivider.setVisibility(View.VISIBLE); } else { // edit button + mActionLink.setVisibility(View.GONE); + mActionLinkDivider.setVisibility(View.GONE); mActionEdit.setVisibility(View.GONE); mActionEditDivider.setVisibility(View.GONE); } @@ -347,4 +360,10 @@ public class ViewKeyMainFragment extends LoaderFragment implements startActivityForResult(editIntent, 0); } + private void linkKey(Uri dataUri) { + Intent editIntent = new Intent(getActivity(), AffirmationWizard.class); + editIntent.setData(KeyRings.buildUnifiedKeyRingUri(dataUri)); + startActivityForResult(editIntent, 0); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java new file mode 100644 index 000000000..30cfe9a79 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.affirmations; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +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.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; + +import org.openintents.openpgp.util.OpenPgpApi; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; +import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.ui.NfcActivity; +import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; + +import java.util.Date; + +public class AffirmationCreateHttpsStep1Fragment extends Fragment { + + public static final int REQUEST_CODE_PASSPHRASE = 0x00008001; + public static final int REQUEST_CODE_NFC = 0x00008002; + + AffirmationWizard mAffirmationWizard; + + String mProofUri, mProofText; + + EditText mEditUri; + + // For NFC data + protected String mSigningKeyPassphrase = null; + protected Date mNfcTimestamp = null; + protected byte[] mNfcHash = null; + + /** + * Creates new instance of this fragment + */ + public static AffirmationCreateHttpsStep1Fragment newInstance() { + AffirmationCreateHttpsStep1Fragment frag = new AffirmationCreateHttpsStep1Fragment(); + + Bundle args = new Bundle(); + frag.setArguments(args); + + return frag; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mAffirmationWizard = (AffirmationWizard) getActivity(); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.affirmation_create_https_fragment_step1, container, false); + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + + String uri = "https://" + mEditUri.getText(); + + if (!checkUri(uri)) { + return; + } + + mProofUri = uri; + mProofText = GenericHttpsResource.generate(mAffirmationWizard.mFingerprint, null); + + generateResourceAndNext(); + } + }); + + mEditUri = (EditText) view.findViewById(R.id.affirmation_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.uid_mail_ok, 0); + } else { + mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0, + R.drawable.uid_mail_bad, 0); + } + } else { + // remove drawable if email is empty + mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + } + } + }); + + mEditUri.setText("mugenguild.com/pgpkey.txt"); + + return view; + } + + public void generateResourceAndNext () { + + // Send all information needed to service to edit key in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT); + intent.putExtra(KeychainIntentService.EXTRA_DATA, createEncryptBundle()); + + // Message is received after encrypting is done in KeychainIntentService + KeychainIntentServiceHandler serviceHandler = new KeychainIntentServiceHandler( + mAffirmationWizard, getString(R.string.progress_encrypting), + ProgressDialog.STYLE_HORIZONTAL) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + SignEncryptResult pgpResult = + message.getData().getParcelable(SignEncryptResult.EXTRA_RESULT); + + if (pgpResult.isPending()) { + if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) == + SignEncryptResult.RESULT_PENDING_PASSPHRASE) { + startPassphraseDialog(pgpResult.getKeyIdPassphraseNeeded()); + } else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) == + SignEncryptResult.RESULT_PENDING_NFC) { + + mNfcTimestamp = pgpResult.getNfcTimestamp(); + startNfcSign(pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcHash(), pgpResult.getNfcAlgo()); + } else { + throw new RuntimeException("Unhandled pending result!"); + } + return; + } + + if (pgpResult.success()) { + String proofText = new String( + message.getData().getByteArray(KeychainIntentService.RESULT_BYTES)); + + AffirmationCreateHttpsStep2Fragment frag = + AffirmationCreateHttpsStep2Fragment.newInstance(mProofUri, proofText); + + mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + } else { + pgpResult.createNotify(getActivity()).show(); + } + + // no matter the result, reset parameters + mSigningKeyPassphrase = null; + mNfcHash = null; + mNfcTimestamp = null; + } + } + }; + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(serviceHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + serviceHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + + } + + protected Bundle createEncryptBundle() { + // fill values for this action + Bundle data = new Bundle(); + + data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_BYTES); + data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES); + data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, mProofText.getBytes()); + + // Always use armor for messages + data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, true); + + data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_MASTER_ID, mAffirmationWizard.mMasterKeyId); + data.putString(KeychainIntentService.ENCRYPT_SIGNATURE_KEY_PASSPHRASE, mSigningKeyPassphrase); + data.putSerializable(KeychainIntentService.ENCRYPT_SIGNATURE_NFC_TIMESTAMP, mNfcTimestamp); + data.putByteArray(KeychainIntentService.ENCRYPT_SIGNATURE_NFC_HASH, mNfcHash); + + return data; + } + + protected void startPassphraseDialog(long subkeyId) { + Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); + intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, subkeyId); + startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); + } + + protected void startNfcSign(long keyId, String pin, byte[] hashToSign, int hashAlgo) { + // build PendingIntent for Yubikey NFC operations + Intent intent = new Intent(getActivity(), NfcActivity.class); + intent.setAction(NfcActivity.ACTION_SIGN_HASH); + + // pass params through to activity that it can be returned again later to repeat pgp operation + intent.putExtra(NfcActivity.EXTRA_DATA, new Intent()); // not used, only relevant to OpenPgpService + intent.putExtra(NfcActivity.EXTRA_KEY_ID, keyId); + intent.putExtra(NfcActivity.EXTRA_PIN, pin); + intent.putExtra(NfcActivity.EXTRA_NFC_HASH_TO_SIGN, hashToSign); + intent.putExtra(NfcActivity.EXTRA_NFC_HASH_ALGO, hashAlgo); + + startActivityForResult(intent, REQUEST_CODE_NFC); + } + + private static boolean checkUri(String uri) { + return Patterns.WEB_URL.matcher(uri).matches(); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case REQUEST_CODE_PASSPHRASE: { + if (resultCode == AffirmationWizard.RESULT_OK && data != null) { + mSigningKeyPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + generateResourceAndNext(); + return; + } + break; + } + + case REQUEST_CODE_NFC: { + if (resultCode == AffirmationWizard.RESULT_OK && data != null) { + mNfcHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH); + generateResourceAndNext(); + return; + } + break; + } + + default: { + super.onActivityResult(requestCode, resultCode, data); + break; + } + } + } + + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java new file mode 100644 index 000000000..ee7ba1a77 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.affirmations; + +import android.content.Intent; +import android.graphics.PorterDuff; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +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 android.widget.ImageView; + +import org.openintents.openpgp.OpenPgpSignatureResult; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +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.IOException; +import java.io.PrintWriter; +import java.net.URI; +import java.net.URISyntaxException; + +public class AffirmationCreateHttpsStep2Fragment extends Fragment { + + private static final int REQUEST_CODE_OUTPUT = 0x00007007; + + public static final String URI = "uri", TEXT = "text"; + + AffirmationWizard mAffirmationWizard; + + EditText mEditUri; + ImageView mVerifyImage; + View mVerifyProgress; + + String mResourceUri; + String mProofString; + + /** + * Creates new instance of this fragment + */ + public static AffirmationCreateHttpsStep2Fragment newInstance(String uri, String proofText) { + AffirmationCreateHttpsStep2Fragment frag = new AffirmationCreateHttpsStep2Fragment(); + + Bundle args = new Bundle(); + args.putString(URI, uri); + args.putString(TEXT, proofText); + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.affirmation_create_https_fragment_step2, container, false); + + mResourceUri = getArguments().getString(URI); + mProofString = getArguments().getString(TEXT); + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + + // AffirmationCreateHttpsStep2Fragment frag = + // AffirmationCreateHttpsStep2Fragment.newInstance(); + + // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + } + }); + + mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); + mVerifyProgress = view.findViewById(R.id.verify_progress); + + view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSend(); + } + }); + + view.findViewById(R.id.button_save).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSave(); + } + }); + + view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofVerify(); + } + }); + + mEditUri = (EditText) view.findViewById(R.id.affirmation_create_https_uri); + mEditUri.setText(mResourceUri); + + setVerifyProgress(false, null); + + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mAffirmationWizard = (AffirmationWizard) getActivity(); + } + + public void setVerifyProgress(boolean on, Boolean success) { + mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); + mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); + if (success == null) { + mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), + PorterDuff.Mode.SRC_IN); + } else if (success) { + mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark), + PorterDuff.Mode.SRC_IN); + } else { + mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), + PorterDuff.Mode.SRC_IN); + } + } + + public void proofVerify() { + setVerifyProgress(true, null); + + try { + final GenericHttpsResource resource = GenericHttpsResource.createNew(new URI(mResourceUri)); + + new AsyncTask() { + + @Override + protected DecryptVerifyResult doInBackground(Void... params) { + + try { + return resource.verify(getActivity(), new ProviderHelper(getActivity()), null); + } catch (IOException e) { + e.printStackTrace(); + } + + return null; + } + + @Override + protected void onPostExecute(DecryptVerifyResult result) { + super.onPostExecute(result); + if (result.success()) { + switch (result.getSignatureResult().getStatus()) { + case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED: + setVerifyProgress(false, true); + break; + default: + setVerifyProgress(false, false); + // on error, show error message + result.createNotify(getActivity()).show(); + } + } else { + setVerifyProgress(false, false); + // on error, show error message + result.createNotify(getActivity()).show(); + } + } + }.execute(); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + + } + + private void proofSend() { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, mProofString); + sendIntent.setType("text/plain"); + startActivity(sendIntent); + } + + private void proofSave () { + String state = Environment.getExternalStorageState(); + if (!Environment.MEDIA_MOUNTED.equals(state)) { + Notify.showNotify(getActivity(), "External storage not available!", Style.ERROR); + return; + } + + String targetName = "pgpkey.txt"; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + File targetFile = new File(Constants.Path.APP_DIR, targetName); + FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file), + getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT); + } else { + FileHelper.saveDocument(this, "text/plain", targetName, REQUEST_CODE_OUTPUT); + } + } + + private void saveFile(Uri uri) { + try { + PrintWriter out = + new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); + out.print(mProofString); + if (out.checkError()) { + Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR); + } + } catch (FileNotFoundException e) { + Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR); + e.printStackTrace(); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case REQUEST_CODE_OUTPUT: + if (data == null) { + return; + } + Uri uri = data.getData(); + saveFile(uri); + break; + } + super.onActivityResult(requestCode, resultCode, data); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationSelectFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationSelectFragment.java new file mode 100644 index 000000000..784e75789 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationSelectFragment.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.affirmations; + +import android.os.Bundle; +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 AffirmationSelectFragment extends Fragment { + + AffirmationWizard mAffirmationWizard; + + /** + * Creates new instance of this fragment + */ + public static AffirmationSelectFragment newInstance() { + AffirmationSelectFragment frag = new AffirmationSelectFragment(); + + Bundle args = new Bundle(); + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.affirmation_select_fragment, container, false); + + view.findViewById(R.id.affirmation_create_https_button) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + AffirmationCreateHttpsStep1Fragment frag = + AffirmationCreateHttpsStep1Fragment.newInstance(); + + mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + } + }); + + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mAffirmationWizard = (AffirmationWizard) getActivity(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationWizard.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationWizard.java new file mode 100644 index 000000000..99b88405a --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationWizard.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.affirmations; + +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 org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; +import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.Log; + +public class AffirmationWizard extends ActionBarActivity { + + 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; + + long mMasterKeyId; + byte[] mFingerprint; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.create_key_activity); + + try { + Uri uri = getIntent().getData(); + CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(uri); + mMasterKeyId = ring.extractOrGetMasterKeyId(); + mFingerprint = ring.getFingerprint(); + } catch (PgpKeyNotFoundException e) { + Log.e(Constants.TAG, "Invalid uri given, key does not exist!"); + finish(); + return; + } + + // pass extras into fragment + AffirmationSelectFragment frag = AffirmationSelectFragment.newInstance(); + loadFragment(null, frag, FRAG_ACTION_START); + } + + public void loadFragment(Bundle savedInstanceState, Fragment fragment, int action) { + // However, if we're being restored from a previous state, + // then we don't need to do anything and should return or else + // we could end up with overlapping fragments. + if (savedInstanceState != null) { + return; + } + + // 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; + + } + // do it immediately! + getSupportFragmentManager().executePendingTransactions(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/HttpsPrefixedText.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/HttpsPrefixedText.java new file mode 100644 index 000000000..292343eb7 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/HttpsPrefixedText.java @@ -0,0 +1,38 @@ +package org.sufficientlysecure.keychain.ui.widget; + +import android.content.Context; +import android.graphics.*; +import android.support.annotation.NonNull; +import android.util.AttributeSet; +import android.widget.EditText; + +/** */ +public class HttpsPrefixedText extends EditText { + + private String mPrefix; // can be hardcoded for demo purposes + private Rect mPrefixRect = new Rect(); + + public HttpsPrefixedText(Context context, AttributeSet attrs) { + super(context, attrs); + mPrefix = "https://"; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + getPaint().getTextBounds(mPrefix, 0, mPrefix.length(), mPrefixRect); + + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onDraw(@NonNull Canvas canvas) { + super.onDraw(canvas); + canvas.drawText(mPrefix, super.getCompoundPaddingLeft(), getBaseline(), getPaint()); + } + + @Override + public int getCompoundPaddingLeft() { + return super.getCompoundPaddingLeft() + mPrefixRect.width(); + } + +} \ No newline at end of file -- cgit v1.2.3 From e0847cafaf53eac9b364343c1f5e74554b51053d Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 12 Jan 2015 19:27:46 +0100 Subject: even more intermediate result --- .../operations/results/LinkedVerifyResult.java | 63 ++++++++ .../operations/results/OperationResult.java | 16 ++ .../keychain/pgp/affirmation/Affirmation.java | 48 +++--- .../pgp/affirmation/AffirmationResource.java | 77 +++++++++- .../pgp/affirmation/resources/DnsResouce.java | 45 +++++- .../resources/GenericHttpsResource.java | 74 ++++----- .../pgp/affirmation/resources/TwitterResource.java | 117 +++++++++++++- .../pgp/affirmation/resources/UnknownResource.java | 6 +- .../keychain/service/SaveKeyringParcel.java | 2 + .../AffirmationCreateHttpsStep1Fragment.java | 168 ++------------------- .../AffirmationCreateHttpsStep2Fragment.java | 53 +++---- 11 files changed, 403 insertions(+), 266 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/LinkedVerifyResult.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 new file mode 100644 index 000000000..2dae38cc4 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/LinkedVerifyResult.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 Vincent Breitmoser + * + * 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.app.Activity; +import android.content.Intent; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.View; + +import com.github.johnpersano.supertoasts.SuperCardToast; +import com.github.johnpersano.supertoasts.SuperToast; +import com.github.johnpersano.supertoasts.SuperToast.Duration; +import com.github.johnpersano.supertoasts.util.OnClickWrapper; +import com.github.johnpersano.supertoasts.util.Style; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.LogDisplayActivity; +import org.sufficientlysecure.keychain.ui.LogDisplayFragment; + +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/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index 1388c0eac..c2f5757f8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -657,6 +657,22 @@ public abstract class OperationResult implements Parcelable { MSG_DEL_CONSOLIDATE (LogLevel.DEBUG, R.string.msg_del_consolidate), MSG_DEL_OK (LogLevel.OK, R.plurals.msg_del_ok), MSG_DEL_FAIL (LogLevel.WARN, R.plurals.msg_del_fail), + + MSG_LV (LogLevel.START, R.string.msg_lv), + MSG_LV_MATCH (LogLevel.DEBUG, R.string.msg_lv_match), + MSG_LV_MATCH_ERROR (LogLevel.ERROR, R.string.msg_lv_match_error), + MSG_LV_FP_OK (LogLevel.DEBUG, R.string.msg_lv_fp_ok), + MSG_LV_FP_ERROR (LogLevel.ERROR, R.string.msg_lv_fp_error), + MSG_LV_NONCE_OK (LogLevel.OK, R.string.msg_lv_nonce_ok), + MSG_LV_NONCE_ERROR (LogLevel.ERROR, R.string.msg_lv_nonce_error), + + MSG_LV_FETCH (LogLevel.DEBUG, R.string.msg_lv_fetch), + MSG_LV_FETCH_REDIR (LogLevel.DEBUG, R.string.msg_lv_fetch_redir), + MSG_LV_FETCH_OK (LogLevel.DEBUG, R.string.msg_lv_fetch_ok), + MSG_LV_FETCH_ERROR (LogLevel.ERROR, R.string.msg_lv_fetch_error), + MSG_LV_FETCH_ERROR_URL (LogLevel.ERROR, R.string.msg_lv_fetch_error_url), + MSG_LV_FETCH_ERROR_IO (LogLevel.ERROR, R.string.msg_lv_fetch_error_io), + ; public final int mMsgId; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java index f12ebd481..892231cbe 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java @@ -1,11 +1,15 @@ package org.sufficientlysecure.keychain.pgp.affirmation; import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.util.BigIntegers; import org.spongycastle.util.Strings; +import org.spongycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.util.Log; +import java.math.BigInteger; import java.net.URI; +import java.security.SecureRandom; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -16,13 +20,17 @@ import java.util.Set; public class Affirmation { protected byte[] mData; - public final long mNonce; + public final String mNonce; public final URI mSubUri; final Set mFlags; final HashMap mParams; - protected Affirmation(byte[] data, long nonce, Set flags, + protected Affirmation(byte[] data, String nonce, Set flags, HashMap params, URI subUri) { + if ( ! nonce.matches("[0-9a-zA-Z]+")) { + throw new AssertionError("bug: nonce must be hexstring!"); + } + mData = data; mNonce = nonce; mFlags = flags; @@ -30,7 +38,7 @@ public class Affirmation { mSubUri = subUri; } - Affirmation(long nonce, Set flags, + Affirmation(String nonce, Set flags, HashMap params, URI subUri) { this(null, nonce, flags, params, subUri); } @@ -72,15 +80,12 @@ public class Affirmation { b.append("@"); b.append(mSubUri); + byte[] nonceBytes = Hex.decode(mNonce); byte[] data = Strings.toUTF8ByteArray(b.toString()); - byte[] result = new byte[data.length+4]; - result[0] = (byte) (mNonce >> 24 & 255); - result[1] = (byte) (mNonce >> 16 & 255); - result[2] = (byte) (mNonce >> 8 & 255); - result[3] = (byte) (mNonce & 255); - - System.arraycopy(data, 0, result, 4, result.length); + byte[] result = new byte[data.length+12]; + System.arraycopy(nonceBytes, 0, result, 0, 12); + System.arraycopy(data, 0, result, 12, result.length); return result; } @@ -94,11 +99,10 @@ public class Affirmation { } byte[] data = subpacket.getData(); - - long nonce = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + String nonce = Hex.toHexString(data, 0, 12); try { - return parseUri(nonce, Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 4, data.length))); + return parseUri(nonce, Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 12, data.length))); } catch (IllegalArgumentException e) { Log.e(Constants.TAG, "error parsing uri in (suspected) affirmation packet"); @@ -106,11 +110,7 @@ public class Affirmation { } } - public static Affirmation generateForUri(String uri) { - return parseUri(generateNonce(), uri); - } - - protected static Affirmation parseUri (long nonce, String uriString) { + protected static Affirmation parseUri (String nonce, String uriString) { URI uri = URI.create(uriString); if ("pgpid".equals(uri.getScheme())) { @@ -146,12 +146,18 @@ public class Affirmation { } } - return new Affirmation(null, nonce, flags, params, subUri); + return new Affirmation(nonce, flags, params, subUri); } - public static long generateNonce() { - return 1234567890L; // new SecureRandom().nextLong(); + public static String generateNonce() { + // TODO make this actually random + // byte[] data = new byte[96]; + // new SecureRandom().nextBytes(data); + // return Hex.toHexString(data); + + // debug for now + return "0123456789ABCDEF01234567"; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java index e356ccb8e..45919a89a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java @@ -1,12 +1,21 @@ package org.sufficientlysecure.keychain.pgp.affirmation; +import android.content.Context; + +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.pgp.affirmation.resources.GenericHttpsResource; import org.sufficientlysecure.keychain.pgp.affirmation.resources.UnknownResource; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.Log; import java.net.URI; -import java.security.SecureRandom; import java.util.HashMap; import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public abstract class AffirmationResource { @@ -14,13 +23,73 @@ public abstract class AffirmationResource { protected final Set mFlags; protected final HashMap mParams; + static Pattern magicPattern = + Pattern.compile("\\[Verifying my PGP key: pgpid\\+cookie:([a-zA-Z0-9]+)#([a-zA-Z0-9]+)\\]"); + protected AffirmationResource(Set flags, HashMap params, URI uri) { mFlags = flags; mParams = params; mUri = uri; } - public abstract boolean verify(); + public static String generate (Context context, byte[] fingerprint, String nonce) { + + return "[Verifying my PGP key: pgpid+cookie:" + + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + "#" + nonce + "]"; + + } + + public LinkedVerifyResult verify(byte[] fingerprint, String nonce) { + + OperationLog log = new OperationLog(); + log.add(LogType.MSG_LV, 0); + + // Try to fetch resource. Logs for itself + String res = fetchResource(log, 1); + 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, res); + + return verifyString(log, 1, res, nonce, fingerprint); + + } + + protected abstract String fetchResource (OperationLog log, int indent); + + protected LinkedVerifyResult verifyString (OperationLog log, int indent, + String res, + String nonce, byte[] fingerprint) { + + log.add(LogType.MSG_LV_MATCH, indent); + Matcher match = magicPattern.matcher(res); + if (!match.find()) { + log.add(LogType.MSG_LV_MATCH_ERROR, 2); + return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); + } + + String candidateFp = match.group(1); + String nonceCandidate = match.group(2); + + 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); + + if (!nonce.equals(nonceCandidate)) { + log.add(LogType.MSG_LV_NONCE_ERROR, indent); + return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); + } + + log.add(LogType.MSG_LV_NONCE_OK, indent); + return new LinkedVerifyResult(LinkedVerifyResult.RESULT_OK, log); + + } public static AffirmationResource findResourceType (Set flags, HashMap params, URI uri) { @@ -36,8 +105,4 @@ public abstract class AffirmationResource { } - public static long generateNonce() { - return 1234567890L; // new SecureRandom().nextLong(); - } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java index 3e39a695d..20216972a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java @@ -1,4 +1,47 @@ package org.sufficientlysecure.keychain.pgp.affirmation.resources; -public class DnsResouce { +import android.content.Context; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.Log; + +import java.net.URI; +import java.util.HashMap; +import java.util.Set; + +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.TYPE; +import de.measite.minidns.record.TXT; + +public class DnsResouce extends AffirmationResource { + + DnsResouce(Set flags, HashMap params, URI uri) { + super(flags, params, uri); + } + + public static String generate (Context context, byte[] fingerprint, String nonce) { + + return "pgpid+cookie:" + + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + ";" + nonce + ""; + + } + + @Override + protected String fetchResource (OperationLog log, int indent) { + + Client c = new Client(); + DNSMessage msg = c.query(new Question("mugenguild.com", TYPE.TXT)); + Record aw = msg.getAnswers()[0]; + TXT txt = (TXT) aw.getPayload(); + Log.d(Constants.TAG, txt.getText()); + return txt.getText(); + + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java index 42615d105..c8c3cbb4d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java @@ -5,9 +5,14 @@ import android.content.Context; import com.textuality.keybase.lib.Search; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; +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.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.pgp.Progressable; +import org.sufficientlysecure.keychain.pgp.affirmation.Affirmation; import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; @@ -17,7 +22,6 @@ import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; @@ -33,58 +37,26 @@ public class GenericHttpsResource extends AffirmationResource { super(flags, params, uri); } - @Override - public boolean verify() { - return false; - } - - public static String generate (byte[] fingerprint, String uri) { - long nonce = generateNonce(); - - StringBuilder b = new StringBuilder(); - b.append("---\r\n"); - - b.append("fingerprint="); - b.append(KeyFormattingUtils.convertFingerprintToHex(fingerprint)); - b.append('\r').append('\n'); + public static String generateText (Context context, byte[] fingerprint, String nonce) { + String cookie = AffirmationResource.generate(context, fingerprint, nonce); - b.append("nonce="); - b.append(nonce); - b.append('\r').append('\n'); - - if (uri != null) { - b.append("uri="); - b.append(uri); - b.append('\r').append('\n'); - } - b.append("---\r\n"); - - return b.toString(); + return String.format(context.getResources().getString(R.string.linked_id_generic_text), + cookie, "0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprint).substring(24)); } - public DecryptVerifyResult verify - (Context context, ProviderHelper providerHelper, Progressable progress) - throws IOException { - - byte[] data = fetchResource(mUri).getBytes(); - InputData input = new InputData(new ByteArrayInputStream(data), data.length); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - PgpDecryptVerify.Builder b = - new PgpDecryptVerify.Builder(context, providerHelper, progress, input, out); - PgpDecryptVerify op = b.build(); - - Log.d(Constants.TAG, new String(out.toByteArray())); - - return op.execute(); - } + @Override + protected String fetchResource (OperationLog log, int indent) { - protected static String fetchResource (URI uri) throws IOException { + log.add(LogType.MSG_LV_FETCH, indent, mUri.toString()); + indent += 1; try { + HttpsURLConnection conn = null; - URL url = uri.toURL(); + URL url = mUri.toURL(); int status = 0; int redirects = 0; + while (redirects < 5) { conn = (HttpsURLConnection) url.openConnection(); conn.addRequestProperty("User-Agent", "OpenKeychain"); @@ -95,18 +67,28 @@ public class GenericHttpsResource extends AffirmationResource { if (status == 301) { redirects++; url = new URL(conn.getHeaderFields().get("Location").get(0)); + log.add(LogType.MSG_LV_FETCH_REDIR, indent, url.toString()); } else { break; } } + if (status >= 200 && status < 300) { + log.add(LogType.MSG_LV_FETCH_OK, indent, Integer.toString(status)); return Search.snarf(conn.getInputStream()); } else { - throw new IOException("Fetch failed, status " + status + ": " + Search.snarf(conn.getErrorStream())); + // log verbose output to logcat + Log.e(Constants.TAG, Search.snarf(conn.getErrorStream())); + log.add(LogType.MSG_LV_FETCH_ERROR, indent, Integer.toString(status)); + return null; } } catch (MalformedURLException e) { - throw new IOException(e); + log.add(LogType.MSG_LV_FETCH_ERROR_URL, indent); + return null; + } catch (IOException e) { + log.add(LogType.MSG_LV_FETCH_ERROR_IO, indent); + return null; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java index 4fc3590f8..b426c16b9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java @@ -1,4 +1,119 @@ package org.sufficientlysecure.keychain.pgp.affirmation.resources; -public class TwitterResource { +import android.util.Base64; +import android.util.JsonReader; + +import com.textuality.keybase.lib.JWalk; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.BasicHttpParams; +import org.json.JSONException; +import org.json.JSONObject; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Set; + +public class TwitterResource extends AffirmationResource { + + TwitterResource(Set flags, HashMap params, URI uri) { + super(flags, params, uri); + } + + private String getTwitterStream(String screenName) { + String results = null; + + // Step 1: Encode consumer key and secret + try { + // URL encode the consumer key and secret + String urlApiKey = URLEncoder.encode("6IhPnWbYxASAoAzH2QaUtHD0J", "UTF-8"); + String urlApiSecret = URLEncoder.encode("L0GnuiOnapWbSBbQtLIqtpeS5BTtvh06dmoMoKQfHQS8UwHuWm", "UTF-8"); + + // Concatenate the encoded consumer key, a colon character, and the + // encoded consumer secret + String combined = urlApiKey + ":" + urlApiSecret; + + // Base64 encode the string + String base64Encoded = Base64.encodeToString(combined.getBytes(), Base64.NO_WRAP); + + // 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)); + String auth = JWalk.getString(rawAuthorization, "access_token"); + + // Applications should verify that the value associated with the + // token_type key of the returned object is bearer + if (auth != null && JWalk.getString(rawAuthorization, "token_type").equals("bearer")) { + + // Step 3: Authenticate API requests with bearer token + HttpGet httpGet = + new HttpGet("https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=" + screenName); + + // construct a normal HTTPS request and include an Authorization + // header with the value of Bearer <> + httpGet.setHeader("Authorization", "Bearer " + auth); + httpGet.setHeader("Content-Type", "application/json"); + // update the results with the body of the response + results = getResponseBody(httpGet); + } + } catch (UnsupportedEncodingException ex) { + } catch (JSONException ex) { + } catch (IllegalStateException ex1) { + } + return results; + } + + private static String getResponseBody(HttpRequestBase request) { + StringBuilder sb = new StringBuilder(); + try { + + DefaultHttpClient httpClient = new DefaultHttpClient(new BasicHttpParams()); + HttpResponse response = httpClient.execute(request); + int statusCode = response.getStatusLine().getStatusCode(); + String reason = response.getStatusLine().getReasonPhrase(); + + if (statusCode == 200) { + + HttpEntity entity = response.getEntity(); + InputStream inputStream = entity.getContent(); + + BufferedReader bReader = new BufferedReader( + new InputStreamReader(inputStream, "UTF-8"), 8); + String line = null; + while ((line = bReader.readLine()) != null) { + sb.append(line); + } + } else { + sb.append(reason); + } + } catch (UnsupportedEncodingException ex) { + } catch (ClientProtocolException ex1) { + } catch (IOException ex2) { + } + return sb.toString(); + } + + @Override + protected String fetchResource(OperationLog log, int indent) { + return getTwitterStream("Valodim"); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java index e2d050eb4..2f67c948e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java @@ -1,7 +1,9 @@ package org.sufficientlysecure.keychain.pgp.affirmation.resources; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; +import java.io.IOException; import java.net.URI; import java.util.HashMap; import java.util.Set; @@ -13,8 +15,8 @@ public class UnknownResource extends AffirmationResource { } @Override - public boolean verify() { - return false; + protected String fetchResource(OperationLog log, int indent) { + return null; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index 810190fee..5e953ec1e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -21,6 +21,8 @@ package org.sufficientlysecure.keychain.service; import android.os.Parcel; import android.os.Parcelable; +import org.sufficientlysecure.keychain.pgp.affirmation.Affi; + import java.io.Serializable; import java.util.ArrayList; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java index 30cfe9a79..818008bb0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java @@ -17,11 +17,7 @@ package org.sufficientlysecure.keychain.ui.affirmations; -import android.app.ProgressDialog; -import android.content.Intent; import android.os.Bundle; -import android.os.Message; -import android.os.Messenger; import android.support.v4.app.Fragment; import android.text.Editable; import android.text.TextWatcher; @@ -32,33 +28,16 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.EditText; -import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.SignEncryptResult; +import org.sufficientlysecure.keychain.pgp.affirmation.Affirmation; import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.ui.NfcActivity; -import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; - -import java.util.Date; public class AffirmationCreateHttpsStep1Fragment extends Fragment { - public static final int REQUEST_CODE_PASSPHRASE = 0x00008001; - public static final int REQUEST_CODE_NFC = 0x00008002; - AffirmationWizard mAffirmationWizard; - String mProofUri, mProofText; - EditText mEditUri; - // For NFC data - protected String mSigningKeyPassphrase = null; - protected Date mNfcTimestamp = null; - protected byte[] mNfcHash = null; - /** * Creates new instance of this fragment */ @@ -93,10 +72,15 @@ public class AffirmationCreateHttpsStep1Fragment extends Fragment { return; } - mProofUri = uri; - mProofText = GenericHttpsResource.generate(mAffirmationWizard.mFingerprint, null); + String proofNonce = Affirmation.generateNonce(); + String proofText = GenericHttpsResource.generateText(getActivity(), + mAffirmationWizard.mFingerprint, proofNonce); + + AffirmationCreateHttpsStep2Fragment frag = + AffirmationCreateHttpsStep2Fragment.newInstance(uri, proofNonce, proofText); + + mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); - generateResourceAndNext(); } }); @@ -134,142 +118,8 @@ public class AffirmationCreateHttpsStep1Fragment extends Fragment { return view; } - public void generateResourceAndNext () { - - // Send all information needed to service to edit key in other thread - Intent intent = new Intent(getActivity(), KeychainIntentService.class); - intent.setAction(KeychainIntentService.ACTION_SIGN_ENCRYPT); - intent.putExtra(KeychainIntentService.EXTRA_DATA, createEncryptBundle()); - - // Message is received after encrypting is done in KeychainIntentService - KeychainIntentServiceHandler serviceHandler = new KeychainIntentServiceHandler( - mAffirmationWizard, getString(R.string.progress_encrypting), - ProgressDialog.STYLE_HORIZONTAL) { - public void handleMessage(Message message) { - // handle messages by standard KeychainIntentServiceHandler first - super.handleMessage(message); - - if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - SignEncryptResult pgpResult = - message.getData().getParcelable(SignEncryptResult.EXTRA_RESULT); - - if (pgpResult.isPending()) { - if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_PASSPHRASE) == - SignEncryptResult.RESULT_PENDING_PASSPHRASE) { - startPassphraseDialog(pgpResult.getKeyIdPassphraseNeeded()); - } else if ((pgpResult.getResult() & SignEncryptResult.RESULT_PENDING_NFC) == - SignEncryptResult.RESULT_PENDING_NFC) { - - mNfcTimestamp = pgpResult.getNfcTimestamp(); - startNfcSign(pgpResult.getNfcKeyId(), pgpResult.getNfcPassphrase(), pgpResult.getNfcHash(), pgpResult.getNfcAlgo()); - } else { - throw new RuntimeException("Unhandled pending result!"); - } - return; - } - - if (pgpResult.success()) { - String proofText = new String( - message.getData().getByteArray(KeychainIntentService.RESULT_BYTES)); - - AffirmationCreateHttpsStep2Fragment frag = - AffirmationCreateHttpsStep2Fragment.newInstance(mProofUri, proofText); - - mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); - } else { - pgpResult.createNotify(getActivity()).show(); - } - - // no matter the result, reset parameters - mSigningKeyPassphrase = null; - mNfcHash = null; - mNfcTimestamp = null; - } - } - }; - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(serviceHandler); - intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - serviceHandler.showProgressDialog(getActivity()); - - // start service with intent - getActivity().startService(intent); - - } - - protected Bundle createEncryptBundle() { - // fill values for this action - Bundle data = new Bundle(); - - data.putInt(KeychainIntentService.SOURCE, KeychainIntentService.IO_BYTES); - data.putInt(KeychainIntentService.TARGET, KeychainIntentService.IO_BYTES); - data.putByteArray(KeychainIntentService.ENCRYPT_MESSAGE_BYTES, mProofText.getBytes()); - - // Always use armor for messages - data.putBoolean(KeychainIntentService.ENCRYPT_USE_ASCII_ARMOR, true); - - data.putLong(KeychainIntentService.ENCRYPT_SIGNATURE_MASTER_ID, mAffirmationWizard.mMasterKeyId); - data.putString(KeychainIntentService.ENCRYPT_SIGNATURE_KEY_PASSPHRASE, mSigningKeyPassphrase); - data.putSerializable(KeychainIntentService.ENCRYPT_SIGNATURE_NFC_TIMESTAMP, mNfcTimestamp); - data.putByteArray(KeychainIntentService.ENCRYPT_SIGNATURE_NFC_HASH, mNfcHash); - - return data; - } - - protected void startPassphraseDialog(long subkeyId) { - Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); - intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, subkeyId); - startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); - } - - protected void startNfcSign(long keyId, String pin, byte[] hashToSign, int hashAlgo) { - // build PendingIntent for Yubikey NFC operations - Intent intent = new Intent(getActivity(), NfcActivity.class); - intent.setAction(NfcActivity.ACTION_SIGN_HASH); - - // pass params through to activity that it can be returned again later to repeat pgp operation - intent.putExtra(NfcActivity.EXTRA_DATA, new Intent()); // not used, only relevant to OpenPgpService - intent.putExtra(NfcActivity.EXTRA_KEY_ID, keyId); - intent.putExtra(NfcActivity.EXTRA_PIN, pin); - intent.putExtra(NfcActivity.EXTRA_NFC_HASH_TO_SIGN, hashToSign); - intent.putExtra(NfcActivity.EXTRA_NFC_HASH_ALGO, hashAlgo); - - startActivityForResult(intent, REQUEST_CODE_NFC); - } - private static boolean checkUri(String uri) { return Patterns.WEB_URL.matcher(uri).matches(); } - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case REQUEST_CODE_PASSPHRASE: { - if (resultCode == AffirmationWizard.RESULT_OK && data != null) { - mSigningKeyPassphrase = data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); - generateResourceAndNext(); - return; - } - break; - } - - case REQUEST_CODE_NFC: { - if (resultCode == AffirmationWizard.RESULT_OK && data != null) { - mNfcHash = data.getByteArrayExtra(OpenPgpApi.EXTRA_NFC_SIGNED_HASH); - generateResourceAndNext(); - return; - } - break; - } - - default: { - super.onActivityResult(requestCode, resultCode, data); - break; - } - } - } - - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java index ee7ba1a77..55a6be40c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java @@ -31,20 +31,18 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.EditText; import android.widget.ImageView; +import android.widget.TextView; -import org.openintents.openpgp.OpenPgpSignatureResult; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; -import org.sufficientlysecure.keychain.provider.ProviderHelper; 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.IOException; import java.io.PrintWriter; import java.net.URI; import java.net.URISyntaxException; @@ -53,25 +51,29 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { private static final int REQUEST_CODE_OUTPUT = 0x00007007; - public static final String URI = "uri", TEXT = "text"; + public static final String URI = "uri", NONCE = "nonce", TEXT = "text"; AffirmationWizard mAffirmationWizard; EditText mEditUri; ImageView mVerifyImage; View mVerifyProgress; + TextView mVerifyStatus; String mResourceUri; - String mProofString; + String mResourceNonce, mResourceString; /** * Creates new instance of this fragment */ - public static AffirmationCreateHttpsStep2Fragment newInstance(String uri, String proofText) { + public static AffirmationCreateHttpsStep2Fragment newInstance + (String uri, String proofNonce, String proofText) { + AffirmationCreateHttpsStep2Fragment frag = new AffirmationCreateHttpsStep2Fragment(); Bundle args = new Bundle(); args.putString(URI, uri); + args.putString(NONCE, proofNonce); args.putString(TEXT, proofText); frag.setArguments(args); @@ -83,7 +85,8 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { final View view = inflater.inflate(R.layout.affirmation_create_https_fragment_step2, container, false); mResourceUri = getArguments().getString(URI); - mProofString = getArguments().getString(TEXT); + mResourceNonce = getArguments().getString(NONCE); + mResourceString = getArguments().getString(TEXT); view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { @Override @@ -98,6 +101,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); mVerifyProgress = view.findViewById(R.id.verify_progress); + mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { @Override @@ -124,6 +128,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { mEditUri.setText(mResourceUri); setVerifyProgress(false, null); + mVerifyStatus.setText(R.string.linked_verify_pending); return view; } @@ -139,14 +144,17 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); if (success == null) { + mVerifyStatus.setText(R.string.linked_verifying); mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout); mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), PorterDuff.Mode.SRC_IN); } else if (success) { + mVerifyStatus.setText(R.string.linked_verify_success); mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout); mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark), PorterDuff.Mode.SRC_IN); } else { + mVerifyStatus.setText(R.string.linked_verify_error); mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout); mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), PorterDuff.Mode.SRC_IN); @@ -159,33 +167,18 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { try { final GenericHttpsResource resource = GenericHttpsResource.createNew(new URI(mResourceUri)); - new AsyncTask() { + new AsyncTask() { @Override - protected DecryptVerifyResult doInBackground(Void... params) { - - try { - return resource.verify(getActivity(), new ProviderHelper(getActivity()), null); - } catch (IOException e) { - e.printStackTrace(); - } - - return null; + protected LinkedVerifyResult doInBackground(Void... params) { + return resource.verify(mAffirmationWizard.mFingerprint, mResourceNonce); } @Override - protected void onPostExecute(DecryptVerifyResult result) { + protected void onPostExecute(LinkedVerifyResult result) { super.onPostExecute(result); if (result.success()) { - switch (result.getSignatureResult().getStatus()) { - case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED: - setVerifyProgress(false, true); - break; - default: - setVerifyProgress(false, false); - // on error, show error message - result.createNotify(getActivity()).show(); - } + setVerifyProgress(false, true); } else { setVerifyProgress(false, false); // on error, show error message @@ -202,7 +195,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { private void proofSend() { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, mProofString); + sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); sendIntent.setType("text/plain"); startActivity(sendIntent); } @@ -229,7 +222,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { try { PrintWriter out = new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); - out.print(mProofString); + out.print(mResourceString); if (out.checkError()) { Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR); } -- cgit v1.2.3 From 8408113322242b8811b4a0fa874c172c274b9f0e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 12 Jan 2015 20:03:03 +0100 Subject: add support for user attributes (during canonicalization) --- .../operations/results/OperationResult.java | 15 ++ .../keychain/pgp/UncachedKeyRing.java | 166 +++++++++++++++++++++ .../keychain/pgp/WrappedSignature.java | 9 ++ .../keychain/pgp/affirmation/LinkedIdentity.java | 160 ++++++++++++++++++++ 4 files changed, 350 insertions(+) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index c2f5757f8..4e7c6a72f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -416,6 +416,21 @@ public abstract class OperationResult implements Parcelable { MSG_KC_UID_REVOKE_OLD (LogLevel.DEBUG, R.string.msg_kc_uid_revoke_old), MSG_KC_UID_REMOVE (LogLevel.DEBUG, R.string.msg_kc_uid_remove), MSG_KC_UID_WARN_ENCODING (LogLevel.WARN, R.string.msg_kc_uid_warn_encoding), + MSG_KC_UAT_JPEG (LogLevel.DEBUG, R.string.msg_kc_uat_jpeg), + MSG_KC_UAT_UNKNOWN (LogLevel.DEBUG, R.string.msg_kc_uat_unknown), + MSG_KC_UAT_BAD_ERR (LogLevel.WARN, R.string.msg_kc_uat_bad_err), + MSG_KC_UAT_BAD_LOCAL (LogLevel.WARN, R.string.msg_kc_uat_bad_local), + MSG_KC_UAT_BAD_TIME (LogLevel.WARN, R.string.msg_kc_uat_bad_time), + MSG_KC_UAT_BAD_TYPE (LogLevel.WARN, R.string.msg_kc_uat_bad_type), + MSG_KC_UAT_BAD (LogLevel.WARN, R.string.msg_kc_uat_bad), + MSG_KC_UAT_CERT_DUP (LogLevel.DEBUG, R.string.msg_kc_uat_cert_dup), + MSG_KC_UAT_DUP (LogLevel.DEBUG, R.string.msg_kc_uat_dup), + MSG_KC_UAT_FOREIGN (LogLevel.DEBUG, R.string.msg_kc_uat_foreign), + MSG_KC_UAT_NO_CERT (LogLevel.DEBUG, R.string.msg_kc_uat_no_cert), + MSG_KC_UAT_REVOKE_DUP (LogLevel.DEBUG, R.string.msg_kc_uat_revoke_dup), + MSG_KC_UAT_REVOKE_OLD (LogLevel.DEBUG, R.string.msg_kc_uat_revoke_old), + MSG_KC_UAT_REMOVE (LogLevel.DEBUG, R.string.msg_kc_uat_remove), + MSG_KC_UAT_WARN_ENCODING (LogLevel.WARN, R.string.msg_kc_uat_warn_encoding), // keyring consolidation diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index a445e161f..404228a3e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.pgp; import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.bcpg.PublicKeyAlgorithmTags; import org.spongycastle.bcpg.SignatureSubpacketTags; +import org.spongycastle.bcpg.UserAttributeSubpacketTags; import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPObjectFactory; @@ -30,6 +31,7 @@ import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; import org.spongycastle.openpgp.PGPUtil; import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; import org.sufficientlysecure.keychain.Constants; @@ -605,6 +607,170 @@ public class UncachedKeyRing { return null; } + ArrayList processedUserAttributes = new ArrayList<>(); + for (PGPUserAttributeSubpacketVector userAttribute : + new IterableIterator(masterKey.getUserAttributes())) { + + if (userAttribute.getSubpacket(UserAttributeSubpacketTags.IMAGE_ATTRIBUTE) != null) { + log.add(LogType.MSG_KC_UAT_JPEG, indent); + } else { + log.add(LogType.MSG_KC_UAT_UNKNOWN, indent); + } + + try { + indent += 1; + + // check for duplicate user attributes + if (processedUserAttributes.contains(userAttribute)) { + log.add(LogType.MSG_KC_UAT_DUP, indent); + // strip out the first found user id with this name + modified = PGPPublicKey.removeCertification(modified, userAttribute); + } + processedUserAttributes.add(userAttribute); + + PGPSignature selfCert = null; + revocation = null; + + // look through signatures for this specific user id + @SuppressWarnings("unchecked") + Iterator signaturesIt = masterKey.getSignaturesForUserAttribute(userAttribute); + if (signaturesIt != null) { + for (PGPSignature zert : new IterableIterator(signaturesIt)) { + WrappedSignature cert = new WrappedSignature(zert); + long certId = cert.getKeyId(); + + int type = zert.getSignatureType(); + if (type != PGPSignature.DEFAULT_CERTIFICATION + && type != PGPSignature.NO_CERTIFICATION + && type != PGPSignature.CASUAL_CERTIFICATION + && type != PGPSignature.POSITIVE_CERTIFICATION + && type != PGPSignature.CERTIFICATION_REVOCATION) { + log.add(LogType.MSG_KC_UAT_BAD_TYPE, + indent, "0x" + Integer.toString(zert.getSignatureType(), 16)); + modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); + badCerts += 1; + continue; + } + + if (cert.getCreationTime().after(nowPlusOneDay)) { + // Creation date in the future? No way! + log.add(LogType.MSG_KC_UAT_BAD_TIME, indent); + modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); + badCerts += 1; + continue; + } + + if (cert.isLocal()) { + // Creation date in the future? No way! + log.add(LogType.MSG_KC_UAT_BAD_LOCAL, indent); + modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); + badCerts += 1; + continue; + } + + // If this is a foreign signature, ... + if (certId != masterKeyId) { + // never mind any further for public keys, but remove them from secret ones + if (isSecret()) { + log.add(LogType.MSG_KC_UAT_FOREIGN, + indent, KeyFormattingUtils.convertKeyIdToHex(certId)); + modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); + badCerts += 1; + } + continue; + } + + // Otherwise, first make sure it checks out + try { + cert.init(masterKey); + if (!cert.verifySignature(masterKey, userAttribute)) { + log.add(LogType.MSG_KC_UAT_BAD, + indent); + modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); + badCerts += 1; + continue; + } + } catch (PgpGeneralException e) { + log.add(LogType.MSG_KC_UAT_BAD_ERR, + indent); + modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); + badCerts += 1; + continue; + } + + switch (type) { + case PGPSignature.DEFAULT_CERTIFICATION: + case PGPSignature.NO_CERTIFICATION: + case PGPSignature.CASUAL_CERTIFICATION: + case PGPSignature.POSITIVE_CERTIFICATION: + if (selfCert == null) { + selfCert = zert; + } else if (selfCert.getCreationTime().before(cert.getCreationTime())) { + log.add(LogType.MSG_KC_UAT_CERT_DUP, + indent); + modified = PGPPublicKey.removeCertification(modified, userAttribute, selfCert); + redundantCerts += 1; + selfCert = zert; + } else { + log.add(LogType.MSG_KC_UAT_CERT_DUP, + indent); + modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); + redundantCerts += 1; + } + // If there is a revocation certificate, and it's older than this, drop it + if (revocation != null + && revocation.getCreationTime().before(selfCert.getCreationTime())) { + log.add(LogType.MSG_KC_UAT_REVOKE_OLD, + indent); + modified = PGPPublicKey.removeCertification(modified, userAttribute, revocation); + revocation = null; + redundantCerts += 1; + } + break; + + case PGPSignature.CERTIFICATION_REVOCATION: + // If this is older than the (latest) self cert, drop it + if (selfCert != null && selfCert.getCreationTime().after(zert.getCreationTime())) { + log.add(LogType.MSG_KC_UAT_REVOKE_OLD, + indent); + modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); + redundantCerts += 1; + continue; + } + // first revocation? remember it. + if (revocation == null) { + revocation = zert; + // more revocations? at least one is superfluous, then. + } else if (revocation.getCreationTime().before(cert.getCreationTime())) { + log.add(LogType.MSG_KC_UAT_REVOKE_DUP, + indent); + modified = PGPPublicKey.removeCertification(modified, userAttribute, revocation); + redundantCerts += 1; + revocation = zert; + } else { + log.add(LogType.MSG_KC_UAT_REVOKE_DUP, + indent); + modified = PGPPublicKey.removeCertification(modified, userAttribute, zert); + redundantCerts += 1; + } + break; + } + } + } + + // If no valid certificate (if only a revocation) remains, drop it + if (selfCert == null && revocation == null) { + log.add(LogType.MSG_KC_UAT_REMOVE, + indent); + modified = PGPPublicKey.removeCertification(modified, userAttribute); + } + + } finally { + indent -= 1; + } + } + + // Replace modified key in the keyring ring = replacePublicKey(ring, modified); indent -= 1; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java index c395ca52d..3dc02d3ed 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java @@ -29,6 +29,7 @@ import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; @@ -199,6 +200,14 @@ public class WrappedSignature { } } + boolean verifySignature(PGPPublicKey key, PGPUserAttributeSubpacketVector attribute) throws PgpGeneralException { + try { + return mSig.verifyCertification(attribute, key); + } catch (PGPException e) { + throw new PgpGeneralException("Error!", e); + } + } + public boolean verifySignature(UncachedPublicKey key, byte[] rawUserId) throws PgpGeneralException { return verifySignature(key.getPublicKey(), rawUserId); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java new file mode 100644 index 000000000..dcbaa1c1c --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java @@ -0,0 +1,160 @@ +package org.sufficientlysecure.keychain.pgp.affirmation; + +import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.util.Strings; +import org.spongycastle.util.encoders.Hex; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.util.Log; + +import java.net.URI; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Set; + +public class LinkedIdentity { + + protected byte[] mData; + public final String mNonce; + public final URI mSubUri; + final Set mFlags; + final HashMap mParams; + + protected LinkedIdentity(byte[] data, String nonce, Set flags, + HashMap params, URI subUri) { + if ( ! nonce.matches("[0-9a-zA-Z]+")) { + throw new AssertionError("bug: nonce must be hexstring!"); + } + + mData = data; + mNonce = nonce; + mFlags = flags; + mParams = params; + mSubUri = subUri; + } + + LinkedIdentity(String nonce, Set flags, + HashMap params, URI subUri) { + this(null, nonce, flags, params, subUri); + } + + public byte[] encode() { + if (mData != null) { + return mData; + } + + StringBuilder b = new StringBuilder(); + b.append("pgpid:"); + + // 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; + Iterator> it = mParams.entrySet().iterator(); + while (it.hasNext()) { + if (!first) { + b.append(";"); + } + first = false; + Entry entry = it.next(); + b.append(entry.getKey()).append("=").append(entry.getValue()); + } + } + + b.append("@"); + b.append(mSubUri); + + byte[] nonceBytes = Hex.decode(mNonce); + byte[] data = Strings.toUTF8ByteArray(b.toString()); + + byte[] result = new byte[data.length+12]; + System.arraycopy(nonceBytes, 0, result, 0, 12); + System.arraycopy(data, 0, result, 12, result.length); + + return result; + } + + /** This method parses an affirmation from a UserAttributeSubpacket, or returns null if the + * subpacket can not be parsed as a valid affirmation. + */ + public static LinkedIdentity parseAffirmation(UserAttributeSubpacket subpacket) { + if (subpacket.getType() != 100) { + return null; + } + + byte[] data = subpacket.getData(); + String nonce = Hex.toHexString(data, 0, 12); + + try { + return parseUri(nonce, Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 12, data.length))); + + } catch (IllegalArgumentException e) { + Log.e(Constants.TAG, "error parsing uri in (suspected) affirmation packet"); + return null; + } + } + + protected static LinkedIdentity parseUri (String nonce, String uriString) { + URI uri = URI.create(uriString); + + if ("pgpid".equals(uri.getScheme())) { + Log.e(Constants.TAG, "unknown uri scheme in (suspected) affirmation packet"); + return null; + } + + if (!uri.isOpaque()) { + Log.e(Constants.TAG, "non-opaque uri in (suspected) affirmation packet"); + return null; + } + + String specific = uri.getSchemeSpecificPart(); + if (!specific.contains("@")) { + Log.e(Constants.TAG, "unknown uri scheme in affirmation packet"); + return null; + } + + String[] pieces = specific.split("@", 2); + URI subUri = URI.create(pieces[1]); + + Set flags = new HashSet(); + HashMap params = new HashMap(); + { + 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 new LinkedIdentity(nonce, flags, params, subUri); + + } + + public static String generateNonce() { + // TODO make this actually random + // byte[] data = new byte[96]; + // new SecureRandom().nextBytes(data); + // return Hex.toHexString(data); + + // debug for now + return "0123456789ABCDEF01234567"; + } + +} -- cgit v1.2.3 From 9fe701c866673d80cabc418ac675718447f76145 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 12 Jan 2015 20:04:15 +0100 Subject: work on affirmations (begin rename to LinkedIdentity --- .../keychain/pgp/affirmation/Affirmation.java | 163 --------------------- .../keychain/pgp/affirmation/LinkedIdentity.java | 3 +- .../resources/GenericHttpsResource.java | 9 -- .../keychain/service/SaveKeyringParcel.java | 7 +- .../AffirmationCreateHttpsStep1Fragment.java | 4 +- 5 files changed, 10 insertions(+), 176 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java deleted file mode 100644 index 892231cbe..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/Affirmation.java +++ /dev/null @@ -1,163 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.affirmation; - -import org.spongycastle.bcpg.UserAttributeSubpacket; -import org.spongycastle.util.BigIntegers; -import org.spongycastle.util.Strings; -import org.spongycastle.util.encoders.Hex; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.util.Log; - -import java.math.BigInteger; -import java.net.URI; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.Set; - -public class Affirmation { - - protected byte[] mData; - public final String mNonce; - public final URI mSubUri; - final Set mFlags; - final HashMap mParams; - - protected Affirmation(byte[] data, String nonce, Set flags, - HashMap params, URI subUri) { - if ( ! nonce.matches("[0-9a-zA-Z]+")) { - throw new AssertionError("bug: nonce must be hexstring!"); - } - - mData = data; - mNonce = nonce; - mFlags = flags; - mParams = params; - mSubUri = subUri; - } - - Affirmation(String nonce, Set flags, - HashMap params, URI subUri) { - this(null, nonce, flags, params, subUri); - } - - public byte[] encode() { - if (mData != null) { - return mData; - } - - StringBuilder b = new StringBuilder(); - b.append("pgpid:"); - - // 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; - Iterator> it = mParams.entrySet().iterator(); - while (it.hasNext()) { - if (!first) { - b.append(";"); - } - first = false; - Entry entry = it.next(); - b.append(entry.getKey()).append("=").append(entry.getValue()); - } - } - - b.append("@"); - b.append(mSubUri); - - byte[] nonceBytes = Hex.decode(mNonce); - byte[] data = Strings.toUTF8ByteArray(b.toString()); - - byte[] result = new byte[data.length+12]; - System.arraycopy(nonceBytes, 0, result, 0, 12); - System.arraycopy(data, 0, result, 12, result.length); - - return result; - } - - /** This method parses an affirmation from a UserAttributeSubpacket, or returns null if the - * subpacket can not be parsed as a valid affirmation. - */ - public static Affirmation parseAffirmation(UserAttributeSubpacket subpacket) { - if (subpacket.getType() != 100) { - return null; - } - - byte[] data = subpacket.getData(); - String nonce = Hex.toHexString(data, 0, 12); - - try { - return parseUri(nonce, Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 12, data.length))); - - } catch (IllegalArgumentException e) { - Log.e(Constants.TAG, "error parsing uri in (suspected) affirmation packet"); - return null; - } - } - - protected static Affirmation parseUri (String nonce, String uriString) { - URI uri = URI.create(uriString); - - if ("pgpid".equals(uri.getScheme())) { - Log.e(Constants.TAG, "unknown uri scheme in (suspected) affirmation packet"); - return null; - } - - if (!uri.isOpaque()) { - Log.e(Constants.TAG, "non-opaque uri in (suspected) affirmation packet"); - return null; - } - - String specific = uri.getSchemeSpecificPart(); - if (!specific.contains("@")) { - Log.e(Constants.TAG, "unknown uri scheme in affirmation packet"); - return null; - } - - String[] pieces = specific.split("@", 2); - URI subUri = URI.create(pieces[1]); - - Set flags = new HashSet(); - HashMap params = new HashMap(); - { - 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 new Affirmation(nonce, flags, params, subUri); - - } - - public static String generateNonce() { - // TODO make this actually random - // byte[] data = new byte[96]; - // new SecureRandom().nextBytes(data); - // return Hex.toHexString(data); - - // debug for now - return "0123456789ABCDEF01234567"; - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java index dcbaa1c1c..1e27b2c64 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java @@ -6,6 +6,7 @@ import org.spongycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.util.Log; +import java.io.Serializable; import java.net.URI; import java.util.Arrays; import java.util.HashMap; @@ -14,7 +15,7 @@ import java.util.Iterator; import java.util.Map.Entry; import java.util.Set; -public class LinkedIdentity { +public class LinkedIdentity implements Serializable { protected byte[] mData; public final String mNonce; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java index c8c3cbb4d..8f4d0c41b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java @@ -6,21 +6,12 @@ import com.textuality.keybase.lib.Search; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; -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.pgp.PgpDecryptVerify; -import org.sufficientlysecure.keychain.pgp.Progressable; -import org.sufficientlysecure.keychain.pgp.affirmation.Affirmation; import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; -import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index 5e953ec1e..b914f4619 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -21,7 +21,7 @@ package org.sufficientlysecure.keychain.service; import android.os.Parcel; import android.os.Parcelable; -import org.sufficientlysecure.keychain.pgp.affirmation.Affi; +import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; import java.io.Serializable; import java.util.ArrayList; @@ -51,6 +51,7 @@ public class SaveKeyringParcel implements Parcelable { public ChangeUnlockParcel mNewUnlock; public ArrayList mAddUserIds; + public ArrayList mAddLinkedIdentity; public ArrayList mAddSubKeys; public ArrayList mChangeSubKeys; @@ -73,6 +74,7 @@ public class SaveKeyringParcel implements Parcelable { public void reset() { mNewUnlock = null; mAddUserIds = new ArrayList(); + mAddLinkedIdentity = new ArrayList(); mAddSubKeys = new ArrayList(); mChangePrimaryUserId = null; mChangeSubKeys = new ArrayList(); @@ -164,6 +166,7 @@ public class SaveKeyringParcel implements Parcelable { mNewUnlock = source.readParcelable(getClass().getClassLoader()); mAddUserIds = source.createStringArrayList(); + mAddLinkedIdentity = (ArrayList) source.readSerializable(); mAddSubKeys = (ArrayList) source.readSerializable(); mChangeSubKeys = (ArrayList) source.readSerializable(); @@ -186,6 +189,7 @@ public class SaveKeyringParcel implements Parcelable { destination.writeParcelable(mNewUnlock, 0); destination.writeStringList(mAddUserIds); + destination.writeSerializable(mAddLinkedIdentity); destination.writeSerializable(mAddSubKeys); destination.writeSerializable(mChangeSubKeys); @@ -216,6 +220,7 @@ public class SaveKeyringParcel implements Parcelable { String out = "mMasterKeyId: " + mMasterKeyId + "\n"; out += "mNewUnlock: " + mNewUnlock + "\n"; out += "mAddUserIds: " + mAddUserIds + "\n"; + out += "mAddLinkedIdentity: " + mAddLinkedIdentity + "\n"; out += "mAddSubKeys: " + mAddSubKeys + "\n"; out += "mChangeSubKeys: " + mChangeSubKeys + "\n"; out += "mChangePrimaryUserId: " + mChangePrimaryUserId + "\n"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java index 818008bb0..7d0ba8937 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java @@ -29,7 +29,7 @@ import android.view.ViewGroup; import android.widget.EditText; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.affirmation.Affirmation; +import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; public class AffirmationCreateHttpsStep1Fragment extends Fragment { @@ -72,7 +72,7 @@ public class AffirmationCreateHttpsStep1Fragment extends Fragment { return; } - String proofNonce = Affirmation.generateNonce(); + String proofNonce = LinkedIdentity.generateNonce(); String proofText = GenericHttpsResource.generateText(getActivity(), mAffirmationWizard.mFingerprint, proofNonce); -- cgit v1.2.3 From ea72b29f2dbdef8104701d7eee6b35112078716e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 12 Jan 2015 20:59:37 +0100 Subject: support addition of user attributes --- .../operations/results/OperationResult.java | 2 + .../keychain/pgp/PgpKeyOperation.java | 52 ++++++++++++++++++++-- .../keychain/service/SaveKeyringParcel.java | 11 ++--- 3 files changed, 57 insertions(+), 8 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index 4e7c6a72f..a3e30a4b1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -495,6 +495,8 @@ public abstract class OperationResult implements Parcelable { MSG_MF_UID_PRIMARY (LogLevel.INFO, R.string.msg_mf_uid_primary), MSG_MF_UID_REVOKE (LogLevel.INFO, R.string.msg_mf_uid_revoke), MSG_MF_UID_ERROR_EMPTY (LogLevel.ERROR, R.string.msg_mf_uid_error_empty), + MSG_MF_UAT_ADD_IMAGE (LogLevel.INFO, R.string.msg_mf_uat_add_image), + MSG_MF_UAT_ADD_UNKNOWN (LogLevel.INFO, R.string.msg_mf_uat_add_unknown), MSG_MF_UNLOCK_ERROR (LogLevel.ERROR, R.string.msg_mf_unlock_error), MSG_MF_UNLOCK (LogLevel.DEBUG, R.string.msg_mf_unlock), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 128928bb3..8facbfd2a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -34,6 +34,7 @@ import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignatureGenerator; import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor; import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; @@ -478,7 +479,7 @@ public class PgpKeyOperation { PGPPublicKey modifiedPublicKey = masterPublicKey; // 2a. Add certificates for new user ids - subProgressPush(15, 25); + subProgressPush(15, 23); for (int i = 0; i < saveParcel.mAddUserIds.size(); i++) { progress(R.string.progress_modify_adduid, (i - 1) * (100 / saveParcel.mAddUserIds.size())); @@ -522,8 +523,33 @@ public class PgpKeyOperation { } subProgressPop(); - // 2b. Add revocations for revoked user ids - subProgressPush(25, 40); + // 2b. Add certificates for new user ids + subProgressPush(23, 32); + for (int i = 0; i < saveParcel.mAddUserAttribute.size(); i++) { + + progress(R.string.progress_modify_adduat, (i - 1) * (100 / saveParcel.mAddUserAttribute.size())); + WrappedUserAttribute attribute = saveParcel.mAddUserAttribute.get(i); + + switch (attribute.getType()) { + case WrappedUserAttribute.UAT_UNKNOWN: + log.add(LogType.MSG_MF_UAT_ADD_UNKNOWN, indent); + break; + case WrappedUserAttribute.UAT_IMAGE: + log.add(LogType.MSG_MF_UAT_ADD_IMAGE, indent); + break; + } + + PGPUserAttributeSubpacketVector vector = attribute.getVector(); + + // generate and add new certificate + PGPSignature cert = generateUserAttributeSignature(masterPrivateKey, + masterPublicKey, vector); + modifiedPublicKey = PGPPublicKey.addCertification(modifiedPublicKey, vector, cert); + } + subProgressPop(); + + // 2c. Add revocations for revoked user ids + subProgressPush(32, 40); for (int i = 0; i < saveParcel.mRevokeUserIds.size(); i++) { progress(R.string.progress_modify_revokeuid, (i - 1) * (100 / saveParcel.mRevokeUserIds.size())); @@ -1174,6 +1200,26 @@ public class PgpKeyOperation { return sGen.generateCertification(userId, pKey); } + private static PGPSignature generateUserAttributeSignature( + PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, + PGPUserAttributeSubpacketVector vector) + throws IOException, PGPException, SignatureException { + PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( + masterPrivateKey.getPublicKeyPacket().getAlgorithm(), HashAlgorithmTags.SHA512) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); + + PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); + { + /* critical subpackets: we consider those important for a modern pgp implementation */ + hashedPacketsGen.setSignatureCreationTime(true, new Date()); + } + + sGen.setHashedSubpackets(hashedPacketsGen.generate()); + sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); + return sGen.generateCertification(vector, pKey); + } + private static PGPSignature generateRevocationSignature( PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId) throws IOException, PGPException, SignatureException { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index b914f4619..d9de274ee 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.service; import android.os.Parcel; import android.os.Parcelable; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; import java.io.Serializable; @@ -51,7 +52,7 @@ public class SaveKeyringParcel implements Parcelable { public ChangeUnlockParcel mNewUnlock; public ArrayList mAddUserIds; - public ArrayList mAddLinkedIdentity; + public ArrayList mAddUserAttribute; public ArrayList mAddSubKeys; public ArrayList mChangeSubKeys; @@ -74,7 +75,7 @@ public class SaveKeyringParcel implements Parcelable { public void reset() { mNewUnlock = null; mAddUserIds = new ArrayList(); - mAddLinkedIdentity = new ArrayList(); + mAddUserAttribute = new ArrayList(); mAddSubKeys = new ArrayList(); mChangePrimaryUserId = null; mChangeSubKeys = new ArrayList(); @@ -166,7 +167,7 @@ public class SaveKeyringParcel implements Parcelable { mNewUnlock = source.readParcelable(getClass().getClassLoader()); mAddUserIds = source.createStringArrayList(); - mAddLinkedIdentity = (ArrayList) source.readSerializable(); + mAddUserAttribute = (ArrayList) source.readSerializable(); mAddSubKeys = (ArrayList) source.readSerializable(); mChangeSubKeys = (ArrayList) source.readSerializable(); @@ -189,7 +190,7 @@ public class SaveKeyringParcel implements Parcelable { destination.writeParcelable(mNewUnlock, 0); destination.writeStringList(mAddUserIds); - destination.writeSerializable(mAddLinkedIdentity); + destination.writeSerializable(mAddUserAttribute); destination.writeSerializable(mAddSubKeys); destination.writeSerializable(mChangeSubKeys); @@ -220,7 +221,7 @@ public class SaveKeyringParcel implements Parcelable { String out = "mMasterKeyId: " + mMasterKeyId + "\n"; out += "mNewUnlock: " + mNewUnlock + "\n"; out += "mAddUserIds: " + mAddUserIds + "\n"; - out += "mAddLinkedIdentity: " + mAddLinkedIdentity + "\n"; + out += "mAddUserAttribute: " + mAddUserAttribute + "\n"; out += "mAddSubKeys: " + mAddSubKeys + "\n"; out += "mChangeSubKeys: " + mChangeSubKeys + "\n"; out += "mChangePrimaryUserId: " + mChangePrimaryUserId + "\n"; -- cgit v1.2.3 From d51621538ab53e0f4c5a43dc9688a44b5fa2317d Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 12 Jan 2015 21:04:30 +0100 Subject: forgot to commit WrappedUserAttribute --- .../keychain/pgp/WrappedUserAttribute.java | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java new file mode 100644 index 000000000..44db3d361 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014 Vincent Breitmoser + * + * 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.pgp; + +import org.spongycastle.bcpg.UserAttributeSubpacketTags; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; + +public class WrappedUserAttribute { + + public static final int UAT_UNKNOWN = 0; + public static final int UAT_IMAGE = UserAttributeSubpacketTags.IMAGE_ATTRIBUTE; + public static final int UAT_LINKED_ID = 100; + + final private PGPUserAttributeSubpacketVector mVector; + + WrappedUserAttribute(PGPUserAttributeSubpacketVector vector) { + mVector = vector; + } + + PGPUserAttributeSubpacketVector getVector() { + return mVector; + } + + public int getType() { + if (mVector.getSubpacket(UserAttributeSubpacketTags.IMAGE_ATTRIBUTE) != null) { + return UAT_IMAGE; + } + return 0; + } + +} -- cgit v1.2.3 From e71d62084017ff7c3c38f32ea27a78e5f1fa1a69 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 13 Jan 2015 00:09:48 +0100 Subject: some work on twitter resource --- .../pgp/affirmation/resources/TwitterResource.java | 7 +- .../AffirmationCreateDnsStep1Fragment.java | 125 +++++++++++ .../AffirmationCreateTwitterStep1Fragment.java | 132 +++++++++++ .../AffirmationCreateTwitterStep2Fragment.java | 142 ++++++++++++ .../AffirmationCreateTwitterStep3Fragment.java | 243 +++++++++++++++++++++ .../ui/affirmations/AffirmationSelectFragment.java | 22 ++ 6 files changed, 670 insertions(+), 1 deletion(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep3Fragment.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java index b426c16b9..f131a8da2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java @@ -1,7 +1,7 @@ package org.sufficientlysecure.keychain.pgp.affirmation.resources; +import android.content.Context; import android.util.Base64; -import android.util.JsonReader; import com.textuality.keybase.lib.JWalk; @@ -35,6 +35,11 @@ public class TwitterResource extends AffirmationResource { super(flags, params, uri); } + public static String generateText (Context context, byte[] fingerprint, String nonce) { + // nothing special here for now, might change this later + return AffirmationResource.generate(context, fingerprint, nonce); + } + private String getTwitterStream(String screenName) { String results = null; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java new file mode 100644 index 000000000..fedbe120d --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.affirmations; + +import android.os.Bundle; +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.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; +import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; + +public class AffirmationCreateDnsStep1Fragment extends Fragment { + + AffirmationWizard mAffirmationWizard; + + EditText mEditUri; + + /** + * Creates new instance of this fragment + */ + public static AffirmationCreateDnsStep1Fragment newInstance() { + AffirmationCreateDnsStep1Fragment frag = new AffirmationCreateDnsStep1Fragment(); + + Bundle args = new Bundle(); + frag.setArguments(args); + + return frag; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mAffirmationWizard = (AffirmationWizard) getActivity(); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.affirmation_create_dns_fragment_step1, container, false); + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + + String uri = "https://" + mEditUri.getText(); + + if (!checkUri(uri)) { + return; + } + + String proofNonce = LinkedIdentity.generateNonce(); + String proofText = GenericHttpsResource.generateText(getActivity(), + mAffirmationWizard.mFingerprint, proofNonce); + + AffirmationCreateHttpsStep2Fragment frag = + AffirmationCreateHttpsStep2Fragment.newInstance(uri, proofNonce, proofText); + + mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + + } + }); + + mEditUri = (EditText) view.findViewById(R.id.affirmation_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.uid_mail_ok, 0); + } else { + mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0, + R.drawable.uid_mail_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/affirmations/AffirmationCreateTwitterStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java new file mode 100644 index 000000000..6b47631c0 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.affirmations; + +import android.os.AsyncTask; +import android.os.Bundle; +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.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; +import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; +import org.sufficientlysecure.keychain.pgp.affirmation.resources.TwitterResource; +import org.sufficientlysecure.keychain.ui.util.Notify; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; + +public class AffirmationCreateTwitterStep1Fragment extends Fragment { + + AffirmationWizard mAffirmationWizard; + + EditText mEditHandle; + + /** + * Creates new instance of this fragment + */ + public static AffirmationCreateTwitterStep1Fragment newInstance() { + AffirmationCreateTwitterStep1Fragment frag = new AffirmationCreateTwitterStep1Fragment(); + + Bundle args = new Bundle(); + frag.setArguments(args); + + return frag; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mAffirmationWizard = (AffirmationWizard) getActivity(); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.affirmation_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(); + + new AsyncTask() { + + @Override + protected Boolean doInBackground(Void... params) { + return true; // checkHandle(handle); + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + + if (result == null) { + Notify.showNotify(getActivity(), "Connection error while checking username!", Notify.Style.ERROR); + return; + } + + if (!result) { + Notify.showNotify(getActivity(), "This handle does not exist on Twitter!", Notify.Style.ERROR); + return; + } + + String proofNonce = LinkedIdentity.generateNonce(); + String proofText = TwitterResource.generateText(getActivity(), + mAffirmationWizard.mFingerprint, proofNonce); + + AffirmationCreateTwitterStep2Fragment frag = + AffirmationCreateTwitterStep2Fragment.newInstance(handle, proofNonce, proofText); + + mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + } + }.execute(); + + } + }); + + mEditHandle = (EditText) view.findViewById(R.id.linked_create_twitter_handle); + mEditHandle.setText("Valodim"); + + return view; + } + + private static Boolean checkHandle(String handle) { + try { + HttpURLConnection nection = + (HttpURLConnection) new URL("https://twitter.com/" + handle).openConnection(); + nection.setRequestMethod("HEAD"); + return nection.getResponseCode() == 200; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java new file mode 100644 index 000000000..8367d750b --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.affirmations; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.text.Editable; +import android.text.InputFilter; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; + +public class AffirmationCreateTwitterStep2Fragment extends Fragment { + + private static final int REQUEST_CODE_OUTPUT = 0x00007007; + + public static final String HANDLE = "uri", NONCE = "nonce", TEXT = "text"; + + AffirmationWizard mAffirmationWizard; + + EditText mEditTweetCustom, mEditTweetPreview; + ImageView mVerifyImage; + View mVerifyProgress; + TextView mVerifyStatus, mEditTweetTextLen; + + String mResourceHandle; + String mResourceNonce, mResourceString; + + /** + * Creates new instance of this fragment + */ + public static AffirmationCreateTwitterStep2Fragment newInstance + (String handle, String proofNonce, String proofText) { + + AffirmationCreateTwitterStep2Fragment frag = new AffirmationCreateTwitterStep2Fragment(); + + Bundle args = new Bundle(); + args.putString(HANDLE, handle); + args.putString(NONCE, proofNonce); + args.putString(TEXT, proofText); + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.affirmation_create_twitter_fragment_step2, container, false); + + mResourceHandle = getArguments().getString(HANDLE); + mResourceNonce = getArguments().getString(NONCE); + mResourceString = getArguments().getString(TEXT); + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + + AffirmationCreateTwitterStep3Fragment frag = + AffirmationCreateTwitterStep3Fragment.newInstance(mResourceHandle, + mResourceNonce, mResourceString, + mEditTweetCustom.getText().toString()); + + mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + } + }); + + mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); + mVerifyProgress = view.findViewById(R.id.verify_progress); + mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); + + mEditTweetPreview = (EditText) view.findViewById(R.id.linked_create_twitter_preview); + mEditTweetPreview.setText(mResourceString); + + mEditTweetCustom = (EditText) view.findViewById(R.id.linked_create_twitter_custom); + mEditTweetCustom.setFilters(new InputFilter[] { + new InputFilter.LengthFilter(139 - mResourceString.length()) + }); + + mEditTweetTextLen = (TextView) view.findViewById(R.id.linked_create_twitter_textlen); + mEditTweetTextLen.setText(mResourceString.length() + "/140"); + + mEditTweetCustom.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) { + if (editable != null && editable.length() > 0) { + String str = editable + " " + mResourceString; + mEditTweetPreview.setText(str); + + mEditTweetTextLen.setText(str.length() + "/140"); + mEditTweetTextLen.setTextColor(getResources().getColor(str.length() == 140 + ? R.color.android_red_dark + : R.color.primary_dark_material_light)); + + + } else { + mEditTweetPreview.setText(mResourceString); + mEditTweetTextLen.setText(mResourceString.length() + "/140"); + } + } + }); + + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mAffirmationWizard = (AffirmationWizard) getActivity(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep3Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep3Fragment.java new file mode 100644 index 000000000..3b4c38893 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep3Fragment.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.affirmations; + +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.PorterDuff; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +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 android.widget.ImageView; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; +import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; +import org.sufficientlysecure.keychain.pgp.affirmation.resources.TwitterResource; +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; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +public class AffirmationCreateTwitterStep3Fragment extends Fragment { + + public static final String HANDLE = "uri", NONCE = "nonce", TEXT = "text", CUSTOM = "custom"; + + AffirmationWizard mAffirmationWizard; + + EditText mEditTweetPreview; + ImageView mVerifyImage; + View mVerifyProgress; + TextView mVerifyStatus; + + String mResourceHandle, mCustom, mFullString; + String mResourceNonce, mResourceString; + + /** + * Creates new instance of this fragment + */ + public static AffirmationCreateTwitterStep3Fragment newInstance + (String handle, String proofNonce, String proofText, String customText) { + + AffirmationCreateTwitterStep3Fragment frag = new AffirmationCreateTwitterStep3Fragment(); + + Bundle args = new Bundle(); + args.putString(HANDLE, handle); + args.putString(NONCE, proofNonce); + args.putString(TEXT, proofText); + args.putString(CUSTOM, customText); + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.affirmation_create_twitter_fragment_step3, container, false); + + mResourceHandle = getArguments().getString(HANDLE); + mResourceNonce = getArguments().getString(NONCE); + mResourceString = getArguments().getString(TEXT); + mCustom = getArguments().getString(CUSTOM); + + mFullString = mCustom.isEmpty() ? mResourceString : (mCustom + " " + mResourceString); + + mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); + mVerifyProgress = view.findViewById(R.id.verify_progress); + mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); + + mEditTweetPreview = (EditText) view.findViewById(R.id.linked_create_twitter_preview); + mEditTweetPreview.setText(mFullString); + + 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(); + } + }); + + view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofVerify(); + } + }); + + setVerifyProgress(false, null); + mVerifyStatus.setText(R.string.linked_verify_pending); + + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + + // AffirmationCreateHttpsStep2Fragment frag = + // AffirmationCreateHttpsStep2Fragment.newInstance(); + + // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + } + }); + + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mAffirmationWizard = (AffirmationWizard) getActivity(); + } + + public void setVerifyProgress(boolean on, Boolean success) { + mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); + mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); + if (success == null) { + mVerifyStatus.setText(R.string.linked_verifying); + mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), + PorterDuff.Mode.SRC_IN); + } else if (success) { + mVerifyStatus.setText(R.string.linked_verify_success); + mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark), + PorterDuff.Mode.SRC_IN); + } else { + mVerifyStatus.setText(R.string.linked_verify_error); + mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), + PorterDuff.Mode.SRC_IN); + } + } + + public void proofVerify() { + setVerifyProgress(true, null); + + /* + try { + final TwitterResource resource = TwitterResource.createNew(new URI(mResourceHandle)); + + new AsyncTask() { + + @Override + protected LinkedVerifyResult doInBackground(Void... params) { + return resource.verify(mAffirmationWizard.mFingerprint, mResourceNonce); + } + + @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(); + } + } + }.execute(); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + */ + + } + + private void proofShare() { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, mFullString); + sendIntent.setType("text/plain"); + startActivity(sendIntent); + } + + private void proofSend() { + + Intent tweetIntent = new Intent(Intent.ACTION_SEND); + tweetIntent.putExtra(Intent.EXTRA_TEXT, mFullString); + tweetIntent.setType("text/plain"); + + PackageManager packManager = getActivity().getPackageManager(); + List resolvedInfoList = packManager.queryIntentActivities(tweetIntent, + PackageManager.MATCH_DEFAULT_ONLY); + + boolean resolved = false; + for(ResolveInfo resolveInfo : resolvedInfoList){ + if(resolveInfo.activityInfo.packageName.startsWith("com.twitter.android")) { + tweetIntent.setClassName( + resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name ); + resolved = true; + break; + } + } + + if (resolved) { + startActivity(tweetIntent); + } else { + Notify.showNotify(getActivity(), + "Twitter app is not installed, please use the send intent!", + Notify.Style.ERROR); + } + + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationSelectFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationSelectFragment.java index 784e75789..c3667b5a2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationSelectFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationSelectFragment.java @@ -56,6 +56,28 @@ public class AffirmationSelectFragment extends Fragment { } }); + view.findViewById(R.id.affirmation_create_dns_button) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + AffirmationCreateDnsStep1Fragment frag = + AffirmationCreateDnsStep1Fragment.newInstance(); + + mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + } + }); + + view.findViewById(R.id.affirmation_create_twitter_button) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + AffirmationCreateTwitterStep1Fragment frag = + AffirmationCreateTwitterStep1Fragment.newInstance(); + + mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + } + }); + return view; } -- cgit v1.2.3 From f48b8121bd7bc12274e1e3476e04415af96bead0 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 13 Jan 2015 19:06:38 +0100 Subject: add back button to wizard fragments --- .../ui/affirmations/AffirmationCreateDnsStep1Fragment.java | 7 +++++++ .../ui/affirmations/AffirmationCreateHttpsStep1Fragment.java | 7 +++++++ .../ui/affirmations/AffirmationCreateHttpsStep2Fragment.java | 7 +++++++ .../ui/affirmations/AffirmationCreateTwitterStep1Fragment.java | 7 +++++++ .../ui/affirmations/AffirmationCreateTwitterStep2Fragment.java | 7 +++++++ .../ui/affirmations/AffirmationCreateTwitterStep3Fragment.java | 7 +++++++ 6 files changed, 42 insertions(+) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java index fedbe120d..4b23f0f06 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java @@ -84,6 +84,13 @@ public class AffirmationCreateDnsStep1Fragment extends Fragment { } }); + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mAffirmationWizard.loadFragment(null, null, AffirmationWizard.FRAG_ACTION_TO_LEFT); + } + }); + mEditUri = (EditText) view.findViewById(R.id.affirmation_create_https_uri); mEditUri.addTextChangedListener(new TextWatcher() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java index 7d0ba8937..2b00d7483 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java @@ -84,6 +84,13 @@ public class AffirmationCreateHttpsStep1Fragment extends Fragment { } }); + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mAffirmationWizard.loadFragment(null, null, AffirmationWizard.FRAG_ACTION_TO_LEFT); + } + }); + mEditUri = (EditText) view.findViewById(R.id.affirmation_create_https_uri); mEditUri.addTextChangedListener(new TextWatcher() { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java index 55a6be40c..2f9b77f4a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java @@ -99,6 +99,13 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { } }); + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mAffirmationWizard.loadFragment(null, null, AffirmationWizard.FRAG_ACTION_TO_LEFT); + } + }); + mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); mVerifyProgress = view.findViewById(R.id.verify_progress); mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java index 6b47631c0..995823499 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java @@ -111,6 +111,13 @@ public class AffirmationCreateTwitterStep1Fragment extends Fragment { } }); + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mAffirmationWizard.loadFragment(null, null, AffirmationWizard.FRAG_ACTION_TO_LEFT); + } + }); + mEditHandle = (EditText) view.findViewById(R.id.linked_create_twitter_handle); mEditHandle.setText("Valodim"); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java index 8367d750b..fad8cadd4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java @@ -86,6 +86,13 @@ public class AffirmationCreateTwitterStep2Fragment extends Fragment { } }); + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mAffirmationWizard.loadFragment(null, null, AffirmationWizard.FRAG_ACTION_TO_LEFT); + } + }); + mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); mVerifyProgress = view.findViewById(R.id.verify_progress); mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep3Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep3Fragment.java index 3b4c38893..5ceda3342 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep3Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep3Fragment.java @@ -101,6 +101,13 @@ public class AffirmationCreateTwitterStep3Fragment extends Fragment { mEditTweetPreview = (EditText) view.findViewById(R.id.linked_create_twitter_preview); mEditTweetPreview.setText(mFullString); + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mAffirmationWizard.loadFragment(null, null, AffirmationWizard.FRAG_ACTION_TO_LEFT); + } + }); + view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { -- cgit v1.2.3 From 189c321528b98b1a6d74d016806f63cd49ef5b08 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 13 Jan 2015 20:34:19 +0100 Subject: hack to make WrappedUserAttribute serializable --- .../keychain/pgp/WrappedUserAttribute.java | 49 +++++++++++++++++++++- 1 file changed, 47 insertions(+), 2 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java index 44db3d361..5c5bf0864 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java @@ -18,16 +18,27 @@ package org.sufficientlysecure.keychain.pgp; +import org.spongycastle.bcpg.BCPGInputStream; +import org.spongycastle.bcpg.BCPGOutputStream; +import org.spongycastle.bcpg.Packet; +import org.spongycastle.bcpg.UserAttributePacket; +import org.spongycastle.bcpg.UserAttributeSubpacket; import org.spongycastle.bcpg.UserAttributeSubpacketTags; import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; -public class WrappedUserAttribute { +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectStreamException; +import java.io.Serializable; + +public class WrappedUserAttribute implements Serializable { public static final int UAT_UNKNOWN = 0; public static final int UAT_IMAGE = UserAttributeSubpacketTags.IMAGE_ATTRIBUTE; public static final int UAT_LINKED_ID = 100; - final private PGPUserAttributeSubpacketVector mVector; + private PGPUserAttributeSubpacketVector mVector; WrappedUserAttribute(PGPUserAttributeSubpacketVector vector) { mVector = vector; @@ -44,4 +55,38 @@ public class WrappedUserAttribute { return 0; } + public static WrappedUserAttribute fromSubpacket (int type, byte[] data) { + UserAttributeSubpacket subpacket = new UserAttributeSubpacket(type, data); + PGPUserAttributeSubpacketVector vector = new PGPUserAttributeSubpacketVector( + new UserAttributeSubpacket[] { subpacket }); + + return new WrappedUserAttribute(vector); + + } + + /** Writes this object to an ObjectOutputStream. */ + private void writeObject(java.io.ObjectOutputStream out) throws IOException { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + BCPGOutputStream bcpg = new BCPGOutputStream(baos); + bcpg.writePacket(new UserAttributePacket(mVector.toSubpacketArray())); + out.writeObject(baos.toByteArray()); + + } + + private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { + + byte[] data = (byte[]) in.readObject(); + BCPGInputStream bcpg = new BCPGInputStream(new ByteArrayInputStream(data)); + Packet p = bcpg.readPacket(); + if ( ! UserAttributePacket.class.isInstance(p)) { + throw new IOException("Could not decode UserAttributePacket!"); + } + mVector = new PGPUserAttributeSubpacketVector(((UserAttributePacket) p).getSubpackets()); + + } + + private void readObjectNoData() throws ObjectStreamException { + } + } -- cgit v1.2.3 From 08ea4e1b7e0547bdc3049125817710f226550a55 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 13 Jan 2015 20:35:27 +0100 Subject: add support for user attributes in merge() routine --- .../keychain/pgp/UncachedKeyRing.java | 30 ++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index 404228a3e..04fb955fa 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -1018,8 +1018,8 @@ public class UncachedKeyRing { /** This operation merges information from a different keyring, returning a combined * UncachedKeyRing. * - * The combined keyring contains the subkeys and user ids of both input keyrings, but it does - * not necessarily have the canonicalized property. + * The combined keyring contains the subkeys, user ids and user attributes of both input + * keyrings, but it does not necessarily have the canonicalized property. * * @param other The UncachedKeyRing to merge. Must not be empty, and of the same masterKeyId * @return A consolidated UncachedKeyRing with the data of both input keyrings. Same type as @@ -1139,6 +1139,32 @@ public class UncachedKeyRing { modified = PGPPublicKey.addCertification(modified, rawUserId, cert); } } + + // Copy over all user attribute certificates + for (PGPUserAttributeSubpacketVector vector : + new IterableIterator(key.getUserAttributes())) { + @SuppressWarnings("unchecked") + Iterator signaturesIt = key.getSignaturesForUserAttribute(vector); + // no signatures for this user attribute attribute, skip it + if (signaturesIt == null) { + continue; + } + for (PGPSignature cert : new IterableIterator(signaturesIt)) { + // Don't merge foreign stuff into secret keys + if (cert.getKeyID() != masterKeyId && isSecret()) { + continue; + } + byte[] encoded = cert.getEncoded(); + // Known cert, skip it + if (certs.contains(encoded)) { + continue; + } + newCerts += 1; + certs.add(encoded); + modified = PGPPublicKey.addCertification(modified, vector, cert); + } + } + // If anything changed, save the updated (sub)key if (modified != resultKey) { result = replacePublicKey(result, modified); -- cgit v1.2.3 From 4b5de13e4297d688901e8070f627a9a213097bb5 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 13 Jan 2015 20:36:37 +0100 Subject: certification of the first linked identity packet! --- .../pgp/affirmation/AffirmationResource.java | 17 +- .../keychain/pgp/affirmation/LinkedIdentity.java | 22 ++- .../resources/GenericHttpsResource.java | 5 +- .../keychain/ui/PassphraseDialogActivity.java | 3 + .../AffirmationCreateHttpsStep2Fragment.java | 184 +++++++++++++++++---- .../AffirmationCreateTwitterStep1Fragment.java | 1 + .../AffirmationCreateTwitterStep2Fragment.java | 1 + 7 files changed, 188 insertions(+), 45 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java index 45919a89a..ffe89931a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java @@ -13,13 +13,14 @@ 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.Matcher; import java.util.regex.Pattern; public abstract class AffirmationResource { - protected final URI mUri; + protected final URI mSubUri; protected final Set mFlags; protected final HashMap mParams; @@ -29,7 +30,19 @@ public abstract class AffirmationResource { protected AffirmationResource(Set flags, HashMap params, URI uri) { mFlags = flags; mParams = params; - mUri = uri; + mSubUri = uri; + } + + public Set getFlags () { + return new HashSet(mFlags); + } + + public HashMap getParams () { + return new HashMap(mParams); + } + + public URI getSubUri () { + return mSubUri; } public static String generate (Context context, byte[] fingerprint, String nonce) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java index 1e27b2c64..ee9933da3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java @@ -1,12 +1,13 @@ package org.sufficientlysecure.keychain.pgp.affirmation; import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; import org.spongycastle.util.Strings; import org.spongycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.util.Log; -import java.io.Serializable; import java.net.URI; import java.util.Arrays; import java.util.HashMap; @@ -15,7 +16,7 @@ import java.util.Iterator; import java.util.Map.Entry; import java.util.Set; -public class LinkedIdentity implements Serializable { +public class LinkedIdentity { protected byte[] mData; public final String mNonce; @@ -41,7 +42,7 @@ public class LinkedIdentity implements Serializable { this(null, nonce, flags, params, subUri); } - public byte[] encode() { + public byte[] getEncoded() { if (mData != null) { return mData; } @@ -79,11 +80,14 @@ public class LinkedIdentity implements Serializable { b.append(mSubUri); byte[] nonceBytes = Hex.decode(mNonce); + if (nonceBytes.length != 12) { + throw new AssertionError("nonce must be 12 bytes"); + } byte[] data = Strings.toUTF8ByteArray(b.toString()); byte[] result = new byte[data.length+12]; System.arraycopy(nonceBytes, 0, result, 0, 12); - System.arraycopy(data, 0, result, 12, result.length); + System.arraycopy(data, 0, result, 12, data.length); return result; } @@ -91,7 +95,7 @@ public class LinkedIdentity implements Serializable { /** This method parses an affirmation from a UserAttributeSubpacket, or returns null if the * subpacket can not be parsed as a valid affirmation. */ - public static LinkedIdentity parseAffirmation(UserAttributeSubpacket subpacket) { + static LinkedIdentity parseAffirmation(UserAttributeSubpacket subpacket) { if (subpacket.getType() != 100) { return null; } @@ -148,6 +152,14 @@ public class LinkedIdentity implements Serializable { } + public static LinkedIdentity fromResource (AffirmationResource res, String nonce) { + return new LinkedIdentity(nonce, res.getFlags(), res.getParams(), res.getSubUri()); + } + + public WrappedUserAttribute toUserAttribute () { + return WrappedUserAttribute.fromSubpacket(WrappedUserAttribute.UAT_LINKED_ID, getEncoded()); + } + public static String generateNonce() { // TODO make this actually random // byte[] data = new byte[96]; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java index 8f4d0c41b..74c0689b5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java @@ -9,6 +9,7 @@ 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.affirmation.AffirmationResource; +import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; @@ -38,13 +39,13 @@ public class GenericHttpsResource extends AffirmationResource { @Override protected String fetchResource (OperationLog log, int indent) { - log.add(LogType.MSG_LV_FETCH, indent, mUri.toString()); + log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); indent += 1; try { HttpsURLConnection conn = null; - URL url = mUri.toURL(); + URL url = mSubUri.toURL(); int status = 0; int redirects = 0; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java index fd9324992..5bface1fb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -214,6 +214,9 @@ public class PassphraseDialogActivity extends FragmentActivity { case DIVERT_TO_CARD: message = getString(R.string.yubikey_pin_for, userId); break; + // special case: empty passphrase just returns the empty passphrase + case PASSPHRASE_EMPTY: + finishCaching(""); default: message = "This should not happen!"; break; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java index 2f9b77f4a..eb1088989 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java @@ -17,6 +17,8 @@ package org.sufficientlysecure.keychain.ui.affirmations; +import android.app.Activity; +import android.app.ProgressDialog; import android.content.Intent; import android.graphics.PorterDuff; import android.net.Uri; @@ -24,6 +26,8 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Environment; +import android.os.Message; +import android.os.Messenger; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; @@ -36,7 +40,16 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.ui.EditKeyActivity; +import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.util.FileHelper; @@ -50,6 +63,7 @@ import java.net.URISyntaxException; public class AffirmationCreateHttpsStep2Fragment extends Fragment { private static final int REQUEST_CODE_OUTPUT = 0x00007007; + private static final int REQUEST_CODE_PASSPHRASE = 0x00007008; public static final String URI = "uri", NONCE = "nonce", TEXT = "text"; @@ -63,6 +77,9 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { String mResourceUri; String mResourceNonce, mResourceString; + // This is a resource, set AFTER it has been verified + GenericHttpsResource mVerifiedResource = null; + /** * Creates new instance of this fragment */ @@ -91,11 +108,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - - // AffirmationCreateHttpsStep2Fragment frag = - // AffirmationCreateHttpsStep2Fragment.newInstance(); - - // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + startCertify(); } }); @@ -149,7 +162,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { public void setVerifyProgress(boolean on, Boolean success) { mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); - mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); + mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); if (success == null) { mVerifyStatus.setText(R.string.linked_verifying); mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout); @@ -168,6 +181,46 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { } } + private void proofSend () { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); + sendIntent.setType("text/plain"); + startActivity(sendIntent); + } + + private void proofSave () { + String state = Environment.getExternalStorageState(); + if (!Environment.MEDIA_MOUNTED.equals(state)) { + Notify.showNotify(getActivity(), "External storage not available!", Style.ERROR); + return; + } + + String targetName = "pgpkey.txt"; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + File targetFile = new File(Constants.Path.APP_DIR, targetName); + FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file), + getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT); + } else { + FileHelper.saveDocument(this, "text/plain", targetName, REQUEST_CODE_OUTPUT); + } + } + + private void saveFile(Uri uri) { + try { + PrintWriter out = + new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); + out.print(mResourceString); + if (out.checkError()) { + Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR); + } + } catch (FileNotFoundException e) { + Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR); + e.printStackTrace(); + } + } + public void proofVerify() { setVerifyProgress(true, null); @@ -186,6 +239,7 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { super.onPostExecute(result); if (result.success()) { setVerifyProgress(false, true); + mVerifiedResource = resource; } else { setVerifyProgress(false, false); // on error, show error message @@ -199,49 +253,100 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { } - private void proofSend() { - Intent sendIntent = new Intent(); - sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); - sendIntent.setType("text/plain"); - startActivity(sendIntent); - } + public void startCertify() { - private void proofSave () { - String state = Environment.getExternalStorageState(); - if (!Environment.MEDIA_MOUNTED.equals(state)) { - Notify.showNotify(getActivity(), "External storage not available!", Style.ERROR); + if (mVerifiedResource == null) { + Notify.showNotify(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR); return; } - String targetName = "pgpkey.txt"; + Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); + intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mAffirmationWizard.mMasterKeyId); + startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - File targetFile = new File(Constants.Path.APP_DIR, targetName); - FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file), - getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT); - } else { - FileHelper.saveDocument(this, "text/plain", targetName, REQUEST_CODE_OUTPUT); - } } - private void saveFile(Uri uri) { - try { - PrintWriter out = - new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); - out.print(mResourceString); - if (out.checkError()) { - Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR); + public void certifyLinkedIdentity (String passphrase) { + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( + getActivity(), + getString(R.string.progress_saving), + ProgressDialog.STYLE_HORIZONTAL, + true) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + + // get returned data bundle + Bundle returnData = message.getData(); + if (returnData == null) { + return; + } + final OperationResult result = + returnData.getParcelable(OperationResult.EXTRA_RESULT); + if (result == null) { + return; + } + + // if bad -> display here! + if (!result.success()) { + result.createNotify(getActivity()).show(); + return; + } + + result.createNotify(getActivity()).show(); + + // if good -> finish, return result to showkey and display there! + // Intent intent = new Intent(); + // intent.putExtra(OperationResult.EXTRA_RESULT, result); + // getActivity().setResult(EditKeyActivity.RESULT_OK, intent); + + // AffirmationCreateHttpsStep3Fragment frag = + // AffirmationCreateHttpsStep3Fragment.newInstance( + // mResourceUri, mResourceNonce, mResourceString); + + // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + + } } - } catch (FileNotFoundException e) { - Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR); - e.printStackTrace(); - } + }; + + SaveKeyringParcel skp = + new SaveKeyringParcel(mAffirmationWizard.mMasterKeyId, mAffirmationWizard.mFingerprint); + + WrappedUserAttribute ua = + LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); + + skp.mAddUserAttribute.add(ua); + + // Send all information needed to service to import key in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING); + + // fill values for this action + Bundle data = new Bundle(); + data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); + data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, skp); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + } + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { + // For saving a file case REQUEST_CODE_OUTPUT: if (data == null) { return; @@ -249,6 +354,13 @@ public class AffirmationCreateHttpsStep2Fragment extends Fragment { Uri uri = data.getData(); saveFile(uri); break; + case REQUEST_CODE_PASSPHRASE: + if (resultCode == Activity.RESULT_OK && data != null) { + String passphrase = + data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + certifyLinkedIdentity(passphrase); + } + break; } super.onActivityResult(requestCode, resultCode, data); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java index 995823499..f52a18807 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java @@ -33,6 +33,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; import org.sufficientlysecure.keychain.pgp.affirmation.resources.TwitterResource; +import org.sufficientlysecure.keychain.ui.CreateKeyActivity; import org.sufficientlysecure.keychain.ui.util.Notify; import java.io.IOException; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java index fad8cadd4..e12d2f86b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java @@ -31,6 +31,7 @@ import android.widget.ImageView; import android.widget.TextView; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.CreateKeyActivity; public class AffirmationCreateTwitterStep2Fragment extends Fragment { -- cgit v1.2.3 From 8b0e04b9dfd36a4bb8740b76aca5eac36ac35a58 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 13 Jan 2015 23:09:48 +0100 Subject: make user_ids table typed, with attribute_data support --- .../keychain/provider/KeychainContract.java | 10 ++- .../keychain/provider/KeychainDatabase.java | 35 ++++---- .../keychain/provider/KeychainProvider.java | 92 ++++++++++++++-------- .../keychain/provider/ProviderHelper.java | 19 +++-- .../keychain/ui/CertifyKeyFragment.java | 22 +++--- .../keychain/ui/EditKeyFragment.java | 3 +- .../keychain/ui/ViewKeyMainFragment.java | 4 +- .../keychain/ui/adapter/UserIdsAdapter.java | 16 ++-- .../keychain/util/ContactHelper.java | 7 +- 9 files changed, 120 insertions(+), 88 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 2c02e429d..f4e00c36c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -51,9 +51,11 @@ public class KeychainContract { String EXPIRY = "expiry"; } - interface UserIdsColumns { + interface UserPacketsColumns { String MASTER_KEY_ID = "master_key_id"; // foreign key to key_rings._ID + String TYPE = "type"; // not a database id String USER_ID = "user_id"; // not a database id + String ATTRIBUTE_DATA = "attribute_data"; // not a database id String RANK = "rank"; // ONLY used for sorting! no key, no nothing! String IS_PRIMARY = "is_primary"; String IS_REVOKED = "is_revoked"; @@ -105,7 +107,7 @@ public class KeychainContract { public static final String BASE_API_APPS = "api_apps"; public static final String PATH_ACCOUNTS = "accounts"; - public static class KeyRings implements BaseColumns, KeysColumns, UserIdsColumns { + public static class KeyRings implements BaseColumns, KeysColumns, UserPacketsColumns { public static final String MASTER_KEY_ID = KeysColumns.MASTER_KEY_ID; public static final String IS_REVOKED = KeysColumns.IS_REVOKED; public static final String VERIFIED = CertsColumns.VERIFIED; @@ -225,7 +227,7 @@ public class KeychainContract { } - public static class UserIds implements UserIdsColumns, BaseColumns { + public static class UserPackets implements UserPacketsColumns, BaseColumns { public static final String VERIFIED = "verified"; public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_KEY_RINGS).build(); @@ -304,7 +306,7 @@ public class KeychainContract { } public static class Certs implements CertsColumns, BaseColumns { - public static final String USER_ID = UserIdsColumns.USER_ID; + public static final String USER_ID = UserPacketsColumns.USER_ID; public static final String SIGNER_UID = "signer_user_id"; public static final int UNVERIFIED = 0; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 84a50dc65..5ce5eec17 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -33,7 +33,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns; import org.sufficientlysecure.keychain.ui.ConsolidateDialogActivity; import org.sufficientlysecure.keychain.util.Log; @@ -52,7 +52,7 @@ import java.io.IOException; */ public class KeychainDatabase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "openkeychain.db"; - private static final int DATABASE_VERSION = 6; + private static final int DATABASE_VERSION = 7; static Boolean apgHack = false; private Context mContext; @@ -60,7 +60,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { String KEY_RINGS_PUBLIC = "keyrings_public"; String KEY_RINGS_SECRET = "keyrings_secret"; String KEYS = "keys"; - String USER_IDS = "user_ids"; + String USER_PACKETS = "user_ids"; String CERTS = "certs"; String API_APPS = "api_apps"; String API_ACCOUNTS = "api_accounts"; @@ -106,18 +106,20 @@ public class KeychainDatabase extends SQLiteOpenHelper { + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE" + ")"; - private static final String CREATE_USER_IDS = - "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS + "(" - + UserIdsColumns.MASTER_KEY_ID + " INTEGER, " - + UserIdsColumns.USER_ID + " TEXT, " + private static final String CREATE_USER_PACKETS = + "CREATE TABLE IF NOT EXISTS " + Tables.USER_PACKETS + "(" + + UserPacketsColumns.MASTER_KEY_ID + " INTEGER, " + + UserPacketsColumns.TYPE + " INT, " + + UserPacketsColumns.USER_ID + " TEXT, " + + UserPacketsColumns.ATTRIBUTE_DATA + " BLOB, " - + UserIdsColumns.IS_PRIMARY + " INTEGER, " - + UserIdsColumns.IS_REVOKED + " INTEGER, " - + UserIdsColumns.RANK+ " INTEGER, " + + UserPacketsColumns.IS_PRIMARY + " INTEGER, " + + UserPacketsColumns.IS_REVOKED + " INTEGER, " + + UserPacketsColumns.RANK+ " INTEGER, " - + "PRIMARY KEY(" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.USER_ID + "), " - + "UNIQUE (" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.RANK + "), " - + "FOREIGN KEY(" + UserIdsColumns.MASTER_KEY_ID + ") REFERENCES " + + "PRIMARY KEY(" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.USER_ID + "), " + + "UNIQUE (" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.RANK + "), " + + "FOREIGN KEY(" + UserPacketsColumns.MASTER_KEY_ID + ") REFERENCES " + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE" + ")"; @@ -138,7 +140,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { + "FOREIGN KEY(" + CertsColumns.MASTER_KEY_ID + ") REFERENCES " + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE," + "FOREIGN KEY(" + CertsColumns.MASTER_KEY_ID + ", " + CertsColumns.RANK + ") REFERENCES " - + Tables.USER_IDS + "(" + UserIdsColumns.MASTER_KEY_ID + ", " + UserIdsColumns.RANK + ") ON DELETE CASCADE" + + Tables.USER_PACKETS + "(" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.RANK + ") ON DELETE CASCADE" + ")"; private static final String CREATE_API_APPS = @@ -189,7 +191,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { db.execSQL(CREATE_KEYRINGS_PUBLIC); db.execSQL(CREATE_KEYRINGS_SECRET); db.execSQL(CREATE_KEYS); - db.execSQL(CREATE_USER_IDS); + db.execSQL(CREATE_USER_PACKETS); db.execSQL(CREATE_CERTS); db.execSQL(CREATE_API_APPS); db.execSQL(CREATE_API_APPS_ACCOUNTS); @@ -238,6 +240,9 @@ public class KeychainDatabase extends SQLiteOpenHelper { case 5: // do consolidate for 3.0 beta3 // fall through + case 6: + db.execSQL("ALTER TABLE user_ids ADD COLUMN type INTEGER"); + db.execSQL("ALTER TABLE user_ids ADD COLUMN attribute_data BLOB"); } // always do consolidate after upgrade diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index d40287690..13a923f03 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -37,7 +37,8 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.util.Log; @@ -205,7 +206,7 @@ public class KeychainProvider extends ContentProvider { return Keys.CONTENT_TYPE; case KEY_RING_USER_IDS: - return UserIds.CONTENT_TYPE; + return UserPackets.CONTENT_TYPE; case KEY_RING_SECRET: return KeyRings.CONTENT_ITEM_TYPE; @@ -262,7 +263,7 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(KeyRings.EXPIRY, Tables.KEYS + "." + Keys.EXPIRY); projectionMap.put(KeyRings.ALGORITHM, Tables.KEYS + "." + Keys.ALGORITHM); projectionMap.put(KeyRings.FINGERPRINT, Tables.KEYS + "." + Keys.FINGERPRINT); - projectionMap.put(KeyRings.USER_ID, UserIds.USER_ID); + projectionMap.put(KeyRings.USER_ID, UserPackets.USER_ID); projectionMap.put(KeyRings.VERIFIED, KeyRings.VERIFIED); projectionMap.put(KeyRings.PUBKEY_DATA, Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.KEY_RING_DATA @@ -296,11 +297,12 @@ public class KeychainProvider extends ContentProvider { qb.setTables( Tables.KEYS - + " INNER JOIN " + Tables.USER_IDS + " ON (" + + " INNER JOIN " + Tables.USER_PACKETS + " ON (" + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = " - + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID - + " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = 0" + + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + // we KNOW that the rank zero user packet is a user id! + + " AND " + Tables.USER_PACKETS + "." + UserPackets.RANK + " = 0" + ") LEFT JOIN " + Tables.CERTS + " ON (" + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " = " @@ -376,7 +378,7 @@ public class KeychainProvider extends ContentProvider { String subkey = Long.valueOf(uri.getLastPathSegment()).toString(); qb.appendWhere(" AND EXISTS (" + " SELECT 1 FROM " + Tables.KEYS + " AS tmp" - + " WHERE tmp." + UserIds.MASTER_KEY_ID + + " WHERE tmp." + UserPackets.MASTER_KEY_ID + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " AND tmp." + Keys.KEY_ID + " = " + subkey + "" + ")"); @@ -398,15 +400,15 @@ public class KeychainProvider extends ContentProvider { if (i != 0) { emailWhere += " OR "; } - emailWhere += "tmp." + UserIds.USER_ID + " LIKE "; + emailWhere += "tmp." + UserPackets.USER_ID + " LIKE "; // match '*', so it has to be at the *end* of the user id emailWhere += DatabaseUtils.sqlEscapeString("%<" + chunks[i] + ">"); gotCondition = true; } if(gotCondition) { qb.appendWhere(" AND EXISTS (" - + " SELECT 1 FROM " + Tables.USER_IDS + " AS tmp" - + " WHERE tmp." + UserIds.MASTER_KEY_ID + + " SELECT 1 FROM " + Tables.USER_PACKETS + " AS tmp" + + " WHERE tmp." + UserPackets.MASTER_KEY_ID + " = " + Tables.KEYS + "." + Keys.MASTER_KEY_ID + " AND (" + emailWhere + ")" + ")"); @@ -420,7 +422,7 @@ public class KeychainProvider extends ContentProvider { } if (TextUtils.isEmpty(sortOrder)) { - sortOrder = Tables.USER_IDS + "." + UserIds.USER_ID + " ASC"; + sortOrder = Tables.USER_PACKETS + "." + UserPackets.USER_ID + " ASC"; } // uri to watch is all /key_rings/ @@ -459,36 +461,42 @@ public class KeychainProvider extends ContentProvider { case KEY_RINGS_USER_IDS: case KEY_RING_USER_IDS: { HashMap projectionMap = new HashMap(); - projectionMap.put(UserIds._ID, Tables.USER_IDS + ".oid AS _id"); - projectionMap.put(UserIds.MASTER_KEY_ID, Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID); - projectionMap.put(UserIds.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); - projectionMap.put(UserIds.RANK, Tables.USER_IDS + "." + UserIds.RANK); - projectionMap.put(UserIds.IS_PRIMARY, Tables.USER_IDS + "." + UserIds.IS_PRIMARY); - projectionMap.put(UserIds.IS_REVOKED, Tables.USER_IDS + "." + UserIds.IS_REVOKED); + projectionMap.put(UserPackets._ID, Tables.USER_PACKETS + ".oid AS _id"); + projectionMap.put(UserPackets.MASTER_KEY_ID, Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID); + projectionMap.put(UserPackets.TYPE, Tables.USER_PACKETS + "." + UserPackets.TYPE); + projectionMap.put(UserPackets.USER_ID, Tables.USER_PACKETS + "." + UserPackets.USER_ID); + projectionMap.put(UserPackets.ATTRIBUTE_DATA, Tables.USER_PACKETS + "." + UserPackets.ATTRIBUTE_DATA); + projectionMap.put(UserPackets.RANK, Tables.USER_PACKETS + "." + UserPackets.RANK); + projectionMap.put(UserPackets.IS_PRIMARY, Tables.USER_PACKETS + "." + UserPackets.IS_PRIMARY); + projectionMap.put(UserPackets.IS_REVOKED, Tables.USER_PACKETS + "." + UserPackets.IS_REVOKED); // we take the minimum (>0) here, where "1" is "verified by known secret key" - projectionMap.put(UserIds.VERIFIED, "MIN(" + Certs.VERIFIED + ") AS " + UserIds.VERIFIED); + projectionMap.put(UserPackets.VERIFIED, "MIN(" + Certs.VERIFIED + ") AS " + UserPackets.VERIFIED); qb.setProjectionMap(projectionMap); - qb.setTables(Tables.USER_IDS + qb.setTables(Tables.USER_PACKETS + " LEFT JOIN " + Tables.CERTS + " ON (" - + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " = " + + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = " + Tables.CERTS + "." + Certs.MASTER_KEY_ID - + " AND " + Tables.USER_IDS + "." + UserIds.RANK + " = " + + " AND " + Tables.USER_PACKETS + "." + UserPackets.RANK + " = " + Tables.CERTS + "." + Certs.RANK + " AND " + Tables.CERTS + "." + Certs.VERIFIED + " > 0" + ")"); - groupBy = Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID - + ", " + Tables.USER_IDS + "." + UserIds.RANK; + groupBy = Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + + ", " + Tables.USER_PACKETS + "." + UserPackets.RANK; + + // for now, we only respect user ids here, so TYPE must be NULL + // TODO expand with KEY_RING_USER_PACKETS query type which lifts this restriction + qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL AND "); // If we are searching for a particular keyring's ids, add where if (match == KEY_RING_USER_IDS) { - qb.appendWhere(Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " = "); + qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = "); qb.appendWhereEscapeString(uri.getPathSegments().get(1)); } if (TextUtils.isEmpty(sortOrder)) { - sortOrder = Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " ASC" - + "," + Tables.USER_IDS + "." + UserIds.RANK + " ASC"; + sortOrder = Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " ASC" + + "," + Tables.USER_PACKETS + "." + UserPackets.RANK + " ASC"; } break; @@ -542,20 +550,24 @@ public class KeychainProvider extends ContentProvider { projectionMap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION); projectionMap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER); projectionMap.put(Certs.DATA, Tables.CERTS + "." + Certs.DATA); - projectionMap.put(Certs.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID); - projectionMap.put(Certs.SIGNER_UID, "signer." + UserIds.USER_ID + " AS " + Certs.SIGNER_UID); + projectionMap.put(Certs.USER_ID, Tables.USER_PACKETS + "." + UserPackets.USER_ID); + projectionMap.put(Certs.SIGNER_UID, "signer." + UserPackets.USER_ID + " AS " + Certs.SIGNER_UID); qb.setProjectionMap(projectionMap); qb.setTables(Tables.CERTS - + " JOIN " + Tables.USER_IDS + " ON (" + + " JOIN " + Tables.USER_PACKETS + " ON (" + Tables.CERTS + "." + Certs.MASTER_KEY_ID + " = " - + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " AND " + Tables.CERTS + "." + Certs.RANK + " = " - + Tables.USER_IDS + "." + UserIds.RANK - + ") LEFT JOIN " + Tables.USER_IDS + " AS signer ON (" + + Tables.USER_PACKETS + "." + UserPackets.RANK + // for now, we only return user ids here, so TYPE must be NULL + // TODO at some point, we should lift this restriction + + " AND " + + Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL" + + ") LEFT JOIN " + Tables.USER_PACKETS + " AS signer ON (" + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = " - + "signer." + UserIds.MASTER_KEY_ID + + "signer." + UserPackets.MASTER_KEY_ID + " AND " + "signer." + Keys.RANK + " = 0" + ")"); @@ -662,8 +674,18 @@ public class KeychainProvider extends ContentProvider { break; case KEY_RING_USER_IDS: - db.insertOrThrow(Tables.USER_IDS, null, values); - keyId = values.getAsLong(UserIds.MASTER_KEY_ID); + // iff TYPE is null, user_id MUST be null as well + if ( ! (values.get(UserPacketsColumns.TYPE) == null + ? (values.get(UserPacketsColumns.USER_ID) != null && values.get(UserPacketsColumns.ATTRIBUTE_DATA) == null) + : (values.get(UserPacketsColumns.ATTRIBUTE_DATA) != null && values.get(UserPacketsColumns.USER_ID) == null) + )) { + throw new AssertionError("Incorrect type for user packet! This is a bug!"); + } + if (values.get(UserPacketsColumns.RANK) == 0 && values.get(UserPacketsColumns.USER_ID) == null) { + throw new AssertionError("Rank 0 user packet must be a user id!"); + } + db.insertOrThrow(Tables.USER_PACKETS, null, values); + keyId = values.getAsLong(UserPackets.MASTER_KEY_ID); break; case KEY_RING_CERTS: diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 4f1b4b6c1..b3f98117d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -31,6 +31,7 @@ import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; import org.sufficientlysecure.keychain.util.Preferences; @@ -53,7 +54,6 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AppSettings; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; @@ -1236,13 +1236,16 @@ public class ProviderHelper { private ContentProviderOperation buildUserIdOperations(long masterKeyId, UserIdItem item, int rank) { ContentValues values = new ContentValues(); - values.put(UserIds.MASTER_KEY_ID, masterKeyId); - values.put(UserIds.USER_ID, item.userId); - values.put(UserIds.IS_PRIMARY, item.isPrimary); - values.put(UserIds.IS_REVOKED, item.isRevoked); - values.put(UserIds.RANK, rank); - - Uri uri = UserIds.buildUserIdsUri(masterKeyId); + values.put(UserPackets.MASTER_KEY_ID, masterKeyId); + values.put(UserPackets.USER_ID, item.userId); + values.put(UserPackets.IS_PRIMARY, item.isPrimary); + values.put(UserPackets.IS_REVOKED, item.isRevoked); + values.put(UserPackets.RANK, rank); + // we explicitly set these to null here, to indicate that this is a user id, not an attribute + values.put(UserPackets.TYPE, (String) null); + values.put(UserPackets.ATTRIBUTE_DATA, (String) null); + + Uri uri = UserPackets.buildUserIdsUri(masterKeyId); return ContentProviderOperation.newInsert(uri).withValues(values).build(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java index 4d10d8639..841485c7c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java @@ -49,7 +49,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.CertifyActionsParcel; @@ -82,11 +82,11 @@ public class CertifyKeyFragment extends LoaderFragment private long mSignMasterKeyId = Constants.key.none; public static final String[] USER_IDS_PROJECTION = new String[]{ - UserIds._ID, - UserIds.MASTER_KEY_ID, - UserIds.USER_ID, - UserIds.IS_PRIMARY, - UserIds.IS_REVOKED + UserPackets._ID, + UserPackets.MASTER_KEY_ID, + UserPackets.USER_ID, + UserPackets.IS_PRIMARY, + UserPackets.IS_REVOKED }; private static final int INDEX_MASTER_KEY_ID = 1; private static final int INDEX_USER_ID = 2; @@ -182,7 +182,7 @@ public class CertifyKeyFragment extends LoaderFragment @Override public Loader onCreateLoader(int id, Bundle args) { - Uri uri = UserIds.buildUserIdsUri(); + Uri uri = UserPackets.buildUserIdsUri(); String selection, ids[]; { @@ -196,15 +196,15 @@ public class CertifyKeyFragment extends LoaderFragment } } // put together selection string - selection = UserIds.IS_REVOKED + " = 0" + " AND " - + Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + selection = UserPackets.IS_REVOKED + " = 0" + " AND " + + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " IN (" + placeholders + ")"; } return new CursorLoader(getActivity(), uri, USER_IDS_PROJECTION, selection, ids, - Tables.USER_IDS + "." + UserIds.MASTER_KEY_ID + " ASC" - + ", " + Tables.USER_IDS + "." + UserIds.USER_ID + " ASC" + Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " ASC" + + ", " + Tables.USER_PACKETS + "." + UserPackets.USER_ID + " ASC" ); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java index 0f4bfefd4..afe6afb3c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -47,6 +47,7 @@ import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; import org.sufficientlysecure.keychain.service.KeychainIntentService; @@ -334,7 +335,7 @@ public class EditKeyFragment extends LoaderFragment implements switch (id) { case LOADER_ID_USER_IDS: { - Uri baseUri = KeychainContract.UserIds.buildUserIdsUri(mDataUri); + Uri baseUri = UserPackets.buildUserIdsUri(mDataUri); return new CursorLoader(getActivity(), baseUri, UserIdsAdapter.USER_IDS_PROJECTION, null, null, null); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index 3dd4258e7..f40cfeb71 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -37,11 +37,11 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.ui.affirmations.AffirmationWizard; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; @@ -208,7 +208,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null); } case LOADER_ID_USER_IDS: { - Uri baseUri = UserIds.buildUserIdsUri(mDataUri); + Uri baseUri = UserPackets.buildUserIdsUri(mDataUri); return new CursorLoader(getActivity(), baseUri, UserIdsAdapter.USER_IDS_PROJECTION, null, null, null); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java index a2e1930d3..a778c7fa7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java @@ -19,7 +19,6 @@ package org.sufficientlysecure.keychain.ui.adapter; import android.content.Context; import android.database.Cursor; -import android.graphics.PorterDuff; import android.graphics.Typeface; import android.support.v4.widget.CursorAdapter; import android.view.LayoutInflater; @@ -32,10 +31,9 @@ import android.widget.ImageView; import android.widget.TextView; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.util.FormattingUtils; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; @@ -47,12 +45,12 @@ public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemC private SaveKeyringParcel mSaveKeyringParcel; public static final String[] USER_IDS_PROJECTION = new String[]{ - UserIds._ID, - UserIds.USER_ID, - UserIds.RANK, - UserIds.VERIFIED, - UserIds.IS_PRIMARY, - UserIds.IS_REVOKED + UserPackets._ID, + UserPackets.USER_ID, + UserPackets.RANK, + UserPackets.VERIFIED, + UserPackets.IS_PRIMARY, + UserPackets.IS_REVOKED }; private static final int INDEX_ID = 0; private static final int INDEX_USER_ID = 1; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java index 1c4eece6b..4ce7a1bac 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ContactHelper.java @@ -35,6 +35,7 @@ import android.util.Patterns; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.provider.KeychainContract; @@ -57,10 +58,10 @@ public class ContactHelper { KeychainContract.KeyRings.EXPIRY, KeychainContract.KeyRings.IS_REVOKED}; public static final String[] USER_IDS_PROJECTION = new String[]{ - KeychainContract.UserIds.USER_ID + UserPackets.USER_ID }; - public static final String NON_REVOKED_SELECTION = KeychainContract.UserIds.IS_REVOKED + "=0"; + public static final String NON_REVOKED_SELECTION = UserPackets.IS_REVOKED + "=0"; public static final String[] ID_PROJECTION = new String[]{ContactsContract.RawContacts._ID}; public static final String[] SOURCE_ID_PROJECTION = new String[]{ContactsContract.RawContacts.SOURCE_ID}; @@ -413,7 +414,7 @@ public class ContactHelper { int rawContactId, long masterKeyId) { ops.add(selectByRawContactAndItemType(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI), rawContactId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE).build()); - Cursor ids = resolver.query(KeychainContract.UserIds.buildUserIdsUri(masterKeyId), + Cursor ids = resolver.query(UserPackets.buildUserIdsUri(masterKeyId), USER_IDS_PROJECTION, NON_REVOKED_SELECTION, null, null); if (ids != null) { while (ids.moveToNext()) { -- cgit v1.2.3 From 965003784bc1972f17b9b3e3d86c6ed07f131489 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 14 Jan 2015 00:00:04 +0100 Subject: actually import user attributes (though they are not shown anywhere yet) --- .../operations/results/OperationResult.java | 12 ++ .../keychain/pgp/PgpKeyOperation.java | 2 +- .../keychain/pgp/UncachedPublicKey.java | 30 +++++ .../keychain/pgp/WrappedSignature.java | 3 + .../keychain/pgp/WrappedUserAttribute.java | 22 +++- .../keychain/provider/KeychainProvider.java | 4 +- .../keychain/provider/ProviderHelper.java | 129 +++++++++++++++++++-- 7 files changed, 185 insertions(+), 17 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index a3e30a4b1..031990d61 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -343,6 +343,18 @@ public abstract class OperationResult implements Parcelable { MSG_IP_UID_REORDER(LogLevel.DEBUG, R.string.msg_ip_uid_reorder), MSG_IP_UID_PROCESSING (LogLevel.DEBUG, R.string.msg_ip_uid_processing), MSG_IP_UID_REVOKED (LogLevel.DEBUG, R.string.msg_ip_uid_revoked), + MSG_IP_UAT_CLASSIFYING (LogLevel.DEBUG, R.string.msg_ip_uat_classifying), + MSG_IP_UAT_PROCESSING_IMAGE (LogLevel.DEBUG, R.string.msg_ip_uat_processing_image), + MSG_IP_UAT_PROCESSING_UNKNOWN (LogLevel.DEBUG, R.string.msg_ip_uat_processing_unknown), + MSG_IP_UAT_REVOKED (LogLevel.DEBUG, R.string.msg_ip_uat_revoked), + MSG_IP_UAT_CERT_BAD (LogLevel.WARN, R.string.msg_ip_uat_cert_bad), + MSG_IP_UAT_CERT_OLD (LogLevel.DEBUG, R.string.msg_ip_uat_cert_old), + MSG_IP_UAT_CERT_NONREVOKE (LogLevel.DEBUG, R.string.msg_ip_uat_cert_nonrevoke), + MSG_IP_UAT_CERT_NEW (LogLevel.DEBUG, R.string.msg_ip_uat_cert_new), + MSG_IP_UAT_CERT_ERROR (LogLevel.WARN, R.string.msg_ip_uat_cert_error), + MSG_IP_UAT_CERTS_UNKNOWN (LogLevel.DEBUG, R.plurals.msg_ip_uat_certs_unknown), + MSG_IP_UAT_CERT_GOOD_REVOKE (LogLevel.DEBUG, R.string.msg_ip_uat_cert_good_revoke), + MSG_IP_UAT_CERT_GOOD (LogLevel.DEBUG, R.string.msg_ip_uat_cert_good), // import secret MSG_IS(LogLevel.START, R.string.msg_is), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 8facbfd2a..18a5410bf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -531,7 +531,7 @@ public class PgpKeyOperation { WrappedUserAttribute attribute = saveParcel.mAddUserAttribute.get(i); switch (attribute.getType()) { - case WrappedUserAttribute.UAT_UNKNOWN: + case WrappedUserAttribute.UAT_NONE: log.add(LogType.MSG_MF_UAT_ADD_UNKNOWN, indent); break; case WrappedUserAttribute.UAT_IMAGE: diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java index fe3ab96a5..9e3528515 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedPublicKey.java @@ -24,6 +24,7 @@ import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignatureSubpacketVector; +import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.util.IterableIterator; @@ -215,6 +216,15 @@ public class UncachedPublicKey { return userIds; } + public ArrayList getUnorderedUserAttributes() { + ArrayList userAttributes = new ArrayList(); + for (PGPUserAttributeSubpacketVector userAttribute : + new IterableIterator(mPublicKey.getUserAttributes())) { + userAttributes.add(new WrappedUserAttribute(userAttribute)); + } + return userAttributes; + } + public boolean isElGamalEncrypt() { return getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT; } @@ -270,6 +280,25 @@ public class UncachedPublicKey { } } + public Iterator getSignaturesForUserAttribute(WrappedUserAttribute attribute) { + final Iterator it = mPublicKey.getSignaturesForUserAttribute(attribute.getVector()); + if (it != null) { + return new Iterator() { + public void remove() { + it.remove(); + } + public WrappedSignature next() { + return new WrappedSignature(it.next()); + } + public boolean hasNext() { + return it.hasNext(); + } + }; + } else { + return null; + } + } + /** Get all key usage flags. * If at least one key flag subpacket is present return these. If no * subpacket is present it returns null. @@ -299,4 +328,5 @@ public class UncachedPublicKey { } return mCacheUsage; } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java index 3dc02d3ed..cb03970e2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSignature.java @@ -214,6 +214,9 @@ public class WrappedSignature { public boolean verifySignature(CanonicalizedPublicKey key, String uid) throws PgpGeneralException { return verifySignature(key.getPublicKey(), uid); } + public boolean verifySignature(UncachedPublicKey key, WrappedUserAttribute attribute) throws PgpGeneralException { + return verifySignature(key.getPublicKey(), attribute.getVector()); + } public static WrappedSignature fromBytes(byte[] data) { PGPObjectFactory factory = new PGPObjectFactory(data); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java index 5c5bf0864..852a63a2e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java @@ -34,9 +34,8 @@ import java.io.Serializable; public class WrappedUserAttribute implements Serializable { - public static final int UAT_UNKNOWN = 0; + public static final int UAT_NONE = 0; public static final int UAT_IMAGE = UserAttributeSubpacketTags.IMAGE_ATTRIBUTE; - public static final int UAT_LINKED_ID = 100; private PGPUserAttributeSubpacketVector mVector; @@ -49,8 +48,9 @@ public class WrappedUserAttribute implements Serializable { } public int getType() { - if (mVector.getSubpacket(UserAttributeSubpacketTags.IMAGE_ATTRIBUTE) != null) { - return UAT_IMAGE; + UserAttributeSubpacket[] subpackets = mVector.toSubpacketArray(); + if (subpackets.length > 0) { + return subpackets[0].getType(); } return 0; } @@ -64,6 +64,20 @@ public class WrappedUserAttribute implements Serializable { } + public byte[] getEncoded () throws IOException { + UserAttributeSubpacket[] subpackets = mVector.toSubpacketArray(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + for (UserAttributeSubpacket subpacket : subpackets) { + subpacket.encode(out); + } + return out.toByteArray(); + } + + public static WrappedUserAttribute fromData (byte[] data) { + // TODO + return null; + } + /** Writes this object to an ObjectOutputStream. */ private void writeObject(java.io.ObjectOutputStream out) throws IOException { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 13a923f03..29b9c5f9f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -486,10 +486,12 @@ public class KeychainProvider extends ContentProvider { // for now, we only respect user ids here, so TYPE must be NULL // TODO expand with KEY_RING_USER_PACKETS query type which lifts this restriction - qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL AND "); + qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL"); // If we are searching for a particular keyring's ids, add where if (match == KEY_RING_USER_IDS) { + // TODO remove with the thing above + qb.appendWhere(" AND "); qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = "); qb.appendWhereEscapeString(uri.getPathSegments().get(1)); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index b3f98117d..c3990229d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -31,6 +31,7 @@ import android.support.v4.util.LongSparseArray; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; @@ -439,18 +440,18 @@ public class ProviderHelper { // classify and order user ids. primary are moved to the front, revoked to the back, // otherwise the order in the keyfile is preserved. + List uids = new ArrayList(); + if (trustedKeys.size() == 0) { log(LogType.MSG_IP_UID_CLASSIFYING_ZERO); } else { log(LogType.MSG_IP_UID_CLASSIFYING, trustedKeys.size()); } mIndent += 1; - List uids = new ArrayList(); - for (byte[] rawUserId : new IterableIterator( - masterKey.getUnorderedRawUserIds().iterator())) { + for (byte[] rawUserId : masterKey.getUnorderedRawUserIds()) { String userId = Utf8Util.fromUTF8ByteArrayReplaceBadEncoding(rawUserId); - UserIdItem item = new UserIdItem(); + UserPacketItem item = new UserPacketItem(); uids.add(item); item.userId = userId; @@ -533,6 +534,105 @@ public class ProviderHelper { } mIndent -= 1; + ArrayList userAttributes = masterKey.getUnorderedUserAttributes(); + // Don't spam the log if there aren't even any attributes + if ( ! userAttributes.isEmpty()) { + log(LogType.MSG_IP_UAT_CLASSIFYING); + } + + mIndent += 1; + for (WrappedUserAttribute userAttribute : userAttributes) { + + UserPacketItem item = new UserPacketItem(); + uids.add(item); + item.type = userAttribute.getType(); + item.attributeData = userAttribute.getEncoded(); + + int unknownCerts = 0; + + switch (item.type) { + case WrappedUserAttribute.UAT_IMAGE: + log(LogType.MSG_IP_UAT_PROCESSING_IMAGE); + break; + default: + log(LogType.MSG_IP_UAT_PROCESSING_UNKNOWN); + break; + } + mIndent += 1; + // look through signatures for this specific key + for (WrappedSignature cert : new IterableIterator( + masterKey.getSignaturesForUserAttribute(userAttribute))) { + long certId = cert.getKeyId(); + // self signature + if (certId == masterKeyId) { + + // NOTE self-certificates are already verified during canonicalization, + // AND we know there is at most one cert plus at most one revocation + if (!cert.isRevocation()) { + item.selfCert = cert; + } else { + item.isRevoked = true; + log(LogType.MSG_IP_UAT_REVOKED); + } + continue; + + } + + // do we have a trusted key for this? + if (trustedKeys.indexOfKey(certId) < 0) { + unknownCerts += 1; + continue; + } + + // verify signatures from known private keys + CanonicalizedPublicKey trustedKey = trustedKeys.get(certId); + + try { + cert.init(trustedKey); + // if it doesn't certify, leave a note and skip + if ( ! cert.verifySignature(masterKey, userAttribute)) { + log(LogType.MSG_IP_UAT_CERT_BAD); + continue; + } + + log(cert.isRevocation() + ? LogType.MSG_IP_UAT_CERT_GOOD_REVOKE + : LogType.MSG_IP_UAT_CERT_GOOD, + KeyFormattingUtils.convertKeyIdToHexShort(trustedKey.getKeyId()) + ); + + // check if there is a previous certificate + WrappedSignature prev = item.trustedCerts.get(cert.getKeyId()); + if (prev != null) { + // if it's newer, skip this one + if (prev.getCreationTime().after(cert.getCreationTime())) { + log(LogType.MSG_IP_UAT_CERT_OLD); + continue; + } + // if the previous one was a non-revokable certification, no need to look further + if (!prev.isRevocation() && !prev.isRevokable()) { + log(LogType.MSG_IP_UAT_CERT_NONREVOKE); + continue; + } + log(LogType.MSG_IP_UAT_CERT_NEW); + } + item.trustedCerts.put(cert.getKeyId(), cert); + + } catch (PgpGeneralException e) { + log(LogType.MSG_IP_UAT_CERT_ERROR, + KeyFormattingUtils.convertKeyIdToHex(cert.getKeyId())); + } + + } + + if (unknownCerts > 0) { + log(LogType.MSG_IP_UAT_CERTS_UNKNOWN, unknownCerts); + } + mIndent -= 1; + + } + mIndent -= 1; + progress.setProgress(LogType.MSG_IP_UID_REORDER.getMsgId(), 65, 100); log(LogType.MSG_IP_UID_REORDER); // primary before regular before revoked (see UserIdItem.compareTo) @@ -540,7 +640,7 @@ public class ProviderHelper { Collections.sort(uids); // iterate and put into db for (int userIdRank = 0; userIdRank < uids.size(); userIdRank++) { - UserIdItem item = uids.get(userIdRank); + UserPacketItem item = uids.get(userIdRank); operations.add(buildUserIdOperations(masterKeyId, item, userIdRank)); if (item.selfCert != null) { // TODO get rid of "self verified" status? this cannot even happen anymore! @@ -605,15 +705,23 @@ public class ProviderHelper { } - private static class UserIdItem implements Comparable { + private static class UserPacketItem implements Comparable { + Integer type; String userId; + byte[] attributeData; boolean isPrimary = false; boolean isRevoked = false; WrappedSignature selfCert; LongSparseArray trustedCerts = new LongSparseArray(); @Override - public int compareTo(UserIdItem o) { + public int compareTo(UserPacketItem o) { + // if one is a user id, but the other isn't, the user id always comes first. + // we compare for null values here, so != is the correct operator! + // noinspection NumberEquality + if (type != o.type) { + return type == null ? -1 : 1; + } // if one key is primary but the other isn't, the primary one always comes first if (isPrimary != o.isPrimary) { return isPrimary ? -1 : 1; @@ -1234,16 +1342,15 @@ public class ProviderHelper { * Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing */ private ContentProviderOperation - buildUserIdOperations(long masterKeyId, UserIdItem item, int rank) { + buildUserIdOperations(long masterKeyId, UserPacketItem item, int rank) { ContentValues values = new ContentValues(); values.put(UserPackets.MASTER_KEY_ID, masterKeyId); + values.put(UserPackets.TYPE, item.type); values.put(UserPackets.USER_ID, item.userId); + values.put(UserPackets.ATTRIBUTE_DATA, item.attributeData); values.put(UserPackets.IS_PRIMARY, item.isPrimary); values.put(UserPackets.IS_REVOKED, item.isRevoked); values.put(UserPackets.RANK, rank); - // we explicitly set these to null here, to indicate that this is a user id, not an attribute - values.put(UserPackets.TYPE, (String) null); - values.put(UserPackets.ATTRIBUTE_DATA, (String) null); Uri uri = UserPackets.buildUserIdsUri(masterKeyId); -- cgit v1.2.3 From a75394fbd3e8e6b7526a1f9fc40a4e4a781e269a Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 14 Jan 2015 11:07:09 +0100 Subject: fix log entry for addition of user attributes --- .../java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 18a5410bf..56f7b3309 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -531,12 +531,12 @@ public class PgpKeyOperation { WrappedUserAttribute attribute = saveParcel.mAddUserAttribute.get(i); switch (attribute.getType()) { - case WrappedUserAttribute.UAT_NONE: - log.add(LogType.MSG_MF_UAT_ADD_UNKNOWN, indent); - break; case WrappedUserAttribute.UAT_IMAGE: log.add(LogType.MSG_MF_UAT_ADD_IMAGE, indent); break; + default: + log.add(LogType.MSG_MF_UAT_ADD_UNKNOWN, indent); + break; } PGPUserAttributeSubpacketVector vector = attribute.getVector(); -- cgit v1.2.3 From 2d5abc7da9a99311583b4bcc0e7fe9735f9bb828 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 14 Jan 2015 13:05:13 +0100 Subject: small fixes to user attribute handling --- .../operations/results/OperationResult.java | 1 + .../keychain/pgp/PgpKeyOperation.java | 4 ++++ .../keychain/pgp/WrappedUserAttribute.java | 25 +++++++++++++++++++--- 3 files changed, 27 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index 031990d61..af401b05a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -507,6 +507,7 @@ public abstract class OperationResult implements Parcelable { MSG_MF_UID_PRIMARY (LogLevel.INFO, R.string.msg_mf_uid_primary), MSG_MF_UID_REVOKE (LogLevel.INFO, R.string.msg_mf_uid_revoke), MSG_MF_UID_ERROR_EMPTY (LogLevel.ERROR, R.string.msg_mf_uid_error_empty), + MSG_MF_UAT_ERROR_EMPTY (LogLevel.ERROR, R.string.msg_mf_uat_error_empty), MSG_MF_UAT_ADD_IMAGE (LogLevel.INFO, R.string.msg_mf_uat_add_image), MSG_MF_UAT_ADD_UNKNOWN (LogLevel.INFO, R.string.msg_mf_uat_add_unknown), MSG_MF_UNLOCK_ERROR (LogLevel.ERROR, R.string.msg_mf_unlock_error), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 56f7b3309..4bab7f2b9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -531,6 +531,10 @@ public class PgpKeyOperation { WrappedUserAttribute attribute = saveParcel.mAddUserAttribute.get(i); switch (attribute.getType()) { + // the 'none' type must not succeed + case WrappedUserAttribute.UAT_NONE: + log.add(LogType.MSG_MF_UAT_ERROR_EMPTY, indent); + return new PgpEditKeyResult(PgpEditKeyResult.RESULT_ERROR, log, null); case WrappedUserAttribute.UAT_IMAGE: log.add(LogType.MSG_MF_UAT_ADD_IMAGE, indent); break; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java index 852a63a2e..038718c65 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java @@ -23,6 +23,7 @@ import org.spongycastle.bcpg.BCPGOutputStream; import org.spongycastle.bcpg.Packet; import org.spongycastle.bcpg.UserAttributePacket; import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.bcpg.UserAttributeSubpacketInputStream; import org.spongycastle.bcpg.UserAttributeSubpacketTags; import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; @@ -31,6 +32,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectStreamException; import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; public class WrappedUserAttribute implements Serializable { @@ -73,9 +76,17 @@ public class WrappedUserAttribute implements Serializable { return out.toByteArray(); } - public static WrappedUserAttribute fromData (byte[] data) { - // TODO - return null; + public static WrappedUserAttribute fromData (byte[] data) throws IOException { + UserAttributeSubpacketInputStream in = + new UserAttributeSubpacketInputStream(new ByteArrayInputStream(data)); + ArrayList list = new ArrayList(); + while (in.available() > 0) { + list.add(in.readPacket()); + } + UserAttributeSubpacket[] result = new UserAttributeSubpacket[list.size()]; + list.toArray(result); + return new WrappedUserAttribute( + new PGPUserAttributeSubpacketVector(result)); } /** Writes this object to an ObjectOutputStream. */ @@ -103,4 +114,12 @@ public class WrappedUserAttribute implements Serializable { private void readObjectNoData() throws ObjectStreamException { } + @Override + public boolean equals(Object o) { + if (!WrappedUserAttribute.class.isInstance(o)) { + return false; + } + return mVector.equals(((WrappedUserAttribute) o).mVector); + } + } -- cgit v1.2.3 From 55ea957644820cc56c71295f50bb04f1108236b0 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 14 Jan 2015 14:12:23 +0100 Subject: fix ambiguous column name in advanced key info --- .../org/sufficientlysecure/keychain/ui/ViewKeyAdvancedFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvancedFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvancedFragment.java index 9c37f2d94..61bd126ce 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvancedFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvancedFragment.java @@ -80,7 +80,7 @@ public class ViewKeyAdvancedFragment extends LoaderFragment implements static final String CERTS_SORT_ORDER = KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.RANK + " ASC, " + KeychainContract.Certs.VERIFIED + " DESC, " - + KeychainContract.Certs.TYPE + " DESC, " + + KeychainDatabase.Tables.CERTS + "." + KeychainContract.Certs.TYPE + " DESC, " + KeychainContract.Certs.SIGNER_UID + " ASC"; /** -- cgit v1.2.3 From c03bcc2799774cef4ac5f35ca6225059a13f45c8 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 14 Jan 2015 18:32:50 +0100 Subject: work on dns resource, working (dummy) verification --- .../pgp/affirmation/AffirmationResource.java | 12 +- .../keychain/pgp/affirmation/LinkedIdentity.java | 2 +- .../pgp/affirmation/resources/DnsResouce.java | 47 --- .../pgp/affirmation/resources/DnsResource.java | 70 ++++ .../AffirmationCreateDnsStep1Fragment.java | 29 +- .../AffirmationCreateDnsStep2Fragment.java | 360 +++++++++++++++++++++ 6 files changed, 454 insertions(+), 66 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep2Fragment.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java index ffe89931a..80398396e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java @@ -64,7 +64,7 @@ public abstract class AffirmationResource { return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); } - Log.d(Constants.TAG, res); + Log.d(Constants.TAG, "Resource data: '" + res + "'"); return verifyString(log, 1, res, nonce, fingerprint); @@ -72,19 +72,23 @@ public abstract class AffirmationResource { protected abstract String fetchResource (OperationLog log, int indent); + protected Matcher matchResource (OperationLog log, int indent, String res) { + return magicPattern.matcher(res); + } + protected LinkedVerifyResult verifyString (OperationLog log, int indent, String res, String nonce, byte[] fingerprint) { log.add(LogType.MSG_LV_MATCH, indent); - Matcher match = magicPattern.matcher(res); + 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); - String nonceCandidate = match.group(2); + String candidateFp = match.group(1).toLowerCase(); + String nonceCandidate = match.group(2).toLowerCase(); String fp = KeyFormattingUtils.convertFingerprintToHex(fingerprint); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java index ee9933da3..00d898df9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java @@ -167,7 +167,7 @@ public class LinkedIdentity { // return Hex.toHexString(data); // debug for now - return "0123456789ABCDEF01234567"; + return "0123456789abcdef01234567"; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java deleted file mode 100644 index 20216972a..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResouce.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.affirmation.resources; - -import android.content.Context; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.Log; - -import java.net.URI; -import java.util.HashMap; -import java.util.Set; - -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.TYPE; -import de.measite.minidns.record.TXT; - -public class DnsResouce extends AffirmationResource { - - DnsResouce(Set flags, HashMap params, URI uri) { - super(flags, params, uri); - } - - public static String generate (Context context, byte[] fingerprint, String nonce) { - - return "pgpid+cookie:" - + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + ";" + nonce + ""; - - } - - @Override - protected String fetchResource (OperationLog log, int indent) { - - Client c = new Client(); - DNSMessage msg = c.query(new Question("mugenguild.com", TYPE.TXT)); - Record aw = msg.getAnswers()[0]; - TXT txt = (TXT) aw.getPayload(); - Log.d(Constants.TAG, txt.getText()); - return txt.getText(); - - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResource.java new file mode 100644 index 000000000..272aa5dcd --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResource.java @@ -0,0 +1,70 @@ +package org.sufficientlysecure.keychain.pgp.affirmation.resources; + +import android.content.Context; + +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; +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.TYPE; +import de.measite.minidns.record.TXT; + +public class DnsResource extends AffirmationResource { + + static Pattern magicPattern = + Pattern.compile("pgpid\\+cookie=([a-zA-Z0-9]+)(?:#|;)([a-zA-Z0-9]+)"); + + DnsResource(Set flags, HashMap params, URI uri) { + super(flags, params, uri); + } + + public static String generateText (Context context, byte[] fingerprint, String nonce) { + + return "pgpid+cookie=" + + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + ";" + nonce + ""; + + } + + 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; + } + return new DnsResource(flags, params, uri); + } + + @Override + protected String fetchResource (OperationLog log, int indent) { + + Client c = new Client(); + DNSMessage msg = c.query(new Question("mugenguild.com", TYPE.TXT)); + 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); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java index 4b23f0f06..70258eb5a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java @@ -30,13 +30,13 @@ import android.widget.EditText; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; -import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; +import org.sufficientlysecure.keychain.pgp.affirmation.resources.DnsResource; public class AffirmationCreateDnsStep1Fragment extends Fragment { AffirmationWizard mAffirmationWizard; - EditText mEditUri; + EditText mEditDns; /** * Creates new instance of this fragment @@ -66,18 +66,19 @@ public class AffirmationCreateDnsStep1Fragment extends Fragment { @Override public void onClick(View v) { - String uri = "https://" + mEditUri.getText(); + String uri = mEditDns.getText().toString(); if (!checkUri(uri)) { + mEditDns.setError("Please enter a valid domain name!"); return; } String proofNonce = LinkedIdentity.generateNonce(); - String proofText = GenericHttpsResource.generateText(getActivity(), + String proofText = DnsResource.generateText(getActivity(), mAffirmationWizard.mFingerprint, proofNonce); - AffirmationCreateHttpsStep2Fragment frag = - AffirmationCreateHttpsStep2Fragment.newInstance(uri, proofNonce, proofText); + AffirmationCreateDnsStep2Fragment frag = + AffirmationCreateDnsStep2Fragment.newInstance(uri, proofNonce, proofText); mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); @@ -91,9 +92,9 @@ public class AffirmationCreateDnsStep1Fragment extends Fragment { } }); - mEditUri = (EditText) view.findViewById(R.id.affirmation_create_https_uri); + mEditDns = (EditText) view.findViewById(R.id.affirmation_create_dns_domain); - mEditUri.addTextChangedListener(new TextWatcher() { + mEditDns.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { } @@ -104,29 +105,29 @@ public class AffirmationCreateDnsStep1Fragment extends Fragment { @Override public void afterTextChanged(Editable editable) { - String uri = "https://" + editable; + String uri = editable.toString(); if (uri.length() > 0) { if (checkUri(uri)) { - mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0, + mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.uid_mail_ok, 0); } else { - mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0, + mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.uid_mail_bad, 0); } } else { // remove drawable if email is empty - mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); } } }); - mEditUri.setText("mugenguild.com/pgpkey.txt"); + mEditDns.setText("mugenguild.com"); return view; } private static boolean checkUri(String uri) { - return Patterns.WEB_URL.matcher(uri).matches(); + return Patterns.DOMAIN_NAME.matcher(uri).matches(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep2Fragment.java new file mode 100644 index 000000000..87568ddc2 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep2Fragment.java @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.affirmations; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Intent; +import android.graphics.PorterDuff; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.Message; +import android.os.Messenger; +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 android.widget.ImageView; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; +import org.sufficientlysecure.keychain.pgp.affirmation.resources.DnsResource; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; +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; + +public class AffirmationCreateDnsStep2Fragment extends Fragment { + + private static final int REQUEST_CODE_OUTPUT = 0x00007007; + private static final int REQUEST_CODE_PASSPHRASE = 0x00007008; + + public static final String DOMAIN = "domain", NONCE = "nonce", TEXT = "text"; + + AffirmationWizard mAffirmationWizard; + + EditText mEditUri; + ImageView mVerifyImage; + View mVerifyProgress; + TextView mVerifyStatus; + + String mResourceDomain; + String mResourceNonce, mResourceString; + + // This is a resource, set AFTER it has been verified + DnsResource mVerifiedResource = null; + + /** + * Creates new instance of this fragment + */ + public static AffirmationCreateDnsStep2Fragment newInstance + (String uri, String proofNonce, String proofText) { + + AffirmationCreateDnsStep2Fragment frag = new AffirmationCreateDnsStep2Fragment(); + + Bundle args = new Bundle(); + args.putString(DOMAIN, uri); + args.putString(NONCE, proofNonce); + args.putString(TEXT, proofText); + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.affirmation_create_dns_fragment_step2, container, false); + + mResourceDomain = getArguments().getString(DOMAIN); + mResourceNonce = getArguments().getString(NONCE); + mResourceString = getArguments().getString(TEXT); + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + startCertify(); + } + }); + + view.findViewById(R.id.back_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mAffirmationWizard.loadFragment(null, null, AffirmationWizard.FRAG_ACTION_TO_LEFT); + } + }); + + mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); + mVerifyProgress = view.findViewById(R.id.verify_progress); + mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); + + view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSend(); + } + }); + + view.findViewById(R.id.button_save).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSave(); + } + }); + + view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofVerify(); + } + }); + + mEditUri = (EditText) view.findViewById(R.id.affirmation_create_dns_text); + mEditUri.setText(mResourceString); + + setVerifyProgress(false, null); + mVerifyStatus.setText(R.string.linked_verify_pending); + + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mAffirmationWizard = (AffirmationWizard) getActivity(); + } + + public void setVerifyProgress(boolean on, Boolean success) { + mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); + mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); + if (success == null) { + mVerifyStatus.setText(R.string.linked_verifying); + mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), + PorterDuff.Mode.SRC_IN); + } else if (success) { + mVerifyStatus.setText(R.string.linked_verify_success); + mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark), + PorterDuff.Mode.SRC_IN); + } else { + mVerifyStatus.setText(R.string.linked_verify_error); + mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), + PorterDuff.Mode.SRC_IN); + } + } + + private void proofSend () { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); + sendIntent.setType("text/plain"); + startActivity(sendIntent); + } + + private void proofSave () { + String state = Environment.getExternalStorageState(); + if (!Environment.MEDIA_MOUNTED.equals(state)) { + Notify.showNotify(getActivity(), "External storage not available!", Style.ERROR); + return; + } + + String targetName = "pgpkey.txt"; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + File targetFile = new File(Constants.Path.APP_DIR, targetName); + FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file), + getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT); + } else { + FileHelper.saveDocument(this, "text/plain", targetName, REQUEST_CODE_OUTPUT); + } + } + + private void saveFile(Uri uri) { + try { + PrintWriter out = + new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); + out.print(mResourceString); + if (out.checkError()) { + Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR); + } + } catch (FileNotFoundException e) { + Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR); + e.printStackTrace(); + } + } + + public void proofVerify() { + setVerifyProgress(true, null); + + final DnsResource resource = DnsResource.createNew(mResourceDomain); + + new AsyncTask() { + + @Override + protected LinkedVerifyResult doInBackground(Void... params) { + return resource.verify(mAffirmationWizard.mFingerprint, mResourceNonce); + } + + @Override + protected void onPostExecute(LinkedVerifyResult result) { + super.onPostExecute(result); + if (result.success()) { + setVerifyProgress(false, true); + mVerifiedResource = resource; + } else { + setVerifyProgress(false, false); + // on error, show error message + result.createNotify(getActivity()).show(); + } + } + }.execute(); + + } + + public void startCertify() { + + if (mVerifiedResource == null) { + Notify.showNotify(getActivity(), R.string.linked_need_verify, Style.ERROR); + return; + } + + Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); + intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mAffirmationWizard.mMasterKeyId); + startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); + + } + + public void certifyLinkedIdentity (String passphrase) { + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( + getActivity(), + getString(R.string.progress_saving), + ProgressDialog.STYLE_HORIZONTAL, + true) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + + // get returned data bundle + Bundle returnData = message.getData(); + if (returnData == null) { + return; + } + final OperationResult result = + returnData.getParcelable(OperationResult.EXTRA_RESULT); + if (result == null) { + return; + } + + // if bad -> display here! + if (!result.success()) { + result.createNotify(getActivity()).show(); + return; + } + + result.createNotify(getActivity()).show(); + + // if good -> finish, return result to showkey and display there! + // Intent intent = new Intent(); + // intent.putExtra(OperationResult.EXTRA_RESULT, result); + // getActivity().setResult(EditKeyActivity.RESULT_OK, intent); + + // AffirmationCreateHttpsStep3Fragment frag = + // AffirmationCreateHttpsStep3Fragment.newInstance( + // mResourceDomain, mResourceNonce, mResourceString); + + // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + + } + } + }; + + SaveKeyringParcel skp = + new SaveKeyringParcel(mAffirmationWizard.mMasterKeyId, mAffirmationWizard.mFingerprint); + + WrappedUserAttribute ua = + LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); + + skp.mAddUserAttribute.add(ua); + + // Send all information needed to service to import key in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING); + + // fill values for this action + Bundle data = new Bundle(); + data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); + data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, skp); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + + } + + + @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; + case REQUEST_CODE_PASSPHRASE: + if (resultCode == Activity.RESULT_OK && data != null) { + String passphrase = + data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + certifyLinkedIdentity(passphrase); + } + break; + } + super.onActivityResult(requestCode, resultCode, data); + } + +} -- cgit v1.2.3 From 92b8d874ed0d7e8f835dbd8ce4205e74841d7162 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 15 Jan 2015 16:59:11 +0100 Subject: affirmation -> linked identity --- .../keychain/pgp/WrappedUserAttribute.java | 1 + .../pgp/affirmation/AffirmationResource.java | 125 ------- .../keychain/pgp/affirmation/LinkedIdentity.java | 173 ---------- .../pgp/affirmation/resources/DnsResource.java | 70 ---- .../resources/GenericHttpsResource.java | 104 ------ .../pgp/affirmation/resources/TwitterResource.java | 124 ------- .../pgp/affirmation/resources/UnknownResource.java | 22 -- .../keychain/pgp/linked/LinkedIdentity.java | 172 ++++++++++ .../keychain/pgp/linked/LinkedResource.java | 125 +++++++ .../keychain/pgp/linked/resources/DnsResource.java | 70 ++++ .../pgp/linked/resources/GenericHttpsResource.java | 103 ++++++ .../pgp/linked/resources/TwitterResource.java | 124 +++++++ .../pgp/linked/resources/UnknownResource.java | 21 ++ .../keychain/service/KeychainIntentService.java | 1 - .../keychain/service/SaveKeyringParcel.java | 1 - .../keychain/ui/ViewKeyMainFragment.java | 4 +- .../AffirmationCreateDnsStep1Fragment.java | 133 -------- .../AffirmationCreateDnsStep2Fragment.java | 360 -------------------- .../AffirmationCreateHttpsStep1Fragment.java | 132 -------- .../AffirmationCreateHttpsStep2Fragment.java | 368 --------------------- .../AffirmationCreateTwitterStep1Fragment.java | 140 -------- .../AffirmationCreateTwitterStep2Fragment.java | 150 --------- .../AffirmationCreateTwitterStep3Fragment.java | 250 -------------- .../ui/affirmations/AffirmationSelectFragment.java | 91 ----- .../ui/affirmations/AffirmationWizard.java | 98 ------ .../ui/linked/LinkedIdCreateDnsStep1Fragment.java | 133 ++++++++ .../ui/linked/LinkedIdCreateDnsStep2Fragment.java | 360 ++++++++++++++++++++ .../linked/LinkedIdCreateHttpsStep1Fragment.java | 132 ++++++++ .../linked/LinkedIdCreateHttpsStep2Fragment.java | 366 ++++++++++++++++++++ .../linked/LinkedIdCreateTwitterStep1Fragment.java | 134 ++++++++ .../linked/LinkedIdCreateTwitterStep2Fragment.java | 149 +++++++++ .../linked/LinkedIdCreateTwitterStep3Fragment.java | 235 +++++++++++++ .../keychain/ui/linked/LinkedIdSelectFragment.java | 91 +++++ .../keychain/ui/linked/LinkedIdWizard.java | 98 ++++++ 34 files changed, 2316 insertions(+), 2344 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/DnsResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/GenericHttpsResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/TwitterResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep2Fragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep3Fragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationSelectFragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationWizard.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep2Fragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdSelectFragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdWizard.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java index 038718c65..370c3f89d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java @@ -39,6 +39,7 @@ public class WrappedUserAttribute implements Serializable { public static final int UAT_NONE = 0; public static final int UAT_IMAGE = UserAttributeSubpacketTags.IMAGE_ATTRIBUTE; + public static final int UAT_LINKED_ID = 100; private PGPUserAttributeSubpacketVector mVector; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java deleted file mode 100644 index 80398396e..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/AffirmationResource.java +++ /dev/null @@ -1,125 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.affirmation; - -import android.content.Context; - -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.pgp.affirmation.resources.GenericHttpsResource; -import org.sufficientlysecure.keychain.pgp.affirmation.resources.UnknownResource; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -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.Matcher; -import java.util.regex.Pattern; - -public abstract class AffirmationResource { - - protected final URI mSubUri; - protected final Set mFlags; - protected final HashMap mParams; - - static Pattern magicPattern = - Pattern.compile("\\[Verifying my PGP key: pgpid\\+cookie:([a-zA-Z0-9]+)#([a-zA-Z0-9]+)\\]"); - - protected AffirmationResource(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); - } - - public URI getSubUri () { - return mSubUri; - } - - public static String generate (Context context, byte[] fingerprint, String nonce) { - - return "[Verifying my PGP key: pgpid+cookie:" - + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + "#" + nonce + "]"; - - } - - public LinkedVerifyResult verify(byte[] fingerprint, String nonce) { - - OperationLog log = new OperationLog(); - log.add(LogType.MSG_LV, 0); - - // Try to fetch resource. Logs for itself - String res = fetchResource(log, 1); - 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, nonce, fingerprint); - - } - - protected abstract String fetchResource (OperationLog log, int indent); - - protected Matcher matchResource (OperationLog log, int indent, String res) { - return magicPattern.matcher(res); - } - - protected LinkedVerifyResult verifyString (OperationLog log, int indent, - String res, - String nonce, 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 nonceCandidate = match.group(2).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); - - if (!nonce.equals(nonceCandidate)) { - log.add(LogType.MSG_LV_NONCE_ERROR, indent); - return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); - } - - log.add(LogType.MSG_LV_NONCE_OK, indent); - return new LinkedVerifyResult(LinkedVerifyResult.RESULT_OK, log); - - } - - public static AffirmationResource findResourceType - (Set flags, HashMap params, URI uri) { - - AffirmationResource res; - - res = GenericHttpsResource.create(flags, params, uri); - if (res != null) { - return res; - } - - return new UnknownResource(flags, params, uri); - - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java deleted file mode 100644 index 00d898df9..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/LinkedIdentity.java +++ /dev/null @@ -1,173 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.affirmation; - -import org.spongycastle.bcpg.UserAttributeSubpacket; -import org.spongycastle.openpgp.PGPUserAttributeSubpacketVector; -import org.spongycastle.util.Strings; -import org.spongycastle.util.encoders.Hex; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.util.Log; - -import java.net.URI; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.Set; - -public class LinkedIdentity { - - protected byte[] mData; - public final String mNonce; - public final URI mSubUri; - final Set mFlags; - final HashMap mParams; - - protected LinkedIdentity(byte[] data, String nonce, Set flags, - HashMap params, URI subUri) { - if ( ! nonce.matches("[0-9a-zA-Z]+")) { - throw new AssertionError("bug: nonce must be hexstring!"); - } - - mData = data; - mNonce = nonce; - mFlags = flags; - mParams = params; - mSubUri = subUri; - } - - LinkedIdentity(String nonce, Set flags, - HashMap params, URI subUri) { - this(null, nonce, flags, params, subUri); - } - - public byte[] getEncoded() { - if (mData != null) { - return mData; - } - - StringBuilder b = new StringBuilder(); - b.append("pgpid:"); - - // 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; - Iterator> it = mParams.entrySet().iterator(); - while (it.hasNext()) { - if (!first) { - b.append(";"); - } - first = false; - Entry entry = it.next(); - b.append(entry.getKey()).append("=").append(entry.getValue()); - } - } - - b.append("@"); - b.append(mSubUri); - - byte[] nonceBytes = Hex.decode(mNonce); - if (nonceBytes.length != 12) { - throw new AssertionError("nonce must be 12 bytes"); - } - byte[] data = Strings.toUTF8ByteArray(b.toString()); - - byte[] result = new byte[data.length+12]; - System.arraycopy(nonceBytes, 0, result, 0, 12); - System.arraycopy(data, 0, result, 12, data.length); - - return result; - } - - /** This method parses an affirmation from a UserAttributeSubpacket, or returns null if the - * subpacket can not be parsed as a valid affirmation. - */ - static LinkedIdentity parseAffirmation(UserAttributeSubpacket subpacket) { - if (subpacket.getType() != 100) { - return null; - } - - byte[] data = subpacket.getData(); - String nonce = Hex.toHexString(data, 0, 12); - - try { - return parseUri(nonce, Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 12, data.length))); - - } catch (IllegalArgumentException e) { - Log.e(Constants.TAG, "error parsing uri in (suspected) affirmation packet"); - return null; - } - } - - protected static LinkedIdentity parseUri (String nonce, String uriString) { - URI uri = URI.create(uriString); - - if ("pgpid".equals(uri.getScheme())) { - Log.e(Constants.TAG, "unknown uri scheme in (suspected) affirmation packet"); - return null; - } - - if (!uri.isOpaque()) { - Log.e(Constants.TAG, "non-opaque uri in (suspected) affirmation packet"); - return null; - } - - String specific = uri.getSchemeSpecificPart(); - if (!specific.contains("@")) { - Log.e(Constants.TAG, "unknown uri scheme in affirmation packet"); - return null; - } - - String[] pieces = specific.split("@", 2); - URI subUri = URI.create(pieces[1]); - - Set flags = new HashSet(); - HashMap params = new HashMap(); - { - 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 new LinkedIdentity(nonce, flags, params, subUri); - - } - - public static LinkedIdentity fromResource (AffirmationResource res, String nonce) { - return new LinkedIdentity(nonce, res.getFlags(), res.getParams(), res.getSubUri()); - } - - public WrappedUserAttribute toUserAttribute () { - return WrappedUserAttribute.fromSubpacket(WrappedUserAttribute.UAT_LINKED_ID, getEncoded()); - } - - public static String generateNonce() { - // TODO make this actually random - // byte[] data = new byte[96]; - // new SecureRandom().nextBytes(data); - // return Hex.toHexString(data); - - // debug for now - return "0123456789abcdef01234567"; - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResource.java deleted file mode 100644 index 272aa5dcd..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/DnsResource.java +++ /dev/null @@ -1,70 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.affirmation.resources; - -import android.content.Context; - -import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; -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.TYPE; -import de.measite.minidns.record.TXT; - -public class DnsResource extends AffirmationResource { - - static Pattern magicPattern = - Pattern.compile("pgpid\\+cookie=([a-zA-Z0-9]+)(?:#|;)([a-zA-Z0-9]+)"); - - DnsResource(Set flags, HashMap params, URI uri) { - super(flags, params, uri); - } - - public static String generateText (Context context, byte[] fingerprint, String nonce) { - - return "pgpid+cookie=" - + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + ";" + nonce + ""; - - } - - 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; - } - return new DnsResource(flags, params, uri); - } - - @Override - protected String fetchResource (OperationLog log, int indent) { - - Client c = new Client(); - DNSMessage msg = c.query(new Question("mugenguild.com", TYPE.TXT)); - 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); - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java deleted file mode 100644 index 74c0689b5..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/GenericHttpsResource.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.affirmation.resources; - -import android.content.Context; - -import com.textuality.keybase.lib.Search; - -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.affirmation.AffirmationResource; -import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.Log; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; - -import javax.net.ssl.HttpsURLConnection; - -public class GenericHttpsResource extends AffirmationResource { - - GenericHttpsResource(Set flags, HashMap params, URI uri) { - super(flags, params, uri); - } - - public static String generateText (Context context, byte[] fingerprint, String nonce) { - String cookie = AffirmationResource.generate(context, fingerprint, nonce); - - 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) { - - log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); - indent += 1; - - try { - - HttpsURLConnection conn = null; - URL url = mSubUri.toURL(); - int status = 0; - int redirects = 0; - - while (redirects < 5) { - conn = (HttpsURLConnection) url.openConnection(); - conn.addRequestProperty("User-Agent", "OpenKeychain"); - conn.setConnectTimeout(5000); - conn.setReadTimeout(25000); - conn.connect(); - status = conn.getResponseCode(); - if (status == 301) { - redirects++; - url = new URL(conn.getHeaderFields().get("Location").get(0)); - log.add(LogType.MSG_LV_FETCH_REDIR, indent, url.toString()); - } else { - break; - } - } - - if (status >= 200 && status < 300) { - log.add(LogType.MSG_LV_FETCH_OK, indent, Integer.toString(status)); - return Search.snarf(conn.getInputStream()); - } else { - // log verbose output to logcat - Log.e(Constants.TAG, Search.snarf(conn.getErrorStream())); - log.add(LogType.MSG_LV_FETCH_ERROR, indent, Integer.toString(status)); - return null; - } - - } catch (MalformedURLException e) { - log.add(LogType.MSG_LV_FETCH_ERROR_URL, indent); - return null; - } catch (IOException e) { - log.add(LogType.MSG_LV_FETCH_ERROR_IO, indent); - return null; - } - - } - - 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); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java deleted file mode 100644 index f131a8da2..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/TwitterResource.java +++ /dev/null @@ -1,124 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.affirmation.resources; - -import android.content.Context; -import android.util.Base64; - -import com.textuality.keybase.lib.JWalk; - -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpRequestBase; -import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.params.BasicHttpParams; -import org.json.JSONException; -import org.json.JSONObject; -import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.net.URLEncoder; -import java.util.HashMap; -import java.util.Set; - -public class TwitterResource extends AffirmationResource { - - TwitterResource(Set flags, HashMap params, URI uri) { - super(flags, params, uri); - } - - public static String generateText (Context context, byte[] fingerprint, String nonce) { - // nothing special here for now, might change this later - return AffirmationResource.generate(context, fingerprint, nonce); - } - - private String getTwitterStream(String screenName) { - String results = null; - - // Step 1: Encode consumer key and secret - try { - // URL encode the consumer key and secret - String urlApiKey = URLEncoder.encode("6IhPnWbYxASAoAzH2QaUtHD0J", "UTF-8"); - String urlApiSecret = URLEncoder.encode("L0GnuiOnapWbSBbQtLIqtpeS5BTtvh06dmoMoKQfHQS8UwHuWm", "UTF-8"); - - // Concatenate the encoded consumer key, a colon character, and the - // encoded consumer secret - String combined = urlApiKey + ":" + urlApiSecret; - - // Base64 encode the string - String base64Encoded = Base64.encodeToString(combined.getBytes(), Base64.NO_WRAP); - - // 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)); - String auth = JWalk.getString(rawAuthorization, "access_token"); - - // Applications should verify that the value associated with the - // token_type key of the returned object is bearer - if (auth != null && JWalk.getString(rawAuthorization, "token_type").equals("bearer")) { - - // Step 3: Authenticate API requests with bearer token - HttpGet httpGet = - new HttpGet("https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=" + screenName); - - // construct a normal HTTPS request and include an Authorization - // header with the value of Bearer <> - httpGet.setHeader("Authorization", "Bearer " + auth); - httpGet.setHeader("Content-Type", "application/json"); - // update the results with the body of the response - results = getResponseBody(httpGet); - } - } catch (UnsupportedEncodingException ex) { - } catch (JSONException ex) { - } catch (IllegalStateException ex1) { - } - return results; - } - - private static String getResponseBody(HttpRequestBase request) { - StringBuilder sb = new StringBuilder(); - try { - - DefaultHttpClient httpClient = new DefaultHttpClient(new BasicHttpParams()); - HttpResponse response = httpClient.execute(request); - int statusCode = response.getStatusLine().getStatusCode(); - String reason = response.getStatusLine().getReasonPhrase(); - - if (statusCode == 200) { - - HttpEntity entity = response.getEntity(); - InputStream inputStream = entity.getContent(); - - BufferedReader bReader = new BufferedReader( - new InputStreamReader(inputStream, "UTF-8"), 8); - String line = null; - while ((line = bReader.readLine()) != null) { - sb.append(line); - } - } else { - sb.append(reason); - } - } catch (UnsupportedEncodingException ex) { - } catch (ClientProtocolException ex1) { - } catch (IOException ex2) { - } - return sb.toString(); - } - - @Override - protected String fetchResource(OperationLog log, int indent) { - return getTwitterStream("Valodim"); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java deleted file mode 100644 index 2f67c948e..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/affirmation/resources/UnknownResource.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.affirmation.resources; - -import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.pgp.affirmation.AffirmationResource; - -import java.io.IOException; -import java.net.URI; -import java.util.HashMap; -import java.util.Set; - -public class UnknownResource extends AffirmationResource { - - public UnknownResource(Set flags, HashMap params, URI uri) { - super(flags, params, uri); - } - - @Override - protected String fetchResource(OperationLog log, int indent) { - return null; - } - -} 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 new file mode 100644 index 000000000..3f7501adc --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java @@ -0,0 +1,172 @@ +package org.sufficientlysecure.keychain.pgp.linked; + +import org.spongycastle.bcpg.UserAttributeSubpacket; +import org.spongycastle.util.Strings; +import org.spongycastle.util.encoders.Hex; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.util.Log; + +import java.net.URI; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map.Entry; +import java.util.Set; + +public class LinkedIdentity { + + protected byte[] mData; + public final String mNonce; + public final URI mSubUri; + final Set mFlags; + final HashMap mParams; + + protected LinkedIdentity(byte[] data, String nonce, Set flags, + HashMap params, URI subUri) { + if ( ! nonce.matches("[0-9a-zA-Z]+")) { + throw new AssertionError("bug: nonce must be hexstring!"); + } + + mData = data; + mNonce = nonce; + mFlags = flags; + mParams = params; + mSubUri = subUri; + } + + LinkedIdentity(String nonce, Set flags, + HashMap params, URI subUri) { + this(null, nonce, flags, params, subUri); + } + + public byte[] getEncoded() { + if (mData != null) { + return mData; + } + + StringBuilder b = new StringBuilder(); + b.append("pgpid:"); + + // 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; + Iterator> it = mParams.entrySet().iterator(); + while (it.hasNext()) { + if (!first) { + b.append(";"); + } + first = false; + Entry entry = it.next(); + b.append(entry.getKey()).append("=").append(entry.getValue()); + } + } + + b.append("@"); + b.append(mSubUri); + + byte[] nonceBytes = Hex.decode(mNonce); + if (nonceBytes.length != 12) { + throw new AssertionError("nonce must be 12 bytes"); + } + byte[] data = Strings.toUTF8ByteArray(b.toString()); + + byte[] result = new byte[data.length+12]; + System.arraycopy(nonceBytes, 0, result, 0, 12); + System.arraycopy(data, 0, result, 12, data.length); + + return result; + } + + /** 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 LinkedIdentity parseAttributeSubpacket(UserAttributeSubpacket subpacket) { + if (subpacket.getType() != 100) { + return null; + } + + byte[] data = subpacket.getData(); + String nonce = Hex.toHexString(data, 0, 12); + + try { + return parseUri(nonce, Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 12, data.length))); + + } catch (IllegalArgumentException e) { + Log.e(Constants.TAG, "error parsing uri in (suspected) linked id packet"); + return null; + } + } + + protected static LinkedIdentity parseUri (String nonce, String uriString) { + URI uri = URI.create(uriString); + + if ("pgpid".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(); + { + 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 new LinkedIdentity(nonce, flags, params, subUri); + + } + + public static LinkedIdentity fromResource (LinkedResource res, String nonce) { + return new LinkedIdentity(nonce, res.getFlags(), res.getParams(), res.getSubUri()); + } + + public WrappedUserAttribute toUserAttribute () { + return WrappedUserAttribute.fromSubpacket(WrappedUserAttribute.UAT_LINKED_ID, getEncoded()); + } + + public static String generateNonce() { + // TODO make this actually random + // byte[] data = new byte[96]; + // new SecureRandom().nextBytes(data); + // return Hex.toHexString(data); + + // debug for now + return "0123456789abcdef01234567"; + } + +} 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 new file mode 100644 index 000000000..a3f288dbb --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java @@ -0,0 +1,125 @@ +package org.sufficientlysecure.keychain.pgp.linked; + +import android.content.Context; + +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.pgp.linked.resources.GenericHttpsResource; +import org.sufficientlysecure.keychain.pgp.linked.resources.UnknownResource; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +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.Matcher; +import java.util.regex.Pattern; + +public abstract class LinkedResource { + + protected final URI mSubUri; + protected final Set mFlags; + protected final HashMap mParams; + + static Pattern magicPattern = + Pattern.compile("\\[Verifying my PGP key: pgpid\\+cookie:([a-zA-Z0-9]+)#([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); + } + + public URI getSubUri () { + return mSubUri; + } + + public static String generate (Context context, byte[] fingerprint, String nonce) { + + return "[Verifying my PGP key: pgpid+cookie:" + + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + "#" + nonce + "]"; + + } + + public LinkedVerifyResult verify(byte[] fingerprint, String nonce) { + + OperationLog log = new OperationLog(); + log.add(LogType.MSG_LV, 0); + + // Try to fetch resource. Logs for itself + String res = fetchResource(log, 1); + 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, nonce, fingerprint); + + } + + protected abstract String fetchResource (OperationLog log, int indent); + + protected Matcher matchResource (OperationLog log, int indent, String res) { + return magicPattern.matcher(res); + } + + protected LinkedVerifyResult verifyString (OperationLog log, int indent, + String res, + String nonce, 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 nonceCandidate = match.group(2).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); + + if (!nonce.equals(nonceCandidate)) { + log.add(LogType.MSG_LV_NONCE_ERROR, indent); + return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); + } + + log.add(LogType.MSG_LV_NONCE_OK, indent); + return new LinkedVerifyResult(LinkedVerifyResult.RESULT_OK, log); + + } + + public static LinkedResource findResourceType + (Set flags, HashMap params, URI uri) { + + LinkedResource res; + + res = GenericHttpsResource.create(flags, params, uri); + if (res != null) { + return res; + } + + return new UnknownResource(flags, params, uri); + + } + +} 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 new file mode 100644 index 000000000..1c66ffeca --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/DnsResource.java @@ -0,0 +1,70 @@ +package org.sufficientlysecure.keychain.pgp.linked.resources; + +import android.content.Context; + +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.pgp.linked.LinkedResource; +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.TYPE; +import de.measite.minidns.record.TXT; + +public class DnsResource extends LinkedResource { + + static Pattern magicPattern = + Pattern.compile("pgpid\\+cookie=([a-zA-Z0-9]+)(?:#|;)([a-zA-Z0-9]+)"); + + DnsResource(Set flags, HashMap params, URI uri) { + super(flags, params, uri); + } + + public static String generateText (Context context, byte[] fingerprint, String nonce) { + + return "pgpid+cookie=" + + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + ";" + nonce + ""; + + } + + 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; + } + return new DnsResource(flags, params, uri); + } + + @Override + protected String fetchResource (OperationLog log, int indent) { + + Client c = new Client(); + DNSMessage msg = c.query(new Question("mugenguild.com", TYPE.TXT)); + 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); + } +} 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 new file mode 100644 index 000000000..abe773f6c --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/GenericHttpsResource.java @@ -0,0 +1,103 @@ +package org.sufficientlysecure.keychain.pgp.linked.resources; + +import android.content.Context; + +import com.textuality.keybase.lib.Search; + +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.LinkedResource; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.Log; + +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +import javax.net.ssl.HttpsURLConnection; + +public class GenericHttpsResource extends LinkedResource { + + GenericHttpsResource(Set flags, HashMap params, URI uri) { + super(flags, params, uri); + } + + public static String generateText (Context context, byte[] fingerprint, String nonce) { + String cookie = LinkedResource.generate(context, fingerprint, nonce); + + 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) { + + log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); + indent += 1; + + try { + + HttpsURLConnection conn = null; + URL url = mSubUri.toURL(); + int status = 0; + int redirects = 0; + + while (redirects < 5) { + conn = (HttpsURLConnection) url.openConnection(); + conn.addRequestProperty("User-Agent", "OpenKeychain"); + conn.setConnectTimeout(5000); + conn.setReadTimeout(25000); + conn.connect(); + status = conn.getResponseCode(); + if (status == 301) { + redirects++; + url = new URL(conn.getHeaderFields().get("Location").get(0)); + log.add(LogType.MSG_LV_FETCH_REDIR, indent, url.toString()); + } else { + break; + } + } + + if (status >= 200 && status < 300) { + log.add(LogType.MSG_LV_FETCH_OK, indent, Integer.toString(status)); + return Search.snarf(conn.getInputStream()); + } else { + // log verbose output to logcat + Log.e(Constants.TAG, Search.snarf(conn.getErrorStream())); + log.add(LogType.MSG_LV_FETCH_ERROR, indent, Integer.toString(status)); + return null; + } + + } catch (MalformedURLException e) { + log.add(LogType.MSG_LV_FETCH_ERROR_URL, indent); + return null; + } catch (IOException e) { + log.add(LogType.MSG_LV_FETCH_ERROR_IO, indent); + return null; + } + + } + + 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); + } + +} 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 new file mode 100644 index 000000000..1b0db1fa1 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/TwitterResource.java @@ -0,0 +1,124 @@ +package org.sufficientlysecure.keychain.pgp.linked.resources; + +import android.content.Context; +import android.util.Base64; + +import com.textuality.keybase.lib.JWalk; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.BasicHttpParams; +import org.json.JSONException; +import org.json.JSONObject; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.pgp.linked.LinkedResource; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLEncoder; +import java.util.HashMap; +import java.util.Set; + +public class TwitterResource extends LinkedResource { + + TwitterResource(Set flags, HashMap params, URI uri) { + super(flags, params, uri); + } + + public static String generateText (Context context, byte[] fingerprint, String nonce) { + // nothing special here for now, might change this later + return LinkedResource.generate(context, fingerprint, nonce); + } + + private String getTwitterStream(String screenName) { + String results = null; + + // Step 1: Encode consumer key and secret + try { + // URL encode the consumer key and secret + String urlApiKey = URLEncoder.encode("6IhPnWbYxASAoAzH2QaUtHD0J", "UTF-8"); + String urlApiSecret = URLEncoder.encode("L0GnuiOnapWbSBbQtLIqtpeS5BTtvh06dmoMoKQfHQS8UwHuWm", "UTF-8"); + + // Concatenate the encoded consumer key, a colon character, and the + // encoded consumer secret + String combined = urlApiKey + ":" + urlApiSecret; + + // Base64 encode the string + String base64Encoded = Base64.encodeToString(combined.getBytes(), Base64.NO_WRAP); + + // 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)); + String auth = JWalk.getString(rawAuthorization, "access_token"); + + // Applications should verify that the value associated with the + // token_type key of the returned object is bearer + if (auth != null && JWalk.getString(rawAuthorization, "token_type").equals("bearer")) { + + // Step 3: Authenticate API requests with bearer token + HttpGet httpGet = + new HttpGet("https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=" + screenName); + + // construct a normal HTTPS request and include an Authorization + // header with the value of Bearer <> + httpGet.setHeader("Authorization", "Bearer " + auth); + httpGet.setHeader("Content-Type", "application/json"); + // update the results with the body of the response + results = getResponseBody(httpGet); + } + } catch (UnsupportedEncodingException ex) { + } catch (JSONException ex) { + } catch (IllegalStateException ex1) { + } + return results; + } + + private static String getResponseBody(HttpRequestBase request) { + StringBuilder sb = new StringBuilder(); + try { + + DefaultHttpClient httpClient = new DefaultHttpClient(new BasicHttpParams()); + HttpResponse response = httpClient.execute(request); + int statusCode = response.getStatusLine().getStatusCode(); + String reason = response.getStatusLine().getReasonPhrase(); + + if (statusCode == 200) { + + HttpEntity entity = response.getEntity(); + InputStream inputStream = entity.getContent(); + + BufferedReader bReader = new BufferedReader( + new InputStreamReader(inputStream, "UTF-8"), 8); + String line = null; + while ((line = bReader.readLine()) != null) { + sb.append(line); + } + } else { + sb.append(reason); + } + } catch (UnsupportedEncodingException ex) { + } catch (ClientProtocolException ex1) { + } catch (IOException ex2) { + } + return sb.toString(); + } + + @Override + protected String fetchResource(OperationLog log, int indent) { + return getTwitterStream("Valodim"); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java new file mode 100644 index 000000000..ae99cdd86 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java @@ -0,0 +1,21 @@ +package org.sufficientlysecure.keychain.pgp.linked.resources; + +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.pgp.linked.LinkedResource; + +import java.net.URI; +import java.util.HashMap; +import java.util.Set; + +public class UnknownResource extends LinkedResource { + + public UnknownResource(Set flags, HashMap params, URI uri) { + super(flags, params, uri); + } + + @Override + protected String fetchResource(OperationLog log, int indent) { + return null; + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 479810203..3626076b7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -33,7 +33,6 @@ import org.sufficientlysecure.keychain.operations.EditKeyOperation; import org.sufficientlysecure.keychain.operations.results.DeleteResult; import org.sufficientlysecure.keychain.operations.results.EditKeyResult; import org.sufficientlysecure.keychain.operations.results.ExportResult; -import org.sufficientlysecure.keychain.operations.results.PgpEditKeyResult; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.operations.results.CertifyResult; import org.sufficientlysecure.keychain.util.FileHelper; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index d9de274ee..2f2224b24 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -22,7 +22,6 @@ import android.os.Parcel; import android.os.Parcelable; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; import java.io.Serializable; import java.util.ArrayList; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index f40cfeb71..8101006e1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -38,7 +38,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; -import org.sufficientlysecure.keychain.ui.affirmations.AffirmationWizard; +import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; @@ -361,7 +361,7 @@ public class ViewKeyMainFragment extends LoaderFragment implements } private void linkKey(Uri dataUri) { - Intent editIntent = new Intent(getActivity(), AffirmationWizard.class); + Intent editIntent = new Intent(getActivity(), LinkedIdWizard.class); editIntent.setData(KeyRings.buildUnifiedKeyRingUri(dataUri)); startActivityForResult(editIntent, 0); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java deleted file mode 100644 index 70258eb5a..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep1Fragment.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * 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.affirmations; - -import android.os.Bundle; -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.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.EditText; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; -import org.sufficientlysecure.keychain.pgp.affirmation.resources.DnsResource; - -public class AffirmationCreateDnsStep1Fragment extends Fragment { - - AffirmationWizard mAffirmationWizard; - - EditText mEditDns; - - /** - * Creates new instance of this fragment - */ - public static AffirmationCreateDnsStep1Fragment newInstance() { - AffirmationCreateDnsStep1Fragment frag = new AffirmationCreateDnsStep1Fragment(); - - Bundle args = new Bundle(); - frag.setArguments(args); - - return frag; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mAffirmationWizard = (AffirmationWizard) getActivity(); - - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.affirmation_create_dns_fragment_step1, container, false); - - view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - - String uri = mEditDns.getText().toString(); - - if (!checkUri(uri)) { - mEditDns.setError("Please enter a valid domain name!"); - return; - } - - String proofNonce = LinkedIdentity.generateNonce(); - String proofText = DnsResource.generateText(getActivity(), - mAffirmationWizard.mFingerprint, proofNonce); - - AffirmationCreateDnsStep2Fragment frag = - AffirmationCreateDnsStep2Fragment.newInstance(uri, proofNonce, proofText); - - mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); - - } - }); - - view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mAffirmationWizard.loadFragment(null, null, AffirmationWizard.FRAG_ACTION_TO_LEFT); - } - }); - - mEditDns = (EditText) view.findViewById(R.id.affirmation_create_dns_domain); - - mEditDns.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 = editable.toString(); - if (uri.length() > 0) { - if (checkUri(uri)) { - mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0, - R.drawable.uid_mail_ok, 0); - } else { - mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0, - R.drawable.uid_mail_bad, 0); - } - } else { - // remove drawable if email is empty - mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); - } - } - }); - - mEditDns.setText("mugenguild.com"); - - return view; - } - - private static boolean checkUri(String uri) { - return Patterns.DOMAIN_NAME.matcher(uri).matches(); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep2Fragment.java deleted file mode 100644 index 87568ddc2..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateDnsStep2Fragment.java +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * 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.affirmations; - -import android.app.Activity; -import android.app.ProgressDialog; -import android.content.Intent; -import android.graphics.PorterDuff; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.os.Message; -import android.os.Messenger; -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 android.widget.ImageView; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; -import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; -import org.sufficientlysecure.keychain.pgp.affirmation.resources.DnsResource; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.service.SaveKeyringParcel; -import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; -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; - -public class AffirmationCreateDnsStep2Fragment extends Fragment { - - private static final int REQUEST_CODE_OUTPUT = 0x00007007; - private static final int REQUEST_CODE_PASSPHRASE = 0x00007008; - - public static final String DOMAIN = "domain", NONCE = "nonce", TEXT = "text"; - - AffirmationWizard mAffirmationWizard; - - EditText mEditUri; - ImageView mVerifyImage; - View mVerifyProgress; - TextView mVerifyStatus; - - String mResourceDomain; - String mResourceNonce, mResourceString; - - // This is a resource, set AFTER it has been verified - DnsResource mVerifiedResource = null; - - /** - * Creates new instance of this fragment - */ - public static AffirmationCreateDnsStep2Fragment newInstance - (String uri, String proofNonce, String proofText) { - - AffirmationCreateDnsStep2Fragment frag = new AffirmationCreateDnsStep2Fragment(); - - Bundle args = new Bundle(); - args.putString(DOMAIN, uri); - args.putString(NONCE, proofNonce); - args.putString(TEXT, proofText); - frag.setArguments(args); - - return frag; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.affirmation_create_dns_fragment_step2, container, false); - - mResourceDomain = getArguments().getString(DOMAIN); - mResourceNonce = getArguments().getString(NONCE); - mResourceString = getArguments().getString(TEXT); - - view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - startCertify(); - } - }); - - view.findViewById(R.id.back_button).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - mAffirmationWizard.loadFragment(null, null, AffirmationWizard.FRAG_ACTION_TO_LEFT); - } - }); - - mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); - mVerifyProgress = view.findViewById(R.id.verify_progress); - mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); - - view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofSend(); - } - }); - - view.findViewById(R.id.button_save).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofSave(); - } - }); - - view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofVerify(); - } - }); - - mEditUri = (EditText) view.findViewById(R.id.affirmation_create_dns_text); - mEditUri.setText(mResourceString); - - setVerifyProgress(false, null); - mVerifyStatus.setText(R.string.linked_verify_pending); - - return view; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mAffirmationWizard = (AffirmationWizard) getActivity(); - } - - public void setVerifyProgress(boolean on, Boolean success) { - mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); - mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); - if (success == null) { - mVerifyStatus.setText(R.string.linked_verifying); - mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout); - mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), - PorterDuff.Mode.SRC_IN); - } else if (success) { - mVerifyStatus.setText(R.string.linked_verify_success); - mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout); - mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark), - PorterDuff.Mode.SRC_IN); - } else { - mVerifyStatus.setText(R.string.linked_verify_error); - mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout); - mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), - PorterDuff.Mode.SRC_IN); - } - } - - private void proofSend () { - Intent sendIntent = new Intent(); - sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); - sendIntent.setType("text/plain"); - startActivity(sendIntent); - } - - private void proofSave () { - String state = Environment.getExternalStorageState(); - if (!Environment.MEDIA_MOUNTED.equals(state)) { - Notify.showNotify(getActivity(), "External storage not available!", Style.ERROR); - return; - } - - String targetName = "pgpkey.txt"; - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - File targetFile = new File(Constants.Path.APP_DIR, targetName); - FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file), - getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT); - } else { - FileHelper.saveDocument(this, "text/plain", targetName, REQUEST_CODE_OUTPUT); - } - } - - private void saveFile(Uri uri) { - try { - PrintWriter out = - new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); - out.print(mResourceString); - if (out.checkError()) { - Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR); - } - } catch (FileNotFoundException e) { - Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR); - e.printStackTrace(); - } - } - - public void proofVerify() { - setVerifyProgress(true, null); - - final DnsResource resource = DnsResource.createNew(mResourceDomain); - - new AsyncTask() { - - @Override - protected LinkedVerifyResult doInBackground(Void... params) { - return resource.verify(mAffirmationWizard.mFingerprint, mResourceNonce); - } - - @Override - protected void onPostExecute(LinkedVerifyResult result) { - super.onPostExecute(result); - if (result.success()) { - setVerifyProgress(false, true); - mVerifiedResource = resource; - } else { - setVerifyProgress(false, false); - // on error, show error message - result.createNotify(getActivity()).show(); - } - } - }.execute(); - - } - - public void startCertify() { - - if (mVerifiedResource == null) { - Notify.showNotify(getActivity(), R.string.linked_need_verify, Style.ERROR); - return; - } - - Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); - intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mAffirmationWizard.mMasterKeyId); - startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); - - } - - public void certifyLinkedIdentity (String passphrase) { - KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( - getActivity(), - getString(R.string.progress_saving), - ProgressDialog.STYLE_HORIZONTAL, - true) { - public void handleMessage(Message message) { - // handle messages by standard KeychainIntentServiceHandler first - super.handleMessage(message); - - if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - - // get returned data bundle - Bundle returnData = message.getData(); - if (returnData == null) { - return; - } - final OperationResult result = - returnData.getParcelable(OperationResult.EXTRA_RESULT); - if (result == null) { - return; - } - - // if bad -> display here! - if (!result.success()) { - result.createNotify(getActivity()).show(); - return; - } - - result.createNotify(getActivity()).show(); - - // if good -> finish, return result to showkey and display there! - // Intent intent = new Intent(); - // intent.putExtra(OperationResult.EXTRA_RESULT, result); - // getActivity().setResult(EditKeyActivity.RESULT_OK, intent); - - // AffirmationCreateHttpsStep3Fragment frag = - // AffirmationCreateHttpsStep3Fragment.newInstance( - // mResourceDomain, mResourceNonce, mResourceString); - - // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); - - } - } - }; - - SaveKeyringParcel skp = - new SaveKeyringParcel(mAffirmationWizard.mMasterKeyId, mAffirmationWizard.mFingerprint); - - WrappedUserAttribute ua = - LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); - - skp.mAddUserAttribute.add(ua); - - // Send all information needed to service to import key in other thread - Intent intent = new Intent(getActivity(), KeychainIntentService.class); - intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING); - - // fill values for this action - Bundle data = new Bundle(); - data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); - data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, skp); - intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(getActivity()); - - // start service with intent - getActivity().startService(intent); - - } - - - @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; - case REQUEST_CODE_PASSPHRASE: - if (resultCode == Activity.RESULT_OK && data != null) { - String passphrase = - data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); - certifyLinkedIdentity(passphrase); - } - break; - } - super.onActivityResult(requestCode, resultCode, data); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java deleted file mode 100644 index 2b00d7483..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep1Fragment.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * 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.affirmations; - -import android.os.Bundle; -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.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.EditText; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; -import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; - -public class AffirmationCreateHttpsStep1Fragment extends Fragment { - - AffirmationWizard mAffirmationWizard; - - EditText mEditUri; - - /** - * Creates new instance of this fragment - */ - public static AffirmationCreateHttpsStep1Fragment newInstance() { - AffirmationCreateHttpsStep1Fragment frag = new AffirmationCreateHttpsStep1Fragment(); - - Bundle args = new Bundle(); - frag.setArguments(args); - - return frag; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mAffirmationWizard = (AffirmationWizard) getActivity(); - - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.affirmation_create_https_fragment_step1, container, false); - - view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - - String uri = "https://" + mEditUri.getText(); - - if (!checkUri(uri)) { - return; - } - - String proofNonce = LinkedIdentity.generateNonce(); - String proofText = GenericHttpsResource.generateText(getActivity(), - mAffirmationWizard.mFingerprint, proofNonce); - - AffirmationCreateHttpsStep2Fragment frag = - AffirmationCreateHttpsStep2Fragment.newInstance(uri, proofNonce, proofText); - - mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); - - } - }); - - view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mAffirmationWizard.loadFragment(null, null, AffirmationWizard.FRAG_ACTION_TO_LEFT); - } - }); - - mEditUri = (EditText) view.findViewById(R.id.affirmation_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.uid_mail_ok, 0); - } else { - mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0, - R.drawable.uid_mail_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/affirmations/AffirmationCreateHttpsStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java deleted file mode 100644 index eb1088989..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateHttpsStep2Fragment.java +++ /dev/null @@ -1,368 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * 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.affirmations; - -import android.app.Activity; -import android.app.ProgressDialog; -import android.content.Intent; -import android.graphics.PorterDuff; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.os.Message; -import android.os.Messenger; -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 android.widget.ImageView; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; -import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; -import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.service.PassphraseCacheService; -import org.sufficientlysecure.keychain.service.SaveKeyringParcel; -import org.sufficientlysecure.keychain.ui.EditKeyActivity; -import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; -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; -import java.net.URI; -import java.net.URISyntaxException; - -public class AffirmationCreateHttpsStep2Fragment extends Fragment { - - private static final int REQUEST_CODE_OUTPUT = 0x00007007; - private static final int REQUEST_CODE_PASSPHRASE = 0x00007008; - - public static final String URI = "uri", NONCE = "nonce", TEXT = "text"; - - AffirmationWizard mAffirmationWizard; - - EditText mEditUri; - ImageView mVerifyImage; - View mVerifyProgress; - TextView mVerifyStatus; - - String mResourceUri; - String mResourceNonce, mResourceString; - - // This is a resource, set AFTER it has been verified - GenericHttpsResource mVerifiedResource = null; - - /** - * Creates new instance of this fragment - */ - public static AffirmationCreateHttpsStep2Fragment newInstance - (String uri, String proofNonce, String proofText) { - - AffirmationCreateHttpsStep2Fragment frag = new AffirmationCreateHttpsStep2Fragment(); - - Bundle args = new Bundle(); - args.putString(URI, uri); - args.putString(NONCE, proofNonce); - args.putString(TEXT, proofText); - frag.setArguments(args); - - return frag; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.affirmation_create_https_fragment_step2, container, false); - - mResourceUri = getArguments().getString(URI); - mResourceNonce = getArguments().getString(NONCE); - mResourceString = getArguments().getString(TEXT); - - view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - startCertify(); - } - }); - - view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mAffirmationWizard.loadFragment(null, null, AffirmationWizard.FRAG_ACTION_TO_LEFT); - } - }); - - mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); - mVerifyProgress = view.findViewById(R.id.verify_progress); - mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); - - view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofSend(); - } - }); - - view.findViewById(R.id.button_save).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofSave(); - } - }); - - view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofVerify(); - } - }); - - mEditUri = (EditText) view.findViewById(R.id.affirmation_create_https_uri); - mEditUri.setText(mResourceUri); - - setVerifyProgress(false, null); - mVerifyStatus.setText(R.string.linked_verify_pending); - - return view; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mAffirmationWizard = (AffirmationWizard) getActivity(); - } - - public void setVerifyProgress(boolean on, Boolean success) { - mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); - mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); - if (success == null) { - mVerifyStatus.setText(R.string.linked_verifying); - mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout); - mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), - PorterDuff.Mode.SRC_IN); - } else if (success) { - mVerifyStatus.setText(R.string.linked_verify_success); - mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout); - mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark), - PorterDuff.Mode.SRC_IN); - } else { - mVerifyStatus.setText(R.string.linked_verify_error); - mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout); - mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), - PorterDuff.Mode.SRC_IN); - } - } - - private void proofSend () { - Intent sendIntent = new Intent(); - sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); - sendIntent.setType("text/plain"); - startActivity(sendIntent); - } - - private void proofSave () { - String state = Environment.getExternalStorageState(); - if (!Environment.MEDIA_MOUNTED.equals(state)) { - Notify.showNotify(getActivity(), "External storage not available!", Style.ERROR); - return; - } - - String targetName = "pgpkey.txt"; - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - File targetFile = new File(Constants.Path.APP_DIR, targetName); - FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file), - getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT); - } else { - FileHelper.saveDocument(this, "text/plain", targetName, REQUEST_CODE_OUTPUT); - } - } - - private void saveFile(Uri uri) { - try { - PrintWriter out = - new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); - out.print(mResourceString); - if (out.checkError()) { - Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR); - } - } catch (FileNotFoundException e) { - Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR); - e.printStackTrace(); - } - } - - public void proofVerify() { - setVerifyProgress(true, null); - - try { - final GenericHttpsResource resource = GenericHttpsResource.createNew(new URI(mResourceUri)); - - new AsyncTask() { - - @Override - protected LinkedVerifyResult doInBackground(Void... params) { - return resource.verify(mAffirmationWizard.mFingerprint, mResourceNonce); - } - - @Override - protected void onPostExecute(LinkedVerifyResult result) { - super.onPostExecute(result); - if (result.success()) { - setVerifyProgress(false, true); - mVerifiedResource = resource; - } else { - setVerifyProgress(false, false); - // on error, show error message - result.createNotify(getActivity()).show(); - } - } - }.execute(); - } catch (URISyntaxException e) { - e.printStackTrace(); - } - - } - - public void startCertify() { - - if (mVerifiedResource == null) { - Notify.showNotify(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR); - return; - } - - Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); - intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mAffirmationWizard.mMasterKeyId); - startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); - - } - - public void certifyLinkedIdentity (String passphrase) { - KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( - getActivity(), - getString(R.string.progress_saving), - ProgressDialog.STYLE_HORIZONTAL, - true) { - public void handleMessage(Message message) { - // handle messages by standard KeychainIntentServiceHandler first - super.handleMessage(message); - - if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - - // get returned data bundle - Bundle returnData = message.getData(); - if (returnData == null) { - return; - } - final OperationResult result = - returnData.getParcelable(OperationResult.EXTRA_RESULT); - if (result == null) { - return; - } - - // if bad -> display here! - if (!result.success()) { - result.createNotify(getActivity()).show(); - return; - } - - result.createNotify(getActivity()).show(); - - // if good -> finish, return result to showkey and display there! - // Intent intent = new Intent(); - // intent.putExtra(OperationResult.EXTRA_RESULT, result); - // getActivity().setResult(EditKeyActivity.RESULT_OK, intent); - - // AffirmationCreateHttpsStep3Fragment frag = - // AffirmationCreateHttpsStep3Fragment.newInstance( - // mResourceUri, mResourceNonce, mResourceString); - - // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); - - } - } - }; - - SaveKeyringParcel skp = - new SaveKeyringParcel(mAffirmationWizard.mMasterKeyId, mAffirmationWizard.mFingerprint); - - WrappedUserAttribute ua = - LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); - - skp.mAddUserAttribute.add(ua); - - // Send all information needed to service to import key in other thread - Intent intent = new Intent(getActivity(), KeychainIntentService.class); - intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING); - - // fill values for this action - Bundle data = new Bundle(); - data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); - data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, skp); - intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(getActivity()); - - // start service with intent - getActivity().startService(intent); - - } - - - @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; - case REQUEST_CODE_PASSPHRASE: - if (resultCode == Activity.RESULT_OK && data != null) { - String passphrase = - data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); - certifyLinkedIdentity(passphrase); - } - break; - } - super.onActivityResult(requestCode, resultCode, data); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java deleted file mode 100644 index f52a18807..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep1Fragment.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * 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.affirmations; - -import android.os.AsyncTask; -import android.os.Bundle; -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.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.EditText; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.affirmation.LinkedIdentity; -import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; -import org.sufficientlysecure.keychain.pgp.affirmation.resources.TwitterResource; -import org.sufficientlysecure.keychain.ui.CreateKeyActivity; -import org.sufficientlysecure.keychain.ui.util.Notify; - -import java.io.IOException; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLConnection; - -public class AffirmationCreateTwitterStep1Fragment extends Fragment { - - AffirmationWizard mAffirmationWizard; - - EditText mEditHandle; - - /** - * Creates new instance of this fragment - */ - public static AffirmationCreateTwitterStep1Fragment newInstance() { - AffirmationCreateTwitterStep1Fragment frag = new AffirmationCreateTwitterStep1Fragment(); - - Bundle args = new Bundle(); - frag.setArguments(args); - - return frag; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mAffirmationWizard = (AffirmationWizard) getActivity(); - - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.affirmation_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(); - - new AsyncTask() { - - @Override - protected Boolean doInBackground(Void... params) { - return true; // checkHandle(handle); - } - - @Override - protected void onPostExecute(Boolean result) { - super.onPostExecute(result); - - if (result == null) { - Notify.showNotify(getActivity(), "Connection error while checking username!", Notify.Style.ERROR); - return; - } - - if (!result) { - Notify.showNotify(getActivity(), "This handle does not exist on Twitter!", Notify.Style.ERROR); - return; - } - - String proofNonce = LinkedIdentity.generateNonce(); - String proofText = TwitterResource.generateText(getActivity(), - mAffirmationWizard.mFingerprint, proofNonce); - - AffirmationCreateTwitterStep2Fragment frag = - AffirmationCreateTwitterStep2Fragment.newInstance(handle, proofNonce, proofText); - - mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); - } - }.execute(); - - } - }); - - view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mAffirmationWizard.loadFragment(null, null, AffirmationWizard.FRAG_ACTION_TO_LEFT); - } - }); - - mEditHandle = (EditText) view.findViewById(R.id.linked_create_twitter_handle); - mEditHandle.setText("Valodim"); - - return view; - } - - private static Boolean checkHandle(String handle) { - try { - HttpURLConnection nection = - (HttpURLConnection) new URL("https://twitter.com/" + handle).openConnection(); - nection.setRequestMethod("HEAD"); - return nection.getResponseCode() == 200; - } catch (IOException e) { - e.printStackTrace(); - return null; - } - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java deleted file mode 100644 index e12d2f86b..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep2Fragment.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * 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.affirmations; - -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.text.Editable; -import android.text.InputFilter; -import android.text.TextWatcher; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.ui.CreateKeyActivity; - -public class AffirmationCreateTwitterStep2Fragment extends Fragment { - - private static final int REQUEST_CODE_OUTPUT = 0x00007007; - - public static final String HANDLE = "uri", NONCE = "nonce", TEXT = "text"; - - AffirmationWizard mAffirmationWizard; - - EditText mEditTweetCustom, mEditTweetPreview; - ImageView mVerifyImage; - View mVerifyProgress; - TextView mVerifyStatus, mEditTweetTextLen; - - String mResourceHandle; - String mResourceNonce, mResourceString; - - /** - * Creates new instance of this fragment - */ - public static AffirmationCreateTwitterStep2Fragment newInstance - (String handle, String proofNonce, String proofText) { - - AffirmationCreateTwitterStep2Fragment frag = new AffirmationCreateTwitterStep2Fragment(); - - Bundle args = new Bundle(); - args.putString(HANDLE, handle); - args.putString(NONCE, proofNonce); - args.putString(TEXT, proofText); - frag.setArguments(args); - - return frag; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.affirmation_create_twitter_fragment_step2, container, false); - - mResourceHandle = getArguments().getString(HANDLE); - mResourceNonce = getArguments().getString(NONCE); - mResourceString = getArguments().getString(TEXT); - - view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - - AffirmationCreateTwitterStep3Fragment frag = - AffirmationCreateTwitterStep3Fragment.newInstance(mResourceHandle, - mResourceNonce, mResourceString, - mEditTweetCustom.getText().toString()); - - mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); - } - }); - - view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mAffirmationWizard.loadFragment(null, null, AffirmationWizard.FRAG_ACTION_TO_LEFT); - } - }); - - mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); - mVerifyProgress = view.findViewById(R.id.verify_progress); - mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); - - mEditTweetPreview = (EditText) view.findViewById(R.id.linked_create_twitter_preview); - mEditTweetPreview.setText(mResourceString); - - mEditTweetCustom = (EditText) view.findViewById(R.id.linked_create_twitter_custom); - mEditTweetCustom.setFilters(new InputFilter[] { - new InputFilter.LengthFilter(139 - mResourceString.length()) - }); - - mEditTweetTextLen = (TextView) view.findViewById(R.id.linked_create_twitter_textlen); - mEditTweetTextLen.setText(mResourceString.length() + "/140"); - - mEditTweetCustom.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) { - if (editable != null && editable.length() > 0) { - String str = editable + " " + mResourceString; - mEditTweetPreview.setText(str); - - mEditTweetTextLen.setText(str.length() + "/140"); - mEditTweetTextLen.setTextColor(getResources().getColor(str.length() == 140 - ? R.color.android_red_dark - : R.color.primary_dark_material_light)); - - - } else { - mEditTweetPreview.setText(mResourceString); - mEditTweetTextLen.setText(mResourceString.length() + "/140"); - } - } - }); - - return view; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mAffirmationWizard = (AffirmationWizard) getActivity(); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep3Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep3Fragment.java deleted file mode 100644 index 5ceda3342..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationCreateTwitterStep3Fragment.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * 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.affirmations; - -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.graphics.PorterDuff; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -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 android.widget.ImageView; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; -import org.sufficientlysecure.keychain.pgp.affirmation.resources.GenericHttpsResource; -import org.sufficientlysecure.keychain.pgp.affirmation.resources.TwitterResource; -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; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.List; - -public class AffirmationCreateTwitterStep3Fragment extends Fragment { - - public static final String HANDLE = "uri", NONCE = "nonce", TEXT = "text", CUSTOM = "custom"; - - AffirmationWizard mAffirmationWizard; - - EditText mEditTweetPreview; - ImageView mVerifyImage; - View mVerifyProgress; - TextView mVerifyStatus; - - String mResourceHandle, mCustom, mFullString; - String mResourceNonce, mResourceString; - - /** - * Creates new instance of this fragment - */ - public static AffirmationCreateTwitterStep3Fragment newInstance - (String handle, String proofNonce, String proofText, String customText) { - - AffirmationCreateTwitterStep3Fragment frag = new AffirmationCreateTwitterStep3Fragment(); - - Bundle args = new Bundle(); - args.putString(HANDLE, handle); - args.putString(NONCE, proofNonce); - args.putString(TEXT, proofText); - args.putString(CUSTOM, customText); - frag.setArguments(args); - - return frag; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.affirmation_create_twitter_fragment_step3, container, false); - - mResourceHandle = getArguments().getString(HANDLE); - mResourceNonce = getArguments().getString(NONCE); - mResourceString = getArguments().getString(TEXT); - mCustom = getArguments().getString(CUSTOM); - - mFullString = mCustom.isEmpty() ? mResourceString : (mCustom + " " + mResourceString); - - mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); - mVerifyProgress = view.findViewById(R.id.verify_progress); - mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); - - mEditTweetPreview = (EditText) view.findViewById(R.id.linked_create_twitter_preview); - mEditTweetPreview.setText(mFullString); - - view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mAffirmationWizard.loadFragment(null, null, AffirmationWizard.FRAG_ACTION_TO_LEFT); - } - }); - - 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(); - } - }); - - view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofVerify(); - } - }); - - setVerifyProgress(false, null); - mVerifyStatus.setText(R.string.linked_verify_pending); - - - view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - - // AffirmationCreateHttpsStep2Fragment frag = - // AffirmationCreateHttpsStep2Fragment.newInstance(); - - // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); - } - }); - - return view; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mAffirmationWizard = (AffirmationWizard) getActivity(); - } - - public void setVerifyProgress(boolean on, Boolean success) { - mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); - mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); - if (success == null) { - mVerifyStatus.setText(R.string.linked_verifying); - mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout); - mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), - PorterDuff.Mode.SRC_IN); - } else if (success) { - mVerifyStatus.setText(R.string.linked_verify_success); - mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout); - mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark), - PorterDuff.Mode.SRC_IN); - } else { - mVerifyStatus.setText(R.string.linked_verify_error); - mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout); - mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), - PorterDuff.Mode.SRC_IN); - } - } - - public void proofVerify() { - setVerifyProgress(true, null); - - /* - try { - final TwitterResource resource = TwitterResource.createNew(new URI(mResourceHandle)); - - new AsyncTask() { - - @Override - protected LinkedVerifyResult doInBackground(Void... params) { - return resource.verify(mAffirmationWizard.mFingerprint, mResourceNonce); - } - - @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(); - } - } - }.execute(); - } catch (URISyntaxException e) { - e.printStackTrace(); - } - */ - - } - - private void proofShare() { - Intent sendIntent = new Intent(); - sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, mFullString); - sendIntent.setType("text/plain"); - startActivity(sendIntent); - } - - private void proofSend() { - - Intent tweetIntent = new Intent(Intent.ACTION_SEND); - tweetIntent.putExtra(Intent.EXTRA_TEXT, mFullString); - tweetIntent.setType("text/plain"); - - PackageManager packManager = getActivity().getPackageManager(); - List resolvedInfoList = packManager.queryIntentActivities(tweetIntent, - PackageManager.MATCH_DEFAULT_ONLY); - - boolean resolved = false; - for(ResolveInfo resolveInfo : resolvedInfoList){ - if(resolveInfo.activityInfo.packageName.startsWith("com.twitter.android")) { - tweetIntent.setClassName( - resolveInfo.activityInfo.packageName, - resolveInfo.activityInfo.name ); - resolved = true; - break; - } - } - - if (resolved) { - startActivity(tweetIntent); - } else { - Notify.showNotify(getActivity(), - "Twitter app is not installed, please use the send intent!", - Notify.Style.ERROR); - } - - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationSelectFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationSelectFragment.java deleted file mode 100644 index c3667b5a2..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationSelectFragment.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * 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.affirmations; - -import android.os.Bundle; -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 AffirmationSelectFragment extends Fragment { - - AffirmationWizard mAffirmationWizard; - - /** - * Creates new instance of this fragment - */ - public static AffirmationSelectFragment newInstance() { - AffirmationSelectFragment frag = new AffirmationSelectFragment(); - - Bundle args = new Bundle(); - frag.setArguments(args); - - return frag; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.affirmation_select_fragment, container, false); - - view.findViewById(R.id.affirmation_create_https_button) - .setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - AffirmationCreateHttpsStep1Fragment frag = - AffirmationCreateHttpsStep1Fragment.newInstance(); - - mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); - } - }); - - view.findViewById(R.id.affirmation_create_dns_button) - .setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - AffirmationCreateDnsStep1Fragment frag = - AffirmationCreateDnsStep1Fragment.newInstance(); - - mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); - } - }); - - view.findViewById(R.id.affirmation_create_twitter_button) - .setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - AffirmationCreateTwitterStep1Fragment frag = - AffirmationCreateTwitterStep1Fragment.newInstance(); - - mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); - } - }); - - return view; - } - - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mAffirmationWizard = (AffirmationWizard) getActivity(); - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationWizard.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationWizard.java deleted file mode 100644 index 99b88405a..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/affirmations/AffirmationWizard.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * 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.affirmations; - -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 org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; -import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.util.Log; - -public class AffirmationWizard extends ActionBarActivity { - - 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; - - long mMasterKeyId; - byte[] mFingerprint; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.create_key_activity); - - try { - Uri uri = getIntent().getData(); - CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(uri); - mMasterKeyId = ring.extractOrGetMasterKeyId(); - mFingerprint = ring.getFingerprint(); - } catch (PgpKeyNotFoundException e) { - Log.e(Constants.TAG, "Invalid uri given, key does not exist!"); - finish(); - return; - } - - // pass extras into fragment - AffirmationSelectFragment frag = AffirmationSelectFragment.newInstance(); - loadFragment(null, frag, FRAG_ACTION_START); - } - - public void loadFragment(Bundle savedInstanceState, Fragment fragment, int action) { - // However, if we're being restored from a previous state, - // then we don't need to do anything and should return or else - // we could end up with overlapping fragments. - if (savedInstanceState != null) { - return; - } - - // 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; - - } - // do it immediately! - getSupportFragmentManager().executePendingTransactions(); - } - -} 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 new file mode 100644 index 000000000..ba57ace4b --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.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.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.resources.DnsResource; + +public class LinkedIdCreateDnsStep1Fragment extends Fragment { + + LinkedIdWizard mLinkedIdWizard; + + EditText mEditDns; + + /** + * Creates new instance of this fragment + */ + public static LinkedIdCreateDnsStep1Fragment newInstance() { + LinkedIdCreateDnsStep1Fragment frag = new LinkedIdCreateDnsStep1Fragment(); + + Bundle args = new Bundle(); + frag.setArguments(args); + + return frag; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.linked_create_dns_fragment_step1, container, false); + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + + String uri = mEditDns.getText().toString(); + + if (!checkUri(uri)) { + mEditDns.setError("Please enter a valid domain name!"); + return; + } + + String proofNonce = LinkedIdentity.generateNonce(); + String proofText = DnsResource.generateText(getActivity(), + mLinkedIdWizard.mFingerprint, proofNonce); + + LinkedIdCreateDnsStep2Fragment frag = + LinkedIdCreateDnsStep2Fragment.newInstance(uri, proofNonce, proofText); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + + } + }); + + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); + } + }); + + mEditDns = (EditText) view.findViewById(R.id.linked_create_dns_domain); + + mEditDns.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 = editable.toString(); + if (uri.length() > 0) { + if (checkUri(uri)) { + mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0, + R.drawable.uid_mail_ok, 0); + } else { + mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0, + R.drawable.uid_mail_bad, 0); + } + } else { + // remove drawable if email is empty + mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); + } + } + }); + + mEditDns.setText("mugenguild.com"); + + return view; + } + + private static boolean checkUri(String uri) { + return Patterns.DOMAIN_NAME.matcher(uri).matches(); + } + +} 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 new file mode 100644 index 000000000..ff8d03dd4 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.app.Activity; +import android.app.ProgressDialog; +import android.content.Intent; +import android.graphics.PorterDuff; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.Message; +import android.os.Messenger; +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 android.widget.ImageView; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.resources.DnsResource; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; +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; + +public class LinkedIdCreateDnsStep2Fragment extends Fragment { + + private static final int REQUEST_CODE_OUTPUT = 0x00007007; + private static final int REQUEST_CODE_PASSPHRASE = 0x00007008; + + public static final String DOMAIN = "domain", NONCE = "nonce", TEXT = "text"; + + LinkedIdWizard mLinkedIdWizard; + + EditText mEditUri; + ImageView mVerifyImage; + View mVerifyProgress; + TextView mVerifyStatus; + + String mResourceDomain; + String mResourceNonce, mResourceString; + + // This is a resource, set AFTER it has been verified + DnsResource mVerifiedResource = null; + + /** + * Creates new instance of this fragment + */ + public static LinkedIdCreateDnsStep2Fragment newInstance + (String uri, String proofNonce, String proofText) { + + LinkedIdCreateDnsStep2Fragment frag = new LinkedIdCreateDnsStep2Fragment(); + + Bundle args = new Bundle(); + args.putString(DOMAIN, uri); + args.putString(NONCE, proofNonce); + args.putString(TEXT, proofText); + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.linked_create_dns_fragment_step2, container, false); + + mResourceDomain = getArguments().getString(DOMAIN); + mResourceNonce = getArguments().getString(NONCE); + mResourceString = getArguments().getString(TEXT); + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + startCertify(); + } + }); + + view.findViewById(R.id.back_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); + } + }); + + mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); + mVerifyProgress = view.findViewById(R.id.verify_progress); + mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); + + view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSend(); + } + }); + + view.findViewById(R.id.button_save).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSave(); + } + }); + + view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofVerify(); + } + }); + + mEditUri = (EditText) view.findViewById(R.id.linked_create_dns_text); + mEditUri.setText(mResourceString); + + setVerifyProgress(false, null); + mVerifyStatus.setText(R.string.linked_verify_pending); + + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + } + + public void setVerifyProgress(boolean on, Boolean success) { + mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); + mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); + if (success == null) { + mVerifyStatus.setText(R.string.linked_verifying); + mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), + PorterDuff.Mode.SRC_IN); + } else if (success) { + mVerifyStatus.setText(R.string.linked_verify_success); + mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark), + PorterDuff.Mode.SRC_IN); + } else { + mVerifyStatus.setText(R.string.linked_verify_error); + mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), + PorterDuff.Mode.SRC_IN); + } + } + + private void proofSend () { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); + sendIntent.setType("text/plain"); + startActivity(sendIntent); + } + + private void proofSave () { + String state = Environment.getExternalStorageState(); + if (!Environment.MEDIA_MOUNTED.equals(state)) { + Notify.showNotify(getActivity(), "External storage not available!", Style.ERROR); + return; + } + + String targetName = "pgpkey.txt"; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + File targetFile = new File(Constants.Path.APP_DIR, targetName); + FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file), + getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT); + } else { + FileHelper.saveDocument(this, "text/plain", targetName, REQUEST_CODE_OUTPUT); + } + } + + private void saveFile(Uri uri) { + try { + PrintWriter out = + new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); + out.print(mResourceString); + if (out.checkError()) { + Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR); + } + } catch (FileNotFoundException e) { + Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR); + e.printStackTrace(); + } + } + + public void proofVerify() { + setVerifyProgress(true, null); + + final DnsResource resource = DnsResource.createNew(mResourceDomain); + + new AsyncTask() { + + @Override + protected LinkedVerifyResult doInBackground(Void... params) { + return resource.verify(mLinkedIdWizard.mFingerprint, mResourceNonce); + } + + @Override + protected void onPostExecute(LinkedVerifyResult result) { + super.onPostExecute(result); + if (result.success()) { + setVerifyProgress(false, true); + mVerifiedResource = resource; + } else { + setVerifyProgress(false, false); + // on error, show error message + result.createNotify(getActivity()).show(); + } + } + }.execute(); + + } + + public void startCertify() { + + if (mVerifiedResource == null) { + Notify.showNotify(getActivity(), R.string.linked_need_verify, Style.ERROR); + return; + } + + Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); + intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mLinkedIdWizard.mMasterKeyId); + startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); + + } + + public void certifyLinkedIdentity (String passphrase) { + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( + getActivity(), + getString(R.string.progress_saving), + ProgressDialog.STYLE_HORIZONTAL, + true) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + + // get returned data bundle + Bundle returnData = message.getData(); + if (returnData == null) { + return; + } + final OperationResult result = + returnData.getParcelable(OperationResult.EXTRA_RESULT); + if (result == null) { + return; + } + + // if bad -> display here! + if (!result.success()) { + result.createNotify(getActivity()).show(); + return; + } + + result.createNotify(getActivity()).show(); + + // if good -> finish, return result to showkey and display there! + // Intent intent = new Intent(); + // intent.putExtra(OperationResult.EXTRA_RESULT, result); + // getActivity().setResult(EditKeyActivity.RESULT_OK, intent); + + // AffirmationCreateHttpsStep3Fragment frag = + // AffirmationCreateHttpsStep3Fragment.newInstance( + // mResourceDomain, mResourceNonce, mResourceString); + + // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + + } + } + }; + + SaveKeyringParcel skp = + new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint); + + WrappedUserAttribute ua = + LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); + + skp.mAddUserAttribute.add(ua); + + // Send all information needed to service to import key in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING); + + // fill values for this action + Bundle data = new Bundle(); + data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); + data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, skp); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + + } + + + @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; + case REQUEST_CODE_PASSPHRASE: + if (resultCode == Activity.RESULT_OK && data != null) { + String passphrase = + data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + certifyLinkedIdentity(passphrase); + } + break; + } + super.onActivityResult(requestCode, resultCode, data); + } + +} 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 new file mode 100644 index 000000000..7c5f1f032 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.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.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.resources.GenericHttpsResource; + +public class LinkedIdCreateHttpsStep1Fragment extends Fragment { + + LinkedIdWizard mLinkedIdWizard; + + EditText mEditUri; + + /** + * Creates new instance of this fragment + */ + public static LinkedIdCreateHttpsStep1Fragment newInstance() { + LinkedIdCreateHttpsStep1Fragment frag = new LinkedIdCreateHttpsStep1Fragment(); + + Bundle args = new Bundle(); + frag.setArguments(args); + + return frag; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + + } + + @Override + public View onCreateView(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(new OnClickListener() { + @Override + public void onClick(View v) { + + String uri = "https://" + mEditUri.getText(); + + if (!checkUri(uri)) { + return; + } + + String proofNonce = LinkedIdentity.generateNonce(); + String proofText = GenericHttpsResource.generateText(getActivity(), + mLinkedIdWizard.mFingerprint, proofNonce); + + LinkedIdCreateHttpsStep2Fragment frag = + LinkedIdCreateHttpsStep2Fragment.newInstance(uri, proofNonce, proofText); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + + } + }); + + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); + } + }); + + mEditUri = (EditText) 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.uid_mail_ok, 0); + } else { + mEditUri.setCompoundDrawablesWithIntrinsicBounds(0, 0, + R.drawable.uid_mail_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 new file mode 100644 index 000000000..0d78bad27 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateHttpsStep2Fragment.java @@ -0,0 +1,366 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.app.Activity; +import android.app.ProgressDialog; +import android.content.Intent; +import android.graphics.PorterDuff; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.Message; +import android.os.Messenger; +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 android.widget.ImageView; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.resources.GenericHttpsResource; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; +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; +import java.net.URI; +import java.net.URISyntaxException; + +public class LinkedIdCreateHttpsStep2Fragment extends Fragment { + + private static final int REQUEST_CODE_OUTPUT = 0x00007007; + private static final int REQUEST_CODE_PASSPHRASE = 0x00007008; + + public static final String URI = "uri", NONCE = "nonce", TEXT = "text"; + + LinkedIdWizard mLinkedIdWizard; + + EditText mEditUri; + ImageView mVerifyImage; + View mVerifyProgress; + TextView mVerifyStatus; + + String mResourceUri; + String mResourceNonce, mResourceString; + + // This is a resource, set AFTER it has been verified + GenericHttpsResource mVerifiedResource = null; + + /** + * Creates new instance of this fragment + */ + public static LinkedIdCreateHttpsStep2Fragment newInstance + (String uri, String proofNonce, String proofText) { + + LinkedIdCreateHttpsStep2Fragment frag = new LinkedIdCreateHttpsStep2Fragment(); + + Bundle args = new Bundle(); + args.putString(URI, uri); + args.putString(NONCE, proofNonce); + args.putString(TEXT, proofText); + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.linked_create_https_fragment_step2, container, false); + + mResourceUri = getArguments().getString(URI); + mResourceNonce = getArguments().getString(NONCE); + mResourceString = getArguments().getString(TEXT); + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + startCertify(); + } + }); + + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); + } + }); + + mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); + mVerifyProgress = view.findViewById(R.id.verify_progress); + mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); + + view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSend(); + } + }); + + view.findViewById(R.id.button_save).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofSave(); + } + }); + + view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofVerify(); + } + }); + + mEditUri = (EditText) view.findViewById(R.id.linked_create_https_uri); + mEditUri.setText(mResourceUri); + + setVerifyProgress(false, null); + mVerifyStatus.setText(R.string.linked_verify_pending); + + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + } + + public void setVerifyProgress(boolean on, Boolean success) { + mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); + mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); + if (success == null) { + mVerifyStatus.setText(R.string.linked_verifying); + mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), + PorterDuff.Mode.SRC_IN); + } else if (success) { + mVerifyStatus.setText(R.string.linked_verify_success); + mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark), + PorterDuff.Mode.SRC_IN); + } else { + mVerifyStatus.setText(R.string.linked_verify_error); + mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), + PorterDuff.Mode.SRC_IN); + } + } + + private void proofSend () { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); + sendIntent.setType("text/plain"); + startActivity(sendIntent); + } + + private void proofSave () { + String state = Environment.getExternalStorageState(); + if (!Environment.MEDIA_MOUNTED.equals(state)) { + Notify.showNotify(getActivity(), "External storage not available!", Style.ERROR); + return; + } + + String targetName = "pgpkey.txt"; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + File targetFile = new File(Constants.Path.APP_DIR, targetName); + FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file), + getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT); + } else { + FileHelper.saveDocument(this, "text/plain", targetName, REQUEST_CODE_OUTPUT); + } + } + + private void saveFile(Uri uri) { + try { + PrintWriter out = + new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); + out.print(mResourceString); + if (out.checkError()) { + Notify.showNotify(getActivity(), "Error writing file!", Style.ERROR); + } + } catch (FileNotFoundException e) { + Notify.showNotify(getActivity(), "File could not be opened for writing!", Style.ERROR); + e.printStackTrace(); + } + } + + public void proofVerify() { + setVerifyProgress(true, null); + + try { + final GenericHttpsResource resource = GenericHttpsResource.createNew(new URI(mResourceUri)); + + new AsyncTask() { + + @Override + protected LinkedVerifyResult doInBackground(Void... params) { + return resource.verify(mLinkedIdWizard.mFingerprint, mResourceNonce); + } + + @Override + protected void onPostExecute(LinkedVerifyResult result) { + super.onPostExecute(result); + if (result.success()) { + setVerifyProgress(false, true); + mVerifiedResource = resource; + } else { + setVerifyProgress(false, false); + // on error, show error message + result.createNotify(getActivity()).show(); + } + } + }.execute(); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + + } + + public void startCertify() { + + if (mVerifiedResource == null) { + Notify.showNotify(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR); + return; + } + + Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); + intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mLinkedIdWizard.mMasterKeyId); + startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); + + } + + public void certifyLinkedIdentity (String passphrase) { + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( + getActivity(), + getString(R.string.progress_saving), + ProgressDialog.STYLE_HORIZONTAL, + true) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + + // get returned data bundle + Bundle returnData = message.getData(); + if (returnData == null) { + return; + } + final OperationResult result = + returnData.getParcelable(OperationResult.EXTRA_RESULT); + if (result == null) { + return; + } + + // if bad -> display here! + if (!result.success()) { + result.createNotify(getActivity()).show(); + return; + } + + result.createNotify(getActivity()).show(); + + // if good -> finish, return result to showkey and display there! + // Intent intent = new Intent(); + // intent.putExtra(OperationResult.EXTRA_RESULT, result); + // getActivity().setResult(EditKeyActivity.RESULT_OK, intent); + + // AffirmationCreateHttpsStep3Fragment frag = + // AffirmationCreateHttpsStep3Fragment.newInstance( + // mResourceUri, mResourceNonce, mResourceString); + + // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + + } + } + }; + + SaveKeyringParcel skp = + new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint); + + WrappedUserAttribute ua = + LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); + + skp.mAddUserAttribute.add(ua); + + // Send all information needed to service to import key in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING); + + // fill values for this action + Bundle data = new Bundle(); + data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); + data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, skp); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + + } + + + @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; + case REQUEST_CODE_PASSPHRASE: + if (resultCode == Activity.RESULT_OK && data != null) { + String passphrase = + data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + certifyLinkedIdentity(passphrase); + } + break; + } + 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 new file mode 100644 index 000000000..25a4f6463 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.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.pgp.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.resources.TwitterResource; +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; + + EditText mEditHandle; + + /** + * Creates new instance of this fragment + */ + public static LinkedIdCreateTwitterStep1Fragment newInstance() { + LinkedIdCreateTwitterStep1Fragment frag = new LinkedIdCreateTwitterStep1Fragment(); + + Bundle args = new Bundle(); + frag.setArguments(args); + + return frag; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + + } + + @Override + public View onCreateView(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(); + + new AsyncTask() { + + @Override + protected Boolean doInBackground(Void... params) { + return true; // checkHandle(handle); + } + + @Override + protected void onPostExecute(Boolean result) { + super.onPostExecute(result); + + if (result == null) { + Notify.showNotify(getActivity(), "Connection error while checking username!", Notify.Style.ERROR); + return; + } + + if (!result) { + Notify.showNotify(getActivity(), "This handle does not exist on Twitter!", Notify.Style.ERROR); + return; + } + + String proofNonce = LinkedIdentity.generateNonce(); + String proofText = TwitterResource.generateText(getActivity(), + mLinkedIdWizard.mFingerprint, proofNonce); + + LinkedIdCreateTwitterStep2Fragment frag = + LinkedIdCreateTwitterStep2Fragment.newInstance(handle, proofNonce, proofText); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + } + }.execute(); + + } + }); + + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); + } + }); + + mEditHandle = (EditText) view.findViewById(R.id.linked_create_twitter_handle); + mEditHandle.setText("Valodim"); + + return view; + } + + private static Boolean checkHandle(String handle) { + try { + HttpURLConnection nection = + (HttpURLConnection) new URL("https://twitter.com/" + handle).openConnection(); + nection.setRequestMethod("HEAD"); + return nection.getResponseCode() == 200; + } catch (IOException e) { + e.printStackTrace(); + 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 new file mode 100644 index 000000000..c02a76669 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep2Fragment.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.v4.app.Fragment; +import android.text.Editable; +import android.text.InputFilter; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; + +public class LinkedIdCreateTwitterStep2Fragment extends Fragment { + + private static final int REQUEST_CODE_OUTPUT = 0x00007007; + + public static final String HANDLE = "uri", NONCE = "nonce", TEXT = "text"; + + LinkedIdWizard mLinkedIdWizard; + + EditText mEditTweetCustom, mEditTweetPreview; + ImageView mVerifyImage; + View mVerifyProgress; + TextView mVerifyStatus, mEditTweetTextLen; + + String mResourceHandle; + String mResourceNonce, mResourceString; + + /** + * Creates new instance of this fragment + */ + public static LinkedIdCreateTwitterStep2Fragment newInstance + (String handle, String proofNonce, String proofText) { + + LinkedIdCreateTwitterStep2Fragment frag = new LinkedIdCreateTwitterStep2Fragment(); + + Bundle args = new Bundle(); + args.putString(HANDLE, handle); + args.putString(NONCE, proofNonce); + args.putString(TEXT, proofText); + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.linked_create_twitter_fragment_step2, container, false); + + mResourceHandle = getArguments().getString(HANDLE); + mResourceNonce = getArguments().getString(NONCE); + mResourceString = getArguments().getString(TEXT); + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + + LinkedIdCreateTwitterStep3Fragment frag = + LinkedIdCreateTwitterStep3Fragment.newInstance(mResourceHandle, + mResourceNonce, mResourceString, + mEditTweetCustom.getText().toString()); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + } + }); + + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); + } + }); + + mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); + mVerifyProgress = view.findViewById(R.id.verify_progress); + mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); + + mEditTweetPreview = (EditText) view.findViewById(R.id.linked_create_twitter_preview); + mEditTweetPreview.setText(mResourceString); + + mEditTweetCustom = (EditText) view.findViewById(R.id.linked_create_twitter_custom); + mEditTweetCustom.setFilters(new InputFilter[] { + new InputFilter.LengthFilter(139 - mResourceString.length()) + }); + + mEditTweetTextLen = (TextView) view.findViewById(R.id.linked_create_twitter_textlen); + mEditTweetTextLen.setText(mResourceString.length() + "/140"); + + mEditTweetCustom.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) { + if (editable != null && editable.length() > 0) { + String str = editable + " " + mResourceString; + mEditTweetPreview.setText(str); + + mEditTweetTextLen.setText(str.length() + "/140"); + mEditTweetTextLen.setTextColor(getResources().getColor(str.length() == 140 + ? R.color.android_red_dark + : R.color.primary_dark_material_light)); + + + } else { + mEditTweetPreview.setText(mResourceString); + mEditTweetTextLen.setText(mResourceString.length() + "/140"); + } + } + }); + + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java new file mode 100644 index 000000000..23e50d9dc --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.PorterDuff; +import android.os.Bundle; +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 android.widget.ImageView; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.util.Notify; + +import java.util.List; + +public class LinkedIdCreateTwitterStep3Fragment extends Fragment { + + public static final String HANDLE = "uri", NONCE = "nonce", TEXT = "text", CUSTOM = "custom"; + + LinkedIdWizard mLinkedIdWizard; + + EditText mEditTweetPreview; + ImageView mVerifyImage; + View mVerifyProgress; + TextView mVerifyStatus; + + String mResourceHandle, mCustom, mFullString; + String mResourceNonce, mResourceString; + + /** + * Creates new instance of this fragment + */ + public static LinkedIdCreateTwitterStep3Fragment newInstance + (String handle, String proofNonce, String proofText, String customText) { + + LinkedIdCreateTwitterStep3Fragment frag = new LinkedIdCreateTwitterStep3Fragment(); + + Bundle args = new Bundle(); + args.putString(HANDLE, handle); + args.putString(NONCE, proofNonce); + args.putString(TEXT, proofText); + args.putString(CUSTOM, customText); + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.linked_create_twitter_fragment_step3, container, false); + + mResourceHandle = getArguments().getString(HANDLE); + mResourceNonce = getArguments().getString(NONCE); + mResourceString = getArguments().getString(TEXT); + mCustom = getArguments().getString(CUSTOM); + + mFullString = mCustom.isEmpty() ? mResourceString : (mCustom + " " + mResourceString); + + mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); + mVerifyProgress = view.findViewById(R.id.verify_progress); + mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); + + mEditTweetPreview = (EditText) view.findViewById(R.id.linked_create_twitter_preview); + mEditTweetPreview.setText(mFullString); + + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); + } + }); + + 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(); + } + }); + + view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofVerify(); + } + }); + + setVerifyProgress(false, null); + mVerifyStatus.setText(R.string.linked_verify_pending); + + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + + // AffirmationCreateHttpsStep2Fragment frag = + // AffirmationCreateHttpsStep2Fragment.newInstance(); + + // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + } + }); + + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + } + + public void setVerifyProgress(boolean on, Boolean success) { + mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); + mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); + if (success == null) { + mVerifyStatus.setText(R.string.linked_verifying); + mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), + PorterDuff.Mode.SRC_IN); + } else if (success) { + mVerifyStatus.setText(R.string.linked_verify_success); + mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark), + PorterDuff.Mode.SRC_IN); + } else { + mVerifyStatus.setText(R.string.linked_verify_error); + mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout); + mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), + PorterDuff.Mode.SRC_IN); + } + } + + public void proofVerify() { + setVerifyProgress(true, null); + + /* + try { + final TwitterResource resource = TwitterResource.createNew(new URI(mResourceHandle)); + + new AsyncTask() { + + @Override + protected LinkedVerifyResult doInBackground(Void... params) { + return resource.verify(mAffirmationWizard.mFingerprint, mResourceNonce); + } + + @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(); + } + } + }.execute(); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + */ + + } + + private void proofShare() { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, mFullString); + sendIntent.setType("text/plain"); + startActivity(sendIntent); + } + + private void proofSend() { + + Intent tweetIntent = new Intent(Intent.ACTION_SEND); + tweetIntent.putExtra(Intent.EXTRA_TEXT, mFullString); + tweetIntent.setType("text/plain"); + + PackageManager packManager = getActivity().getPackageManager(); + List resolvedInfoList = packManager.queryIntentActivities(tweetIntent, + PackageManager.MATCH_DEFAULT_ONLY); + + boolean resolved = false; + for(ResolveInfo resolveInfo : resolvedInfoList){ + if(resolveInfo.activityInfo.packageName.startsWith("com.twitter.android")) { + tweetIntent.setClassName( + resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name ); + resolved = true; + break; + } + } + + if (resolved) { + startActivity(tweetIntent); + } else { + Notify.showNotify(getActivity(), + "Twitter app is not installed, please use the send intent!", + Notify.Style.ERROR); + } + + } + +} 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 new file mode 100644 index 000000000..abe7dbaf1 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdSelectFragment.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.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 { + + LinkedIdWizard mLinkedIdWizard; + + /** + * Creates new instance of this fragment + */ + public static LinkedIdSelectFragment newInstance() { + LinkedIdSelectFragment frag = new LinkedIdSelectFragment(); + + Bundle args = new Bundle(); + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(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(new View.OnClickListener() { + @Override + public void onClick(View v) { + LinkedIdCreateHttpsStep1Fragment frag = + LinkedIdCreateHttpsStep1Fragment.newInstance(); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + } + }); + + view.findViewById(R.id.linked_create_dns_button) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + LinkedIdCreateDnsStep1Fragment frag = + LinkedIdCreateDnsStep1Fragment.newInstance(); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + } + }); + + view.findViewById(R.id.linked_create_twitter_button) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + LinkedIdCreateTwitterStep1Fragment frag = + LinkedIdCreateTwitterStep1Fragment.newInstance(); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + } + }); + + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + } + +} 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 new file mode 100644 index 000000000..b8f3329c1 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdWizard.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.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 org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; +import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.util.Log; + +public class LinkedIdWizard extends ActionBarActivity { + + 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; + + long mMasterKeyId; + byte[] mFingerprint; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.create_key_activity); + + try { + Uri uri = getIntent().getData(); + CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(uri); + mMasterKeyId = ring.extractOrGetMasterKeyId(); + mFingerprint = ring.getFingerprint(); + } catch (PgpKeyNotFoundException e) { + Log.e(Constants.TAG, "Invalid uri given, key does not exist!"); + finish(); + return; + } + + // pass extras into fragment + LinkedIdSelectFragment frag = LinkedIdSelectFragment.newInstance(); + loadFragment(null, frag, FRAG_ACTION_START); + } + + public void loadFragment(Bundle savedInstanceState, Fragment fragment, int action) { + // However, if we're being restored from a previous state, + // then we don't need to do anything and should return or else + // we could end up with overlapping fragments. + if (savedInstanceState != null) { + return; + } + + // 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; + + } + // do it immediately! + getSupportFragmentManager().executePendingTransactions(); + } + +} -- cgit v1.2.3 From 6935509d22fd1db7d9af29141511efe426acdba6 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 16 Jan 2015 13:50:25 +0100 Subject: show notification if KeyListActivity is spawned with an EXTRA_RESULT --- .../java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java | 3 ++- .../java/org/sufficientlysecure/keychain/ui/KeyListActivity.java | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java index f18e475fc..d71c9c465 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/FirstTimeActivity.java @@ -96,7 +96,7 @@ public class FirstTimeActivity extends ActionBarActivity { if (srcData != null) { intent.putExtras(srcData); } - startActivityForResult(intent, 0); + startActivity(intent); finish(); } @@ -105,4 +105,5 @@ public class FirstTimeActivity extends ActionBarActivity { public boolean onKeyDown(int keyCode, KeyEvent event) { return keyCode == KeyEvent.KEYCODE_MENU || super.onKeyDown(keyCode, event); } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java index ba03400d7..8d26cc955 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java @@ -65,6 +65,13 @@ public class KeyListActivity extends DrawerActivity { setContentView(R.layout.key_list_activity); + Intent data = getIntent(); + // If we got an EXTRA_RESULT in the intent, show the notification + if (data != null && data.hasExtra(OperationResult.EXTRA_RESULT)) { + OperationResult result = data.getParcelableExtra(OperationResult.EXTRA_RESULT); + result.createNotify(this).show(); + } + // now setup navigation drawer in DrawerActivity... activateDrawerNavigation(savedInstanceState); } -- cgit v1.2.3 From b6a1463161779b820a8b03a9d17c8a35dccea338 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 16 Jan 2015 13:57:18 +0100 Subject: dns resource is parametrized by fqdn, class and type --- .../keychain/pgp/linked/resources/DnsResource.java | 37 +++++++++++++++++++--- .../ui/linked/LinkedIdCreateDnsStep1Fragment.java | 2 +- 2 files changed, 34 insertions(+), 5 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index 1c66ffeca..a2836e666 100644 --- 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 @@ -17,16 +17,26 @@ 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 LinkedResource { - static Pattern magicPattern = + final static Pattern magicPattern = Pattern.compile("pgpid\\+cookie=([a-zA-Z0-9]+)(?:#|;)([a-zA-Z0-9]+)"); - DnsResource(Set flags, HashMap params, URI uri) { + 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, String nonce) { @@ -49,14 +59,33 @@ public class DnsResource extends LinkedResource { && (params == null || params.isEmpty()))) { return null; } - return new DnsResource(flags, params, uri); + + // + 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); } @Override protected String fetchResource (OperationLog log, int indent) { Client c = new Client(); - DNSMessage msg = c.query(new Question("mugenguild.com", TYPE.TXT)); + DNSMessage msg = c.query(new Question(mFqdn, mType, mClass)); Record aw = msg.getAnswers()[0]; TXT txt = (TXT) aw.getPayload(); return txt.getText().toLowerCase(); 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 ba57ace4b..973067246 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 @@ -121,7 +121,7 @@ public class LinkedIdCreateDnsStep1Fragment extends Fragment { } }); - mEditDns.setText("mugenguild.com"); + mEditDns.setText("test.mugenguild.com"); return view; } -- cgit v1.2.3 From 6c153b15430c00b5213062be4bb5830d52f2589a Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 16 Jan 2015 15:59:26 +0100 Subject: linked id ui work dns/twitter --- .../keychain/pgp/linked/LinkedResource.java | 4 ++++ .../ui/linked/LinkedIdCreateDnsStep2Fragment.java | 6 +++--- .../ui/linked/LinkedIdCreateTwitterStep2Fragment.java | 15 +++++++++------ 3 files changed, 16 insertions(+), 9 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index a3f288dbb..b7d111dc9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java @@ -52,6 +52,10 @@ public abstract class LinkedResource { } + public static String generatePreview () { + return "[Verifying my PGP key: pgpid+cookie:0x…]"; + } + public LinkedVerifyResult verify(byte[] fingerprint, String nonce) { OperationLog log = new OperationLog(); 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 ff8d03dd4..1106a4a48 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 @@ -65,7 +65,7 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment { LinkedIdWizard mLinkedIdWizard; - EditText mEditUri; + TextView mTextView; ImageView mVerifyImage; View mVerifyProgress; TextView mVerifyStatus; @@ -140,8 +140,8 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment { } }); - mEditUri = (EditText) view.findViewById(R.id.linked_create_dns_text); - mEditUri.setText(mResourceString); + mTextView = (TextView) view.findViewById(R.id.linked_create_dns_text); + mTextView.setText(mResourceString); setVerifyProgress(false, null); mVerifyStatus.setText(R.string.linked_verify_pending); 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 c02a76669..ab56e7a5d 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 @@ -31,11 +31,10 @@ import android.widget.ImageView; import android.widget.TextView; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.linked.resources.TwitterResource; public class LinkedIdCreateTwitterStep2Fragment extends Fragment { - private static final int REQUEST_CODE_OUTPUT = 0x00007007; - public static final String HANDLE = "uri", NONCE = "nonce", TEXT = "text"; LinkedIdWizard mLinkedIdWizard; @@ -47,6 +46,7 @@ public class LinkedIdCreateTwitterStep2Fragment extends Fragment { String mResourceHandle; String mResourceNonce, mResourceString; + String mCookiePreview; /** * Creates new instance of this fragment @@ -69,6 +69,8 @@ public class LinkedIdCreateTwitterStep2Fragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.linked_create_twitter_fragment_step2, container, false); + mCookiePreview = TwitterResource.generatePreview(); + mResourceHandle = getArguments().getString(HANDLE); mResourceNonce = getArguments().getString(NONCE); mResourceString = getArguments().getString(TEXT); @@ -98,7 +100,7 @@ public class LinkedIdCreateTwitterStep2Fragment extends Fragment { mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); mEditTweetPreview = (EditText) view.findViewById(R.id.linked_create_twitter_preview); - mEditTweetPreview.setText(mResourceString); + mEditTweetPreview.setText(mCookiePreview); mEditTweetCustom = (EditText) view.findViewById(R.id.linked_create_twitter_custom); mEditTweetCustom.setFilters(new InputFilter[] { @@ -120,17 +122,18 @@ public class LinkedIdCreateTwitterStep2Fragment extends Fragment { @Override public void afterTextChanged(Editable editable) { if (editable != null && editable.length() > 0) { - String str = editable + " " + mResourceString; + String str = editable + " " + mCookiePreview; mEditTweetPreview.setText(str); - mEditTweetTextLen.setText(str.length() + "/140"); + mEditTweetTextLen.setText( + (editable.length() + mResourceString.length() + 1) + "/140"); mEditTweetTextLen.setTextColor(getResources().getColor(str.length() == 140 ? R.color.android_red_dark : R.color.primary_dark_material_light)); } else { - mEditTweetPreview.setText(mResourceString); + mEditTweetPreview.setText(mCookiePreview); mEditTweetTextLen.setText(mResourceString.length() + "/140"); } } -- cgit v1.2.3 From aa22a0defcf94d5f734d3a00288f5c8d916d2e57 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 16 Jan 2015 16:24:34 +0100 Subject: small language fix --- .../keychain/ui/adapter/UserAttributesAdapter.java | 276 +++++++++++++++++++++ .../keychain/ui/adapter/UserIdsAdapter.java | 272 -------------------- 2 files changed, 276 insertions(+), 272 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java new file mode 100644 index 000000000..7b8670f22 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.adapter; + +import android.content.Context; +import android.database.Cursor; +import android.graphics.Typeface; +import android.support.v4.widget.CursorAdapter; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; +import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; + +import java.util.ArrayList; + +public class UserAttributesAdapter extends CursorAdapter implements AdapterView.OnItemClickListener { + private LayoutInflater mInflater; + private final ArrayList mCheckStates; + private SaveKeyringParcel mSaveKeyringParcel; + + public static final String[] USER_PACKETS_PROJECTION = new String[]{ + UserPackets._ID, + UserPackets.TYPE, + UserPackets.USER_ID, + UserPackets.ATTRIBUTE_DATA, + UserPackets.RANK, + UserPackets.VERIFIED, + UserPackets.IS_PRIMARY, + UserPackets.IS_REVOKED + }; + private static final int INDEX_ID = 0; + private static final int INDEX_TYPE = 1; + private static final int INDEX_USER_ID = 2; + private static final int INDEX_ATTRIBUTE_DATA = 3; + private static final int INDEX_RANK = 4; + private static final int INDEX_VERIFIED = 5; + private static final int INDEX_IS_PRIMARY = 6; + private static final int INDEX_IS_REVOKED = 7; + + public UserAttributesAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes, + SaveKeyringParcel saveKeyringParcel) { + super(context, c, flags); + mInflater = LayoutInflater.from(context); + + mCheckStates = showCheckBoxes ? new ArrayList() : null; + mSaveKeyringParcel = saveKeyringParcel; + } + + public UserAttributesAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes) { + this(context, c, flags, showCheckBoxes, null); + } + + public UserAttributesAdapter(Context context, Cursor c, int flags, SaveKeyringParcel saveKeyringParcel) { + this(context, c, flags, false, saveKeyringParcel); + } + + public UserAttributesAdapter(Context context, Cursor c, int flags) { + this(context, c, flags, false, null); + } + + @Override + public Cursor swapCursor(Cursor newCursor) { + if (mCheckStates != null) { + mCheckStates.clear(); + if (newCursor != null) { + int count = newCursor.getCount(); + mCheckStates.ensureCapacity(count); + // initialize to true (use case knowledge: we usually want to sign all uids) + for (int i = 0; i < count; i++) { + newCursor.moveToPosition(i); + int verified = newCursor.getInt(INDEX_VERIFIED); + mCheckStates.add(verified != Certs.VERIFIED_SECRET); + } + } + } + + return super.swapCursor(newCursor); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + TextView vName = (TextView) view.findViewById(R.id.user_id_item_name); + TextView vAddress = (TextView) view.findViewById(R.id.user_id_item_address); + TextView vComment = (TextView) view.findViewById(R.id.user_id_item_comment); + ImageView vVerified = (ImageView) view.findViewById(R.id.user_id_item_certified); + View vVerifiedLayout = view.findViewById(R.id.user_id_item_certified_layout); + ImageView vEditImage = (ImageView) view.findViewById(R.id.user_id_item_edit_image); + ImageView vDeleteButton = (ImageView) view.findViewById(R.id.user_id_item_delete_button); + vDeleteButton.setVisibility(View.GONE); // not used + + String userId = cursor.getString(INDEX_USER_ID); + String[] splitUserId = KeyRing.splitUserId(userId); + if (splitUserId[0] != null) { + vName.setText(splitUserId[0]); + } else { + vName.setText(R.string.user_id_no_name); + } + if (splitUserId[1] != null) { + vAddress.setText(splitUserId[1]); + vAddress.setVisibility(View.VISIBLE); + } else { + vAddress.setVisibility(View.GONE); + } + if (splitUserId[2] != null) { + vComment.setText(splitUserId[2]); + vComment.setVisibility(View.VISIBLE); + } else { + vComment.setVisibility(View.GONE); + } + + boolean isPrimary = cursor.getInt(INDEX_IS_PRIMARY) != 0; + boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; + + // for edit key + if (mSaveKeyringParcel != null) { + boolean changeAnyPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null); + boolean changeThisPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null + && mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)); + boolean revokeThisUserId = (mSaveKeyringParcel.mRevokeUserIds.contains(userId)); + + // only if primary user id will be changed + // (this is not triggered if the user id is currently the primary one) + if (changeAnyPrimaryUserId) { + // change _all_ primary user ids and set new one to true + isPrimary = changeThisPrimaryUserId; + } + + if (revokeThisUserId) { + if (!isRevoked) { + isRevoked = true; + } + } + + vEditImage.setVisibility(View.VISIBLE); + vVerifiedLayout.setVisibility(View.GONE); + } else { + vEditImage.setVisibility(View.GONE); + vVerifiedLayout.setVisibility(View.VISIBLE); + } + + if (isRevoked) { + // set revocation icon (can this even be primary?) + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_REVOKED, true); + + // disable revoked user ids + vName.setEnabled(false); + vAddress.setEnabled(false); + vComment.setEnabled(false); + } else { + vName.setEnabled(true); + vAddress.setEnabled(true); + vComment.setEnabled(true); + + if (isPrimary) { + vName.setTypeface(null, Typeface.BOLD); + vAddress.setTypeface(null, Typeface.BOLD); + } else { + vName.setTypeface(null, Typeface.NORMAL); + vAddress.setTypeface(null, Typeface.NORMAL); + } + + int isVerified = cursor.getInt(INDEX_VERIFIED); + switch (isVerified) { + case Certs.VERIFIED_SECRET: + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_VERIFIED, false); + break; + case Certs.VERIFIED_SELF: + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_UNVERIFIED, false); + break; + default: + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_INVALID, false); + break; + } + } + + // don't care further if checkboxes aren't shown + if (mCheckStates == null) { + return; + } + + final CheckBox vCheckBox = (CheckBox) view.findViewById(R.id.user_id_item_check_box); + final int position = cursor.getPosition(); + vCheckBox.setOnCheckedChangeListener(null); + vCheckBox.setChecked(mCheckStates.get(position)); + vCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean b) { + mCheckStates.set(position, b); + } + }); + vCheckBox.setClickable(false); + } + + public void onItemClick(AdapterView adapter, View view, int position, long id) { + CheckBox box = ((CheckBox) view.findViewById(R.id.user_id_item_check_box)); + if (box != null) { + box.toggle(); + } + } + + public ArrayList getSelectedUserIds() { + ArrayList result = new ArrayList(); + for (int i = 0; i < mCheckStates.size(); i++) { + if (mCheckStates.get(i)) { + mCursor.moveToPosition(i); + result.add(mCursor.getString(INDEX_USER_ID)); + } + } + return result; + } + + public String getUserId(int position) { + mCursor.moveToPosition(position); + return mCursor.getString(INDEX_USER_ID); + } + + public boolean getIsRevoked(int position) { + mCursor.moveToPosition(position); + return mCursor.getInt(INDEX_IS_REVOKED) > 0; + } + + public int getIsVerified(int position) { + mCursor.moveToPosition(position); + return mCursor.getInt(INDEX_VERIFIED); + } + + public boolean getIsRevokedPending(int position) { + mCursor.moveToPosition(position); + String userId = mCursor.getString(INDEX_USER_ID); + + boolean isRevokedPending = false; + if (mSaveKeyringParcel != null) { + if (mSaveKeyringParcel.mRevokeUserIds.contains(userId)) { + isRevokedPending = true; + } + + } + + return isRevokedPending; + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + View view = mInflater.inflate(R.layout.view_key_user_id_item, null); + // only need to do this once ever, since mShowCheckBoxes is final + view.findViewById(R.id.user_id_item_check_box).setVisibility(mCheckStates != null ? View.VISIBLE : View.GONE); + return view; + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java deleted file mode 100644 index a778c7fa7..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * 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.adapter; - -import android.content.Context; -import android.database.Cursor; -import android.graphics.Typeface; -import android.support.v4.widget.CursorAdapter; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.ImageView; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; -import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; -import org.sufficientlysecure.keychain.service.SaveKeyringParcel; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; - -import java.util.ArrayList; - -public class UserIdsAdapter extends CursorAdapter implements AdapterView.OnItemClickListener { - private LayoutInflater mInflater; - private final ArrayList mCheckStates; - private SaveKeyringParcel mSaveKeyringParcel; - - public static final String[] USER_IDS_PROJECTION = new String[]{ - UserPackets._ID, - UserPackets.USER_ID, - UserPackets.RANK, - UserPackets.VERIFIED, - UserPackets.IS_PRIMARY, - UserPackets.IS_REVOKED - }; - private static final int INDEX_ID = 0; - private static final int INDEX_USER_ID = 1; - private static final int INDEX_RANK = 2; - private static final int INDEX_VERIFIED = 3; - private static final int INDEX_IS_PRIMARY = 4; - private static final int INDEX_IS_REVOKED = 5; - - public UserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes, - SaveKeyringParcel saveKeyringParcel) { - super(context, c, flags); - mInflater = LayoutInflater.from(context); - - mCheckStates = showCheckBoxes ? new ArrayList() : null; - mSaveKeyringParcel = saveKeyringParcel; - } - - public UserIdsAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes) { - this(context, c, flags, showCheckBoxes, null); - } - - public UserIdsAdapter(Context context, Cursor c, int flags, SaveKeyringParcel saveKeyringParcel) { - this(context, c, flags, false, saveKeyringParcel); - } - - public UserIdsAdapter(Context context, Cursor c, int flags) { - this(context, c, flags, false, null); - } - - @Override - public Cursor swapCursor(Cursor newCursor) { - if (mCheckStates != null) { - mCheckStates.clear(); - if (newCursor != null) { - int count = newCursor.getCount(); - mCheckStates.ensureCapacity(count); - // initialize to true (use case knowledge: we usually want to sign all uids) - for (int i = 0; i < count; i++) { - newCursor.moveToPosition(i); - int verified = newCursor.getInt(INDEX_VERIFIED); - mCheckStates.add(verified != Certs.VERIFIED_SECRET); - } - } - } - - return super.swapCursor(newCursor); - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - TextView vName = (TextView) view.findViewById(R.id.user_id_item_name); - TextView vAddress = (TextView) view.findViewById(R.id.user_id_item_address); - TextView vComment = (TextView) view.findViewById(R.id.user_id_item_comment); - ImageView vVerified = (ImageView) view.findViewById(R.id.user_id_item_certified); - View vVerifiedLayout = view.findViewById(R.id.user_id_item_certified_layout); - ImageView vEditImage = (ImageView) view.findViewById(R.id.user_id_item_edit_image); - ImageView vDeleteButton = (ImageView) view.findViewById(R.id.user_id_item_delete_button); - vDeleteButton.setVisibility(View.GONE); // not used - - String userId = cursor.getString(INDEX_USER_ID); - String[] splitUserId = KeyRing.splitUserId(userId); - if (splitUserId[0] != null) { - vName.setText(splitUserId[0]); - } else { - vName.setText(R.string.user_id_no_name); - } - if (splitUserId[1] != null) { - vAddress.setText(splitUserId[1]); - vAddress.setVisibility(View.VISIBLE); - } else { - vAddress.setVisibility(View.GONE); - } - if (splitUserId[2] != null) { - vComment.setText(splitUserId[2]); - vComment.setVisibility(View.VISIBLE); - } else { - vComment.setVisibility(View.GONE); - } - - boolean isPrimary = cursor.getInt(INDEX_IS_PRIMARY) != 0; - boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; - - // for edit key - if (mSaveKeyringParcel != null) { - boolean changeAnyPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null); - boolean changeThisPrimaryUserId = (mSaveKeyringParcel.mChangePrimaryUserId != null - && mSaveKeyringParcel.mChangePrimaryUserId.equals(userId)); - boolean revokeThisUserId = (mSaveKeyringParcel.mRevokeUserIds.contains(userId)); - - // only if primary user id will be changed - // (this is not triggered if the user id is currently the primary one) - if (changeAnyPrimaryUserId) { - // change _all_ primary user ids and set new one to true - isPrimary = changeThisPrimaryUserId; - } - - if (revokeThisUserId) { - if (!isRevoked) { - isRevoked = true; - } - } - - vEditImage.setVisibility(View.VISIBLE); - vVerifiedLayout.setVisibility(View.GONE); - } else { - vEditImage.setVisibility(View.GONE); - vVerifiedLayout.setVisibility(View.VISIBLE); - } - - if (isRevoked) { - // set revocation icon (can this even be primary?) - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_REVOKED, true); - - // disable revoked user ids - vName.setEnabled(false); - vAddress.setEnabled(false); - vComment.setEnabled(false); - } else { - vName.setEnabled(true); - vAddress.setEnabled(true); - vComment.setEnabled(true); - - if (isPrimary) { - vName.setTypeface(null, Typeface.BOLD); - vAddress.setTypeface(null, Typeface.BOLD); - } else { - vName.setTypeface(null, Typeface.NORMAL); - vAddress.setTypeface(null, Typeface.NORMAL); - } - - int isVerified = cursor.getInt(INDEX_VERIFIED); - switch (isVerified) { - case Certs.VERIFIED_SECRET: - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_VERIFIED, false); - break; - case Certs.VERIFIED_SELF: - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_UNVERIFIED, false); - break; - default: - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_INVALID, false); - break; - } - } - - // don't care further if checkboxes aren't shown - if (mCheckStates == null) { - return; - } - - final CheckBox vCheckBox = (CheckBox) view.findViewById(R.id.user_id_item_check_box); - final int position = cursor.getPosition(); - vCheckBox.setOnCheckedChangeListener(null); - vCheckBox.setChecked(mCheckStates.get(position)); - vCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton compoundButton, boolean b) { - mCheckStates.set(position, b); - } - }); - vCheckBox.setClickable(false); - } - - public void onItemClick(AdapterView adapter, View view, int position, long id) { - CheckBox box = ((CheckBox) view.findViewById(R.id.user_id_item_check_box)); - if (box != null) { - box.toggle(); - } - } - - public ArrayList getSelectedUserIds() { - ArrayList result = new ArrayList(); - for (int i = 0; i < mCheckStates.size(); i++) { - if (mCheckStates.get(i)) { - mCursor.moveToPosition(i); - result.add(mCursor.getString(INDEX_USER_ID)); - } - } - return result; - } - - public String getUserId(int position) { - mCursor.moveToPosition(position); - return mCursor.getString(INDEX_USER_ID); - } - - public boolean getIsRevoked(int position) { - mCursor.moveToPosition(position); - return mCursor.getInt(INDEX_IS_REVOKED) > 0; - } - - public int getIsVerified(int position) { - mCursor.moveToPosition(position); - return mCursor.getInt(INDEX_VERIFIED); - } - - public boolean getIsRevokedPending(int position) { - mCursor.moveToPosition(position); - String userId = mCursor.getString(INDEX_USER_ID); - - boolean isRevokedPending = false; - if (mSaveKeyringParcel != null) { - if (mSaveKeyringParcel.mRevokeUserIds.contains(userId)) { - isRevokedPending = true; - } - - } - - return isRevokedPending; - } - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - View view = mInflater.inflate(R.layout.view_key_user_id_item, null); - // only need to do this once ever, since mShowCheckBoxes is final - view.findViewById(R.id.user_id_item_check_box).setVisibility(mCheckStates != null ? View.VISIBLE : View.GONE); - return view; - } - -} -- cgit v1.2.3 From 5e53a5417c11f54f437d9f220d3e2e8579b8bf1b Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 2 Mar 2015 14:45:44 +0100 Subject: some fixes for compiling (still not working!) --- .../keychain/ui/adapter/UserAttributesAdapter.java | 17 +---------------- .../ui/linked/LinkedIdCreateDnsStep2Fragment.java | 6 +++--- .../ui/linked/LinkedIdCreateHttpsStep2Fragment.java | 6 +++--- .../ui/linked/LinkedIdCreateTwitterStep3Fragment.java | 6 +++--- 4 files changed, 10 insertions(+), 25 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java index 73c42b1be..36ee8ba88 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java @@ -34,15 +34,12 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; -import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import java.util.ArrayList; -public class UserAttributesAdapter extends CursorAdapter implements AdapterView.OnItemClickListener { +public abstract class UserAttributesAdapter extends CursorAdapter implements AdapterView.OnItemClickListener { private LayoutInflater mInflater; private final ArrayList mCheckStates; private SaveKeyringParcel mSaveKeyringParcel; @@ -77,18 +74,6 @@ public class UserAttributesAdapter extends CursorAdapter implements AdapterView. mShowStatusImages = showStatusImages; } - public UserAttributesAdapter(Context context, Cursor c, int flags, boolean showCheckBoxes) { - this(context, c, flags, showCheckBoxes, null); - } - - public UserAttributesAdapter(Context context, Cursor c, int flags, SaveKeyringParcel saveKeyringParcel) { - this(context, c, flags, false, saveKeyringParcel); - } - - public UserAttributesAdapter(Context context, Cursor c, int flags) { - this(context, c, flags, false, null); - } - @Override public Cursor swapCursor(Cursor newCursor) { if (mCheckStates != null) { 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 1106a4a48..3c413556d 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 @@ -161,17 +161,17 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment { mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); if (success == null) { mVerifyStatus.setText(R.string.linked_verifying); - mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout); + mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout_24px); mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), PorterDuff.Mode.SRC_IN); } else if (success) { mVerifyStatus.setText(R.string.linked_verify_success); - mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout); + mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout_24px); mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark), PorterDuff.Mode.SRC_IN); } else { mVerifyStatus.setText(R.string.linked_verify_error); - mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout); + mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout_24px); mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), PorterDuff.Mode.SRC_IN); } 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 0d78bad27..6f25ef069 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 @@ -163,17 +163,17 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment { mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); if (success == null) { mVerifyStatus.setText(R.string.linked_verifying); - mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout); + mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout_24px); mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), PorterDuff.Mode.SRC_IN); } else if (success) { mVerifyStatus.setText(R.string.linked_verify_success); - mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout); + mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout_24px); mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark), PorterDuff.Mode.SRC_IN); } else { mVerifyStatus.setText(R.string.linked_verify_error); - mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout); + mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout_24px); mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), PorterDuff.Mode.SRC_IN); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java index 23e50d9dc..f395807cf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java @@ -144,17 +144,17 @@ public class LinkedIdCreateTwitterStep3Fragment extends Fragment { mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); if (success == null) { mVerifyStatus.setText(R.string.linked_verifying); - mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout); + mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout_24px); mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), PorterDuff.Mode.SRC_IN); } else if (success) { mVerifyStatus.setText(R.string.linked_verify_success); - mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout); + mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout_24px); mVerifyImage.setColorFilter(getResources().getColor(R.color.android_green_dark), PorterDuff.Mode.SRC_IN); } else { mVerifyStatus.setText(R.string.linked_verify_error); - mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout); + mVerifyImage.setImageResource(R.drawable.status_signature_unknown_cutout_24px); mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), PorterDuff.Mode.SRC_IN); } -- cgit v1.2.3 From 8bec183eda38e423706001b9f030600f47a5761e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 2 Mar 2015 19:05:52 +0100 Subject: add linked it to view key context menu --- .../org/sufficientlysecure/keychain/ui/ViewKeyActivity.java | 11 +++++++++++ .../sufficientlysecure/keychain/ui/linked/LinkedIdWizard.java | 8 ++++++++ 2 files changed, 19 insertions(+) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 0be6c26f6..6b1d87941 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -72,6 +72,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; @@ -334,6 +335,12 @@ public class ViewKeyActivity extends BaseActivity implements certifyFingeprint(mDataUri); return true; } + case R.id.menu_key_view_add_linked_identity: { + Intent intent = new Intent(this, LinkedIdWizard.class); + intent.setData(mDataUri); + startActivity(intent); + return true; + } } } catch (ProviderHelper.NotFoundException e) { Notify.showNotify(this, R.string.error_key_not_found, Notify.Style.ERROR); @@ -346,6 +353,10 @@ public class ViewKeyActivity extends BaseActivity implements public boolean onPrepareOptionsMenu(Menu menu) { MenuItem editKey = menu.findItem(R.id.menu_key_view_edit); editKey.setVisible(mIsSecret); + + MenuItem addLinked = menu.findItem(R.id.menu_key_view_add_linked_identity); + addLinked.setVisible(mIsSecret); + MenuItem certifyFingerprint = menu.findItem(R.id.menu_key_view_certify_fingerprint); certifyFingerprint.setVisible(!mIsSecret && !mIsVerified); 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 b8f3329c1..161efc8fb 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 @@ -27,6 +27,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; +import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.util.Log; @@ -47,7 +48,14 @@ public class LinkedIdWizard extends ActionBarActivity { try { Uri uri = getIntent().getData(); + uri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(uri); CachedPublicKeyRing ring = new ProviderHelper(this).getCachedPublicKeyRing(uri); + if (!ring.hasAnySecret()) { + Log.e(Constants.TAG, "Linked Identities can only be added to secret keys!"); + finish(); + return; + } + mMasterKeyId = ring.extractOrGetMasterKeyId(); mFingerprint = ring.getFingerprint(); } catch (PgpKeyNotFoundException e) { -- cgit v1.2.3 From e059b5550ca895fbd816dae2176abf41bda28711 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 2 Mar 2015 20:40:37 +0100 Subject: nonce is 4 bytes --- .../keychain/pgp/linked/LinkedIdentity.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index 3f7501adc..c46d0aa0a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java @@ -79,14 +79,14 @@ public class LinkedIdentity { b.append(mSubUri); byte[] nonceBytes = Hex.decode(mNonce); - if (nonceBytes.length != 12) { - throw new AssertionError("nonce must be 12 bytes"); + if (nonceBytes.length != 4) { + throw new AssertionError("nonce must be 4 bytes"); } byte[] data = Strings.toUTF8ByteArray(b.toString()); - byte[] result = new byte[data.length+12]; - System.arraycopy(nonceBytes, 0, result, 0, 12); - System.arraycopy(data, 0, result, 12, data.length); + byte[] result = new byte[data.length+4]; + System.arraycopy(nonceBytes, 0, result, 0, 4); + System.arraycopy(data, 0, result, 4, data.length); return result; } @@ -100,10 +100,10 @@ public class LinkedIdentity { } byte[] data = subpacket.getData(); - String nonce = Hex.toHexString(data, 0, 12); + String nonce = Hex.toHexString(data, 0, 4); try { - return parseUri(nonce, Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 12, data.length))); + return parseUri(nonce, Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 4, data.length))); } catch (IllegalArgumentException e) { Log.e(Constants.TAG, "error parsing uri in (suspected) linked id packet"); @@ -161,12 +161,12 @@ public class LinkedIdentity { public static String generateNonce() { // TODO make this actually random - // byte[] data = new byte[96]; + // byte[] data = new byte[4]; // new SecureRandom().nextBytes(data); // return Hex.toHexString(data); // debug for now - return "0123456789abcdef01234567"; + return "01234567"; } } -- cgit v1.2.3 From d4df509a1d01300204d3f608f0430ff7b2a533f3 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 2 Mar 2015 20:59:07 +0100 Subject: remove unused bits from projection --- .../sufficientlysecure/keychain/ui/ViewKeyFragment.java | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index 32630b459..5afec7903 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -118,24 +118,10 @@ public class ViewKeyFragment extends LoaderFragment implements // These are the rows that we will retrieve. static final String[] UNIFIED_PROJECTION = new String[]{ KeychainContract.KeyRings._ID, - KeychainContract.KeyRings.MASTER_KEY_ID, - KeychainContract.KeyRings.USER_ID, - KeychainContract.KeyRings.IS_REVOKED, - KeychainContract.KeyRings.EXPIRY, - KeychainContract.KeyRings.VERIFIED, KeychainContract.KeyRings.HAS_ANY_SECRET, - KeychainContract.KeyRings.FINGERPRINT, - KeychainContract.KeyRings.HAS_ENCRYPT }; - static final int INDEX_MASTER_KEY_ID = 1; - static final int INDEX_USER_ID = 2; - static final int INDEX_IS_REVOKED = 3; - static final int INDEX_EXPIRY = 4; - static final int INDEX_VERIFIED = 5; - static final int INDEX_HAS_ANY_SECRET = 6; - static final int INDEX_FINGERPRINT = 7; - static final int INDEX_HAS_ENCRYPT = 8; + static final int INDEX_HAS_ANY_SECRET = 1; private void loadData(Uri dataUri) { mDataUri = dataUri; -- cgit v1.2.3 From 8222315dbd1fe412ead71e0f12ba54b19705617c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 4 Mar 2015 12:30:56 +0100 Subject: work more on separation of linked identities and resources, initial ui work --- .../keychain/pgp/linked/LinkedCookieResource.java | 210 +++++++++++++++++++++ .../keychain/pgp/linked/LinkedIdentity.java | 172 ----------------- .../keychain/pgp/linked/LinkedResource.java | 129 ------------- .../keychain/pgp/linked/RawLinkedIdentity.java | 85 +++++++++ .../keychain/pgp/linked/resources/DnsResource.java | 4 +- .../pgp/linked/resources/GenericHttpsResource.java | 6 +- .../pgp/linked/resources/TwitterResource.java | 6 +- .../pgp/linked/resources/UnknownResource.java | 4 +- .../keychain/provider/KeychainContract.java | 5 + .../keychain/ui/EditKeyFragment.java | 2 +- .../keychain/ui/ViewKeyAdvUserIdsFragment.java | 2 +- .../keychain/ui/ViewKeyFragment.java | 36 +++- .../keychain/ui/adapter/LinkedIdsAdapter.java | 158 ++++++++++++++++ .../keychain/ui/adapter/UserAttributesAdapter.java | 12 +- .../keychain/ui/adapter/UserIdsAdapter.java | 2 +- .../ui/linked/LinkedIdCreateDnsStep1Fragment.java | 4 +- .../ui/linked/LinkedIdCreateDnsStep2Fragment.java | 10 +- .../linked/LinkedIdCreateHttpsStep1Fragment.java | 4 +- .../linked/LinkedIdCreateHttpsStep2Fragment.java | 9 +- .../linked/LinkedIdCreateTwitterStep1Fragment.java | 4 +- 20 files changed, 523 insertions(+), 341 deletions(-) create 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 create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsAdapter.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 new file mode 100644 index 000000000..6228b29ec --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java @@ -0,0 +1,210 @@ +package org.sufficientlysecure.keychain.pgp.linked; + +import android.content.Context; + +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.pgp.linked.resources.GenericHttpsResource; +import org.sufficientlysecure.keychain.pgp.linked.resources.UnknownResource; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.Log; + +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 { + + protected final URI mSubUri; + protected final Set mFlags; + protected final HashMap mParams; + + static Pattern magicPattern = + Pattern.compile("\\[Verifying my PGP key: openpgp4fpr:([a-zA-Z0-9]+)#([a-zA-Z0-9]+)\\]"); + + protected LinkedCookieResource(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); + } + + public URI toUri () { + + StringBuilder b = new StringBuilder(); + b.append("pgpid+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, String nonce) { + + return "[Verifying my PGP key: openpgp4fpr:" + + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + "#" + nonce + "]"; + + } + + public static String generatePreview () { + return "[Verifying my PGP key: openpgp4fpr:0x…]"; + } + + public LinkedVerifyResult verify(byte[] fingerprint, int nonce) { + + OperationLog log = new OperationLog(); + log.add(LogType.MSG_LV, 0); + + // Try to fetch resource. Logs for itself + String res = fetchResource(log, 1); + 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, nonce, fingerprint); + + } + + protected abstract String fetchResource (OperationLog log, int indent); + + protected Matcher matchResource (OperationLog log, int indent, String res) { + return magicPattern.matcher(res); + } + + protected LinkedVerifyResult verifyString (OperationLog log, int indent, + String res, + int nonce, 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(); + int nonceCandidate = Integer.parseInt(match.group(2).toLowerCase(), 16); + + 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); + + if (nonce != nonceCandidate) { + log.add(LogType.MSG_LV_NONCE_ERROR, indent); + return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); + } + + log.add(LogType.MSG_LV_NONCE_OK, indent); + return new LinkedVerifyResult(LinkedVerifyResult.RESULT_OK, log); + + } + + protected static LinkedCookieResource fromRawLinkedId (RawLinkedIdentity id) { + return fromUri(id.mNonce, id.mUri); + } + + protected static LinkedCookieResource fromUri (int nonce, URI uri) { + + if ("pgpid".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(); + { + 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(nonce, flags, params, subUri); + + } + + protected static LinkedCookieResource findResourceType (int nonce, Set flags, + HashMap params, + URI subUri) { + + LinkedCookieResource res; + + res = GenericHttpsResource.create(flags, params, subUri); + if (res != null) { + return res; + } + + return new UnknownResource(flags, params, subUri); + + } + +} 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 c46d0aa0a..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java +++ /dev/null @@ -1,172 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.linked; - -import org.spongycastle.bcpg.UserAttributeSubpacket; -import org.spongycastle.util.Strings; -import org.spongycastle.util.encoders.Hex; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.util.Log; - -import java.net.URI; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map.Entry; -import java.util.Set; - -public class LinkedIdentity { - - protected byte[] mData; - public final String mNonce; - public final URI mSubUri; - final Set mFlags; - final HashMap mParams; - - protected LinkedIdentity(byte[] data, String nonce, Set flags, - HashMap params, URI subUri) { - if ( ! nonce.matches("[0-9a-zA-Z]+")) { - throw new AssertionError("bug: nonce must be hexstring!"); - } - - mData = data; - mNonce = nonce; - mFlags = flags; - mParams = params; - mSubUri = subUri; - } - - LinkedIdentity(String nonce, Set flags, - HashMap params, URI subUri) { - this(null, nonce, flags, params, subUri); - } - - public byte[] getEncoded() { - if (mData != null) { - return mData; - } - - StringBuilder b = new StringBuilder(); - b.append("pgpid:"); - - // 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; - Iterator> it = mParams.entrySet().iterator(); - while (it.hasNext()) { - if (!first) { - b.append(";"); - } - first = false; - Entry entry = it.next(); - b.append(entry.getKey()).append("=").append(entry.getValue()); - } - } - - b.append("@"); - b.append(mSubUri); - - byte[] nonceBytes = Hex.decode(mNonce); - if (nonceBytes.length != 4) { - throw new AssertionError("nonce must be 4 bytes"); - } - byte[] data = Strings.toUTF8ByteArray(b.toString()); - - byte[] result = new byte[data.length+4]; - System.arraycopy(nonceBytes, 0, result, 0, 4); - System.arraycopy(data, 0, result, 4, data.length); - - return result; - } - - /** 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 LinkedIdentity parseAttributeSubpacket(UserAttributeSubpacket subpacket) { - if (subpacket.getType() != 100) { - return null; - } - - byte[] data = subpacket.getData(); - String nonce = Hex.toHexString(data, 0, 4); - - try { - return parseUri(nonce, Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 4, data.length))); - - } catch (IllegalArgumentException e) { - Log.e(Constants.TAG, "error parsing uri in (suspected) linked id packet"); - return null; - } - } - - protected static LinkedIdentity parseUri (String nonce, String uriString) { - URI uri = URI.create(uriString); - - if ("pgpid".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(); - { - 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 new LinkedIdentity(nonce, flags, params, subUri); - - } - - public static LinkedIdentity fromResource (LinkedResource res, String nonce) { - return new LinkedIdentity(nonce, res.getFlags(), res.getParams(), res.getSubUri()); - } - - public WrappedUserAttribute toUserAttribute () { - return WrappedUserAttribute.fromSubpacket(WrappedUserAttribute.UAT_LINKED_ID, getEncoded()); - } - - public static String generateNonce() { - // TODO make this actually random - // byte[] data = new byte[4]; - // new SecureRandom().nextBytes(data); - // return Hex.toHexString(data); - - // debug for now - return "01234567"; - } - -} 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 b7d111dc9..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.linked; - -import android.content.Context; - -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.pgp.linked.resources.GenericHttpsResource; -import org.sufficientlysecure.keychain.pgp.linked.resources.UnknownResource; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -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.Matcher; -import java.util.regex.Pattern; - -public abstract class LinkedResource { - - protected final URI mSubUri; - protected final Set mFlags; - protected final HashMap mParams; - - static Pattern magicPattern = - Pattern.compile("\\[Verifying my PGP key: pgpid\\+cookie:([a-zA-Z0-9]+)#([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); - } - - public URI getSubUri () { - return mSubUri; - } - - public static String generate (Context context, byte[] fingerprint, String nonce) { - - return "[Verifying my PGP key: pgpid+cookie:" - + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + "#" + nonce + "]"; - - } - - public static String generatePreview () { - return "[Verifying my PGP key: pgpid+cookie:0x…]"; - } - - public LinkedVerifyResult verify(byte[] fingerprint, String nonce) { - - OperationLog log = new OperationLog(); - log.add(LogType.MSG_LV, 0); - - // Try to fetch resource. Logs for itself - String res = fetchResource(log, 1); - 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, nonce, fingerprint); - - } - - protected abstract String fetchResource (OperationLog log, int indent); - - protected Matcher matchResource (OperationLog log, int indent, String res) { - return magicPattern.matcher(res); - } - - protected LinkedVerifyResult verifyString (OperationLog log, int indent, - String res, - String nonce, 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 nonceCandidate = match.group(2).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); - - if (!nonce.equals(nonceCandidate)) { - log.add(LogType.MSG_LV_NONCE_ERROR, indent); - return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); - } - - log.add(LogType.MSG_LV_NONCE_OK, indent); - return new LinkedVerifyResult(LinkedVerifyResult.RESULT_OK, log); - - } - - public static LinkedResource findResourceType - (Set flags, HashMap params, URI uri) { - - LinkedResource res; - - res = GenericHttpsResource.create(flags, params, uri); - if (res != null) { - return res; - } - - return new UnknownResource(flags, params, uri); - - } - -} 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 new file mode 100644 index 000000000..931f2ec6b --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java @@ -0,0 +1,85 @@ +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.net.URI; +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map.Entry; +import java.util.Set; + +/** The RawLinkedIdentity contains raw parsed data from a Linked Identity subpacket. */ +public class RawLinkedIdentity { + + public final int mNonce; + public final URI mUri; + + protected RawLinkedIdentity(int nonce, URI uri) { + mNonce = nonce; + mUri = uri; + } + + public byte[] getEncoded() { + byte[] uriData = Strings.toUTF8ByteArray(mUri.toASCIIString()); + + ByteBuffer buf = ByteBuffer.allocate(4 + uriData.length); + + buf.putInt(mNonce); + buf.put(uriData); + + return buf.array(); + } + + /** 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() != 100) { + return null; + } + + byte[] data = subpacket.getData(); + + return fromSubpacketData(data); + + } + + public static RawLinkedIdentity fromSubpacketData(byte[] data) { + + try { + int nonce = ByteBuffer.wrap(data).getInt(); + String uri = Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 4, data.length)); + + return new RawLinkedIdentity(nonce, URI.create(uri)); + + } catch (IllegalArgumentException e) { + Log.e(Constants.TAG, "error parsing uri in (suspected) linked id packet"); + return null; + } + } + + public static RawLinkedIdentity fromResource (LinkedCookieResource res, int nonce) { + return new RawLinkedIdentity(nonce, res.toUri()); + } + + public WrappedUserAttribute toUserAttribute () { + return WrappedUserAttribute.fromSubpacket(WrappedUserAttribute.UAT_LINKED_ID, getEncoded()); + } + + public static String generateNonce() { + // TODO make this actually random + // byte[] data = new byte[4]; + // new SecureRandom().nextBytes(data); + // return Hex.toHexString(data); + + // debug for now + return "01234567"; + } + +} 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 index a2836e666..796e2f120 100644 --- 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 @@ -3,7 +3,7 @@ package org.sufficientlysecure.keychain.pgp.linked.resources; import android.content.Context; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.pgp.linked.LinkedResource; +import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import java.net.URI; @@ -21,7 +21,7 @@ import de.measite.minidns.Record.CLASS; import de.measite.minidns.Record.TYPE; import de.measite.minidns.record.TXT; -public class DnsResource extends LinkedResource { +public class DnsResource extends LinkedCookieResource { final static Pattern magicPattern = Pattern.compile("pgpid\\+cookie=([a-zA-Z0-9]+)(?:#|;)([a-zA-Z0-9]+)"); 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 index abe773f6c..a0f1cf0aa 100644 --- 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 @@ -8,7 +8,7 @@ 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.LinkedResource; +import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; @@ -22,14 +22,14 @@ import java.util.Set; import javax.net.ssl.HttpsURLConnection; -public class GenericHttpsResource extends LinkedResource { +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 nonce) { - String cookie = LinkedResource.generate(context, fingerprint, nonce); + String cookie = LinkedCookieResource.generate(context, fingerprint, nonce); return String.format(context.getResources().getString(R.string.linked_id_generic_text), cookie, "0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprint).substring(24)); 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 index 1b0db1fa1..84277380d 100644 --- 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 @@ -17,7 +17,7 @@ import org.apache.http.params.BasicHttpParams; import org.json.JSONException; import org.json.JSONObject; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.pgp.linked.LinkedResource; +import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; import java.io.BufferedReader; import java.io.IOException; @@ -29,7 +29,7 @@ import java.net.URLEncoder; import java.util.HashMap; import java.util.Set; -public class TwitterResource extends LinkedResource { +public class TwitterResource extends LinkedCookieResource { TwitterResource(Set flags, HashMap params, URI uri) { super(flags, params, uri); @@ -37,7 +37,7 @@ public class TwitterResource extends LinkedResource { public static String generateText (Context context, byte[] fingerprint, String nonce) { // nothing special here for now, might change this later - return LinkedResource.generate(context, fingerprint, nonce); + return LinkedCookieResource.generate(context, fingerprint, nonce); } private String getTwitterStream(String screenName) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java index ae99cdd86..f29ab5b39 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java @@ -1,13 +1,13 @@ package org.sufficientlysecure.keychain.pgp.linked.resources; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.pgp.linked.LinkedResource; +import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; import java.net.URI; import java.util.HashMap; import java.util.Set; -public class UnknownResource extends LinkedResource { +public class UnknownResource extends LinkedCookieResource { public UnknownResource(Set flags, HashMap params, URI uri) { super(flags, params, uri); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 5856589c4..a7ca613d7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -106,6 +106,7 @@ public class KeychainContract { public static final String PATH_PUBLIC = "public"; public static final String PATH_SECRET = "secret"; public static final String PATH_USER_IDS = "user_ids"; + public static final String PATH_LINKED_IDS = "linked_ids"; public static final String PATH_KEYS = "keys"; public static final String PATH_CERTS = "certs"; @@ -261,6 +262,10 @@ public class KeychainContract { public static Uri buildUserIdsUri(Uri uri) { return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_USER_IDS).build(); } + + public static Uri buildLinkedIdsUri(Uri uri) { + return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_LINKED_IDS).build(); + } } public static class ApiApps implements ApiAppsColumns, BaseColumns { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java index 25ca6e8fd..005b60ce0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyFragment.java @@ -328,7 +328,7 @@ public class EditKeyFragment extends LoaderFragment implements case LOADER_ID_USER_IDS: { Uri baseUri = UserPackets.buildUserIdsUri(mDataUri); return new CursorLoader(getActivity(), baseUri, - UserIdsAdapter.USER_IDS_PROJECTION, null, null, null); + UserIdsAdapter.USER_PACKETS_PROJECTION, null, null, null); } case LOADER_ID_SUBKEYS: { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java index c4e6639a8..492dfd0f7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvUserIdsFragment.java @@ -133,7 +133,7 @@ public class ViewKeyAdvUserIdsFragment extends LoaderFragment implements case LOADER_ID_USER_IDS: { Uri baseUri = UserPackets.buildUserIdsUri(mDataUri); return new CursorLoader(getActivity(), baseUri, - UserIdsAdapter.USER_IDS_PROJECTION, null, null, null); + UserIdsAdapter.USER_PACKETS_PROJECTION, null, null, null); } default: diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index 5afec7903..c6df016ff 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -24,6 +24,7 @@ import android.os.Bundle; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; +import android.support.v7.widget.CardView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -34,6 +35,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; import org.sufficientlysecure.keychain.util.Log; @@ -49,10 +51,14 @@ public class ViewKeyFragment extends LoaderFragment implements private static final int LOADER_ID_UNIFIED = 0; private static final int LOADER_ID_USER_IDS = 1; + private static final int LOADER_ID_LINKED_IDS = 2; private UserIdsAdapter mUserIdsAdapter; + private LinkedIdsAdapter mLinkedIdsAdapter; private Uri mDataUri; + private ListView mLinkedIds; + private CardView mLinkedIdsCard; /** * Creates new instance of this fragment @@ -73,6 +79,11 @@ public class ViewKeyFragment extends LoaderFragment implements View view = inflater.inflate(R.layout.view_key_fragment, getContainer()); mUserIds = (ListView) view.findViewById(R.id.view_key_user_ids); + mLinkedIdsCard = (CardView) view.findViewById(R.id.card_linked_ids); + + mLinkedIds = (ListView) view.findViewById(R.id.view_key_linked_ids); + mLinkedIdsAdapter = new LinkedIdsAdapter(getActivity(), null, 0); + mLinkedIds.setAdapter(mLinkedIdsAdapter); mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override @@ -80,6 +91,7 @@ public class ViewKeyFragment extends LoaderFragment implements showUserIdInfo(position); } }); + mLinkedIdsCard.setVisibility(View.GONE); return root; } @@ -145,23 +157,23 @@ public class ViewKeyFragment extends LoaderFragment implements case LOADER_ID_USER_IDS: return UserIdsAdapter.createLoader(getActivity(), mDataUri); + case LOADER_ID_LINKED_IDS: + return LinkedIdsAdapter.createLoader(getActivity(), mDataUri); + default: return null; } } public void onLoadFinished(Loader loader, Cursor data) { - /* TODO better error handling? May cause problems when a key is deleted, - * because the notification triggers faster than the activity closes. - */ - // Avoid NullPointerExceptions... - if (data.getCount() == 0) { - return; - } // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) switch (loader.getId()) { case LOADER_ID_UNIFIED: { + // Avoid NullPointerExceptions... + if (data.getCount() == 0) { + return; + } if (data.moveToFirst()) { mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; @@ -180,6 +192,11 @@ public class ViewKeyFragment extends LoaderFragment implements break; } + case LOADER_ID_LINKED_IDS: { + mLinkedIdsCard.setVisibility(data.getCount() > 0 ? View.VISIBLE : View.GONE); + mLinkedIdsAdapter.swapCursor(data); + break; + } } setContentShown(true); } @@ -194,6 +211,11 @@ public class ViewKeyFragment extends LoaderFragment implements mUserIdsAdapter.swapCursor(null); break; } + case LOADER_ID_LINKED_IDS: { + mLinkedIdsCard.setVisibility(View.GONE); + mLinkedIdsAdapter.swapCursor(null); + break; + } } } 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 new file mode 100644 index 000000000..1e4968615 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsAdapter.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2014-2015 Dominik Schürmann + * Copyright (C) 2015 Vincent Breitmoser + * + * 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.adapter; + +import android.app.Activity; +import android.content.Context; +import android.database.Cursor; +import android.graphics.Typeface; +import android.net.Uri; +import android.support.v4.content.CursorLoader; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; +import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; + +public class LinkedIdsAdapter extends UserAttributesAdapter { + protected LayoutInflater mInflater; + + public LinkedIdsAdapter(Context context, Cursor c, int flags) { + super(context, c, flags); + mInflater = LayoutInflater.from(context); + } + + @Override + public int getItemViewType(int position) { + RawLinkedIdentity id = (RawLinkedIdentity) getItem(position); + + // TODO return different ids by type + + return 0; + } + + @Override + public int getViewTypeCount() { + return 1; + } + + @Override + public Object getItem(int position) { + Cursor c = getCursor(); + c.moveToPosition(position); + + byte[] data = c.getBlob(INDEX_ATTRIBUTE_DATA); + RawLinkedIdentity identity = RawLinkedIdentity.fromSubpacketData(data); + + return identity; + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + TextView vName = (TextView) view.findViewById(R.id.user_id_item_name); + TextView vAddress = (TextView) view.findViewById(R.id.user_id_item_address); + TextView vComment = (TextView) view.findViewById(R.id.user_id_item_comment); + ImageView vVerified = (ImageView) view.findViewById(R.id.user_id_item_certified); + View vVerifiedLayout = view.findViewById(R.id.user_id_item_certified_layout); + ImageView vEditImage = (ImageView) view.findViewById(R.id.user_id_item_edit_image); + ImageView vDeleteButton = (ImageView) view.findViewById(R.id.user_id_item_delete_button); + vDeleteButton.setVisibility(View.GONE); // not used + + String userId = cursor.getString(INDEX_USER_ID); + String[] splitUserId = KeyRing.splitUserId(userId); + if (splitUserId[0] != null) { + vName.setText(splitUserId[0]); + } else { + vName.setText(R.string.user_id_no_name); + } + if (splitUserId[1] != null) { + vAddress.setText(splitUserId[1]); + vAddress.setVisibility(View.VISIBLE); + } else { + vAddress.setVisibility(View.GONE); + } + if (splitUserId[2] != null) { + vComment.setText(splitUserId[2]); + vComment.setVisibility(View.VISIBLE); + } else { + vComment.setVisibility(View.GONE); + } + + boolean isPrimary = cursor.getInt(INDEX_IS_PRIMARY) != 0; + boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; + vVerifiedLayout.setVisibility(View.VISIBLE); + + if (isRevoked) { + // set revocation icon (can this even be primary?) + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); + + // disable revoked user ids + vName.setEnabled(false); + vAddress.setEnabled(false); + vComment.setEnabled(false); + } else { + vName.setEnabled(true); + vAddress.setEnabled(true); + vComment.setEnabled(true); + + if (isPrimary) { + vName.setTypeface(null, Typeface.BOLD); + vAddress.setTypeface(null, Typeface.BOLD); + } else { + vName.setTypeface(null, Typeface.NORMAL); + vAddress.setTypeface(null, Typeface.NORMAL); + } + + int isVerified = cursor.getInt(INDEX_VERIFIED); + switch (isVerified) { + case Certs.VERIFIED_SECRET: + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_VERIFIED, KeyFormattingUtils.DEFAULT_COLOR); + break; + case Certs.VERIFIED_SELF: + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR); + break; + default: + KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_INVALID, KeyFormattingUtils.DEFAULT_COLOR); + break; + } + } + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return mInflater.inflate(R.layout.view_key_adv_user_id_item, null); + } + + // don't show revoked user ids, irrelevant for average users + public static final String LINKED_IDS_WHERE = UserPackets.IS_REVOKED + " = 0"; + + public static CursorLoader createLoader(Activity activity, Uri dataUri) { + Uri baseUri = UserPackets.buildLinkedIdsUri(dataUri); + return new CursorLoader(activity, baseUri, + UserIdsAdapter.USER_PACKETS_PROJECTION, LINKED_IDS_WHERE, null, null); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java index 457083770..3bc1b200b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java @@ -8,10 +8,11 @@ import android.view.View; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; public abstract class UserAttributesAdapter extends CursorAdapter { - public static final String[] USER_IDS_PROJECTION = new String[]{ + public static final String[] USER_PACKETS_PROJECTION = new String[]{ UserPackets._ID, UserPackets.TYPE, UserPackets.USER_ID, + UserPackets.ATTRIBUTE_DATA, UserPackets.RANK, UserPackets.VERIFIED, UserPackets.IS_PRIMARY, @@ -20,10 +21,11 @@ public abstract class UserAttributesAdapter extends CursorAdapter { protected static final int INDEX_ID = 0; protected static final int INDEX_TYPE = 1; protected static final int INDEX_USER_ID = 2; - protected static final int INDEX_RANK = 3; - protected static final int INDEX_VERIFIED = 4; - protected static final int INDEX_IS_PRIMARY = 5; - protected static final int INDEX_IS_REVOKED = 6; + protected static final int INDEX_ATTRIBUTE_DATA = 3; + protected static final int INDEX_RANK = 4; + protected static final int INDEX_VERIFIED = 5; + protected static final int INDEX_IS_PRIMARY = 6; + protected static final int INDEX_IS_REVOKED = 7; public UserAttributesAdapter(Context context, Cursor c, int flags) { super(context, c, flags); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java index 6a4f61f4b..3c4b0fedf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java @@ -187,7 +187,7 @@ public class UserIdsAdapter extends UserAttributesAdapter { public static CursorLoader createLoader(Activity activity, Uri dataUri) { Uri baseUri = UserPackets.buildUserIdsUri(dataUri); return new CursorLoader(activity, baseUri, - UserIdsAdapter.USER_IDS_PROJECTION, USER_IDS_WHERE, null, null); + UserIdsAdapter.USER_PACKETS_PROJECTION, USER_IDS_WHERE, null, null); } } 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 973067246..14d5f3b5d 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,7 +29,7 @@ import android.view.ViewGroup; import android.widget.EditText; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; import org.sufficientlysecure.keychain.pgp.linked.resources.DnsResource; public class LinkedIdCreateDnsStep1Fragment extends Fragment { @@ -73,7 +73,7 @@ public class LinkedIdCreateDnsStep1Fragment extends Fragment { return; } - String proofNonce = LinkedIdentity.generateNonce(); + String proofNonce = RawLinkedIdentity.generateNonce(); String proofText = DnsResource.generateText(getActivity(), mLinkedIdWizard.mFingerprint, proofNonce); 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 3c413556d..6c9110ffd 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 @@ -33,7 +33,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; -import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; @@ -42,7 +41,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; import org.sufficientlysecure.keychain.pgp.linked.resources.DnsResource; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; @@ -71,7 +70,8 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment { TextView mVerifyStatus; String mResourceDomain; - String mResourceNonce, mResourceString; + int mResourceNonce; + String mResourceString; // This is a resource, set AFTER it has been verified DnsResource mVerifiedResource = null; @@ -98,7 +98,7 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment { final View view = inflater.inflate(R.layout.linked_create_dns_fragment_step2, container, false); mResourceDomain = getArguments().getString(DOMAIN); - mResourceNonce = getArguments().getString(NONCE); + mResourceNonce = getArguments().getInt(NONCE); mResourceString = getArguments().getString(TEXT); view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { @@ -308,7 +308,7 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment { new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint); WrappedUserAttribute ua = - LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); + RawLinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); skp.mAddUserAttribute.add(ua); 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 7c5f1f032..9fe1d43a3 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,7 +29,7 @@ import android.view.ViewGroup; import android.widget.EditText; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; import org.sufficientlysecure.keychain.pgp.linked.resources.GenericHttpsResource; public class LinkedIdCreateHttpsStep1Fragment extends Fragment { @@ -72,7 +72,7 @@ public class LinkedIdCreateHttpsStep1Fragment extends Fragment { return; } - String proofNonce = LinkedIdentity.generateNonce(); + String proofNonce = RawLinkedIdentity.generateNonce(); String proofText = GenericHttpsResource.generateText(getActivity(), mLinkedIdWizard.mFingerprint, proofNonce); 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 6f25ef069..f3fcc4410 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 @@ -42,7 +42,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; import org.sufficientlysecure.keychain.pgp.linked.resources.GenericHttpsResource; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; @@ -73,7 +73,8 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment { TextView mVerifyStatus; String mResourceUri; - String mResourceNonce, mResourceString; + int mResourceNonce; + String mResourceString; // This is a resource, set AFTER it has been verified GenericHttpsResource mVerifiedResource = null; @@ -100,7 +101,7 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment { final View view = inflater.inflate(R.layout.linked_create_https_fragment_step2, container, false); mResourceUri = getArguments().getString(URI); - mResourceNonce = getArguments().getString(NONCE); + mResourceNonce = getArguments().getInt(NONCE); mResourceString = getArguments().getString(TEXT); view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { @@ -314,7 +315,7 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment { new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint); WrappedUserAttribute ua = - LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); + RawLinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); skp.mAddUserAttribute.add(ua); 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 25a4f6463..8113525be 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 @@ -27,7 +27,7 @@ import android.view.ViewGroup; import android.widget.EditText; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; import org.sufficientlysecure.keychain.pgp.linked.resources.TwitterResource; import org.sufficientlysecure.keychain.ui.util.Notify; @@ -92,7 +92,7 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { return; } - String proofNonce = LinkedIdentity.generateNonce(); + String proofNonce = RawLinkedIdentity.generateNonce(); String proofText = TwitterResource.generateText(getActivity(), mLinkedIdWizard.mFingerprint, proofNonce); -- cgit v1.2.3 From 1f324be24316175a111b9424a22fc5fcb6104e2b Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 5 Mar 2015 01:58:36 +0100 Subject: do a TON of UI work --- .../keychain/pgp/WrappedUserAttribute.java | 9 + .../keychain/pgp/linked/LinkedCookieResource.java | 100 ++-------- .../keychain/pgp/linked/LinkedIdentity.java | 75 ++++++++ .../keychain/pgp/linked/LinkedResource.java | 101 +++++++++++ .../keychain/pgp/linked/RawLinkedIdentity.java | 44 +---- .../keychain/pgp/linked/resources/DnsResource.java | 9 +- .../pgp/linked/resources/GenericHttpsResource.java | 2 +- .../pgp/linked/resources/TwitterResource.java | 2 +- .../keychain/provider/KeychainDatabase.java | 14 +- .../keychain/provider/KeychainProvider.java | 19 +- .../keychain/provider/ProviderHelper.java | 17 +- .../keychain/ui/ViewKeyActivity.java | 2 +- .../keychain/ui/ViewKeyFragment.java | 36 +++- .../keychain/ui/adapter/LinkedIdsAdapter.java | 201 +++++++++++++-------- .../keychain/ui/adapter/UserAttributesAdapter.java | 1 + .../ui/linked/LinkedIdCreateDnsStep1Fragment.java | 2 +- .../ui/linked/LinkedIdCreateDnsStep2Fragment.java | 8 +- .../linked/LinkedIdCreateHttpsStep1Fragment.java | 2 +- .../linked/LinkedIdCreateHttpsStep2Fragment.java | 7 +- .../linked/LinkedIdCreateTwitterStep1Fragment.java | 2 +- .../linked/LinkedIdCreateTwitterStep2Fragment.java | 4 +- .../keychain/ui/linked/LinkedIdViewFragment.java | 45 +++++ 22 files changed, 456 insertions(+), 246 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java index 8f967499e..49e4d9793 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java @@ -114,6 +114,15 @@ public class WrappedUserAttribute implements Serializable { } + public byte[][] getSubpackets() { + UserAttributeSubpacket[] subpackets = mVector.toSubpacketArray(); + byte[][] ret = new byte[subpackets.length][]; + for (int i = 0; i < subpackets.length; i++) { + ret[i] = subpackets[i].getData(); + } + return ret; + } + private void readObjectNoData() throws ObjectStreamException { } 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 index 6228b29ec..fb3f2433a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java @@ -19,27 +19,10 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -public abstract class LinkedCookieResource { - - protected final URI mSubUri; - protected final Set mFlags; - protected final HashMap mParams; - - static Pattern magicPattern = - Pattern.compile("\\[Verifying my PGP key: openpgp4fpr:([a-zA-Z0-9]+)#([a-zA-Z0-9]+)\\]"); +public abstract class LinkedCookieResource extends LinkedResource { protected LinkedCookieResource(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); + super(flags, params, uri); } public URI toUri () { @@ -82,10 +65,10 @@ public abstract class LinkedCookieResource { return mSubUri; } - public static String generate (Context context, byte[] fingerprint, String nonce) { + public static String generate (Context context, byte[] fingerprint, int nonce) { return "[Verifying my PGP key: openpgp4fpr:" - + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + "#" + nonce + "]"; + + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + "#" + Integer.toHexString(nonce) + "]"; } @@ -129,7 +112,17 @@ public abstract class LinkedCookieResource { } String candidateFp = match.group(1).toLowerCase(); - int nonceCandidate = Integer.parseInt(match.group(2).toLowerCase(), 16); + try { + int nonceCandidate = Integer.parseInt(match.group(2).toLowerCase(), 16); + + if (nonce != nonceCandidate) { + log.add(LogType.MSG_LV_NONCE_ERROR, indent); + return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); + } + } catch (NumberFormatException e) { + log.add(LogType.MSG_LV_NONCE_ERROR, indent); + return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); + } String fp = KeyFormattingUtils.convertFingerprintToHex(fingerprint); @@ -139,72 +132,9 @@ public abstract class LinkedCookieResource { } log.add(LogType.MSG_LV_FP_OK, indent); - if (nonce != nonceCandidate) { - log.add(LogType.MSG_LV_NONCE_ERROR, indent); - return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); - } - log.add(LogType.MSG_LV_NONCE_OK, indent); return new LinkedVerifyResult(LinkedVerifyResult.RESULT_OK, log); } - protected static LinkedCookieResource fromRawLinkedId (RawLinkedIdentity id) { - return fromUri(id.mNonce, id.mUri); - } - - protected static LinkedCookieResource fromUri (int nonce, URI uri) { - - if ("pgpid".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(); - { - 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(nonce, flags, params, subUri); - - } - - protected static LinkedCookieResource findResourceType (int nonce, Set flags, - HashMap params, - URI subUri) { - - LinkedCookieResource res; - - res = GenericHttpsResource.create(flags, params, subUri); - if (res != null) { - return res; - } - - return new UnknownResource(flags, params, subUri); - - } - } 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 new file mode 100644 index 000000000..f06a23681 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java @@ -0,0 +1,75 @@ +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 java.nio.ByteBuffer; +import java.util.Arrays; + +public class LinkedIdentity extends RawLinkedIdentity { + + public final LinkedResource mResource; + + protected LinkedIdentity(int nonce, URI uri, LinkedResource resource) { + super(nonce, 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() != 100) { + return null; + } + + byte[] data = subpacket.getData(); + + return fromSubpacketData(data); + + } + + static RawLinkedIdentity fromSubpacketData(byte[] data) { + + try { + int nonce = ByteBuffer.wrap(data).getInt(); + String uriStr = Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 4, data.length)); + URI uri = URI.create(uriStr); + + LinkedResource res = LinkedResource.fromUri(uri); + if (res == null) { + return new RawLinkedIdentity(nonce, uri); + } + + return new LinkedIdentity(nonce, 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, int nonce) { + return new RawLinkedIdentity(nonce, res.toUri()); + } + +} 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 new file mode 100644 index 000000000..6077db606 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java @@ -0,0 +1,101 @@ +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.UnknownResource; +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; + +public abstract class LinkedResource { + + protected final URI mSubUri; + protected final Set mFlags; + protected final HashMap mParams; + + static Pattern magicPattern = + Pattern.compile("\\[Verifying my PGP key: openpgp4fpr:([a-zA-Z0-9]+)#([a-zA-Z0-9]+)\\]"); + + protected LinkedResource(Set flags, HashMap params, URI uri) { + mFlags = flags; + mParams = params; + mSubUri = uri; + } + + public abstract URI toUri(); + + public Set getFlags () { + return new HashSet<>(mFlags); + } + + public HashMap getParams () { + return new HashMap<>(mParams); + } + + protected static LinkedCookieResource fromUri (URI uri) { + + if ("pgpid+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(); + { + String[] rawParams = pieces[0].split(";"); + for (String param : rawParams) { + String[] p = param.split("=", 2); + if ("".equals(p[0])) { + continue; + } + 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; + } + + 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 index 931f2ec6b..bfde3c3b9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java @@ -1,18 +1,10 @@ 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.net.URI; import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map.Entry; -import java.util.Set; /** The RawLinkedIdentity contains raw parsed data from a Linked Identity subpacket. */ public class RawLinkedIdentity { @@ -36,50 +28,18 @@ public class RawLinkedIdentity { return buf.array(); } - /** 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() != 100) { - return null; - } - - byte[] data = subpacket.getData(); - - return fromSubpacketData(data); - - } - - public static RawLinkedIdentity fromSubpacketData(byte[] data) { - - try { - int nonce = ByteBuffer.wrap(data).getInt(); - String uri = Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 4, data.length)); - - return new RawLinkedIdentity(nonce, URI.create(uri)); - - } catch (IllegalArgumentException e) { - Log.e(Constants.TAG, "error parsing uri in (suspected) linked id packet"); - return null; - } - } - - public static RawLinkedIdentity fromResource (LinkedCookieResource res, int nonce) { - return new RawLinkedIdentity(nonce, res.toUri()); - } - public WrappedUserAttribute toUserAttribute () { return WrappedUserAttribute.fromSubpacket(WrappedUserAttribute.UAT_LINKED_ID, getEncoded()); } - public static String generateNonce() { + public static int generateNonce() { // TODO make this actually random // byte[] data = new byte[4]; // new SecureRandom().nextBytes(data); // return Hex.toHexString(data); // debug for now - return "01234567"; + return 1234567; } } 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 index 796e2f120..5df008ed7 100644 --- 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 @@ -39,10 +39,11 @@ public class DnsResource extends LinkedCookieResource { mType = type; } - public static String generateText (Context context, byte[] fingerprint, String nonce) { + public static String generateText (Context context, byte[] fingerprint, int nonce) { return "pgpid+cookie=" - + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + ";" + nonce + ""; + + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + ";" + + Integer.toHexString(nonce) + ""; } @@ -81,6 +82,10 @@ public class DnsResource extends LinkedCookieResource { return new DnsResource(flags, params, uri, fqdn, clazz, type); } + public String getFqdn() { + return mFqdn; + } + @Override protected String fetchResource (OperationLog log, int indent) { 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 index a0f1cf0aa..ba94bae75 100644 --- 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 @@ -28,7 +28,7 @@ public class GenericHttpsResource extends LinkedCookieResource { super(flags, params, uri); } - public static String generateText (Context context, byte[] fingerprint, String nonce) { + public static String generateText (Context context, byte[] fingerprint, int nonce) { String cookie = LinkedCookieResource.generate(context, fingerprint, nonce); return String.format(context.getResources().getString(R.string.linked_id_generic_text), 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 index 84277380d..f6ec4f97a 100644 --- 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 @@ -35,7 +35,7 @@ public class TwitterResource extends LinkedCookieResource { super(flags, params, uri); } - public static String generateText (Context context, byte[] fingerprint, String nonce) { + public static String generateText (Context context, byte[] fingerprint, int nonce) { // nothing special here for now, might change this later return LinkedCookieResource.generate(context, fingerprint, nonce); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index b88cd6006..4a162989f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -53,7 +53,7 @@ import java.io.IOException; */ public class KeychainDatabase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "openkeychain.db"; - private static final int DATABASE_VERSION = 8; + private static final int DATABASE_VERSION = 9; static Boolean apgHack = false; private Context mContext; @@ -61,7 +61,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { String KEY_RINGS_PUBLIC = "keyrings_public"; String KEY_RINGS_SECRET = "keyrings_secret"; String KEYS = "keys"; - String USER_PACKETS = "user_ids"; + String USER_PACKETS = "user_packets"; String CERTS = "certs"; String API_APPS = "api_apps"; String API_ACCOUNTS = "api_accounts"; @@ -119,8 +119,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { + UserPacketsColumns.IS_REVOKED + " INTEGER, " + UserPacketsColumns.RANK+ " INTEGER, " - + "PRIMARY KEY(" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.USER_ID + "), " - + "UNIQUE (" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.RANK + "), " + + "PRIMARY KEY(" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.RANK + "), " + "FOREIGN KEY(" + UserPacketsColumns.MASTER_KEY_ID + ") REFERENCES " + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE" + ")"; @@ -267,6 +266,13 @@ public class KeychainDatabase extends SQLiteOpenHelper { } catch (Exception e) { // never mind, the column probably already existed } + case 9: + // tbale name for user_ids changed to user_packets + db.execSQL("DROP TABLE IF EXISTS certs"); + db.execSQL("DROP TABLE IF EXISTS user_ids"); + db.execSQL(CREATE_USER_PACKETS); + db.execSQL(CREATE_CERTS); + } // always do consolidate after upgrade diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 3aa156a8e..31ed89d67 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -62,6 +62,7 @@ public class KeychainProvider extends ContentProvider { private static final int KEY_RING_SECRET = 204; private static final int KEY_RING_CERTS = 205; private static final int KEY_RING_CERTS_SPECIFIC = 206; + private static final int KEY_RING_LINKED_IDS = 207; private static final int API_APPS = 301; private static final int API_APPS_BY_PACKAGE_NAME = 302; @@ -127,6 +128,7 @@ public class KeychainProvider extends ContentProvider { * key_rings/_/unified * key_rings/_/keys * key_rings/_/user_ids + * key_rings/_/linked_ids * key_rings/_/public * key_rings/_/secret * key_rings/_/certs @@ -142,6 +144,9 @@ public class KeychainProvider extends ContentProvider { matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/" + KeychainContract.PATH_USER_IDS, KEY_RING_USER_IDS); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/" + + KeychainContract.PATH_LINKED_IDS, + KEY_RING_LINKED_IDS); matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/" + KeychainContract.PATH_PUBLIC, KEY_RING_PUBLIC); @@ -469,7 +474,8 @@ public class KeychainProvider extends ContentProvider { } case KEY_RINGS_USER_IDS: - case KEY_RING_USER_IDS: { + case KEY_RING_USER_IDS: + case KEY_RING_LINKED_IDS: { HashMap projectionMap = new HashMap<>(); projectionMap.put(UserPackets._ID, Tables.USER_PACKETS + ".oid AS _id"); projectionMap.put(UserPackets.MASTER_KEY_ID, Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID); @@ -494,13 +500,14 @@ public class KeychainProvider extends ContentProvider { groupBy = Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + ", " + Tables.USER_PACKETS + "." + UserPackets.RANK; - // for now, we only respect user ids here, so TYPE must be NULL - // TODO expand with KEY_RING_USER_PACKETS query type which lifts this restriction - qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL"); + if (match == KEY_RING_LINKED_IDS) { + qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " = 100"); + } else { + qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL"); + } // If we are searching for a particular keyring's ids, add where - if (match == KEY_RING_USER_IDS) { - // TODO remove with the thing above + if (match == KEY_RING_USER_IDS || match == KEY_RING_LINKED_IDS) { qb.appendWhere(" AND "); qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " = "); qb.appendWhereEscapeString(uri.getPathSegments().get(1)); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 8a9f36c03..2ff4803fb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -1082,9 +1082,8 @@ public class ProviderHelper { log.add(LogType.MSG_CON_SAVE_SECRET, indent); indent += 1; - final Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[]{ - KeyRings.PRIVKEY_DATA, KeyRings.FINGERPRINT, KeyRings.HAS_ANY_SECRET - }, KeyRings.HAS_ANY_SECRET + " = 1", null, null); + final Cursor cursor = mContentResolver.query(KeyRingData.buildSecretKeyRingUri(), + new String[]{ KeyRingData.KEY_RING_DATA }, null, null, null); if (cursor == null) { log.add(LogType.MSG_CON_ERROR_DB, indent); @@ -1106,8 +1105,7 @@ public class ProviderHelper { if (cursor.isAfterLast()) { return false; } - ring = new ParcelableKeyRing(KeyFormattingUtils.convertFingerprintToHex(cursor.getBlob(1)), cursor.getBlob(0) - ); + ring = new ParcelableKeyRing(cursor.getBlob(0)); cursor.moveToNext(); return true; } @@ -1144,9 +1142,9 @@ public class ProviderHelper { log.add(LogType.MSG_CON_SAVE_PUBLIC, indent); indent += 1; - final Cursor cursor = mContentResolver.query(KeyRings.buildUnifiedKeyRingsUri(), new String[]{ - KeyRings.PUBKEY_DATA, KeyRings.FINGERPRINT - }, null, null, null); + final Cursor cursor = mContentResolver.query( + KeyRingData.buildPublicKeyRingUri(), + new String[]{ KeyRingData.KEY_RING_DATA }, null, null, null); if (cursor == null) { log.add(LogType.MSG_CON_ERROR_DB, indent); @@ -1168,8 +1166,7 @@ public class ProviderHelper { if (cursor.isAfterLast()) { return false; } - ring = new ParcelableKeyRing(KeyFormattingUtils.convertFingerprintToHex(cursor.getBlob(1)), cursor.getBlob(0) - ); + ring = new ParcelableKeyRing(cursor.getBlob(0)); cursor.moveToNext(); return true; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 6b1d87941..51dec767c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -281,7 +281,7 @@ public class ViewKeyActivity extends BaseActivity implements // Add the fragment to the 'fragment_container' FrameLayout // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! getSupportFragmentManager().beginTransaction() - .replace(R.id.view_key_fragment, frag) + .replace(R.id.view_key_fragment, frag, "main") .commitAllowingStateLoss(); // do it immediately! getSupportFragmentManager().executePendingTransactions(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index c6df016ff..20386b372 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -20,15 +20,22 @@ package org.sufficientlysecure.keychain.ui; import android.database.Cursor; import android.net.Uri; +import android.os.Build; import android.os.Bundle; +import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v7.widget.CardView; +import android.transition.Explode; +import android.transition.Fade; +import android.transition.Transition; +import android.transition.TransitionInflater; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; import org.sufficientlysecure.keychain.Constants; @@ -91,11 +98,34 @@ public class ViewKeyFragment extends LoaderFragment implements showUserIdInfo(position); } }); - mLinkedIdsCard.setVisibility(View.GONE); + mLinkedIds.setOnItemClickListener(new OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + showLinkedId(position); + } + }); return root; } + private void showLinkedId(final int position) { + Fragment frag = mLinkedIdsAdapter.getLinkedIdFragment(position); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + Transition trans = TransitionInflater.from(getActivity()) + .inflateTransition(R.transition.linked_id_card_trans); + // setSharedElementReturnTransition(trans); + setExitTransition(new Fade()); + frag.setSharedElementEnterTransition(trans); + } + + getFragmentManager().beginTransaction() + .replace(R.id.view_key_fragment, frag) + .addSharedElement(mLinkedIdsCard, "card_linked_ids") + .addToBackStack(null) + .commit(); + } + private void showUserIdInfo(final int position) { if (!mIsSecret) { final boolean isRevoked = mUserIdsAdapter.getIsRevoked(position); @@ -140,10 +170,8 @@ public class ViewKeyFragment extends LoaderFragment implements Log.i(Constants.TAG, "mDataUri: " + mDataUri); - // Prepare the loaders. Either re-connect with an existing ones, - // or start new ones. - // TODO Is this loader the same as the one in the activity? getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); + getLoaderManager().initLoader(LOADER_ID_LINKED_IDS, null, this); } public Loader onCreateLoader(int id, Bundle args) { 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 1e4968615..a44358172 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 @@ -21,131 +21,130 @@ package org.sufficientlysecure.keychain.ui.adapter; import android.app.Activity; import android.content.Context; import android.database.Cursor; -import android.graphics.Typeface; import android.net.Uri; +import android.support.v4.app.Fragment; import android.support.v4.content.CursorLoader; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.KeyRing; +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.pgp.linked.resources.DnsResource; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; +import org.sufficientlysecure.keychain.ui.ViewKeyFragment; +import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import java.io.IOException; +import java.util.WeakHashMap; + + public class LinkedIdsAdapter extends UserAttributesAdapter { protected LayoutInflater mInflater; + WeakHashMap mLinkedIdentityCache = new WeakHashMap<>(); public LinkedIdsAdapter(Context context, Cursor c, int flags) { super(context, c, flags); mInflater = LayoutInflater.from(context); } + @Override + public void bindView(View view, Context context, Cursor cursor) { + + RawLinkedIdentity id = getItem(cursor.getPosition()); + ViewHolder holder = (ViewHolder) view.getTag(); + + int isVerified = cursor.getInt(INDEX_VERIFIED); + switch (isVerified) { + case Certs.VERIFIED_SECRET: + KeyFormattingUtils.setStatusImage(mContext, holder.vVerified, + null, KeyFormattingUtils.STATE_VERIFIED, KeyFormattingUtils.DEFAULT_COLOR); + break; + case Certs.VERIFIED_SELF: + KeyFormattingUtils.setStatusImage(mContext, holder.vVerified, + null, KeyFormattingUtils.STATE_UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR); + break; + default: + KeyFormattingUtils.setStatusImage(mContext, holder.vVerified, + null, KeyFormattingUtils.STATE_INVALID, KeyFormattingUtils.DEFAULT_COLOR); + break; + } + + if (holder instanceof ViewHolderNonRaw) { + ((ViewHolderNonRaw) holder).setData(mContext, (LinkedIdentity) id); + } + + } + @Override public int getItemViewType(int position) { - RawLinkedIdentity id = (RawLinkedIdentity) getItem(position); + RawLinkedIdentity id = getItem(position); - // TODO return different ids by type + if (id instanceof LinkedIdentity) { + LinkedResource res = ((LinkedIdentity) id).mResource; + if (res instanceof DnsResource) { + return 1; + } + } return 0; } @Override public int getViewTypeCount() { - return 1; + return 2; } @Override - public Object getItem(int position) { + public RawLinkedIdentity getItem(int position) { + RawLinkedIdentity ret = mLinkedIdentityCache.get(position); + if (ret != null) { + return ret; + } + Cursor c = getCursor(); c.moveToPosition(position); byte[] data = c.getBlob(INDEX_ATTRIBUTE_DATA); - RawLinkedIdentity identity = RawLinkedIdentity.fromSubpacketData(data); - - return identity; + try { + ret = LinkedIdentity.fromAttributeData(data); + mLinkedIdentityCache.put(position, ret); + return ret; + } catch (IOException e) { + Log.e(Constants.TAG, "could not read linked identity subpacket data", e); + return null; + } } @Override - public void bindView(View view, Context context, Cursor cursor) { - TextView vName = (TextView) view.findViewById(R.id.user_id_item_name); - TextView vAddress = (TextView) view.findViewById(R.id.user_id_item_address); - TextView vComment = (TextView) view.findViewById(R.id.user_id_item_comment); - ImageView vVerified = (ImageView) view.findViewById(R.id.user_id_item_certified); - View vVerifiedLayout = view.findViewById(R.id.user_id_item_certified_layout); - ImageView vEditImage = (ImageView) view.findViewById(R.id.user_id_item_edit_image); - ImageView vDeleteButton = (ImageView) view.findViewById(R.id.user_id_item_delete_button); - vDeleteButton.setVisibility(View.GONE); // not used - - String userId = cursor.getString(INDEX_USER_ID); - String[] splitUserId = KeyRing.splitUserId(userId); - if (splitUserId[0] != null) { - vName.setText(splitUserId[0]); - } else { - vName.setText(R.string.user_id_no_name); - } - if (splitUserId[1] != null) { - vAddress.setText(splitUserId[1]); - vAddress.setVisibility(View.VISIBLE); - } else { - vAddress.setVisibility(View.GONE); - } - if (splitUserId[2] != null) { - vComment.setText(splitUserId[2]); - vComment.setVisibility(View.VISIBLE); - } else { - vComment.setVisibility(View.GONE); - } - - boolean isPrimary = cursor.getInt(INDEX_IS_PRIMARY) != 0; - boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; - vVerifiedLayout.setVisibility(View.VISIBLE); - - if (isRevoked) { - // set revocation icon (can this even be primary?) - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_REVOKED, R.color.bg_gray); - - // disable revoked user ids - vName.setEnabled(false); - vAddress.setEnabled(false); - vComment.setEnabled(false); - } else { - vName.setEnabled(true); - vAddress.setEnabled(true); - vComment.setEnabled(true); - - if (isPrimary) { - vName.setTypeface(null, Typeface.BOLD); - vAddress.setTypeface(null, Typeface.BOLD); - } else { - vName.setTypeface(null, Typeface.NORMAL); - vAddress.setTypeface(null, Typeface.NORMAL); + public View newView(Context context, Cursor cursor, ViewGroup parent) { + int type = getItemViewType(cursor.getPosition()); + switch(type) { + case 0: { + View v = mInflater.inflate(R.layout.linked_id_item_unknown, null); + ViewHolder holder = new ViewHolder(v); + v.setTag(holder); + return v; } - - int isVerified = cursor.getInt(INDEX_VERIFIED); - switch (isVerified) { - case Certs.VERIFIED_SECRET: - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_VERIFIED, KeyFormattingUtils.DEFAULT_COLOR); - break; - case Certs.VERIFIED_SELF: - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR); - break; - default: - KeyFormattingUtils.setStatusImage(mContext, vVerified, null, KeyFormattingUtils.STATE_INVALID, KeyFormattingUtils.DEFAULT_COLOR); - break; + case 1: { + View v = mInflater.inflate(R.layout.linked_id_item_dns, null); + ViewHolder holder = new ViewHolderDns(v); + v.setTag(holder); + return v; } + default: + throw new AssertionError("all cases must be covered in LinkedIdsAdapter.newView!"); } } - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(R.layout.view_key_adv_user_id_item, null); - } - // don't show revoked user ids, irrelevant for average users public static final String LINKED_IDS_WHERE = UserPackets.IS_REVOKED + " = 0"; @@ -155,4 +154,48 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { UserIdsAdapter.USER_PACKETS_PROJECTION, LINKED_IDS_WHERE, null, null); } + public Fragment getLinkedIdFragment(int position) { + RawLinkedIdentity id = getItem(position); + + return LinkedIdViewFragment.newInstance(id); + } + + static class ViewHolder { + ImageView vVerified; + + ViewHolder(View view) { + vVerified = (ImageView) view.findViewById(R.id.user_id_item_certified); + } + } + + static abstract class ViewHolderNonRaw extends ViewHolder { + ViewHolderNonRaw(View view) { + super(view); + } + + abstract void setData(Context context, LinkedIdentity id); + } + + static class ViewHolderDns extends ViewHolderNonRaw { + TextView vFqdn; + + ViewHolderDns(View view) { + super(view); + + vFqdn = (TextView) view.findViewById(R.id.linked_id_dns_fqdn); + } + + @Override + void setData(Context context, LinkedIdentity id) { + DnsResource res = (DnsResource) id.mResource; + vFqdn.setText(res.getFqdn()); + } + + } + + @Override + public void notifyDataSetChanged() { + mLinkedIdentityCache.clear(); + super.notifyDataSetChanged(); + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java index 3bc1b200b..345ccccc0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java @@ -48,4 +48,5 @@ public abstract class UserAttributesAdapter extends CursorAdapter { mCursor.moveToPosition(position); return mCursor.getInt(INDEX_VERIFIED); } + } 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 14d5f3b5d..26b0a0539 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 @@ -73,7 +73,7 @@ public class LinkedIdCreateDnsStep1Fragment extends Fragment { return; } - String proofNonce = RawLinkedIdentity.generateNonce(); + int proofNonce = RawLinkedIdentity.generateNonce(); String proofText = DnsResource.generateText(getActivity(), mLinkedIdWizard.mFingerprint, proofNonce); 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 6c9110ffd..71a3673f2 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 @@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; import org.sufficientlysecure.keychain.pgp.linked.resources.DnsResource; import org.sufficientlysecure.keychain.service.KeychainIntentService; @@ -80,13 +81,13 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment { * Creates new instance of this fragment */ public static LinkedIdCreateDnsStep2Fragment newInstance - (String uri, String proofNonce, String proofText) { + (String uri, int proofNonce, String proofText) { LinkedIdCreateDnsStep2Fragment frag = new LinkedIdCreateDnsStep2Fragment(); Bundle args = new Bundle(); args.putString(DOMAIN, uri); - args.putString(NONCE, proofNonce); + args.putInt(NONCE, proofNonce); args.putString(TEXT, proofText); frag.setArguments(args); @@ -237,6 +238,7 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment { mVerifiedResource = resource; } else { setVerifyProgress(false, false); + mVerifiedResource = resource; // on error, show error message result.createNotify(getActivity()).show(); } @@ -308,7 +310,7 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment { new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint); WrappedUserAttribute ua = - RawLinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); + LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); skp.mAddUserAttribute.add(ua); 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 9fe1d43a3..78ca4cfe9 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 @@ -72,7 +72,7 @@ public class LinkedIdCreateHttpsStep1Fragment extends Fragment { return; } - String proofNonce = RawLinkedIdentity.generateNonce(); + int proofNonce = RawLinkedIdentity.generateNonce(); String proofText = GenericHttpsResource.generateText(getActivity(), mLinkedIdWizard.mFingerprint, proofNonce); 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 f3fcc4410..78c7caf53 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 @@ -42,6 +42,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; import org.sufficientlysecure.keychain.pgp.linked.resources.GenericHttpsResource; import org.sufficientlysecure.keychain.service.KeychainIntentService; @@ -83,13 +84,13 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment { * Creates new instance of this fragment */ public static LinkedIdCreateHttpsStep2Fragment newInstance - (String uri, String proofNonce, String proofText) { + (String uri, int proofNonce, String proofText) { LinkedIdCreateHttpsStep2Fragment frag = new LinkedIdCreateHttpsStep2Fragment(); Bundle args = new Bundle(); args.putString(URI, uri); - args.putString(NONCE, proofNonce); + args.putInt(NONCE, proofNonce); args.putString(TEXT, proofText); frag.setArguments(args); @@ -315,7 +316,7 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment { new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint); WrappedUserAttribute ua = - RawLinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); + LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); skp.mAddUserAttribute.add(ua); 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 8113525be..e966fd71f 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 @@ -92,7 +92,7 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { return; } - String proofNonce = RawLinkedIdentity.generateNonce(); + int proofNonce = RawLinkedIdentity.generateNonce(); String proofText = TwitterResource.generateText(getActivity(), mLinkedIdWizard.mFingerprint, proofNonce); 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 ab56e7a5d..837b84d40 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 @@ -52,13 +52,13 @@ public class LinkedIdCreateTwitterStep2Fragment extends Fragment { * Creates new instance of this fragment */ public static LinkedIdCreateTwitterStep2Fragment newInstance - (String handle, String proofNonce, String proofText) { + (String handle, int proofNonce, String proofText) { LinkedIdCreateTwitterStep2Fragment frag = new LinkedIdCreateTwitterStep2Fragment(); Bundle args = new Bundle(); args.putString(HANDLE, handle); - args.putString(NONCE, proofNonce); + args.putInt(NONCE, proofNonce); args.putString(TEXT, proofText); frag.setArguments(args); 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 new file mode 100644 index 000000000..0eb0dcd45 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java @@ -0,0 +1,45 @@ +package org.sufficientlysecure.keychain.ui.linked; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v7.widget.CardView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; + + +public class LinkedIdViewFragment extends Fragment { + + private CardView mLinkedIdsCard; + + public static Fragment newInstance(RawLinkedIdentity id) { + LinkedIdViewFragment frag = new LinkedIdViewFragment(); + + Bundle args = new Bundle(); + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.linked_id_view_fragment, null); + + mLinkedIdsCard = (CardView) root.findViewById(R.id.card_linked_ids); + + root.findViewById(R.id.back_button).setClickable(true); + root.findViewById(R.id.back_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + getFragmentManager().popBackStack(); + } + }); + + return root; + } + +} -- cgit v1.2.3 From 9798ef7bfda98cd30582bcac82e4f151d0c7cf94 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 5 Mar 2015 02:10:44 +0100 Subject: fixed length identifiers --- .../keychain/pgp/linked/LinkedCookieResource.java | 6 ++---- .../keychain/pgp/linked/resources/DnsResource.java | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index fb3f2433a..b21f482b7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java @@ -66,10 +66,8 @@ public abstract class LinkedCookieResource extends LinkedResource { } public static String generate (Context context, byte[] fingerprint, int nonce) { - - return "[Verifying my PGP key: openpgp4fpr:" - + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + "#" + Integer.toHexString(nonce) + "]"; - + return String.format("\"[Verifying my PGP key: openpgp4fpr:%s#%08x]\"", + KeyFormattingUtils.convertFingerprintToHex(fingerprint), nonce); } public static String generatePreview () { 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 index 5df008ed7..d5b8a0345 100644 --- 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 @@ -41,9 +41,8 @@ public class DnsResource extends LinkedCookieResource { public static String generateText (Context context, byte[] fingerprint, int nonce) { - return "pgpid+cookie=" - + KeyFormattingUtils.convertFingerprintToHex(fingerprint) + ";" - + Integer.toHexString(nonce) + ""; + return String.format("pgpid+cookie=%s;%08x", + KeyFormattingUtils.convertFingerprintToHex(fingerprint), nonce); } -- cgit v1.2.3 From 98ce06dbc9bea56d2960df2d7b8e7a5bc9e9e43a Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 5 Mar 2015 10:42:17 +0100 Subject: small fixes to the matcher --- .../org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index 6077db606..59ffbfc45 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java @@ -39,7 +39,7 @@ public abstract class LinkedResource { protected static LinkedCookieResource fromUri (URI uri) { - if ("pgpid+cookie".equals(uri.getScheme())) { + if (!"pgpid+cookie".equals(uri.getScheme())) { Log.e(Constants.TAG, "unknown uri scheme in (suspected) linked id packet"); return null; } @@ -60,13 +60,10 @@ public abstract class LinkedResource { 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 ("".equals(p[0])) { - continue; - } if (p.length == 1) { flags.add(param); } else { -- cgit v1.2.3 From 5d2c81d715cf1fd9ff23a8d1aa43fcfb58f7d099 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 5 Mar 2015 13:13:43 +0100 Subject: make linked identity list homogeneous --- .../keychain/pgp/linked/LinkedCookieResource.java | 4 - .../keychain/pgp/linked/LinkedIdentity.java | 18 ++++ .../keychain/pgp/linked/LinkedResource.java | 17 ++- .../keychain/pgp/linked/RawLinkedIdentity.java | 16 +++ .../keychain/pgp/linked/resources/DnsResource.java | 17 +++ .../pgp/linked/resources/GenericHttpsResource.java | 17 +++ .../pgp/linked/resources/TwitterResource.java | 20 ++++ .../pgp/linked/resources/UnknownResource.java | 21 ---- .../keychain/ui/ViewKeyFragment.java | 7 +- .../keychain/ui/adapter/LinkedIdsAdapter.java | 119 ++++++++------------- 10 files changed, 150 insertions(+), 106 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index b21f482b7..4b01d8135 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java @@ -6,18 +6,14 @@ 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.pgp.linked.resources.GenericHttpsResource; -import org.sufficientlysecure.keychain.pgp.linked.resources.UnknownResource; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; 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 { 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 index f06a23681..35813b8b3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java @@ -3,6 +3,7 @@ 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.R; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.util.Log; @@ -11,6 +12,10 @@ import java.net.URI; import java.nio.ByteBuffer; import java.util.Arrays; +import android.content.Context; +import android.support.annotation.DrawableRes; + + public class LinkedIdentity extends RawLinkedIdentity { public final LinkedResource mResource; @@ -72,4 +77,17 @@ public class LinkedIdentity extends RawLinkedIdentity { return new RawLinkedIdentity(nonce, 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 index 59ffbfc45..28f40f0d7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java @@ -3,7 +3,7 @@ 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.UnknownResource; +import org.sufficientlysecure.keychain.pgp.linked.resources.TwitterResource; import org.sufficientlysecure.keychain.util.Log; import java.net.URI; @@ -12,6 +12,9 @@ import java.util.HashSet; import java.util.Set; import java.util.regex.Pattern; +import android.content.Context; +import android.support.annotation.DrawableRes; + public abstract class LinkedResource { protected final URI mSubUri; @@ -58,8 +61,8 @@ public abstract class LinkedResource { String[] pieces = specific.split("@", 2); URI subUri = URI.create(pieces[1]); - Set flags = new HashSet(); - HashMap params = new HashMap(); + Set flags = new HashSet<>(); + HashMap params = new HashMap<>(); if (!pieces[0].isEmpty()) { String[] rawParams = pieces[0].split(";"); for (String param : rawParams) { @@ -90,9 +93,17 @@ public abstract class LinkedResource { if (res != null) { return res; } + res = TwitterResource.create(flags, params, subUri); + if (res != null) { + return res; + } return null; } + public abstract @DrawableRes int getDisplayIcon(); + public abstract String getDisplayTitle(Context context); + public abstract String getDisplayComment(Context context); + } 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 index bfde3c3b9..0de55ac38 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java @@ -1,11 +1,15 @@ 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 java.nio.ByteBuffer; +import android.content.Context; +import android.support.annotation.DrawableRes; + /** The RawLinkedIdentity contains raw parsed data from a Linked Identity subpacket. */ public class RawLinkedIdentity { @@ -42,4 +46,16 @@ public class RawLinkedIdentity { return 1234567; } + 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 index d5b8a0345..8f16aa24d 100644 --- 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 @@ -1,7 +1,9 @@ package org.sufficientlysecure.keychain.pgp.linked.resources; import android.content.Context; +import android.support.annotation.DrawableRes; +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; @@ -100,4 +102,19 @@ public class DnsResource extends LinkedCookieResource { protected Matcher matchResource(OperationLog log, int indent, String res) { return magicPattern.matcher(res); } + + @Override + public @DrawableRes int getDisplayIcon() { + return R.drawable.dns; + } + + @Override + public String getDisplayTitle(Context context) { + return "dns"; + } + + @Override + public String getDisplayComment(Context context) { + return null; + } } 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 index ba94bae75..7d4a38fe4 100644 --- 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 @@ -1,6 +1,7 @@ package org.sufficientlysecure.keychain.pgp.linked.resources; import android.content.Context; +import android.support.annotation.DrawableRes; import com.textuality.keybase.lib.Search; @@ -100,4 +101,20 @@ public class GenericHttpsResource extends LinkedCookieResource { return new GenericHttpsResource(flags, params, uri); } + @Override + public @DrawableRes + int getDisplayIcon() { + return R.drawable.ssl_lock; + } + + @Override + public String getDisplayTitle(Context context) { + return "https"; + } + + @Override + public String getDisplayComment(Context context) { + return null; + } + } 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 index f6ec4f97a..3553ce740 100644 --- 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 @@ -1,6 +1,7 @@ package org.sufficientlysecure.keychain.pgp.linked.resources; import android.content.Context; +import android.support.annotation.DrawableRes; import android.util.Base64; import com.textuality.keybase.lib.JWalk; @@ -16,6 +17,7 @@ import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; import org.json.JSONException; import org.json.JSONObject; +import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; @@ -121,4 +123,22 @@ public class TwitterResource extends LinkedCookieResource { return getTwitterStream("Valodim"); } + @Override + public @DrawableRes int getDisplayIcon() { + return R.drawable.twitter; + } + + @Override + public String getDisplayTitle(Context context) { + return "twitter"; + } + + @Override + public String getDisplayComment(Context context) { + return null; + } + + public static LinkedCookieResource create(Set flags, HashMap params, URI subUri) { + return null; + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java deleted file mode 100644 index f29ab5b39..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/UnknownResource.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.sufficientlysecure.keychain.pgp.linked.resources; - -import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; - -import java.net.URI; -import java.util.HashMap; -import java.util.Set; - -public class UnknownResource extends LinkedCookieResource { - - public UnknownResource(Set flags, HashMap params, URI uri) { - super(flags, params, uri); - } - - @Override - protected String fetchResource(OperationLog log, int indent) { - return null; - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index 20386b372..302bd6be4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -89,8 +89,6 @@ public class ViewKeyFragment extends LoaderFragment implements mLinkedIdsCard = (CardView) view.findViewById(R.id.card_linked_ids); mLinkedIds = (ListView) view.findViewById(R.id.view_key_linked_ids); - mLinkedIdsAdapter = new LinkedIdsAdapter(getActivity(), null, 0); - mLinkedIds.setAdapter(mLinkedIdsAdapter); mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override @@ -171,7 +169,6 @@ public class ViewKeyFragment extends LoaderFragment implements Log.i(Constants.TAG, "mDataUri: " + mDataUri); getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); - getLoaderManager().initLoader(LOADER_ID_LINKED_IDS, null, this); } public Loader onCreateLoader(int id, Bundle args) { @@ -211,6 +208,10 @@ public class ViewKeyFragment extends LoaderFragment implements mUserIds.setAdapter(mUserIdsAdapter); getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); + mLinkedIdsAdapter = new LinkedIdsAdapter(getActivity(), null, 0, !mIsSecret); + mLinkedIds.setAdapter(mLinkedIdsAdapter); + getLoaderManager().initLoader(LOADER_ID_LINKED_IDS, null, this); + break; } } 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 329b95ebc..031972918 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 @@ -34,9 +34,7 @@ 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.LinkedResource; import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; -import org.sufficientlysecure.keychain.pgp.linked.resources.DnsResource; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment; @@ -48,59 +46,45 @@ import java.util.WeakHashMap; public class LinkedIdsAdapter extends UserAttributesAdapter { + private final boolean mShowCertification; protected LayoutInflater mInflater; WeakHashMap mLinkedIdentityCache = new WeakHashMap<>(); - public LinkedIdsAdapter(Context context, Cursor c, int flags) { + public LinkedIdsAdapter(Context context, Cursor c, int flags, boolean showCertification) { super(context, c, flags); mInflater = LayoutInflater.from(context); + mShowCertification = showCertification; } @Override public void bindView(View view, Context context, Cursor cursor) { - RawLinkedIdentity id = getItem(cursor.getPosition()); ViewHolder holder = (ViewHolder) view.getTag(); - int isVerified = cursor.getInt(INDEX_VERIFIED); - switch (isVerified) { - case Certs.VERIFIED_SECRET: - KeyFormattingUtils.setStatusImage(mContext, holder.vVerified, - null, State.VERIFIED, KeyFormattingUtils.DEFAULT_COLOR); - break; - case Certs.VERIFIED_SELF: - KeyFormattingUtils.setStatusImage(mContext, holder.vVerified, - null, State.UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR); - break; - default: - KeyFormattingUtils.setStatusImage(mContext, holder.vVerified, - null, State.INVALID, KeyFormattingUtils.DEFAULT_COLOR); - break; - } - - if (holder instanceof ViewHolderNonRaw) { - ((ViewHolderNonRaw) holder).setData(mContext, (LinkedIdentity) id); - } - - } - - @Override - public int getItemViewType(int position) { - RawLinkedIdentity id = getItem(position); - - if (id instanceof LinkedIdentity) { - LinkedResource res = ((LinkedIdentity) id).mResource; - if (res instanceof DnsResource) { - return 1; + if (mShowCertification) { + holder.vVerified.setVisibility(View.VISIBLE); + int isVerified = cursor.getInt(INDEX_VERIFIED); + switch (isVerified) { + case Certs.VERIFIED_SECRET: + KeyFormattingUtils.setStatusImage(mContext, holder.vVerified, + null, State.VERIFIED, KeyFormattingUtils.DEFAULT_COLOR); + break; + case Certs.VERIFIED_SELF: + KeyFormattingUtils.setStatusImage(mContext, holder.vVerified, + null, State.UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR); + break; + default: + KeyFormattingUtils.setStatusImage(mContext, holder.vVerified, + null, State.INVALID, KeyFormattingUtils.DEFAULT_COLOR); + break; } + } else { + holder.vVerified.setVisibility(View.GONE); } - return 0; - } + RawLinkedIdentity id = getItem(cursor.getPosition()); + holder.setData(mContext, id); - @Override - public int getViewTypeCount() { - return 2; } @Override @@ -126,23 +110,10 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - int type = getItemViewType(cursor.getPosition()); - switch(type) { - case 0: { - View v = mInflater.inflate(R.layout.linked_id_item_unknown, null); - ViewHolder holder = new ViewHolder(v); - v.setTag(holder); - return v; - } - case 1: { - View v = mInflater.inflate(R.layout.linked_id_item_dns, null); - ViewHolder holder = new ViewHolderDns(v); - v.setTag(holder); - return v; - } - default: - throw new AssertionError("all cases must be covered in LinkedIdsAdapter.newView!"); - } + View v = mInflater.inflate(R.layout.linked_id_item, null); + ViewHolder holder = new ViewHolder(v); + v.setTag(holder); + return v; } // don't show revoked user ids, irrelevant for average users @@ -162,35 +133,32 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { static class ViewHolder { ImageView vVerified; + ImageView vIcon; + TextView vTitle; + TextView vComment; ViewHolder(View view) { vVerified = (ImageView) view.findViewById(R.id.user_id_item_certified); - } - } - - static abstract class ViewHolderNonRaw extends ViewHolder { - ViewHolderNonRaw(View view) { - super(view); + vIcon = (ImageView) view.findViewById(R.id.linked_id_type_icon); + vTitle = (TextView) view.findViewById(R.id.linked_id_title); + vComment = (TextView) view.findViewById(R.id.linked_id_comment); } - abstract void setData(Context context, LinkedIdentity id); - } + void setData(Context context, RawLinkedIdentity id) { - static class ViewHolderDns extends ViewHolderNonRaw { - TextView vFqdn; + vTitle.setText(id.getDisplayTitle(context)); - ViewHolderDns(View view) { - super(view); + String comment = id.getDisplayComment(context); + if (comment != null) { + vComment.setVisibility(View.VISIBLE); + vComment.setText(comment); + } else { + vComment.setVisibility(View.GONE); + } - vFqdn = (TextView) view.findViewById(R.id.linked_id_dns_fqdn); - } + vIcon.setImageResource(id.getDisplayIcon()); - @Override - void setData(Context context, LinkedIdentity id) { - DnsResource res = (DnsResource) id.mResource; - vFqdn.setText(res.getFqdn()); } - } @Override @@ -198,4 +166,5 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { mLinkedIdentityCache.clear(); super.notifyDataSetChanged(); } + } -- cgit v1.2.3 From a2419aa688afb61c02a1fcd4a8d1df46fdd97b5e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 5 Mar 2015 18:05:48 +0100 Subject: work on LinkedIdViewFragment --- .../keychain/pgp/linked/LinkedIdentity.java | 1 + .../keychain/pgp/linked/LinkedResource.java | 7 ++ .../keychain/pgp/linked/RawLinkedIdentity.java | 1 + .../keychain/pgp/linked/resources/DnsResource.java | 4 +- .../pgp/linked/resources/GenericHttpsResource.java | 17 ++- .../keychain/provider/ProviderHelper.java | 6 - .../keychain/ui/ViewKeyFragment.java | 11 +- .../keychain/ui/adapter/LinkedIdsAdapter.java | 27 +++-- .../keychain/ui/linked/LinkedIdViewFragment.java | 121 ++++++++++++++++++++- 9 files changed, 170 insertions(+), 25 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index 35813b8b3..ff5995329 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java @@ -13,6 +13,7 @@ import java.nio.ByteBuffer; import java.util.Arrays; import android.content.Context; +import android.content.Intent; import android.support.annotation.DrawableRes; 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 index 28f40f0d7..095fd4ac7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java @@ -13,6 +13,7 @@ import java.util.Set; import java.util.regex.Pattern; import android.content.Context; +import android.content.Intent; import android.support.annotation.DrawableRes; public abstract class LinkedResource { @@ -105,5 +106,11 @@ public abstract class LinkedResource { public abstract @DrawableRes int getDisplayIcon(); 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 index 0de55ac38..8b014db1d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java @@ -8,6 +8,7 @@ import java.net.URI; import java.nio.ByteBuffer; import android.content.Context; +import android.content.Intent; import android.support.annotation.DrawableRes; /** The RawLinkedIdentity contains raw parsed data from a Linked Identity subpacket. */ 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 index 8f16aa24d..cd0706ff3 100644 --- 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 @@ -110,11 +110,11 @@ public class DnsResource extends LinkedCookieResource { @Override public String getDisplayTitle(Context context) { - return "dns"; + return "Domain Name"; } @Override public String getDisplayComment(Context context) { - return null; + 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 index 7d4a38fe4..0ab90af75 100644 --- 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 @@ -1,6 +1,8 @@ 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 com.textuality.keybase.lib.Search; @@ -109,12 +111,23 @@ public class GenericHttpsResource extends LinkedCookieResource { @Override public String getDisplayTitle(Context context) { - return "https"; + return "Website (HTTPS)"; } @Override public String getDisplayComment(Context context) { - return null; + 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/provider/ProviderHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 2ff4803fb..fc5275713 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -39,7 +39,6 @@ import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.operations.ImportExportOperation; import org.sufficientlysecure.keychain.operations.results.ConsolidateResult; -import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.operations.results.SaveKeyringResult; @@ -54,7 +53,6 @@ import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.UncachedKeyRing; import org.sufficientlysecure.keychain.pgp.UncachedPublicKey; import org.sufficientlysecure.keychain.pgp.WrappedSignature; -import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAllowedKeys; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; @@ -62,15 +60,11 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; -import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AppSettings; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ParcelableFileCache; -import org.sufficientlysecure.keychain.util.ParcelableFileCache.IteratorWithSize; -import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.ProgressFixedScaler; import org.sufficientlysecure.keychain.util.ProgressScaler; import org.sufficientlysecure.keychain.util.Utf8Util; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index 302bd6be4..311e1d0ee 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -18,6 +18,8 @@ package org.sufficientlysecure.keychain.ui; +import java.io.IOException; + import android.database.Cursor; import android.net.Uri; import android.os.Build; @@ -45,6 +47,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; +import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; public class ViewKeyFragment extends LoaderFragment implements @@ -107,7 +110,13 @@ public class ViewKeyFragment extends LoaderFragment implements } private void showLinkedId(final int position) { - Fragment frag = mLinkedIdsAdapter.getLinkedIdFragment(position); + Fragment frag; + try { + frag = mLinkedIdsAdapter.getLinkedIdFragment(position); + } catch (IOException e) { + e.printStackTrace(); + return; + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Transition trans = TransitionInflater.from(getActivity()) 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 031972918..7383db1b0 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 @@ -125,26 +125,35 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { UserIdsAdapter.USER_PACKETS_PROJECTION, LINKED_IDS_WHERE, null, null); } - public Fragment getLinkedIdFragment(int position) { + public Fragment getLinkedIdFragment(int position) throws IOException { RawLinkedIdentity id = getItem(position); - return LinkedIdViewFragment.newInstance(id); + Integer isVerified; + if (mShowCertification) { + Cursor cursor = getCursor(); + cursor.moveToPosition(position); + isVerified = cursor.getInt(INDEX_VERIFIED); + } else { + isVerified = null; + } + + return LinkedIdViewFragment.newInstance(id, isVerified); } - static class ViewHolder { - ImageView vVerified; - ImageView vIcon; - TextView vTitle; - TextView vComment; + public static class ViewHolder { + final public ImageView vVerified; + final public ImageView vIcon; + final public TextView vTitle; + final public TextView vComment; - ViewHolder(View view) { + public ViewHolder(View view) { vVerified = (ImageView) view.findViewById(R.id.user_id_item_certified); vIcon = (ImageView) view.findViewById(R.id.linked_id_type_icon); vTitle = (TextView) view.findViewById(R.id.linked_id_title); vComment = (TextView) view.findViewById(R.id.linked_id_comment); } - void setData(Context context, RawLinkedIdentity id) { + public void setData(Context context, RawLinkedIdentity id) { vTitle.setText(id.getDisplayTitle(context)); 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 0eb0dcd45..b526776f0 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 @@ -1,5 +1,9 @@ package org.sufficientlysecure.keychain.ui.linked; +import java.io.IOException; + +import android.content.Context; +import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v7.widget.CardView; @@ -9,17 +13,61 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import org.sufficientlysecure.keychain.R; +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.provider.KeychainContract.Certs; +import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter.ViewHolder; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; public class LinkedIdViewFragment extends Fragment { - private CardView mLinkedIdsCard; + private static final String EXTRA_ENCODED_LID = "encoded_lid"; + private static final String EXTRA_VERIFIED = "verified"; + + private RawLinkedIdentity mLinkedId; + private LinkedCookieResource mLinkedResource; + private Integer mVerified; + + private CardView vLinkedIdsCard; + private Context mContext; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Bundle args = getArguments(); + byte[] data = args.getByteArray(EXTRA_ENCODED_LID); + + try { + mLinkedId = LinkedIdentity.fromAttributeData(data); + } catch (IOException e) { + // TODO um… + e.printStackTrace(); + throw new AssertionError("reconstruction of user attribute must succeed!"); + } - public static Fragment newInstance(RawLinkedIdentity id) { + if (mLinkedId instanceof LinkedIdentity) { + LinkedResource res = ((LinkedIdentity) mLinkedId).mResource; + mLinkedResource = (LinkedCookieResource) res; + } + + mVerified = args.containsKey(EXTRA_VERIFIED) ? args.getInt(EXTRA_VERIFIED) : null; + + mContext = getActivity(); + } + + public static Fragment newInstance(RawLinkedIdentity id, Integer isVerified) throws IOException { LinkedIdViewFragment frag = new LinkedIdViewFragment(); Bundle args = new Bundle(); + args.putByteArray(EXTRA_ENCODED_LID, id.toUserAttribute().getEncoded()); + if (isVerified != null) { + args.putInt(EXTRA_VERIFIED, isVerified); + } frag.setArguments(args); return frag; @@ -29,17 +77,80 @@ public class LinkedIdViewFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.linked_id_view_fragment, null); - mLinkedIdsCard = (CardView) root.findViewById(R.id.card_linked_ids); + vLinkedIdsCard = (CardView) root.findViewById(R.id.card_linked_ids); - root.findViewById(R.id.back_button).setClickable(true); - root.findViewById(R.id.back_button).setOnClickListener(new OnClickListener() { + View back = root.findViewById(R.id.back_button); + back.setClickable(true); + back.findViewById(R.id.back_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { getFragmentManager().popBackStack(); } }); + ViewHolder holder = new ViewHolder(root); + + if (mVerified != null) { + holder.vVerified.setVisibility(View.VISIBLE); + switch (mVerified) { + case Certs.VERIFIED_SECRET: + KeyFormattingUtils.setStatusImage(mContext, holder.vVerified, + null, State.VERIFIED, KeyFormattingUtils.DEFAULT_COLOR); + break; + case Certs.VERIFIED_SELF: + KeyFormattingUtils.setStatusImage(mContext, holder.vVerified, + null, State.UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR); + break; + default: + KeyFormattingUtils.setStatusImage(mContext, holder.vVerified, + null, State.INVALID, KeyFormattingUtils.DEFAULT_COLOR); + break; + } + } else { + holder.vVerified.setVisibility(View.GONE); + } + + holder.setData(mContext, mLinkedId); + + // no resource, nothing further we can do… + if (mLinkedResource == null) { + root.findViewById(R.id.button_view).setVisibility(View.GONE); + root.findViewById(R.id.button_verify).setVisibility(View.GONE); + return root; + } + + View button_view = root.findViewById(R.id.button_view); + if (mLinkedResource.isViewable()) { + button_view.setVisibility(View.VISIBLE); + button_view.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = mLinkedResource.getViewIntent(); + if (intent == null) { + return; + } + getActivity().startActivity(intent); + } + }); + } else { + button_view.setVisibility(View.GONE); + } + + View button_verify = root.findViewById(R.id.button_verify); + button_verify.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + verifyResource(); + } + }); + return root; } + public void verifyResource() { + + // TODO + + } + } -- cgit v1.2.3 From 6e17d5244dcbfaad5c4de902e1f4c2cd913fd596 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 6 Mar 2015 14:20:58 +0100 Subject: first steps in ui towards confirmation of linked ids --- .../keychain/ui/ViewKeyActivity.java | 325 ++++++++++----------- .../keychain/ui/ViewKeyFragment.java | 96 +++--- .../keychain/ui/adapter/LinkedIdsAdapter.java | 4 +- .../keychain/ui/linked/LinkedIdViewFragment.java | 116 +++++++- 4 files changed, 310 insertions(+), 231 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 678ecd289..be1e0a4c1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -261,7 +261,6 @@ public class ViewKeyActivity extends BaseActivity implements initNfc(mDataUri); - startFragment(savedInstanceState, mDataUri); } @Override @@ -269,26 +268,6 @@ public class ViewKeyActivity extends BaseActivity implements setContentView(R.layout.view_key_activity); } - private void startFragment(Bundle savedInstanceState, Uri dataUri) { - // However, if we're being restored from a previous state, - // then we don't need to do anything and should return or else - // we could end up with overlapping fragments. - if (savedInstanceState != null) { - return; - } - - // Create an instance of the fragment - ViewKeyFragment frag = ViewKeyFragment.newInstance(dataUri); - - // Add the fragment to the 'fragment_container' FrameLayout - // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! - getSupportFragmentManager().beginTransaction() - .replace(R.id.view_key_fragment, frag, "main") - .commitAllowingStateLoss(); - // do it immediately! - getSupportFragmentManager().executePendingTransactions(); - } - @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); @@ -807,166 +786,172 @@ public class ViewKeyActivity extends BaseActivity implements // old cursor once we return.) switch (loader.getId()) { case LOADER_ID_UNIFIED: { - if (data.moveToFirst()) { - // get name, email, and comment from USER_ID - String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID)); - if (mainUserId[0] != null) { - mName.setText(mainUserId[0]); + // if there is no data, just break + if (!data.moveToFirst()) { + break; + } + + String oldFingerprint = mFingerprint; + mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID); + byte[] fpData = data.getBlob(INDEX_FINGERPRINT); + mFingerprint = KeyFormattingUtils.convertFingerprintToHex(fpData); + + mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; + mHasEncrypt = data.getInt(INDEX_HAS_ENCRYPT) != 0; + mIsRevoked = data.getInt(INDEX_IS_REVOKED) > 0; + mIsExpired = data.getInt(INDEX_IS_EXPIRED) != 0; + mIsVerified = data.getInt(INDEX_VERIFIED) > 0; + + startFragment(mIsSecret, fpData); + + // get name, email, and comment from USER_ID + String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID)); + if (mainUserId[0] != null) { + mName.setText(mainUserId[0]); + } else { + mName.setText(R.string.user_id_no_name); + } + + // if the refresh animation isn't playing + if (!mRotate.hasStarted() && !mRotateSpin.hasStarted()) { + // re-create options menu based on mIsSecret, mIsVerified + supportInvalidateOptionsMenu(); + // this is done at the end of the animation otherwise + } + + AsyncTask photoTask = + new AsyncTask() { + protected Bitmap doInBackground(Long... mMasterKeyId) { + return ContactHelper.loadPhotoByMasterKeyId(getContentResolver(), mMasterKeyId[0], true); + } + + protected void onPostExecute(Bitmap photo) { + mPhoto.setImageBitmap(photo); + mPhoto.setVisibility(View.VISIBLE); + } + }; + + // Note: order is important + int color; + if (mIsRevoked) { + mStatusText.setText(R.string.view_key_revoked); + mStatusImage.setVisibility(View.VISIBLE); + KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, + State.REVOKED, R.color.icons, true); + color = getResources().getColor(R.color.android_red_light); + + mActionEncryptFile.setVisibility(View.GONE); + mActionEncryptText.setVisibility(View.GONE); + mActionNfc.setVisibility(View.GONE); + mFab.setVisibility(View.GONE); + mQrCodeLayout.setVisibility(View.GONE); + } else if (mIsExpired) { + if (mIsSecret) { + mStatusText.setText(R.string.view_key_expired_secret); } else { - mName.setText(R.string.user_id_no_name); + mStatusText.setText(R.string.view_key_expired); } - - String oldFingerprint = mFingerprint; - mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID); - mFingerprint = KeyFormattingUtils.convertFingerprintToHex(data.getBlob(INDEX_FINGERPRINT)); - - mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; - mHasEncrypt = data.getInt(INDEX_HAS_ENCRYPT) != 0; - mIsRevoked = data.getInt(INDEX_IS_REVOKED) > 0; - mIsExpired = data.getInt(INDEX_IS_EXPIRED) != 0; - mIsVerified = data.getInt(INDEX_VERIFIED) > 0; - - // if the refresh animation isn't playing - if (!mRotate.hasStarted() && !mRotateSpin.hasStarted()) { - // re-create options menu based on mIsSecret, mIsVerified - supportInvalidateOptionsMenu(); - // this is done at the end of the animation otherwise + mStatusImage.setVisibility(View.VISIBLE); + KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, + State.EXPIRED, R.color.icons, true); + color = getResources().getColor(R.color.android_red_light); + + mActionEncryptFile.setVisibility(View.GONE); + mActionEncryptText.setVisibility(View.GONE); + mActionNfc.setVisibility(View.GONE); + mFab.setVisibility(View.GONE); + mQrCodeLayout.setVisibility(View.GONE); + } else if (mIsSecret) { + mStatusText.setText(R.string.view_key_my_key); + mStatusImage.setVisibility(View.GONE); + color = getResources().getColor(R.color.primary); + // reload qr code only if the fingerprint changed + if (!mFingerprint.equals(oldFingerprint)) { + loadQrCode(mFingerprint); } + photoTask.execute(mMasterKeyId); + mQrCodeLayout.setVisibility(View.VISIBLE); + + // and place leftOf qr code + RelativeLayout.LayoutParams nameParams = (RelativeLayout.LayoutParams) + mName.getLayoutParams(); + // remove right margin + nameParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0); + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + nameParams.setMarginEnd(0); + } + nameParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout); + mName.setLayoutParams(nameParams); + + RelativeLayout.LayoutParams statusParams = (RelativeLayout.LayoutParams) + mStatusText.getLayoutParams(); + statusParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0); + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + statusParams.setMarginEnd(0); + } + statusParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout); + mStatusText.setLayoutParams(statusParams); - AsyncTask photoTask = - new AsyncTask() { - protected Bitmap doInBackground(Long... mMasterKeyId) { - return ContactHelper.loadPhotoByMasterKeyId(getContentResolver(), mMasterKeyId[0], true); - } + mActionEncryptFile.setVisibility(View.VISIBLE); + mActionEncryptText.setVisibility(View.VISIBLE); - protected void onPostExecute(Bitmap photo) { - mPhoto.setImageBitmap(photo); - mPhoto.setVisibility(View.VISIBLE); - } - }; + // invokeBeam is available from API 21 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + mActionNfc.setVisibility(View.VISIBLE); + } else { + mActionNfc.setVisibility(View.GONE); + } + mFab.setVisibility(View.VISIBLE); + mFab.setIconDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp)); + } else { + mActionEncryptFile.setVisibility(View.VISIBLE); + mActionEncryptText.setVisibility(View.VISIBLE); + mQrCodeLayout.setVisibility(View.GONE); + mActionNfc.setVisibility(View.GONE); - // Note: order is important - int color; - if (mIsRevoked) { - mStatusText.setText(R.string.view_key_revoked); + if (mIsVerified) { + mStatusText.setText(R.string.view_key_verified); mStatusImage.setVisibility(View.VISIBLE); KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, - State.REVOKED, R.color.icons, true); - color = getResources().getColor(R.color.android_red_light); + State.VERIFIED, R.color.icons, true); + color = getResources().getColor(R.color.primary); + photoTask.execute(mMasterKeyId); - mActionEncryptFile.setVisibility(View.GONE); - mActionEncryptText.setVisibility(View.GONE); - mActionNfc.setVisibility(View.GONE); mFab.setVisibility(View.GONE); - mQrCodeLayout.setVisibility(View.GONE); - } else if (mIsExpired) { - if (mIsSecret) { - mStatusText.setText(R.string.view_key_expired_secret); - } else { - mStatusText.setText(R.string.view_key_expired); - } + } else { + mStatusText.setText(R.string.view_key_unverified); mStatusImage.setVisibility(View.VISIBLE); KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, - State.EXPIRED, R.color.icons, true); - color = getResources().getColor(R.color.android_red_light); + State.UNVERIFIED, R.color.icons, true); + color = getResources().getColor(R.color.android_orange_light); - mActionEncryptFile.setVisibility(View.GONE); - mActionEncryptText.setVisibility(View.GONE); - mActionNfc.setVisibility(View.GONE); - mFab.setVisibility(View.GONE); - mQrCodeLayout.setVisibility(View.GONE); - } else if (mIsSecret) { - mStatusText.setText(R.string.view_key_my_key); - mStatusImage.setVisibility(View.GONE); - color = getResources().getColor(R.color.primary); - // reload qr code only if the fingerprint changed - if (!mFingerprint.equals(oldFingerprint)) { - loadQrCode(mFingerprint); - } - photoTask.execute(mMasterKeyId); - mQrCodeLayout.setVisibility(View.VISIBLE); - - // and place leftOf qr code - RelativeLayout.LayoutParams nameParams = (RelativeLayout.LayoutParams) - mName.getLayoutParams(); - // remove right margin - nameParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0); - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - nameParams.setMarginEnd(0); - } - nameParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout); - mName.setLayoutParams(nameParams); - - RelativeLayout.LayoutParams statusParams = (RelativeLayout.LayoutParams) - mStatusText.getLayoutParams(); - statusParams.setMargins(FormattingUtils.dpToPx(this, 48), 0, 0, 0); - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - statusParams.setMarginEnd(0); - } - statusParams.addRule(RelativeLayout.LEFT_OF, R.id.view_key_qr_code_layout); - mStatusText.setLayoutParams(statusParams); - - mActionEncryptFile.setVisibility(View.VISIBLE); - mActionEncryptText.setVisibility(View.VISIBLE); - - // invokeBeam is available from API 21 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - mActionNfc.setVisibility(View.VISIBLE); - } else { - mActionNfc.setVisibility(View.GONE); - } mFab.setVisibility(View.VISIBLE); - mFab.setIconDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp)); - } else { - mActionEncryptFile.setVisibility(View.VISIBLE); - mActionEncryptText.setVisibility(View.VISIBLE); - mQrCodeLayout.setVisibility(View.GONE); - mActionNfc.setVisibility(View.GONE); - - if (mIsVerified) { - mStatusText.setText(R.string.view_key_verified); - mStatusImage.setVisibility(View.VISIBLE); - KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, - State.VERIFIED, R.color.icons, true); - color = getResources().getColor(R.color.primary); - photoTask.execute(mMasterKeyId); - - mFab.setVisibility(View.GONE); - } else { - mStatusText.setText(R.string.view_key_unverified); - mStatusImage.setVisibility(View.VISIBLE); - KeyFormattingUtils.setStatusImage(this, mStatusImage, mStatusText, - State.UNVERIFIED, R.color.icons, true); - color = getResources().getColor(R.color.android_orange_light); - - mFab.setVisibility(View.VISIBLE); - } } + } - if (mPreviousColor == 0 || mPreviousColor == color) { - mStatusBar.setBackgroundColor(color); - mBigToolbar.setBackgroundColor(color); - mPreviousColor = color; - } else { - ObjectAnimator colorFade1 = - ObjectAnimator.ofObject(mStatusBar, "backgroundColor", - new ArgbEvaluator(), mPreviousColor, color); - ObjectAnimator colorFade2 = - ObjectAnimator.ofObject(mBigToolbar, "backgroundColor", - new ArgbEvaluator(), mPreviousColor, color); - - colorFade1.setDuration(1200); - colorFade2.setDuration(1200); - colorFade1.start(); - colorFade2.start(); - mPreviousColor = color; - } + if (mPreviousColor == 0 || mPreviousColor == color) { + mStatusBar.setBackgroundColor(color); + mBigToolbar.setBackgroundColor(color); + mPreviousColor = color; + } else { + ObjectAnimator colorFade1 = + ObjectAnimator.ofObject(mStatusBar, "backgroundColor", + new ArgbEvaluator(), mPreviousColor, color); + ObjectAnimator colorFade2 = + ObjectAnimator.ofObject(mBigToolbar, "backgroundColor", + new ArgbEvaluator(), mPreviousColor, color); + + colorFade1.setDuration(1200); + colorFade2.setDuration(1200); + colorFade1.start(); + colorFade2.start(); + mPreviousColor = color; + } - //noinspection deprecation - mStatusImage.setAlpha(80); + //noinspection deprecation + mStatusImage.setAlpha(80); - break; - } + break; } } } @@ -975,4 +960,18 @@ public class ViewKeyActivity extends BaseActivity implements public void onLoaderReset(Loader loader) { } + + private void startFragment(boolean isSecret, byte[] fingerprint) { + // Create an instance of the fragment + ViewKeyFragment frag = ViewKeyFragment.newInstance(mDataUri, isSecret, fingerprint); + + // Add the fragment to the 'fragment_container' FrameLayout + // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! + getSupportFragmentManager().beginTransaction() + .replace(R.id.view_key_fragment, frag, "main") + .commitAllowingStateLoss(); + // do it immediately! + getSupportFragmentManager().executePendingTransactions(); + } + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index 311e1d0ee..240dd3547 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -29,7 +29,6 @@ import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v7.widget.CardView; -import android.transition.Explode; import android.transition.Fade; import android.transition.Transition; import android.transition.TransitionInflater; @@ -47,21 +46,21 @@ import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; -import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.Log; public class ViewKeyFragment extends LoaderFragment implements LoaderManager.LoaderCallbacks { public static final String ARG_DATA_URI = "uri"; + private static final String ARG_FINGERPRINT = "fingerprint"; + private static final String ARG_IS_SECRET = "is_secret"; private ListView mUserIds; boolean mIsSecret = false; - private static final int LOADER_ID_UNIFIED = 0; - private static final int LOADER_ID_USER_IDS = 1; - private static final int LOADER_ID_LINKED_IDS = 2; + private static final int LOADER_ID_USER_IDS = 0; + private static final int LOADER_ID_LINKED_IDS = 1; private UserIdsAdapter mUserIdsAdapter; private LinkedIdsAdapter mLinkedIdsAdapter; @@ -69,20 +68,40 @@ public class ViewKeyFragment extends LoaderFragment implements private Uri mDataUri; private ListView mLinkedIds; private CardView mLinkedIdsCard; + private byte[] mFingerprint; /** * Creates new instance of this fragment */ - public static ViewKeyFragment newInstance(Uri dataUri) { + public static ViewKeyFragment newInstance(Uri dataUri, boolean isSecret, byte[] fingerprint) { ViewKeyFragment frag = new ViewKeyFragment(); Bundle args = new Bundle(); args.putParcelable(ARG_DATA_URI, dataUri); + args.putBoolean(ARG_IS_SECRET, isSecret); + args.putByteArray(ARG_FINGERPRINT, fingerprint); frag.setArguments(args); return frag; } + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + Bundle args = getArguments(); + Uri dataUri = args.getParcelable(ARG_DATA_URI); + if (dataUri == null) { + Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); + getActivity().finish(); + return; + } + boolean isSecret = args.getBoolean(ARG_IS_SECRET); + byte[] fingerprint = args.getByteArray(ARG_FINGERPRINT); + + loadData(dataUri, isSecret, fingerprint); + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { View root = super.onCreateView(inflater, superContainer, savedInstanceState); @@ -112,7 +131,7 @@ public class ViewKeyFragment extends LoaderFragment implements private void showLinkedId(final int position) { Fragment frag; try { - frag = mLinkedIdsAdapter.getLinkedIdFragment(position); + frag = mLinkedIdsAdapter.getLinkedIdFragment(position, mFingerprint); } catch (IOException e) { e.printStackTrace(); return; @@ -149,45 +168,28 @@ public class ViewKeyFragment extends LoaderFragment implements } } - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); - if (dataUri == null) { - Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); - getActivity().finish(); - return; - } - - loadData(dataUri); - } - - - // These are the rows that we will retrieve. - static final String[] UNIFIED_PROJECTION = new String[]{ - KeychainContract.KeyRings._ID, - KeychainContract.KeyRings.HAS_ANY_SECRET, - }; - - static final int INDEX_HAS_ANY_SECRET = 1; - - private void loadData(Uri dataUri) { + private void loadData(Uri dataUri, boolean isSecret, byte[] fingerprint) { mDataUri = dataUri; + mIsSecret = isSecret; + mFingerprint = fingerprint; Log.i(Constants.TAG, "mDataUri: " + mDataUri); - getLoaderManager().initLoader(LOADER_ID_UNIFIED, null, this); + // load user ids after we know if it's a secret key + mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0, !mIsSecret, null); + mUserIds.setAdapter(mUserIdsAdapter); + getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); + + mLinkedIdsAdapter = new LinkedIdsAdapter(getActivity(), null, 0, !mIsSecret); + mLinkedIds.setAdapter(mLinkedIdsAdapter); + getLoaderManager().initLoader(LOADER_ID_LINKED_IDS, null, this); + } public Loader onCreateLoader(int id, Bundle args) { setContentShown(false); switch (id) { - case LOADER_ID_UNIFIED: { - Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri); - return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null); - } case LOADER_ID_USER_IDS: return UserIdsAdapter.createLoader(getActivity(), mDataUri); @@ -203,28 +205,6 @@ public class ViewKeyFragment extends LoaderFragment implements // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) switch (loader.getId()) { - case LOADER_ID_UNIFIED: { - // Avoid NullPointerExceptions... - if (data.getCount() == 0) { - return; - } - if (data.moveToFirst()) { - - mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; - - // load user ids after we know if it's a secret key - mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0, !mIsSecret, null); - mUserIds.setAdapter(mUserIdsAdapter); - getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); - - mLinkedIdsAdapter = new LinkedIdsAdapter(getActivity(), null, 0, !mIsSecret); - mLinkedIds.setAdapter(mLinkedIdsAdapter); - getLoaderManager().initLoader(LOADER_ID_LINKED_IDS, null, this); - - break; - } - } - case LOADER_ID_USER_IDS: { mUserIdsAdapter.swapCursor(data); break; 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 7383db1b0..365b8d265 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 @@ -125,7 +125,7 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { UserIdsAdapter.USER_PACKETS_PROJECTION, LINKED_IDS_WHERE, null, null); } - public Fragment getLinkedIdFragment(int position) throws IOException { + public Fragment getLinkedIdFragment(int position, byte[] fingerprint) throws IOException { RawLinkedIdentity id = getItem(position); Integer isVerified; @@ -137,7 +137,7 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { isVerified = null; } - return LinkedIdViewFragment.newInstance(id, isVerified); + return LinkedIdViewFragment.newInstance(id, isVerified, fingerprint); } public static class ViewHolder { 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 b526776f0..5cf0ed115 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 @@ -4,6 +4,7 @@ import java.io.IOException; import android.content.Context; import android.content.Intent; +import android.os.AsyncTask; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v7.widget.CardView; @@ -11,8 +12,13 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.ViewAnimator; import org.sufficientlysecure.keychain.R; +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; @@ -21,12 +27,15 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter.ViewHolder; 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; public class LinkedIdViewFragment extends Fragment { - private static final String EXTRA_ENCODED_LID = "encoded_lid"; - private static final String EXTRA_VERIFIED = "verified"; + private static final String ARG_ENCODED_LID = "encoded_lid"; + private static final String ARG_VERIFIED = "verified"; + private static final String ARG_FINGERPRINT = "fingerprint"; private RawLinkedIdentity mLinkedId; private LinkedCookieResource mLinkedResource; @@ -34,13 +43,20 @@ public class LinkedIdViewFragment extends Fragment { private CardView vLinkedIdsCard; private Context mContext; + private byte[] mFingerprint; + private LayoutInflater mInflater; + private LinearLayout vLinkedCerts; + + private View mCurrentCert; + private boolean mInProgress; + private ViewAnimator mButtonSwitcher; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle args = getArguments(); - byte[] data = args.getByteArray(EXTRA_ENCODED_LID); + byte[] data = args.getByteArray(ARG_ENCODED_LID); try { mLinkedId = LinkedIdentity.fromAttributeData(data); @@ -55,19 +71,24 @@ public class LinkedIdViewFragment extends Fragment { mLinkedResource = (LinkedCookieResource) res; } - mVerified = args.containsKey(EXTRA_VERIFIED) ? args.getInt(EXTRA_VERIFIED) : null; + mVerified = args.containsKey(ARG_VERIFIED) ? args.getInt(ARG_VERIFIED) : null; + mFingerprint = args.getByteArray(ARG_FINGERPRINT); mContext = getActivity(); + mInflater = getLayoutInflater(savedInstanceState); + } - public static Fragment newInstance(RawLinkedIdentity id, Integer isVerified) throws IOException { + public static Fragment newInstance(RawLinkedIdentity id, + Integer isVerified, byte[] fingerprint) throws IOException { LinkedIdViewFragment frag = new LinkedIdViewFragment(); Bundle args = new Bundle(); - args.putByteArray(EXTRA_ENCODED_LID, id.toUserAttribute().getEncoded()); + args.putByteArray(ARG_ENCODED_LID, id.toUserAttribute().getEncoded()); if (isVerified != null) { - args.putInt(EXTRA_VERIFIED, isVerified); + args.putInt(ARG_VERIFIED, isVerified); } + args.putByteArray(ARG_FINGERPRINT, fingerprint); frag.setArguments(args); return frag; @@ -78,6 +99,7 @@ public class LinkedIdViewFragment extends Fragment { View root = inflater.inflate(R.layout.linked_id_view_fragment, null); vLinkedIdsCard = (CardView) root.findViewById(R.id.card_linked_ids); + vLinkedCerts = (LinearLayout) root.findViewById(R.id.linked_id_certs); View back = root.findViewById(R.id.back_button); back.setClickable(true); @@ -136,6 +158,8 @@ public class LinkedIdViewFragment extends Fragment { button_view.setVisibility(View.GONE); } + mButtonSwitcher = (ViewAnimator) root.findViewById(R.id.button_animator); + View button_verify = root.findViewById(R.id.button_verify); button_verify.setOnClickListener(new OnClickListener() { @Override @@ -144,12 +168,88 @@ public class LinkedIdViewFragment extends Fragment { } }); + View button_retry = root.findViewById(R.id.button_retry); + button_retry.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + verifyResource(); + } + }); + + View button_confirm = root.findViewById(R.id.button_confirm); + button_confirm.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + Notify.createNotify(getActivity(), "confirmed!", Notify.LENGTH_LONG, Style.INFO).show(); + } + }); + return root; } + static class ViewHolderCert { + final ViewAnimator vProgress; + final ImageView vIcon; + final TextView vText; + + ViewHolderCert(View view) { + vProgress = (ViewAnimator) view.findViewById(R.id.linked_cert_progress); + vIcon = (ImageView) view.findViewById(R.id.linked_cert_icon); + vText = (TextView) view.findViewById(R.id.linked_cert_text); + } + void setShowProgress(boolean show) { + vProgress.setDisplayedChild(show ? 0 : 1); + } + } + public void verifyResource() { - // TODO + // only one at a time + synchronized (this) { + if (mInProgress) { + return; + } + mInProgress = true; + } + + // is there a current certification? if not create a new one + final ViewHolderCert holder; + if (mCurrentCert == null) { + mCurrentCert = mInflater.inflate(R.layout.linked_id_cert, null); + holder = new ViewHolderCert(mCurrentCert); + mCurrentCert.setTag(holder); + vLinkedCerts.addView(mCurrentCert); + } else { + holder = (ViewHolderCert) mCurrentCert.getTag(); + } + + holder.setShowProgress(true); + holder.vText.setText("Verifying…"); + + new AsyncTask() { + @Override + protected LinkedVerifyResult doInBackground(Void... params) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // nvm + } + return mLinkedResource.verify(mFingerprint, mLinkedId.mNonce); + } + + @Override + protected void onPostExecute(LinkedVerifyResult result) { + holder.setShowProgress(false); + if (result.success()) { + mButtonSwitcher.setDisplayedChild(2); + holder.vText.setText("Ok"); + } else { + mButtonSwitcher.setDisplayedChild(1); + holder.vText.setText("Error"); + } + mInProgress = false; + } + }.execute(); } -- cgit v1.2.3 From a3ac2738ea6140c6a181f9b685249ad2f766f904 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 6 Mar 2015 16:24:48 +0100 Subject: prepare superclass extraction for final linked id creation fragment --- .../keychain/pgp/linked/LinkedCookieResource.java | 2 +- .../keychain/pgp/linked/RawLinkedIdentity.java | 2 +- .../pgp/linked/resources/GenericHttpsResource.java | 4 +- .../linked/LinkedIdCreateHttpsStep2Fragment.java | 55 +++++++++++----------- .../keychain/ui/linked/LinkedIdViewFragment.java | 13 +++-- 5 files changed, 42 insertions(+), 34 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index 4b01d8135..84b79920a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java @@ -62,7 +62,7 @@ public abstract class LinkedCookieResource extends LinkedResource { } public static String generate (Context context, byte[] fingerprint, int nonce) { - return String.format("\"[Verifying my PGP key: openpgp4fpr:%s#%08x]\"", + return String.format("[Verifying my PGP key: openpgp4fpr:%s#%08x]", KeyFormattingUtils.convertFingerprintToHex(fingerprint), nonce); } 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 index 8b014db1d..8f0467734 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java @@ -44,7 +44,7 @@ public class RawLinkedIdentity { // return Hex.toHexString(data); // debug for now - return 1234567; + return 0x8a9bad32; } public @DrawableRes int getDisplayIcon() { 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 index 0ab90af75..eebe0b5ec 100644 --- 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 @@ -88,9 +88,9 @@ public class GenericHttpsResource extends LinkedCookieResource { } public static GenericHttpsResource createNew (URI uri) { - HashSet flags = new HashSet(); + HashSet flags = new HashSet<>(); flags.add("generic"); - HashMap params = new HashMap(); + HashMap params = new HashMap<>(); return create(flags, params, uri); } 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 7d2f3dfdb..6a0c1cac0 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 @@ -42,6 +42,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; import org.sufficientlysecure.keychain.pgp.linked.resources.GenericHttpsResource; @@ -73,12 +74,12 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment { View mVerifyProgress; TextView mVerifyStatus; - String mResourceUri; int mResourceNonce; + URI mResourceUri; String mResourceString; // This is a resource, set AFTER it has been verified - GenericHttpsResource mVerifiedResource = null; + LinkedCookieResource mVerifiedResource = null; /** * Creates new instance of this fragment @@ -97,13 +98,15 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment { return frag; } + GenericHttpsResource getResource() { + return GenericHttpsResource.createNew(mResourceUri); + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.linked_create_https_fragment_step2, container, false); - mResourceUri = getArguments().getString(URI); mResourceNonce = getArguments().getInt(NONCE); - mResourceString = getArguments().getString(TEXT); view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { @Override @@ -144,8 +147,10 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment { } }); + mResourceString = getArguments().getString(TEXT); + mEditUri = (EditText) view.findViewById(R.id.linked_create_https_uri); - mEditUri.setText(mResourceUri); + mEditUri.setText(mResourceUri.toString()); setVerifyProgress(false, null); mVerifyStatus.setText(R.string.linked_verify_pending); @@ -224,32 +229,28 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment { public void proofVerify() { setVerifyProgress(true, null); - try { - final GenericHttpsResource resource = GenericHttpsResource.createNew(new URI(mResourceUri)); + final LinkedCookieResource resource = getResource(); - new AsyncTask() { + new AsyncTask() { - @Override - protected LinkedVerifyResult doInBackground(Void... params) { - return resource.verify(mLinkedIdWizard.mFingerprint, mResourceNonce); - } + @Override + protected LinkedVerifyResult doInBackground(Void... params) { + return resource.verify(mLinkedIdWizard.mFingerprint, mResourceNonce); + } - @Override - protected void onPostExecute(LinkedVerifyResult result) { - super.onPostExecute(result); - if (result.success()) { - setVerifyProgress(false, true); - mVerifiedResource = resource; - } else { - setVerifyProgress(false, false); - // on error, show error message - result.createNotify(getActivity()).show(); - } + @Override + protected void onPostExecute(LinkedVerifyResult result) { + super.onPostExecute(result); + if (result.success()) { + setVerifyProgress(false, true); + mVerifiedResource = resource; + } else { + setVerifyProgress(false, false); + // on error, show error message + result.createNotify(getActivity()).show(); } - }.execute(); - } catch (URISyntaxException e) { - e.printStackTrace(); - } + } + }.execute(); } 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 5cf0ed115..fdc466f04 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 @@ -202,7 +202,14 @@ public class LinkedIdViewFragment extends Fragment { } } - public void verifyResource() { + void showButton(int which) { + if (mButtonSwitcher.getDisplayedChild() == which) { + return; + } + mButtonSwitcher.setDisplayedChild(which); + } + + void verifyResource() { // only one at a time synchronized (this) { @@ -241,10 +248,10 @@ public class LinkedIdViewFragment extends Fragment { protected void onPostExecute(LinkedVerifyResult result) { holder.setShowProgress(false); if (result.success()) { - mButtonSwitcher.setDisplayedChild(2); + showButton(2); holder.vText.setText("Ok"); } else { - mButtonSwitcher.setDisplayedChild(1); + showButton(1); holder.vText.setText("Error"); } mInProgress = false; -- cgit v1.2.3 From 088282483f57cf03d9ee75578cd9d3bf6a74a4d9 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 6 Mar 2015 16:30:36 +0100 Subject: extract linked id create final fragment superclass --- .../ui/linked/LinkedIdCreateFinalFragment.java | 249 +++++++++++++++++++++ .../linked/LinkedIdCreateHttpsStep2Fragment.java | 247 +++----------------- 2 files changed, 275 insertions(+), 221 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 new file mode 100644 index 000000000..b02d70b33 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateFinalFragment.java @@ -0,0 +1,249 @@ +package org.sufficientlysecure.keychain.ui.linked; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Intent; +import android.graphics.PorterDuff; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +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.ImageView; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; +import org.sufficientlysecure.keychain.ui.util.Notify; + +public abstract class LinkedIdCreateFinalFragment extends Fragment { + + public static final String ARG_NONCE = "nonce"; + protected static final int REQUEST_CODE_PASSPHRASE = 0x00007008; + + LinkedIdWizard mLinkedIdWizard; + + private ImageView mVerifyImage; + private View mVerifyProgress; + private TextView mVerifyStatus; + private int mResourceNonce; + + // This is a resource, set AFTER it has been verified + LinkedCookieResource mVerifiedResource = null; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + + mResourceNonce = getArguments().getInt(ARG_NONCE); + } + + protected abstract View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = newView(inflater, container, savedInstanceState); + + view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + startCertify(); + } + }); + + view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); + } + }); + + mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); + mVerifyProgress = view.findViewById(R.id.verify_progress); + mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); + + view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofVerify(); + } + }); + + setVerifyProgress(false, null); + mVerifyStatus.setText(R.string.linked_verify_pending); + + return view; + } + + + abstract LinkedCookieResource getResource(); + + private void setVerifyProgress(boolean on, Boolean success) { + mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); + mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); + if (success == null) { + mVerifyStatus.setText(R.string.linked_verifying); + mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout_24dp); + mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), + PorterDuff.Mode.SRC_IN); + } 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); + } 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); + } + } + + private void proofVerify() { + setVerifyProgress(true, null); + + final LinkedCookieResource resource = getResource(); + + new AsyncTask() { + + @Override + protected LinkedVerifyResult doInBackground(Void... params) { + return resource.verify(mLinkedIdWizard.mFingerprint, mResourceNonce); + } + + @Override + protected void onPostExecute(LinkedVerifyResult result) { + super.onPostExecute(result); + if (result.success()) { + setVerifyProgress(false, true); + mVerifiedResource = resource; + } else { + setVerifyProgress(false, false); + // on error, show error message + result.createNotify(getActivity()).show(); + } + } + }.execute(); + + } + + private void startCertify() { + + if (mVerifiedResource == null) { + Notify.showNotify(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR); + return; + } + + Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); + intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mLinkedIdWizard.mMasterKeyId); + startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); + + } + + private void certifyLinkedIdentity (String passphrase) { + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( + getActivity(), + getString(R.string.progress_saving), + ProgressDialog.STYLE_HORIZONTAL, + true) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + + // get returned data bundle + Bundle returnData = message.getData(); + if (returnData == null) { + return; + } + final OperationResult result = + returnData.getParcelable(OperationResult.EXTRA_RESULT); + if (result == null) { + return; + } + + // if bad -> display here! + if (!result.success()) { + result.createNotify(getActivity()).show(); + return; + } + + result.createNotify(getActivity()).show(); + + // if good -> finish, return result to showkey and display there! + // Intent intent = new Intent(); + // intent.putExtra(OperationResult.EXTRA_RESULT, result); + // getActivity().setResult(EditKeyActivity.RESULT_OK, intent); + + // AffirmationCreateHttpsStep3Fragment frag = + // AffirmationCreateHttpsStep3Fragment.newInstance( + // mResourceUri, mResourceNonce, mResourceString); + + // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + + } + } + }; + + SaveKeyringParcel skp = + new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint); + + WrappedUserAttribute ua = + LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); + + skp.mAddUserAttribute.add(ua); + + // Send all information needed to service to import key in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING); + + // fill values for this action + Bundle data = new Bundle(); + data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); + data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, skp); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + + } + + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case REQUEST_CODE_PASSPHRASE: + if (resultCode == Activity.RESULT_OK && data != null) { + String passphrase = + data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + certifyLinkedIdentity(passphrase); + } + break; + default: + super.onActivityResult(requestCode, resultCode, data); + } + } + +} 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 6a0c1cac0..adae7eaf5 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 @@ -17,39 +17,20 @@ package org.sufficientlysecure.keychain.ui.linked; -import android.app.Activity; -import android.app.ProgressDialog; import android.content.Intent; -import android.graphics.PorterDuff; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Environment; -import android.os.Message; -import android.os.Messenger; -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 android.widget.ImageView; -import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; -import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; -import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; -import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; import org.sufficientlysecure.keychain.pgp.linked.resources.GenericHttpsResource; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.service.SaveKeyringParcel; -import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.util.FileHelper; @@ -60,71 +41,59 @@ import java.io.PrintWriter; import java.net.URI; import java.net.URISyntaxException; -public class LinkedIdCreateHttpsStep2Fragment extends Fragment { +public class LinkedIdCreateHttpsStep2Fragment extends LinkedIdCreateFinalFragment { private static final int REQUEST_CODE_OUTPUT = 0x00007007; - private static final int REQUEST_CODE_PASSPHRASE = 0x00007008; - public static final String URI = "uri", NONCE = "nonce", TEXT = "text"; - - LinkedIdWizard mLinkedIdWizard; + public static final String ARG_URI = "uri", ARG_TEXT = "text"; EditText mEditUri; - ImageView mVerifyImage; - View mVerifyProgress; - TextView mVerifyStatus; - int mResourceNonce; URI mResourceUri; String mResourceString; - // This is a resource, set AFTER it has been verified - LinkedCookieResource mVerifiedResource = null; - - /** - * Creates new instance of this fragment - */ public static LinkedIdCreateHttpsStep2Fragment newInstance (String uri, int proofNonce, String proofText) { LinkedIdCreateHttpsStep2Fragment frag = new LinkedIdCreateHttpsStep2Fragment(); Bundle args = new Bundle(); - args.putString(URI, uri); - args.putInt(NONCE, proofNonce); - args.putString(TEXT, proofText); + args.putInt(ARG_NONCE, proofNonce); + args.putString(ARG_URI, uri); + args.putString(ARG_TEXT, proofText); frag.setArguments(args); return frag; } + @Override GenericHttpsResource getResource() { return GenericHttpsResource.createNew(mResourceUri); } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.linked_create_https_fragment_step2, container, false); + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - mResourceNonce = getArguments().getInt(NONCE); + try { + mResourceUri = new URI(getArguments().getString(ARG_URI)); + } catch (URISyntaxException e) { + e.printStackTrace(); + getActivity().finish(); + } - view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - startCertify(); - } - }); + mResourceString = getArguments().getString(ARG_TEXT); - view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); - } - }); + } + + protected View newView(LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.linked_create_https_fragment_step2, container, false); + } - mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); - mVerifyProgress = view.findViewById(R.id.verify_progress); - mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); + @Override + 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 @@ -140,52 +109,12 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment { } }); - view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofVerify(); - } - }); - - mResourceString = getArguments().getString(TEXT); - mEditUri = (EditText) view.findViewById(R.id.linked_create_https_uri); mEditUri.setText(mResourceUri.toString()); - setVerifyProgress(false, null); - mVerifyStatus.setText(R.string.linked_verify_pending); - return view; } - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mLinkedIdWizard = (LinkedIdWizard) getActivity(); - } - - public void setVerifyProgress(boolean on, Boolean success) { - mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); - mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); - if (success == null) { - mVerifyStatus.setText(R.string.linked_verifying); - mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout_24dp); - mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), - PorterDuff.Mode.SRC_IN); - } 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); - } 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); - } - } - private void proofSend () { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); @@ -226,124 +155,6 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment { } } - public void proofVerify() { - setVerifyProgress(true, null); - - final LinkedCookieResource resource = getResource(); - - new AsyncTask() { - - @Override - protected LinkedVerifyResult doInBackground(Void... params) { - return resource.verify(mLinkedIdWizard.mFingerprint, mResourceNonce); - } - - @Override - protected void onPostExecute(LinkedVerifyResult result) { - super.onPostExecute(result); - if (result.success()) { - setVerifyProgress(false, true); - mVerifiedResource = resource; - } else { - setVerifyProgress(false, false); - // on error, show error message - result.createNotify(getActivity()).show(); - } - } - }.execute(); - - } - - public void startCertify() { - - if (mVerifiedResource == null) { - Notify.showNotify(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR); - return; - } - - Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); - intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mLinkedIdWizard.mMasterKeyId); - startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); - - } - - public void certifyLinkedIdentity (String passphrase) { - KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( - getActivity(), - getString(R.string.progress_saving), - ProgressDialog.STYLE_HORIZONTAL, - true) { - public void handleMessage(Message message) { - // handle messages by standard KeychainIntentServiceHandler first - super.handleMessage(message); - - if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - - // get returned data bundle - Bundle returnData = message.getData(); - if (returnData == null) { - return; - } - final OperationResult result = - returnData.getParcelable(OperationResult.EXTRA_RESULT); - if (result == null) { - return; - } - - // if bad -> display here! - if (!result.success()) { - result.createNotify(getActivity()).show(); - return; - } - - result.createNotify(getActivity()).show(); - - // if good -> finish, return result to showkey and display there! - // Intent intent = new Intent(); - // intent.putExtra(OperationResult.EXTRA_RESULT, result); - // getActivity().setResult(EditKeyActivity.RESULT_OK, intent); - - // AffirmationCreateHttpsStep3Fragment frag = - // AffirmationCreateHttpsStep3Fragment.newInstance( - // mResourceUri, mResourceNonce, mResourceString); - - // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); - - } - } - }; - - SaveKeyringParcel skp = - new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint); - - WrappedUserAttribute ua = - LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); - - skp.mAddUserAttribute.add(ua); - - // Send all information needed to service to import key in other thread - Intent intent = new Intent(getActivity(), KeychainIntentService.class); - intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING); - - // fill values for this action - Bundle data = new Bundle(); - data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); - data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, skp); - intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(getActivity()); - - // start service with intent - getActivity().startService(intent); - - } - - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { @@ -355,15 +166,9 @@ public class LinkedIdCreateHttpsStep2Fragment extends Fragment { Uri uri = data.getData(); saveFile(uri); break; - case REQUEST_CODE_PASSPHRASE: - if (resultCode == Activity.RESULT_OK && data != null) { - String passphrase = - data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); - certifyLinkedIdentity(passphrase); - } - break; + default: + super.onActivityResult(requestCode, resultCode, data); } - super.onActivityResult(requestCode, resultCode, data); } } -- cgit v1.2.3 From 73ba19460b2eeb75566bb55df14bb5916d72b3b2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 6 Mar 2015 16:49:40 +0100 Subject: use final fragment superclass for dns --- .../ui/linked/LinkedIdCreateDnsStep2Fragment.java | 231 ++------------------- .../ui/linked/LinkedIdCreateFinalFragment.java | 2 +- 2 files changed, 19 insertions(+), 214 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 3b0860fa9..de3a63256 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 @@ -17,37 +17,21 @@ package org.sufficientlysecure.keychain.ui.linked; -import android.app.Activity; -import android.app.ProgressDialog; import android.content.Intent; -import android.graphics.PorterDuff; import android.net.Uri; -import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Environment; -import android.os.Message; -import android.os.Messenger; -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.ImageView; import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; -import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; -import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; -import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; import org.sufficientlysecure.keychain.pgp.linked.resources.DnsResource; -import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; -import org.sufficientlysecure.keychain.service.SaveKeyringParcel; -import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.util.FileHelper; @@ -56,38 +40,25 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.PrintWriter; -public class LinkedIdCreateDnsStep2Fragment extends Fragment { +public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment { private static final int REQUEST_CODE_OUTPUT = 0x00007007; - private static final int REQUEST_CODE_PASSPHRASE = 0x00007008; - public static final String DOMAIN = "domain", NONCE = "nonce", TEXT = "text"; - - LinkedIdWizard mLinkedIdWizard; + public static final String DOMAIN = "domain", TEXT = "text"; TextView mTextView; - ImageView mVerifyImage; - View mVerifyProgress; - TextView mVerifyStatus; String mResourceDomain; - int mResourceNonce; String mResourceString; - // This is a resource, set AFTER it has been verified - DnsResource mVerifiedResource = null; - - /** - * Creates new instance of this fragment - */ public static LinkedIdCreateDnsStep2Fragment newInstance (String uri, int proofNonce, String proofText) { LinkedIdCreateDnsStep2Fragment frag = new LinkedIdCreateDnsStep2Fragment(); Bundle args = new Bundle(); + args.putInt(ARG_NONCE, proofNonce); args.putString(DOMAIN, uri); - args.putInt(NONCE, proofNonce); args.putString(TEXT, proofText); frag.setArguments(args); @@ -95,30 +66,22 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment { } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.linked_create_dns_fragment_step2, container, false); + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); mResourceDomain = getArguments().getString(DOMAIN); - mResourceNonce = getArguments().getInt(NONCE); mResourceString = getArguments().getString(TEXT); - view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - startCertify(); - } - }); + } - view.findViewById(R.id.back_button).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); - } - }); + @Override + protected View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.linked_create_dns_fragment_step2, container, false); + } - mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); - mVerifyProgress = view.findViewById(R.id.verify_progress); - mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); + @Override + 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 @@ -134,48 +97,15 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment { } }); - view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofVerify(); - } - }); - mTextView = (TextView) view.findViewById(R.id.linked_create_dns_text); mTextView.setText(mResourceString); - setVerifyProgress(false, null); - mVerifyStatus.setText(R.string.linked_verify_pending); - return view; } @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mLinkedIdWizard = (LinkedIdWizard) getActivity(); - } - - public void setVerifyProgress(boolean on, Boolean success) { - mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); - mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); - if (success == null) { - mVerifyStatus.setText(R.string.linked_verifying); - mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout_24dp); - mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), - PorterDuff.Mode.SRC_IN); - } 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); - } 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); - } + LinkedCookieResource getResource() { + return DnsResource.createNew(mResourceDomain); } private void proofSend () { @@ -218,125 +148,6 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment { } } - public void proofVerify() { - setVerifyProgress(true, null); - - final DnsResource resource = DnsResource.createNew(mResourceDomain); - - new AsyncTask() { - - @Override - protected LinkedVerifyResult doInBackground(Void... params) { - return resource.verify(mLinkedIdWizard.mFingerprint, mResourceNonce); - } - - @Override - protected void onPostExecute(LinkedVerifyResult result) { - super.onPostExecute(result); - if (result.success()) { - setVerifyProgress(false, true); - mVerifiedResource = resource; - } else { - setVerifyProgress(false, false); - mVerifiedResource = resource; - // on error, show error message - result.createNotify(getActivity()).show(); - } - } - }.execute(); - - } - - public void startCertify() { - - if (mVerifiedResource == null) { - Notify.showNotify(getActivity(), R.string.linked_need_verify, Style.ERROR); - return; - } - - Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); - intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mLinkedIdWizard.mMasterKeyId); - startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); - - } - - public void certifyLinkedIdentity (String passphrase) { - KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( - getActivity(), - getString(R.string.progress_saving), - ProgressDialog.STYLE_HORIZONTAL, - true) { - public void handleMessage(Message message) { - // handle messages by standard KeychainIntentServiceHandler first - super.handleMessage(message); - - if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - - // get returned data bundle - Bundle returnData = message.getData(); - if (returnData == null) { - return; - } - final OperationResult result = - returnData.getParcelable(OperationResult.EXTRA_RESULT); - if (result == null) { - return; - } - - // if bad -> display here! - if (!result.success()) { - result.createNotify(getActivity()).show(); - return; - } - - result.createNotify(getActivity()).show(); - - // if good -> finish, return result to showkey and display there! - // Intent intent = new Intent(); - // intent.putExtra(OperationResult.EXTRA_RESULT, result); - // getActivity().setResult(EditKeyActivity.RESULT_OK, intent); - - // AffirmationCreateHttpsStep3Fragment frag = - // AffirmationCreateHttpsStep3Fragment.newInstance( - // mResourceDomain, mResourceNonce, mResourceString); - - // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); - - } - } - }; - - SaveKeyringParcel skp = - new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint); - - WrappedUserAttribute ua = - LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); - - skp.mAddUserAttribute.add(ua); - - // Send all information needed to service to import key in other thread - Intent intent = new Intent(getActivity(), KeychainIntentService.class); - intent.setAction(KeychainIntentService.ACTION_EDIT_KEYRING); - - // fill values for this action - Bundle data = new Bundle(); - data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); - data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, skp); - intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - - // Create a new Messenger for the communication back - Messenger messenger = new Messenger(saveHandler); - intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - - // show progress dialog - saveHandler.showProgressDialog(getActivity()); - - // start service with intent - getActivity().startService(intent); - - } - - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { @@ -348,15 +159,9 @@ public class LinkedIdCreateDnsStep2Fragment extends Fragment { Uri uri = data.getData(); saveFile(uri); break; - case REQUEST_CODE_PASSPHRASE: - if (resultCode == Activity.RESULT_OK && data != null) { - String passphrase = - data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); - certifyLinkedIdentity(passphrase); - } - break; + default: + super.onActivityResult(requestCode, resultCode, data); } - super.onActivityResult(requestCode, resultCode, data); } } 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 b02d70b33..c8c0c1239 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 @@ -33,7 +33,7 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { public static final String ARG_NONCE = "nonce"; protected static final int REQUEST_CODE_PASSPHRASE = 0x00007008; - LinkedIdWizard mLinkedIdWizard; + private LinkedIdWizard mLinkedIdWizard; private ImageView mVerifyImage; private View mVerifyProgress; -- cgit v1.2.3 From 5840e9d2b68c44af706b7162fc60db999bf85958 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 6 Mar 2015 16:54:54 +0100 Subject: use final fragment superclass for twitter --- .../linked/LinkedIdCreateTwitterStep3Fragment.java | 121 ++++----------------- 1 file changed, 23 insertions(+), 98 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java index d298e8251..0c317004b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java @@ -32,67 +32,54 @@ import android.widget.ImageView; import android.widget.TextView; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; import org.sufficientlysecure.keychain.ui.util.Notify; import java.util.List; -public class LinkedIdCreateTwitterStep3Fragment extends Fragment { +public class LinkedIdCreateTwitterStep3Fragment extends LinkedIdCreateFinalFragment { - public static final String HANDLE = "uri", NONCE = "nonce", TEXT = "text", CUSTOM = "custom"; - - LinkedIdWizard mLinkedIdWizard; + public static final String ARG_HANDLE = "uri", ARG_TEXT = "text", ARG_CUSTOM = "custom"; EditText mEditTweetPreview; - ImageView mVerifyImage; - View mVerifyProgress; - TextView mVerifyStatus; String mResourceHandle, mCustom, mFullString; - String mResourceNonce, mResourceString; + String mResourceString; - /** - * Creates new instance of this fragment - */ public static LinkedIdCreateTwitterStep3Fragment newInstance (String handle, String proofNonce, String proofText, String customText) { LinkedIdCreateTwitterStep3Fragment frag = new LinkedIdCreateTwitterStep3Fragment(); Bundle args = new Bundle(); - args.putString(HANDLE, handle); - args.putString(NONCE, proofNonce); - args.putString(TEXT, proofText); - args.putString(CUSTOM, customText); + args.putString(ARG_HANDLE, handle); + args.putString(ARG_NONCE, proofNonce); + args.putString(ARG_TEXT, proofText); + args.putString(ARG_CUSTOM, customText); frag.setArguments(args); return frag; } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.linked_create_twitter_fragment_step3, container, false); + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - mResourceHandle = getArguments().getString(HANDLE); - mResourceNonce = getArguments().getString(NONCE); - mResourceString = getArguments().getString(TEXT); - mCustom = getArguments().getString(CUSTOM); + mResourceHandle = getArguments().getString(ARG_HANDLE); + mResourceString = getArguments().getString(ARG_TEXT); + mCustom = getArguments().getString(ARG_CUSTOM); mFullString = mCustom.isEmpty() ? mResourceString : (mCustom + " " + mResourceString); - mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); - mVerifyProgress = view.findViewById(R.id.verify_progress); - mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = super.onCreateView(inflater, container, savedInstanceState); mEditTweetPreview = (EditText) view.findViewById(R.id.linked_create_twitter_preview); mEditTweetPreview.setText(mFullString); - view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); - } - }); - view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { @@ -107,17 +94,6 @@ public class LinkedIdCreateTwitterStep3Fragment extends Fragment { } }); - view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - proofVerify(); - } - }); - - setVerifyProgress(false, null); - mVerifyStatus.setText(R.string.linked_verify_pending); - - view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { @@ -133,64 +109,13 @@ public class LinkedIdCreateTwitterStep3Fragment extends Fragment { } @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mLinkedIdWizard = (LinkedIdWizard) getActivity(); + LinkedCookieResource getResource() { + return null; } - public void setVerifyProgress(boolean on, Boolean success) { - mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); - mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); - if (success == null) { - mVerifyStatus.setText(R.string.linked_verifying); - mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout_24dp); - mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), - PorterDuff.Mode.SRC_IN); - } 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); - } 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); - } - } - - public void proofVerify() { - setVerifyProgress(true, null); - - /* - try { - final TwitterResource resource = TwitterResource.createNew(new URI(mResourceHandle)); - - new AsyncTask() { - - @Override - protected LinkedVerifyResult doInBackground(Void... params) { - return resource.verify(mAffirmationWizard.mFingerprint, mResourceNonce); - } - - @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(); - } - } - }.execute(); - } catch (URISyntaxException e) { - e.printStackTrace(); - } - */ - + @Override + protected View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.linked_create_twitter_fragment_step3, container, false); } private void proofShare() { -- cgit v1.2.3 From 9e930f0a10189bae12a70299aef07ae85221aadd Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 6 Mar 2015 16:57:17 +0100 Subject: finish after linked id creation --- .../keychain/ui/linked/LinkedIdCreateFinalFragment.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 c8c0c1239..ef76bc9d2 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 @@ -183,18 +183,7 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { return; } - result.createNotify(getActivity()).show(); - - // if good -> finish, return result to showkey and display there! - // Intent intent = new Intent(); - // intent.putExtra(OperationResult.EXTRA_RESULT, result); - // getActivity().setResult(EditKeyActivity.RESULT_OK, intent); - - // AffirmationCreateHttpsStep3Fragment frag = - // AffirmationCreateHttpsStep3Fragment.newInstance( - // mResourceUri, mResourceNonce, mResourceString); - - // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); + getActivity().finish(); } } -- cgit v1.2.3 From 581cad5b3f7b54fae652e2598b4e760eb19678d2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 8 Mar 2015 00:59:16 +0100 Subject: work on certification step --- .../keychain/ui/linked/LinkedIdViewFragment.java | 96 ++++++++++++++++++++-- 1 file changed, 90 insertions(+), 6 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 fdc466f04..7f8f4da04 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 @@ -2,10 +2,14 @@ package org.sufficientlysecure.keychain.ui.linked; import java.io.IOException; +import android.app.Activity; +import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; import android.support.v4.app.Fragment; import android.support.v7.widget.CardView; import android.view.LayoutInflater; @@ -17,18 +21,26 @@ import android.widget.LinearLayout; import android.widget.TextView; import android.widget.ViewAnimator; +import org.spongycastle.util.encoders.Hex; 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.provider.KeychainContract.Certs; +import org.sufficientlysecure.keychain.service.CertifyActionsParcel; +import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter.ViewHolder; 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.widget.CertifyKeySpinner; +import org.sufficientlysecure.keychain.util.Preferences; public class LinkedIdViewFragment extends Fragment { @@ -36,6 +48,7 @@ public class LinkedIdViewFragment extends Fragment { private static final String ARG_ENCODED_LID = "encoded_lid"; private static final String ARG_VERIFIED = "verified"; private static final String ARG_FINGERPRINT = "fingerprint"; + private static final String ARG_MASTER_KEY_ID = "fingerprint"; private RawLinkedIdentity mLinkedId; private LinkedCookieResource mLinkedResource; @@ -50,6 +63,7 @@ public class LinkedIdViewFragment extends Fragment { private View mCurrentCert; private boolean mInProgress; private ViewAnimator mButtonSwitcher; + private CertifyKeySpinner vKeySpinner; @Override public void onCreate(Bundle savedInstanceState) { @@ -100,6 +114,7 @@ public class LinkedIdViewFragment extends Fragment { vLinkedIdsCard = (CardView) root.findViewById(R.id.card_linked_ids); vLinkedCerts = (LinearLayout) root.findViewById(R.id.linked_id_certs); + vKeySpinner = (CertifyKeySpinner) root.findViewById(R.id.cert_key_spinner); View back = root.findViewById(R.id.back_button); back.setClickable(true); @@ -180,7 +195,7 @@ public class LinkedIdViewFragment extends Fragment { button_confirm.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - Notify.createNotify(getActivity(), "confirmed!", Notify.LENGTH_LONG, Style.INFO).show(); + certifyResource(); } }); @@ -230,26 +245,33 @@ public class LinkedIdViewFragment extends Fragment { holder = (ViewHolderCert) mCurrentCert.getTag(); } + vKeySpinner.setVisibility(View.GONE); holder.setShowProgress(true); holder.vText.setText("Verifying…"); new AsyncTask() { @Override protected LinkedVerifyResult doInBackground(Void... params) { - try { - Thread.sleep(1000); + long timer = System.currentTimeMillis(); + LinkedVerifyResult result = mLinkedResource.verify(mFingerprint, mLinkedId.mNonce); + + // 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) { - // nvm + // never mind } - return mLinkedResource.verify(mFingerprint, mLinkedId.mNonce); + + return result; } @Override protected void onPostExecute(LinkedVerifyResult result) { holder.setShowProgress(false); if (result.success()) { - showButton(2); holder.vText.setText("Ok"); + setupForConfirmation(); } else { showButton(1); holder.vText.setText("Error"); @@ -260,4 +282,66 @@ public class LinkedIdViewFragment extends Fragment { } + void setupForConfirmation() { + + // button is 'confirm' + showButton(2); + + vKeySpinner.setVisibility(View.VISIBLE); + + } + + private void certifyResource() { + + Bundle data = new Bundle(); + { + CertifyAction action = new CertifyAction(); + + // fill values for this action + CertifyActionsParcel parcel = new CertifyActionsParcel(vKeySpinner.getSelectedKeyId()); + parcel.mCertifyActions.addAll(certifyActions); + + data.putParcelable(KeychainIntentService.CERTIFY_PARCEL, parcel); + /* if (mUploadKeyCheckbox.isChecked()) { + String keyserver = Preferences.getPreferences(getActivity()).getPreferredKeyserver(); + data.putString(KeychainIntentService.UPLOAD_KEY_SERVER, keyserver); + } */ + } + + // Send all information needed to service to sign key in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_CERTIFY_KEYRING); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); + + // Message is received after signing is done in KeychainIntentService + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), + getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER, true) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { + Bundle data = message.getData(); + CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT); + + Intent intent = new Intent(); + intent.putExtra(CertifyResult.EXTRA_RESULT, result); + getActivity().setResult(Activity.RESULT_OK, intent); + getActivity().finish(); + } + } + }; + + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(saveHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); + + // show progress dialog + saveHandler.showProgressDialog(getActivity()); + + // start service with intent + getActivity().startService(intent); + + } + } -- cgit v1.2.3 From ea7068acdfdcbe78412a42f88a731a81753808ed Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 8 Mar 2015 03:12:26 +0100 Subject: minor changes, add convertFingerprintToKeyId method --- .../keychain/service/CertifyActionsParcel.java | 15 +++++---------- .../keychain/ui/adapter/MultiUserIdsAdapter.java | 2 +- .../keychain/ui/util/KeyFormattingUtils.java | 5 +++++ 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java index f4b941109..9730cd74d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java @@ -23,6 +23,7 @@ import android.os.Parcelable; import java.io.Serializable; import java.util.ArrayList; +import java.util.List; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; @@ -81,17 +82,11 @@ public class CertifyActionsParcel implements Parcelable { final public ArrayList mUserIds; final public ArrayList mUserAttributes; - public CertifyAction(long masterKeyId, ArrayList userIds) { + public CertifyAction(long masterKeyId, List userIds, + List attributes) { mMasterKeyId = masterKeyId; - mUserIds = userIds; - mUserAttributes = null; - } - - public CertifyAction(long masterKeyId, ArrayList userIds, - ArrayList attributes) { - mMasterKeyId = masterKeyId; - mUserIds = userIds; - mUserAttributes = attributes; + mUserIds = new ArrayList<>(userIds); + mUserAttributes = new ArrayList<>(attributes); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java index 015775669..095cb0586 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/MultiUserIdsAdapter.java @@ -164,7 +164,7 @@ public class MultiUserIdsAdapter extends CursorAdapter { CertifyAction action = actions.get(keyId); if (actions.get(keyId) == null) { - actions.put(keyId, new CertifyAction(keyId, uids)); + actions.put(keyId, new CertifyAction(keyId, uids, null)); } else { action.mUserIds.addAll(uids); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java index c5403e054..57a71b084 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java @@ -38,6 +38,7 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Algorithm; import org.sufficientlysecure.keychain.service.SaveKeyringParcel.Curve; import org.sufficientlysecure.keychain.util.Log; +import java.nio.ByteBuffer; import java.security.DigestException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -249,6 +250,10 @@ public class KeyFormattingUtils { return hexString; } + public static long convertFingerprintToKeyId(byte[] fingerprint) { + return ByteBuffer.wrap(fingerprint, 12, 8).getLong(); + } + /** * Makes a human-readable version of a key ID, which is usually 64 bits: lower-case, no * leading 0x, space-separated quartets (for keys whose length in hex is divisible by 4) -- cgit v1.2.3 From b57bcefe080a0070a65efb6e88d76af1eface52d Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 8 Mar 2015 04:41:41 +0100 Subject: return key ids from PassphraseDialogActivity, accept null userids --- .../org/sufficientlysecure/keychain/service/CertifyActionsParcel.java | 4 ++-- .../java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java | 1 - .../org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java | 3 +++ .../java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java | 4 ---- 4 files changed, 5 insertions(+), 7 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java index 9730cd74d..57a11de21 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/CertifyActionsParcel.java @@ -85,8 +85,8 @@ public class CertifyActionsParcel implements Parcelable { public CertifyAction(long masterKeyId, List userIds, List attributes) { mMasterKeyId = masterKeyId; - mUserIds = new ArrayList<>(userIds); - mUserAttributes = new ArrayList<>(attributes); + mUserIds = userIds == null ? null : new ArrayList<>(userIds); + mUserAttributes = attributes == null ? null : new ArrayList<>(attributes); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java index e1467ec61..6ac7630bf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java @@ -31,7 +31,6 @@ import android.os.Bundle; import android.os.Message; import android.os.Messenger; import android.os.Parcel; -import android.os.Parcelable; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java index 29621662f..b06cf0abd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -63,6 +63,7 @@ import org.sufficientlysecure.keychain.util.Preferences; */ public class PassphraseDialogActivity extends FragmentActivity { public static final String MESSAGE_DATA_PASSPHRASE = "passphrase"; + public static final String EXTRA_KEY_ID = "key_id"; public static final String EXTRA_SUBKEY_ID = "secret_key_id"; @@ -413,6 +414,8 @@ public class PassphraseDialogActivity extends FragmentActivity { // also return passphrase back to activity Intent returnIntent = new Intent(); returnIntent.putExtra(MESSAGE_DATA_PASSPHRASE, passphrase); + returnIntent.putExtra(EXTRA_KEY_ID, mSecretRing.getMasterKeyId()); + returnIntent.putExtra(EXTRA_SUBKEY_ID, mSubKeyId); getActivity().setResult(RESULT_OK, returnIntent); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java index c8eceea50..b4890ac4e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java @@ -128,10 +128,6 @@ public abstract class KeySpinner extends TintSpinner implements LoaderManager.Lo } } - public long getSelectedKeyId() { - return mSelectedKeyId; - } - public void setSelectedKeyId(long selectedKeyId) { this.mSelectedKeyId = selectedKeyId; } -- cgit v1.2.3 From 99eb8725e92dde56511eafe2077e227cf9795f29 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 8 Mar 2015 04:42:19 +0100 Subject: successfully certify linked ids --- .../keychain/ui/ViewKeyActivity.java | 25 +++++--- .../keychain/ui/ViewKeyFragment.java | 2 +- .../keychain/ui/linked/LinkedIdViewFragment.java | 73 ++++++++++++++++++---- .../keychain/ui/widget/KeySpinner.java | 1 + 4 files changed, 80 insertions(+), 21 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index c936fb6cc..cbb2737b6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -41,6 +41,7 @@ import android.os.Messenger; import android.provider.ContactsContract; import android.provider.Settings; import android.support.v4.app.ActivityCompat; +import android.support.v4.app.FragmentManager; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; @@ -999,15 +1000,21 @@ public class ViewKeyActivity extends BaseActivity implements private void startFragment(boolean isSecret, byte[] fingerprint) { // Create an instance of the fragment - ViewKeyFragment frag = ViewKeyFragment.newInstance(mDataUri, isSecret, fingerprint); - - // Add the fragment to the 'fragment_container' FrameLayout - // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! - getSupportFragmentManager().beginTransaction() - .replace(R.id.view_key_fragment, frag, "main") - .commitAllowingStateLoss(); - // do it immediately! - getSupportFragmentManager().executePendingTransactions(); + final ViewKeyFragment frag = ViewKeyFragment.newInstance(mDataUri, isSecret, fingerprint); + + new Handler().post(new Runnable() { + @Override + public void run() { + + FragmentManager manager = getSupportFragmentManager(); + manager.popBackStack("linked_id", FragmentManager.POP_BACK_STACK_INCLUSIVE); + // Add the fragment to the 'fragment_container' FrameLayout + // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! + manager.beginTransaction() + .replace(R.id.view_key_fragment, frag, "main") + .commit(); + } + }); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index 240dd3547..c0d4a376b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -148,7 +148,7 @@ public class ViewKeyFragment extends LoaderFragment implements getFragmentManager().beginTransaction() .replace(R.id.view_key_fragment, frag) .addSharedElement(mLinkedIdsCard, "card_linked_ids") - .addToBackStack(null) + .addToBackStack("linked_id") .commit(); } 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 7f8f4da04..6ad7fa443 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 @@ -1,6 +1,7 @@ package org.sufficientlysecure.keychain.ui.linked; import java.io.IOException; +import java.util.Arrays; import android.app.Activity; import android.app.ProgressDialog; @@ -22,9 +23,11 @@ import android.widget.TextView; import android.widget.ViewAnimator; import org.spongycastle.util.encoders.Hex; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.CertifyResult; import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; import org.sufficientlysecure.keychain.pgp.linked.LinkedResource; @@ -34,21 +37,25 @@ import org.sufficientlysecure.keychain.service.CertifyActionsParcel; import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.service.PassphraseCacheService; +import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter.ViewHolder; 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.widget.CertifyKeySpinner; +import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Preferences; public class LinkedIdViewFragment extends Fragment { + public static final int REQUEST_CODE_PASSPHRASE = 0x00008001; + private static final String ARG_ENCODED_LID = "encoded_lid"; private static final String ARG_VERIFIED = "verified"; private static final String ARG_FINGERPRINT = "fingerprint"; - private static final String ARG_MASTER_KEY_ID = "fingerprint"; private RawLinkedIdentity mLinkedId; private LinkedCookieResource mLinkedResource; @@ -195,7 +202,7 @@ public class LinkedIdViewFragment extends Fragment { button_confirm.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - certifyResource(); + initiateCertifying(); } }); @@ -291,15 +298,62 @@ public class LinkedIdViewFragment extends Fragment { } - private void certifyResource() { + private void initiateCertifying() { + // get the user's passphrase for this key (if required) + String passphrase; + long certifyKeyId = vKeySpinner.getSelectedItemId(); + try { + passphrase = PassphraseCacheService.getCachedPassphrase( + getActivity(), certifyKeyId, certifyKeyId); + } catch (PassphraseCacheService.KeyNotFoundException e) { + Log.e(Constants.TAG, "Key not found!", e); + getActivity().finish(); + return; + } + if (passphrase == null) { + Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); + intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, certifyKeyId); + startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); + // bail out; need to wait until the user has entered the passphrase before trying again + } else { + certifyResource(certifyKeyId, ""); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + switch (requestCode) { + case REQUEST_CODE_PASSPHRASE: { + if (resultCode == Activity.RESULT_OK && data != null) { + String passphrase = data.getStringExtra( + PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + long certifyKeyId = data.getLongExtra(PassphraseDialogActivity.EXTRA_KEY_ID, 0L); + if (certifyKeyId == 0L) { + throw new AssertionError("key id must not be 0"); + } + certifyResource(certifyKeyId, passphrase); + } + return; + } + + default: { + super.onActivityResult(requestCode, resultCode, data); + } + } + } + + private void certifyResource(long certifyKeyId, String passphrase) { Bundle data = new Bundle(); { - CertifyAction action = new CertifyAction(); + + long masterKeyId = KeyFormattingUtils.convertFingerprintToKeyId(mFingerprint); + CertifyAction action = new CertifyAction(masterKeyId, null, + Arrays.asList(mLinkedId.toUserAttribute())); // fill values for this action - CertifyActionsParcel parcel = new CertifyActionsParcel(vKeySpinner.getSelectedKeyId()); - parcel.mCertifyActions.addAll(certifyActions); + CertifyActionsParcel parcel = new CertifyActionsParcel(certifyKeyId); + parcel.mCertifyActions.addAll(Arrays.asList(action)); data.putParcelable(KeychainIntentService.CERTIFY_PARCEL, parcel); /* if (mUploadKeyCheckbox.isChecked()) { @@ -315,7 +369,7 @@ public class LinkedIdViewFragment extends Fragment { // Message is received after signing is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), - getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER, true) { + getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER, false) { public void handleMessage(Message message) { // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); @@ -324,10 +378,7 @@ public class LinkedIdViewFragment extends Fragment { Bundle data = message.getData(); CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT); - Intent intent = new Intent(); - intent.putExtra(CertifyResult.EXTRA_RESULT, result); - getActivity().setResult(Activity.RESULT_OK, intent); - getActivity().finish(); + result.createNotify(getActivity()).show(); } } }; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java index b4890ac4e..364712a48 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java @@ -269,6 +269,7 @@ public abstract class KeySpinner extends TintSpinner implements LoaderManager.Lo } return view; } + } boolean setStatus(Context context, Cursor cursor, ImageView statusView) { -- cgit v1.2.3 From 08fd2a2de3795cf8fc4be590ba993c356c8eb67f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 9 Mar 2015 16:30:13 +0100 Subject: use loader in LinkedIdViewFragment --- .../keychain/provider/KeychainContract.java | 10 +- .../keychain/provider/KeychainProvider.java | 7 + .../keychain/ui/ViewKeyActivity.java | 4 +- .../keychain/ui/ViewKeyFragment.java | 2 +- .../keychain/ui/adapter/LinkedIdsAdapter.java | 19 +- .../keychain/ui/adapter/LinkedIdsCertAdapter.java | 57 +++++ .../keychain/ui/adapter/UserAttributesAdapter.java | 16 +- .../keychain/ui/linked/LinkedIdViewFragment.java | 262 ++++++++++++++------- 8 files changed, 267 insertions(+), 110 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsCertAdapter.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index a7ca613d7..08f3b5248 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -266,6 +266,7 @@ public class KeychainContract { public static Uri buildLinkedIdsUri(Uri uri) { return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_LINKED_IDS).build(); } + } public static class ApiApps implements ApiAppsColumns, BaseColumns { @@ -354,7 +355,14 @@ public class KeychainContract { } public static Uri buildCertsUri(Uri uri) { - return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)).appendPath(PATH_CERTS).build(); + return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)) + .appendPath(PATH_CERTS).build(); + } + + public static Uri buildLinkedIdCertsUri(long masterKeyId, int rank) { + return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)) + .appendPath(PATH_LINKED_IDS).appendPath(Integer.toString(rank)) + .appendPath(PATH_CERTS).build(); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index ac69ddd24..0f42b3934 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -63,6 +63,7 @@ public class KeychainProvider extends ContentProvider { private static final int KEY_RING_CERTS = 205; private static final int KEY_RING_CERTS_SPECIFIC = 206; private static final int KEY_RING_LINKED_IDS = 207; + private static final int KEY_RING_LINKED_ID_CERTS = 208; private static final int API_APPS = 301; private static final int API_APPS_BY_PACKAGE_NAME = 302; @@ -129,6 +130,8 @@ public class KeychainProvider extends ContentProvider { * key_rings/_/keys * key_rings/_/user_ids * key_rings/_/linked_ids + * key_rings/_/linked_ids/_ + * key_rings/_/linked_ids/_/certs * key_rings/_/public * key_rings/_/secret * key_rings/_/certs @@ -147,6 +150,10 @@ public class KeychainProvider extends ContentProvider { matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/" + KeychainContract.PATH_LINKED_IDS, KEY_RING_LINKED_IDS); + matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/" + + KeychainContract.PATH_LINKED_IDS + "/*/" + + KeychainContract.PATH_CERTS, + KEY_RING_LINKED_ID_CERTS); matcher.addURI(authority, KeychainContract.BASE_KEY_RINGS + "/*/" + KeychainContract.PATH_PUBLIC, KEY_RING_PUBLIC); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index cbb2737b6..59e0efbd0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -839,7 +839,9 @@ public class ViewKeyActivity extends BaseActivity implements mIsExpired = data.getInt(INDEX_IS_EXPIRED) != 0; mIsVerified = data.getInt(INDEX_VERIFIED) > 0; - startFragment(mIsSecret, fpData); + if (oldFingerprint == null) { + startFragment(mIsSecret, fpData); + } // get name, email, and comment from USER_ID String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID)); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index c0d4a376b..a7dc63122 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -131,7 +131,7 @@ public class ViewKeyFragment extends LoaderFragment implements private void showLinkedId(final int position) { Fragment frag; try { - frag = mLinkedIdsAdapter.getLinkedIdFragment(position, mFingerprint); + frag = mLinkedIdsAdapter.getLinkedIdFragment(mDataUri, position, mFingerprint); } catch (IOException e) { e.printStackTrace(); return; 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 365b8d265..ce4291101 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 @@ -35,6 +35,7 @@ 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.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment; @@ -125,19 +126,15 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { UserIdsAdapter.USER_PACKETS_PROJECTION, LINKED_IDS_WHERE, null, null); } - public Fragment getLinkedIdFragment(int position, byte[] fingerprint) throws IOException { - RawLinkedIdentity id = getItem(position); + public Fragment getLinkedIdFragment(Uri baseUri, + int position, byte[] fingerprint) throws IOException { - Integer isVerified; - if (mShowCertification) { - Cursor cursor = getCursor(); - cursor.moveToPosition(position); - isVerified = cursor.getInt(INDEX_VERIFIED); - } else { - isVerified = null; - } + Cursor c = getCursor(); + c.moveToPosition(position); + int rank = c.getInt(UserIdsAdapter.INDEX_RANK); - return LinkedIdViewFragment.newInstance(id, isVerified, fingerprint); + Uri dataUri = UserPackets.buildLinkedIdsUri(baseUri); + return LinkedIdViewFragment.newInstance(dataUri, rank, mShowCertification, fingerprint); } public static class ViewHolder { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsCertAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsCertAdapter.java new file mode 100644 index 000000000..5ecd9f408 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/LinkedIdsCertAdapter.java @@ -0,0 +1,57 @@ +package org.sufficientlysecure.keychain.ui.adapter; + + +import android.app.Activity; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.support.v4.content.CursorLoader; +import android.view.View; +import android.view.ViewGroup; +import android.widget.CursorAdapter; + +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; + + +public class LinkedIdsCertAdapter extends CursorAdapter { + + public static final String[] USER_CERTS_PROJECTION = new String[]{ + UserPackets._ID, + UserPackets.TYPE, + UserPackets.USER_ID, + UserPackets.ATTRIBUTE_DATA, + UserPackets.RANK, + UserPackets.VERIFIED, + UserPackets.IS_PRIMARY, + UserPackets.IS_REVOKED + }; + protected static final int INDEX_ID = 0; + protected static final int INDEX_TYPE = 1; + protected static final int INDEX_USER_ID = 2; + protected static final int INDEX_ATTRIBUTE_DATA = 3; + protected static final int INDEX_RANK = 4; + protected static final int INDEX_VERIFIED = 5; + protected static final int INDEX_IS_PRIMARY = 6; + protected static final int INDEX_IS_REVOKED = 7; + + public LinkedIdsCertAdapter(Context context, Cursor c, int flags) { + super(context, c, flags); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return null; + } + + public static CursorLoader createLoader(Activity activity, Uri dataUri) { + Uri baseUri = UserPackets.buildLinkedIdsUri(dataUri); + return new CursorLoader(activity, baseUri, + UserIdsAdapter.USER_PACKETS_PROJECTION, null, null, null); + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java index 345ccccc0..e0abaf4b0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserAttributesAdapter.java @@ -18,14 +18,14 @@ public abstract class UserAttributesAdapter extends CursorAdapter { UserPackets.IS_PRIMARY, UserPackets.IS_REVOKED }; - protected static final int INDEX_ID = 0; - protected static final int INDEX_TYPE = 1; - protected static final int INDEX_USER_ID = 2; - protected static final int INDEX_ATTRIBUTE_DATA = 3; - protected static final int INDEX_RANK = 4; - protected static final int INDEX_VERIFIED = 5; - protected static final int INDEX_IS_PRIMARY = 6; - protected static final int INDEX_IS_REVOKED = 7; + public static final int INDEX_ID = 0; + public static final int INDEX_TYPE = 1; + public static final int INDEX_USER_ID = 2; + public static final int INDEX_ATTRIBUTE_DATA = 3; + public static final int INDEX_RANK = 4; + public static final int INDEX_VERIFIED = 5; + public static final int INDEX_IS_PRIMARY = 6; + public static final int INDEX_IS_REVOKED = 7; public UserAttributesAdapter(Context context, Cursor c, int flags) { super(context, c, flags); 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 6ad7fa443..fcb9d7bb4 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 @@ -7,12 +7,16 @@ import android.app.Activity; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Message; import android.os.Messenger; import android.support.v4.app.Fragment; -import android.support.v7.widget.CardView; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -22,151 +26,183 @@ import android.widget.LinearLayout; import android.widget.TextView; import android.widget.ViewAnimator; -import org.spongycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.CertifyResult; import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; -import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; 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.provider.KeychainContract.Certs; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; +import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.service.CertifyActionsParcel; import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; -import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter.ViewHolder; +import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter; +import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsCertAdapter; +import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; 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.widget.CertifyKeySpinner; import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.Preferences; -public class LinkedIdViewFragment extends Fragment { +public class LinkedIdViewFragment extends Fragment implements + LoaderManager.LoaderCallbacks { public static final int REQUEST_CODE_PASSPHRASE = 0x00008001; - private static final String ARG_ENCODED_LID = "encoded_lid"; - private static final String ARG_VERIFIED = "verified"; + private static final String ARG_DATA_URI = "data_uri"; + private static final String ARG_LID_RANK = "rank"; + private static final String ARG_SHOWCERT = "verified"; private static final String ARG_FINGERPRINT = "fingerprint"; + private static final int LOADER_ID_LINKED_CERTS = 1; + private static final int LOADER_ID_LINKED_ID = 2; private RawLinkedIdentity mLinkedId; private LinkedCookieResource mLinkedResource; - private Integer mVerified; + private boolean mShowCert; - private CardView vLinkedIdsCard; private Context mContext; private byte[] mFingerprint; private LayoutInflater mInflater; - private LinearLayout vLinkedCerts; - private View mCurrentCert; private boolean mInProgress; - private ViewAnimator mButtonSwitcher; - private CertifyKeySpinner vKeySpinner; + + private LinkedIdsCertAdapter mCertAdapter; + private Uri mDataUri; + private ViewHolder mViewHolder; + private View mCurrentCert; + private int mLidRank; + + public static Fragment newInstance(Uri dataUri, int rank, + boolean showCertified, byte[] fingerprint) throws IOException { + LinkedIdViewFragment frag = new LinkedIdViewFragment(); + + Bundle args = new Bundle(); + args.putParcelable(ARG_DATA_URI, dataUri); + args.putInt(ARG_LID_RANK, rank); + args.putBoolean(ARG_SHOWCERT, showCertified); + args.putByteArray(ARG_FINGERPRINT, fingerprint); + frag.setArguments(args); + + return frag; + } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle args = getArguments(); - byte[] data = args.getByteArray(ARG_ENCODED_LID); - - try { - mLinkedId = LinkedIdentity.fromAttributeData(data); - } catch (IOException e) { - // TODO um… - e.printStackTrace(); - throw new AssertionError("reconstruction of user attribute must succeed!"); - } - - if (mLinkedId instanceof LinkedIdentity) { - LinkedResource res = ((LinkedIdentity) mLinkedId).mResource; - mLinkedResource = (LinkedCookieResource) res; - } + mDataUri = args.getParcelable(ARG_DATA_URI); + mLidRank = args.getInt(ARG_LID_RANK); - mVerified = args.containsKey(ARG_VERIFIED) ? args.getInt(ARG_VERIFIED) : null; + mShowCert = args.getBoolean(ARG_SHOWCERT); mFingerprint = args.getByteArray(ARG_FINGERPRINT); mContext = getActivity(); mInflater = getLayoutInflater(savedInstanceState); - } - - public static Fragment newInstance(RawLinkedIdentity id, - Integer isVerified, byte[] fingerprint) throws IOException { - LinkedIdViewFragment frag = new LinkedIdViewFragment(); + mCertAdapter = new LinkedIdsCertAdapter(getActivity(), null, 0); + // getLoaderManager().initLoader(LOADER_ID_LINKED_CERTS, null, this); + getLoaderManager().initLoader(LOADER_ID_LINKED_ID, null, this); - Bundle args = new Bundle(); - args.putByteArray(ARG_ENCODED_LID, id.toUserAttribute().getEncoded()); - if (isVerified != null) { - args.putInt(ARG_VERIFIED, isVerified); + } + @Override + public Loader onCreateLoader(int id, Bundle args) { + switch (id) { + case LOADER_ID_LINKED_ID: + return new CursorLoader(getActivity(), mDataUri, + UserIdsAdapter.USER_PACKETS_PROJECTION, + Tables.USER_PACKETS + "." + UserPackets.RANK + + " = " + Integer.toString(mLidRank), null, null); + + case LOADER_ID_LINKED_CERTS: + return LinkedIdsCertAdapter.createLoader(getActivity(), mDataUri); + + default: + return null; } - args.putByteArray(ARG_FINGERPRINT, fingerprint); - frag.setArguments(args); - - return frag; } @Override - public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { - View root = inflater.inflate(R.layout.linked_id_view_fragment, null); + public void onLoadFinished(Loader loader, Cursor cursor) { + switch (loader.getId()) { + case LOADER_ID_LINKED_ID: - vLinkedIdsCard = (CardView) root.findViewById(R.id.card_linked_ids); - vLinkedCerts = (LinearLayout) root.findViewById(R.id.linked_id_certs); - vKeySpinner = (CertifyKeySpinner) root.findViewById(R.id.cert_key_spinner); + // TODO proper error reporting and null checks here! - View back = root.findViewById(R.id.back_button); - back.setClickable(true); - back.findViewById(R.id.back_button).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - getFragmentManager().popBackStack(); - } - }); + if (!cursor.moveToFirst()) { + Log.e(Constants.TAG, "error"); + break; + } - ViewHolder holder = new ViewHolder(root); + try { + int certStatus = cursor.getInt(UserIdsAdapter.INDEX_VERIFIED); - if (mVerified != null) { - holder.vVerified.setVisibility(View.VISIBLE); - switch (mVerified) { + byte[] data = cursor.getBlob(UserIdsAdapter.INDEX_ATTRIBUTE_DATA); + RawLinkedIdentity linkedId = LinkedIdentity.fromAttributeData(data); + + loadIdentity(linkedId, certStatus); + } catch (IOException e) { + e.printStackTrace(); + throw new AssertionError("reconstruction of user attribute must succeed!"); + } + + break; + + case LOADER_ID_LINKED_CERTS: + mCertAdapter.swapCursor(cursor); + break; + } + } + + private void loadIdentity(RawLinkedIdentity linkedId, int certStatus) { + mLinkedId = linkedId; + + if (mLinkedId instanceof LinkedIdentity) { + LinkedResource res = ((LinkedIdentity) mLinkedId).mResource; + mLinkedResource = (LinkedCookieResource) res; + } + + if (mShowCert) { + mViewHolder.mLinkedIdHolder.vVerified.setVisibility(View.VISIBLE); + + switch (certStatus) { case Certs.VERIFIED_SECRET: - KeyFormattingUtils.setStatusImage(mContext, holder.vVerified, + KeyFormattingUtils.setStatusImage(mContext, mViewHolder.mLinkedIdHolder.vVerified, null, State.VERIFIED, KeyFormattingUtils.DEFAULT_COLOR); break; case Certs.VERIFIED_SELF: - KeyFormattingUtils.setStatusImage(mContext, holder.vVerified, + KeyFormattingUtils.setStatusImage(mContext, mViewHolder.mLinkedIdHolder.vVerified, null, State.UNVERIFIED, KeyFormattingUtils.DEFAULT_COLOR); break; default: - KeyFormattingUtils.setStatusImage(mContext, holder.vVerified, + KeyFormattingUtils.setStatusImage(mContext, mViewHolder.mLinkedIdHolder.vVerified, null, State.INVALID, KeyFormattingUtils.DEFAULT_COLOR); break; } } else { - holder.vVerified.setVisibility(View.GONE); + mViewHolder.mLinkedIdHolder.vVerified.setVisibility(View.GONE); } - holder.setData(mContext, mLinkedId); + mViewHolder.mLinkedIdHolder.setData(mContext, mLinkedId); // no resource, nothing further we can do… if (mLinkedResource == null) { - root.findViewById(R.id.button_view).setVisibility(View.GONE); - root.findViewById(R.id.button_verify).setVisibility(View.GONE); - return root; + mViewHolder.vButtonView.setVisibility(View.GONE); + mViewHolder.vButtonVerify.setVisibility(View.GONE); + return; } - View button_view = root.findViewById(R.id.button_view); if (mLinkedResource.isViewable()) { - button_view.setVisibility(View.VISIBLE); - button_view.setOnClickListener(new OnClickListener() { + mViewHolder.vButtonView.setVisibility(View.VISIBLE); + mViewHolder.vButtonView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Intent intent = mLinkedResource.getViewIntent(); @@ -177,29 +213,79 @@ public class LinkedIdViewFragment extends Fragment { } }); } else { - button_view.setVisibility(View.GONE); + mViewHolder.vButtonView.setVisibility(View.GONE); + } + + } + + @Override + public void onLoaderReset(Loader loader) { + switch (loader.getId()) { + case LOADER_ID_LINKED_CERTS: + mCertAdapter.swapCursor(null); + break; + } + + } + + static class ViewHolder { + private final View vButtonView; + LinkedIdsAdapter.ViewHolder mLinkedIdHolder; + + private ViewAnimator mButtonSwitcher; + private LinearLayout vLinkedCerts; + private CertifyKeySpinner vKeySpinner; + private LinearLayout vLinkedVerify; + private final View vButtonVerify; + private final View vButtonRetry; + private final View vButtonConfirm; + private final View vButtonBack; + + ViewHolder(View root) { + vLinkedCerts = (LinearLayout) root.findViewById(R.id.linked_id_certs); + vLinkedVerify = (LinearLayout) root.findViewById(R.id.linked_id_verify); + vKeySpinner = (CertifyKeySpinner) root.findViewById(R.id.cert_key_spinner); + mButtonSwitcher = (ViewAnimator) root.findViewById(R.id.button_animator); + + mLinkedIdHolder = new LinkedIdsAdapter.ViewHolder(root); + + vButtonBack = root.findViewById(R.id.back_button); + 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); } - mButtonSwitcher = (ViewAnimator) root.findViewById(R.id.button_animator); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.linked_id_view_fragment, null); - View button_verify = root.findViewById(R.id.button_verify); - button_verify.setOnClickListener(new OnClickListener() { + mViewHolder = new ViewHolder(root); + root.setTag(mViewHolder); + + mViewHolder.vButtonBack.setClickable(true); + mViewHolder.vButtonBack.findViewById(R.id.back_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - verifyResource(); + getFragmentManager().popBackStack(); } }); - View button_retry = root.findViewById(R.id.button_retry); - button_retry.setOnClickListener(new OnClickListener() { + mViewHolder.vButtonVerify.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { verifyResource(); } }); - - View button_confirm = root.findViewById(R.id.button_confirm); - button_confirm.setOnClickListener(new OnClickListener() { + mViewHolder.vButtonRetry.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + verifyResource(); + } + }); + mViewHolder.vButtonConfirm.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { initiateCertifying(); @@ -225,10 +311,10 @@ public class LinkedIdViewFragment extends Fragment { } void showButton(int which) { - if (mButtonSwitcher.getDisplayedChild() == which) { + if (mViewHolder.mButtonSwitcher.getDisplayedChild() == which) { return; } - mButtonSwitcher.setDisplayedChild(which); + mViewHolder.mButtonSwitcher.setDisplayedChild(which); } void verifyResource() { @@ -247,12 +333,12 @@ public class LinkedIdViewFragment extends Fragment { mCurrentCert = mInflater.inflate(R.layout.linked_id_cert, null); holder = new ViewHolderCert(mCurrentCert); mCurrentCert.setTag(holder); - vLinkedCerts.addView(mCurrentCert); + mViewHolder.vLinkedVerify.addView(mCurrentCert); } else { holder = (ViewHolderCert) mCurrentCert.getTag(); } - vKeySpinner.setVisibility(View.GONE); + mViewHolder.vKeySpinner.setVisibility(View.GONE); holder.setShowProgress(true); holder.vText.setText("Verifying…"); @@ -294,14 +380,14 @@ public class LinkedIdViewFragment extends Fragment { // button is 'confirm' showButton(2); - vKeySpinner.setVisibility(View.VISIBLE); + mViewHolder.vKeySpinner.setVisibility(View.VISIBLE); } private void initiateCertifying() { // get the user's passphrase for this key (if required) String passphrase; - long certifyKeyId = vKeySpinner.getSelectedItemId(); + long certifyKeyId = mViewHolder.vKeySpinner.getSelectedItemId(); try { passphrase = PassphraseCacheService.getCachedPassphrase( getActivity(), certifyKeyId, certifyKeyId); -- cgit v1.2.3 From 138773798ae9325ba92021c8779f71e6fc0c23c6 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 9 Mar 2015 16:56:10 +0100 Subject: fix transition animation --- .../keychain/ui/ViewKeyFragment.java | 31 +++++++++++++++++----- .../keychain/ui/adapter/LinkedIdsAdapter.java | 2 +- .../keychain/ui/linked/LinkedIdViewFragment.java | 17 +++++++++++- 3 files changed, 41 insertions(+), 9 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index a7dc63122..b1da6df9d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -24,9 +24,8 @@ import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.support.v4.app.Fragment; +import android.os.Handler; import android.support.v4.app.LoaderManager; -import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v7.widget.CardView; import android.transition.Fade; @@ -42,10 +41,11 @@ import android.widget.ListView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; -import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; +import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment; +import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment.OnIdentityLoadedListener; import org.sufficientlysecure.keychain.util.Log; public class ViewKeyFragment extends LoaderFragment implements @@ -129,7 +129,7 @@ public class ViewKeyFragment extends LoaderFragment implements } private void showLinkedId(final int position) { - Fragment frag; + final LinkedIdViewFragment frag; try { frag = mLinkedIdsAdapter.getLinkedIdFragment(mDataUri, position, mFingerprint); } catch (IOException e) { @@ -146,10 +146,27 @@ public class ViewKeyFragment extends LoaderFragment implements } getFragmentManager().beginTransaction() - .replace(R.id.view_key_fragment, frag) - .addSharedElement(mLinkedIdsCard, "card_linked_ids") - .addToBackStack("linked_id") + .add(R.id.view_key_fragment, frag) + .hide(frag) .commit(); + + frag.setOnIdentityLoadedListener(new OnIdentityLoadedListener() { + @Override + public void onIdentityLoaded() { + new Handler().post(new Runnable() { + @Override + public void run() { + getFragmentManager().beginTransaction() + .show(frag) + .addSharedElement(mLinkedIdsCard, "card_linked_ids") + .remove(ViewKeyFragment.this) + .addToBackStack("linked_id") + .commit(); + } + }); + } + }); + } private void showUserIdInfo(final int position) { 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 ce4291101..76cb63223 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 @@ -126,7 +126,7 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { UserIdsAdapter.USER_PACKETS_PROJECTION, LINKED_IDS_WHERE, null, null); } - public Fragment getLinkedIdFragment(Uri baseUri, + public LinkedIdViewFragment getLinkedIdFragment(Uri baseUri, int position, byte[] fingerprint) throws IOException { Cursor c = getCursor(); 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 fcb9d7bb4..284e6e0c1 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 @@ -79,8 +79,9 @@ public class LinkedIdViewFragment extends Fragment implements private ViewHolder mViewHolder; private View mCurrentCert; private int mLidRank; + private OnIdentityLoadedListener mIdLoadedListener; - public static Fragment newInstance(Uri dataUri, int rank, + public static LinkedIdViewFragment newInstance(Uri dataUri, int rank, boolean showCertified, byte[] fingerprint) throws IOException { LinkedIdViewFragment frag = new LinkedIdViewFragment(); @@ -149,6 +150,12 @@ public class LinkedIdViewFragment extends Fragment implements RawLinkedIdentity linkedId = LinkedIdentity.fromAttributeData(data); loadIdentity(linkedId, certStatus); + + if (mIdLoadedListener != null) { + mIdLoadedListener.onIdentityLoaded(); + mIdLoadedListener = null; + } + } catch (IOException e) { e.printStackTrace(); throw new AssertionError("reconstruction of user attribute must succeed!"); @@ -162,6 +169,14 @@ public class LinkedIdViewFragment extends Fragment implements } } + public interface OnIdentityLoadedListener { + public void onIdentityLoaded(); + } + + public void setOnIdentityLoadedListener(OnIdentityLoadedListener listener) { + mIdLoadedListener = listener; + } + private void loadIdentity(RawLinkedIdentity linkedId, int certStatus) { mLinkedId = linkedId; -- cgit v1.2.3 From 159b197930457fc58b251d6a18d2db44a6e30d24 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 9 Mar 2015 21:03:27 +0100 Subject: add linked id certs uri to KeychainProvider --- .../keychain/provider/KeychainContract.java | 4 ++-- .../keychain/provider/KeychainProvider.java | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 08f3b5248..0a7cf4a41 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -359,8 +359,8 @@ public class KeychainContract { .appendPath(PATH_CERTS).build(); } - public static Uri buildLinkedIdCertsUri(long masterKeyId, int rank) { - return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)) + public static Uri buildLinkedIdCertsUri(Uri uri, int rank) { + return CONTENT_URI.buildUpon().appendPath(uri.getPathSegments().get(1)) .appendPath(PATH_LINKED_IDS).appendPath(Integer.toString(rank)) .appendPath(PATH_CERTS).build(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 0f42b3934..19634bed8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -566,7 +566,8 @@ public class KeychainProvider extends ContentProvider { } case KEY_RING_CERTS: - case KEY_RING_CERTS_SPECIFIC: { + case KEY_RING_CERTS_SPECIFIC: + case KEY_RING_LINKED_ID_CERTS: { HashMap projectionMap = new HashMap<>(); projectionMap.put(Certs._ID, Tables.CERTS + ".oid AS " + Certs._ID); projectionMap.put(Certs.MASTER_KEY_ID, Tables.CERTS + "." + Certs.MASTER_KEY_ID); @@ -587,10 +588,6 @@ public class KeychainProvider extends ContentProvider { + " AND " + Tables.CERTS + "." + Certs.RANK + " = " + Tables.USER_PACKETS + "." + UserPackets.RANK - // for now, we only return user ids here, so TYPE must be NULL - // TODO at some point, we should lift this restriction - + " AND " - + Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL" + ") LEFT JOIN " + Tables.USER_PACKETS + " AS signer ON (" + Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER + " = " + "signer." + UserPackets.MASTER_KEY_ID @@ -610,6 +607,17 @@ public class KeychainProvider extends ContentProvider { qb.appendWhereEscapeString(uri.getPathSegments().get(4)); } + if (match == KEY_RING_LINKED_ID_CERTS) { + qb.appendWhere(" AND " + Tables.USER_PACKETS + "." + + UserPackets.TYPE + " IS NOT NULL"); + + qb.appendWhere(" AND " + Tables.USER_PACKETS + "." + + UserPackets.RANK + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(3)); + } else { + qb.appendWhere(" AND " + Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL"); + } + break; } -- cgit v1.2.3 From ebf359634139ff2125233a78a976388082abf00c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 9 Mar 2015 21:04:03 +0100 Subject: rearrange linked id layouts, add CertListWidget --- .../keychain/ui/ViewKeyActivity.java | 28 +++-- .../keychain/ui/linked/LinkedIdViewFragment.java | 102 ++++++++-------- .../keychain/ui/widget/CertListWidget.java | 131 +++++++++++++++++++++ 3 files changed, 192 insertions(+), 69 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 59e0efbd0..3edadec2f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -839,9 +839,7 @@ public class ViewKeyActivity extends BaseActivity implements mIsExpired = data.getInt(INDEX_IS_EXPIRED) != 0; mIsVerified = data.getInt(INDEX_VERIFIED) > 0; - if (oldFingerprint == null) { - startFragment(mIsSecret, fpData); - } + startFragment(mIsSecret, fpData); // get name, email, and comment from USER_ID String[] mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID)); @@ -1000,21 +998,25 @@ public class ViewKeyActivity extends BaseActivity implements } - private void startFragment(boolean isSecret, byte[] fingerprint) { - // Create an instance of the fragment - final ViewKeyFragment frag = ViewKeyFragment.newInstance(mDataUri, isSecret, fingerprint); - + private void startFragment(final boolean isSecret, final byte[] fingerprint) { new Handler().post(new Runnable() { @Override public void run() { FragmentManager manager = getSupportFragmentManager(); - manager.popBackStack("linked_id", FragmentManager.POP_BACK_STACK_INCLUSIVE); - // Add the fragment to the 'fragment_container' FrameLayout - // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! - manager.beginTransaction() - .replace(R.id.view_key_fragment, frag, "main") - .commit(); + if (manager.getBackStackEntryCount() == 0) { + // Create an instance of the fragment + final ViewKeyFragment frag = ViewKeyFragment.newInstance( + mDataUri, isSecret, fingerprint); + manager.beginTransaction() + .replace(R.id.view_key_fragment, frag) + .commit(); + manager.popBackStack(); + } else { + // not sure yet if we actually want this! + // manager.popBackStack(); + } + } }); } 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 284e6e0c1..5b8d14c4c 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 @@ -22,7 +22,6 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.TextView; import android.widget.ViewAnimator; @@ -44,10 +43,10 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter; -import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsCertAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; +import org.sufficientlysecure.keychain.ui.widget.CertListWidget; import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner; import org.sufficientlysecure.keychain.util.Log; @@ -61,8 +60,7 @@ public class LinkedIdViewFragment extends Fragment implements private static final String ARG_LID_RANK = "rank"; private static final String ARG_SHOWCERT = "verified"; private static final String ARG_FINGERPRINT = "fingerprint"; - private static final int LOADER_ID_LINKED_CERTS = 1; - private static final int LOADER_ID_LINKED_ID = 2; + private static final int LOADER_ID_LINKED_ID = 1; private RawLinkedIdentity mLinkedId; private LinkedCookieResource mLinkedResource; @@ -74,10 +72,8 @@ public class LinkedIdViewFragment extends Fragment implements private boolean mInProgress; - private LinkedIdsCertAdapter mCertAdapter; private Uri mDataUri; private ViewHolder mViewHolder; - private View mCurrentCert; private int mLidRank; private OnIdentityLoadedListener mIdLoadedListener; @@ -109,11 +105,9 @@ public class LinkedIdViewFragment extends Fragment implements mContext = getActivity(); mInflater = getLayoutInflater(savedInstanceState); - mCertAdapter = new LinkedIdsCertAdapter(getActivity(), null, 0); - // getLoaderManager().initLoader(LOADER_ID_LINKED_CERTS, null, this); getLoaderManager().initLoader(LOADER_ID_LINKED_ID, null, this); - } + @Override public Loader onCreateLoader(int id, Bundle args) { switch (id) { @@ -122,10 +116,6 @@ public class LinkedIdViewFragment extends Fragment implements UserIdsAdapter.USER_PACKETS_PROJECTION, Tables.USER_PACKETS + "." + UserPackets.RANK + " = " + Integer.toString(mLidRank), null, null); - - case LOADER_ID_LINKED_CERTS: - return LinkedIdsCertAdapter.createLoader(getActivity(), mDataUri); - default: return null; } @@ -162,10 +152,6 @@ public class LinkedIdViewFragment extends Fragment implements } break; - - case LOADER_ID_LINKED_CERTS: - mCertAdapter.swapCursor(cursor); - break; } } @@ -180,6 +166,16 @@ public class LinkedIdViewFragment extends Fragment implements private void loadIdentity(RawLinkedIdentity linkedId, int certStatus) { mLinkedId = linkedId; + mViewHolder.setShowVerifying(false); + + { + Bundle args = new Bundle(); + args.putParcelable(CertListWidget.ARG_URI, mDataUri); + args.putInt(CertListWidget.ARG_RANK, mLidRank); + getLoaderManager().initLoader(CertListWidget.LOADER_ID_LINKED_CERTS, + args, mViewHolder.vLinkedCerts); + } + if (mLinkedId instanceof LinkedIdentity) { LinkedResource res = ((LinkedIdentity) mLinkedId).mResource; mLinkedResource = (LinkedCookieResource) res; @@ -235,30 +231,28 @@ public class LinkedIdViewFragment extends Fragment implements @Override public void onLoaderReset(Loader loader) { - switch (loader.getId()) { - case LOADER_ID_LINKED_CERTS: - mCertAdapter.swapCursor(null); - break; - } } static class ViewHolder { private final View vButtonView; + private final ViewAnimator vVerifyingContainer; LinkedIdsAdapter.ViewHolder mLinkedIdHolder; private ViewAnimator mButtonSwitcher; - private LinearLayout vLinkedCerts; + private CertListWidget vLinkedCerts; private CertifyKeySpinner vKeySpinner; - private LinearLayout vLinkedVerify; private final View vButtonVerify; private final View vButtonRetry; private final View vButtonConfirm; private final View vButtonBack; + private final ViewAnimator vProgress; + private final ImageView vIcon; + private final TextView vText; + ViewHolder(View root) { - vLinkedCerts = (LinearLayout) root.findViewById(R.id.linked_id_certs); - vLinkedVerify = (LinearLayout) root.findViewById(R.id.linked_id_verify); + vLinkedCerts = (CertListWidget) root.findViewById(R.id.linked_id_certs); vKeySpinner = (CertifyKeySpinner) root.findViewById(R.id.cert_key_spinner); mButtonSwitcher = (ViewAnimator) root.findViewById(R.id.button_animator); @@ -269,6 +263,26 @@ public class LinkedIdViewFragment extends Fragment implements vButtonRetry = root.findViewById(R.id.button_retry); vButtonConfirm = root.findViewById(R.id.button_confirm); vButtonView = root.findViewById(R.id.button_view); + + vVerifyingContainer = (ViewAnimator) root.findViewById(R.id.linked_verify_container); + + vProgress = (ViewAnimator) root.findViewById(R.id.linked_cert_progress); + vIcon = (ImageView) root.findViewById(R.id.linked_cert_icon); + vText = (TextView) root.findViewById(R.id.linked_cert_text); + } + + void setShowVerifying(boolean show) { + int child = show ? 1 : 0; + if (vVerifyingContainer.getDisplayedChild() != child) { + vVerifyingContainer.setDisplayedChild(child); + } + if (!show) { + vKeySpinner.setVisibility(View.GONE); + } + } + + void setShowProgress(boolean show) { + vProgress.setDisplayedChild(show ? 0 : 1); } } @@ -310,21 +324,6 @@ public class LinkedIdViewFragment extends Fragment implements return root; } - static class ViewHolderCert { - final ViewAnimator vProgress; - final ImageView vIcon; - final TextView vText; - - ViewHolderCert(View view) { - vProgress = (ViewAnimator) view.findViewById(R.id.linked_cert_progress); - vIcon = (ImageView) view.findViewById(R.id.linked_cert_icon); - vText = (TextView) view.findViewById(R.id.linked_cert_text); - } - void setShowProgress(boolean show) { - vProgress.setDisplayedChild(show ? 0 : 1); - } - } - void showButton(int which) { if (mViewHolder.mButtonSwitcher.getDisplayedChild() == which) { return; @@ -342,20 +341,11 @@ public class LinkedIdViewFragment extends Fragment implements mInProgress = true; } - // is there a current certification? if not create a new one - final ViewHolderCert holder; - if (mCurrentCert == null) { - mCurrentCert = mInflater.inflate(R.layout.linked_id_cert, null); - holder = new ViewHolderCert(mCurrentCert); - mCurrentCert.setTag(holder); - mViewHolder.vLinkedVerify.addView(mCurrentCert); - } else { - holder = (ViewHolderCert) mCurrentCert.getTag(); - } + mViewHolder.setShowVerifying(true); mViewHolder.vKeySpinner.setVisibility(View.GONE); - holder.setShowProgress(true); - holder.vText.setText("Verifying…"); + mViewHolder.setShowProgress(true); + mViewHolder.vText.setText("Verifying…"); new AsyncTask() { @Override @@ -376,13 +366,13 @@ public class LinkedIdViewFragment extends Fragment implements @Override protected void onPostExecute(LinkedVerifyResult result) { - holder.setShowProgress(false); + mViewHolder.setShowProgress(false); if (result.success()) { - holder.vText.setText("Ok"); + mViewHolder.vText.setText("Ok"); setupForConfirmation(); } else { showButton(1); - holder.vText.setText("Error"); + mViewHolder.vText.setText("Error"); } mInProgress = false; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java new file mode 100644 index 000000000..3e9cffb69 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java @@ -0,0 +1,131 @@ +package org.sufficientlysecure.keychain.ui.widget; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; + +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.text.SpannableStringBuilder; +import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; +import android.widget.ViewAnimator; + +import com.ocpsoft.pretty.time.PrettyTime; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; + +public class CertListWidget extends ViewAnimator + implements LoaderManager.LoaderCallbacks { + + public static final int LOADER_ID_LINKED_CERTS = 38572; + + public static final String ARG_URI = "uri"; + public static final String ARG_RANK = "rank"; + + + // These are the rows that we will retrieve. + static final String[] CERTS_PROJECTION = new String[]{ + KeychainContract.Certs._ID, + KeychainContract.Certs.MASTER_KEY_ID, + KeychainContract.Certs.VERIFIED, + KeychainContract.Certs.TYPE, + KeychainContract.Certs.RANK, + KeychainContract.Certs.KEY_ID_CERTIFIER, + KeychainContract.Certs.USER_ID, + KeychainContract.Certs.SIGNER_UID, + KeychainContract.Certs.CREATION + }; + public static final int INDEX_MASTER_KEY_ID = 1; + public static final int INDEX_VERIFIED = 2; + public static final int INDEX_TYPE = 3; + public static final int INDEX_RANK = 4; + public static final int INDEX_KEY_ID_CERTIFIER = 5; + public static final int INDEX_USER_ID = 6; + public static final int INDEX_SIGNER_UID = 7; + public static final int INDEX_CREATION = 8; + + private TextView vCollapsed; + private View vExpanded; + private View vExpandButton; + + public CertListWidget(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + View root = getRootView(); + vCollapsed = (TextView) root.findViewById(R.id.cert_collapsed_list); + vExpanded = root.findViewById(R.id.cert_expanded_list); + vExpandButton = root.findViewById(R.id.cert_expand_button); + + // for now + vExpandButton.setVisibility(View.GONE); + } + + void setExpanded(boolean expanded) { + setDisplayedChild(expanded ? 1 : 0); + } + + @Override + public Loader onCreateLoader(int id, Bundle args) { + Uri baseUri = args.getParcelable(ARG_URI); + int rank = args.getInt(ARG_RANK); + + Uri uri = Certs.buildLinkedIdCertsUri(baseUri, rank); + return new CursorLoader(getContext(), uri, + CERTS_PROJECTION, null, null, null); + + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + + if (data == null || !data.moveToFirst()) { + return; + } + + setVisibility(View.VISIBLE); + + // TODO support external certificates + Date userCert = null; + while (!data.isAfterLast()) { + + int verified = data.getInt(INDEX_VERIFIED); + Date creation = new Date(data.getLong(INDEX_CREATION) * 1000); + + if (verified == Certs.VERIFIED_SECRET) { + if (userCert == null || userCert.after(creation)) { + userCert = creation; + } + } + + data.moveToNext(); + } + + if (userCert != null) { + PrettyTime format = new PrettyTime(); + vCollapsed.setText("You verified and confirmed this identity " + + format.format(userCert) + "."); + } else { + vCollapsed.setText("This identity is not yet verified or confirmed."); + } + + } + + @Override + public void onLoaderReset(Loader loader) { + setVisibility(View.GONE); + } + +} -- cgit v1.2.3 From 6df9387c2c08c7e872e4721c5d677188d29164f4 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 9 Mar 2015 23:58:05 +0100 Subject: support backstack for verification --- .../keychain/ui/linked/LinkedIdViewFragment.java | 38 +++++++++++++++------- 1 file changed, 26 insertions(+), 12 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 5b8d14c4c..8e81eb383 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 @@ -14,6 +14,8 @@ import android.os.Bundle; import android.os.Message; import android.os.Messenger; import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentManager.OnBackStackChangedListener; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; @@ -52,7 +54,7 @@ import org.sufficientlysecure.keychain.util.Log; public class LinkedIdViewFragment extends Fragment implements - LoaderManager.LoaderCallbacks { + LoaderManager.LoaderCallbacks, OnBackStackChangedListener { public static final int REQUEST_CODE_PASSPHRASE = 0x00008001; @@ -155,6 +157,12 @@ public class LinkedIdViewFragment extends Fragment implements } } + @Override + public void onBackStackChanged() { + mViewHolder.setShowVerifying(false); + getFragmentManager().removeOnBackStackChangedListener(LinkedIdViewFragment.this); + } + public interface OnIdentityLoadedListener { public void onIdentityLoaded(); } @@ -239,7 +247,7 @@ public class LinkedIdViewFragment extends Fragment implements private final ViewAnimator vVerifyingContainer; LinkedIdsAdapter.ViewHolder mLinkedIdHolder; - private ViewAnimator mButtonSwitcher; + private ViewAnimator vButtonSwitcher; private CertListWidget vLinkedCerts; private CertifyKeySpinner vKeySpinner; private final View vButtonVerify; @@ -254,7 +262,7 @@ public class LinkedIdViewFragment extends Fragment implements ViewHolder(View root) { vLinkedCerts = (CertListWidget) root.findViewById(R.id.linked_id_certs); vKeySpinner = (CertifyKeySpinner) root.findViewById(R.id.cert_key_spinner); - mButtonSwitcher = (ViewAnimator) root.findViewById(R.id.button_animator); + vButtonSwitcher = (ViewAnimator) root.findViewById(R.id.button_animator); mLinkedIdHolder = new LinkedIdsAdapter.ViewHolder(root); @@ -278,6 +286,7 @@ public class LinkedIdViewFragment extends Fragment implements } if (!show) { vKeySpinner.setVisibility(View.GONE); + showButton(0); } } @@ -285,6 +294,13 @@ public class LinkedIdViewFragment extends Fragment implements vProgress.setDisplayedChild(show ? 0 : 1); } + void showButton(int which) { + if (vButtonSwitcher.getDisplayedChild() == which) { + return; + } + vButtonSwitcher.setDisplayedChild(which); + } + } @Override @@ -324,13 +340,6 @@ public class LinkedIdViewFragment extends Fragment implements return root; } - void showButton(int which) { - if (mViewHolder.mButtonSwitcher.getDisplayedChild() == which) { - return; - } - mViewHolder.mButtonSwitcher.setDisplayedChild(which); - } - void verifyResource() { // only one at a time @@ -341,6 +350,11 @@ public class LinkedIdViewFragment extends Fragment implements mInProgress = true; } + FragmentManager manager = getFragmentManager(); + manager.beginTransaction().addToBackStack("verification").commit(); + manager.executePendingTransactions(); + manager.addOnBackStackChangedListener(this); + mViewHolder.setShowVerifying(true); mViewHolder.vKeySpinner.setVisibility(View.GONE); @@ -371,7 +385,7 @@ public class LinkedIdViewFragment extends Fragment implements mViewHolder.vText.setText("Ok"); setupForConfirmation(); } else { - showButton(1); + mViewHolder.showButton(1); mViewHolder.vText.setText("Error"); } mInProgress = false; @@ -383,7 +397,7 @@ public class LinkedIdViewFragment extends Fragment implements void setupForConfirmation() { // button is 'confirm' - showButton(2); + mViewHolder.showButton(2); mViewHolder.vKeySpinner.setVisibility(View.VISIBLE); -- cgit v1.2.3 From f76f84dfb2e406dd613be04dabd9432ffd0f9c4b Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Mar 2015 00:55:48 +0100 Subject: concurrency and continuity fixes for verification backstack handling --- .../keychain/ui/linked/LinkedIdViewFragment.java | 40 ++++++++++++++-------- 1 file changed, 26 insertions(+), 14 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 8e81eb383..f523441c5 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 @@ -11,6 +11,7 @@ import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.support.v4.app.Fragment; @@ -72,7 +73,7 @@ public class LinkedIdViewFragment extends Fragment implements private byte[] mFingerprint; private LayoutInflater mInflater; - private boolean mInProgress; + private AsyncTask mInProgress; private Uri mDataUri; private ViewHolder mViewHolder; @@ -157,12 +158,6 @@ public class LinkedIdViewFragment extends Fragment implements } } - @Override - public void onBackStackChanged() { - mViewHolder.setShowVerifying(false); - getFragmentManager().removeOnBackStackChangedListener(LinkedIdViewFragment.this); - } - public interface OnIdentityLoadedListener { public void onIdentityLoaded(); } @@ -174,6 +169,13 @@ public class LinkedIdViewFragment extends Fragment implements private void loadIdentity(RawLinkedIdentity linkedId, int certStatus) { mLinkedId = linkedId; + new Handler().post(new Runnable() { + @Override + public void run() { + getFragmentManager().popBackStack("verification", + FragmentManager.POP_BACK_STACK_INCLUSIVE); + } + }); mViewHolder.setShowVerifying(false); { @@ -343,11 +345,8 @@ public class LinkedIdViewFragment extends Fragment implements void verifyResource() { // only one at a time - synchronized (this) { - if (mInProgress) { - return; - } - mInProgress = true; + if (mInProgress != null) { + return; } FragmentManager manager = getFragmentManager(); @@ -361,7 +360,7 @@ public class LinkedIdViewFragment extends Fragment implements mViewHolder.setShowProgress(true); mViewHolder.vText.setText("Verifying…"); - new AsyncTask() { + mInProgress = new AsyncTask() { @Override protected LinkedVerifyResult doInBackground(Void... params) { long timer = System.currentTimeMillis(); @@ -381,6 +380,9 @@ public class LinkedIdViewFragment extends Fragment implements @Override protected void onPostExecute(LinkedVerifyResult result) { mViewHolder.setShowProgress(false); + if (isCancelled()) { + return; + } if (result.success()) { mViewHolder.vText.setText("Ok"); setupForConfirmation(); @@ -388,7 +390,7 @@ public class LinkedIdViewFragment extends Fragment implements mViewHolder.showButton(1); mViewHolder.vText.setText("Error"); } - mInProgress = false; + mInProgress = null; } }.execute(); @@ -425,6 +427,16 @@ public class LinkedIdViewFragment extends Fragment implements } } + @Override + public void onBackStackChanged() { + mViewHolder.setShowVerifying(false); + getFragmentManager().removeOnBackStackChangedListener(LinkedIdViewFragment.this); + if (mInProgress != null) { + mInProgress.cancel(false); + mInProgress = null; + } + } + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { -- cgit v1.2.3 From 2a42ff016fbe3974811323f4da3775279d6bdccc Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Mar 2015 02:08:51 +0100 Subject: cleaner handling of verification state --- .../keychain/ui/linked/LinkedIdViewFragment.java | 110 ++++++++++++--------- 1 file changed, 64 insertions(+), 46 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 f523441c5..1c301c642 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 @@ -71,7 +71,6 @@ public class LinkedIdViewFragment extends Fragment implements private Context mContext; private byte[] mFingerprint; - private LayoutInflater mInflater; private AsyncTask mInProgress; @@ -106,9 +105,9 @@ public class LinkedIdViewFragment extends Fragment implements mFingerprint = args.getByteArray(ARG_FINGERPRINT); mContext = getActivity(); - mInflater = getLayoutInflater(savedInstanceState); getLoaderManager().initLoader(LOADER_ID_LINKED_ID, null, this); + } @Override @@ -169,22 +168,7 @@ public class LinkedIdViewFragment extends Fragment implements private void loadIdentity(RawLinkedIdentity linkedId, int certStatus) { mLinkedId = linkedId; - new Handler().post(new Runnable() { - @Override - public void run() { - getFragmentManager().popBackStack("verification", - FragmentManager.POP_BACK_STACK_INCLUSIVE); - } - }); - mViewHolder.setShowVerifying(false); - - { - Bundle args = new Bundle(); - args.putParcelable(CertListWidget.ARG_URI, mDataUri); - args.putInt(CertListWidget.ARG_RANK, mLidRank); - getLoaderManager().initLoader(CertListWidget.LOADER_ID_LINKED_CERTS, - args, mViewHolder.vLinkedCerts); - } + setShowVerifying(false); if (mLinkedId instanceof LinkedIdentity) { LinkedResource res = ((LinkedIdentity) mLinkedId).mResource; @@ -281,17 +265,6 @@ public class LinkedIdViewFragment extends Fragment implements vText = (TextView) root.findViewById(R.id.linked_cert_text); } - void setShowVerifying(boolean show) { - int child = show ? 1 : 0; - if (vVerifyingContainer.getDisplayedChild() != child) { - vVerifyingContainer.setDisplayedChild(child); - } - if (!show) { - vKeySpinner.setVisibility(View.GONE); - showButton(0); - } - } - void setShowProgress(boolean show) { vProgress.setDisplayedChild(show ? 0 : 1); } @@ -305,6 +278,58 @@ public class LinkedIdViewFragment extends Fragment implements } + 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 (mInProgress != null) { + mInProgress.cancel(false); + mInProgress = null; + } + getFragmentManager().removeOnBackStackChangedListener(this); + new Handler().post(new Runnable() { + @Override + public void run() { + getFragmentManager().popBackStack("verification", + FragmentManager.POP_BACK_STACK_INCLUSIVE); + } + }); + + if (!mVerificationState) { + return; + } + mVerificationState = false; + + mViewHolder.showButton(0); + mViewHolder.vKeySpinner.setVisibility(View.GONE); + mViewHolder.vVerifyingContainer.setDisplayedChild(0); + return; + } + + if (mVerificationState) { + return; + } + mVerificationState = true; + + FragmentManager manager = getFragmentManager(); + manager.beginTransaction().addToBackStack("verification").commit(); + manager.executePendingTransactions(); + manager.addOnBackStackChangedListener(this); + mViewHolder.vVerifyingContainer.setDisplayedChild(1); + + } + + @Override + public void onBackStackChanged() { + setShowVerifying(false); + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.linked_id_view_fragment, null); @@ -339,22 +364,25 @@ public class LinkedIdViewFragment extends Fragment implements } }); + { + Bundle args = new Bundle(); + args.putParcelable(CertListWidget.ARG_URI, mDataUri); + args.putInt(CertListWidget.ARG_RANK, mLidRank); + getLoaderManager().initLoader(CertListWidget.LOADER_ID_LINKED_CERTS, + args, mViewHolder.vLinkedCerts); + } + return root; } void verifyResource() { - // only one at a time + // only one at a time (no sync needed, mInProgress is only touched in ui thread) if (mInProgress != null) { return; } - FragmentManager manager = getFragmentManager(); - manager.beginTransaction().addToBackStack("verification").commit(); - manager.executePendingTransactions(); - manager.addOnBackStackChangedListener(this); - - mViewHolder.setShowVerifying(true); + setShowVerifying(true); mViewHolder.vKeySpinner.setVisibility(View.GONE); mViewHolder.setShowProgress(true); @@ -427,16 +455,6 @@ public class LinkedIdViewFragment extends Fragment implements } } - @Override - public void onBackStackChanged() { - mViewHolder.setShowVerifying(false); - getFragmentManager().removeOnBackStackChangedListener(LinkedIdViewFragment.this); - if (mInProgress != null) { - mInProgress.cancel(false); - mInProgress = null; - } - } - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { -- cgit v1.2.3 From f069d9814dd5bb4dcc99be1a08da2d7bcf350cde Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Mar 2015 02:22:47 +0100 Subject: minor additions to CertListWidget --- .../keychain/ui/linked/LinkedIdViewFragment.java | 3 +-- .../keychain/ui/widget/CertListWidget.java | 24 +++++++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 1c301c642..21c9abca3 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 @@ -366,8 +366,7 @@ public class LinkedIdViewFragment extends Fragment implements { Bundle args = new Bundle(); - args.putParcelable(CertListWidget.ARG_URI, mDataUri); - args.putInt(CertListWidget.ARG_RANK, mLidRank); + args.putParcelable(CertListWidget.ARG_URI, Certs.buildLinkedIdCertsUri(mDataUri, mLidRank)); getLoaderManager().initLoader(CertListWidget.LOADER_ID_LINKED_CERTS, args, mViewHolder.vLinkedCerts); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java index 3e9cffb69..094a84820 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java @@ -14,6 +14,7 @@ import android.support.v4.content.Loader; import android.text.SpannableStringBuilder; import android.util.AttributeSet; import android.view.View; +import android.widget.ListView; import android.widget.TextView; import android.widget.ViewAnimator; @@ -53,7 +54,7 @@ public class CertListWidget extends ViewAnimator public static final int INDEX_CREATION = 8; private TextView vCollapsed; - private View vExpanded; + private ListView vExpanded; private View vExpandButton; public CertListWidget(Context context, AttributeSet attrs) { @@ -66,11 +67,24 @@ public class CertListWidget extends ViewAnimator View root = getRootView(); vCollapsed = (TextView) root.findViewById(R.id.cert_collapsed_list); - vExpanded = root.findViewById(R.id.cert_expanded_list); + vExpanded = (ListView) root.findViewById(R.id.cert_expanded_list); vExpandButton = root.findViewById(R.id.cert_expand_button); // for now vExpandButton.setVisibility(View.GONE); + vExpandButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + toggleExpanded(); + } + }); + + // vExpanded.setAdapter(null); + + } + + void toggleExpanded() { + setDisplayedChild(getDisplayedChild() == 1 ? 0 : 1); } void setExpanded(boolean expanded) { @@ -79,13 +93,9 @@ public class CertListWidget extends ViewAnimator @Override public Loader onCreateLoader(int id, Bundle args) { - Uri baseUri = args.getParcelable(ARG_URI); - int rank = args.getInt(ARG_RANK); - - Uri uri = Certs.buildLinkedIdCertsUri(baseUri, rank); + Uri uri = args.getParcelable(ARG_URI); return new CursorLoader(getContext(), uri, CERTS_PROJECTION, null, null, null); - } @Override -- cgit v1.2.3 From deafe946fd652e59773e1ada4b37e77713f3d377 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Mar 2015 03:30:20 +0100 Subject: filter out unknown linked ids --- .../keychain/ui/ViewKeyFragment.java | 9 +-- .../keychain/ui/adapter/LinkedIdsAdapter.java | 53 ++++++++++----- .../keychain/util/FilterCursorWrapper.java | 76 ++++++++++++++++++++++ 3 files changed, 116 insertions(+), 22 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FilterCursorWrapper.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index b1da6df9d..cfc1075af 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -46,6 +46,7 @@ import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment; import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment.OnIdentityLoadedListener; +import org.sufficientlysecure.keychain.util.FilterCursorWrapper; import org.sufficientlysecure.keychain.util.Log; public class ViewKeyFragment extends LoaderFragment implements @@ -218,18 +219,18 @@ public class ViewKeyFragment extends LoaderFragment implements } } - public void onLoadFinished(Loader loader, Cursor data) { + public void onLoadFinished(Loader loader, Cursor cursor) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) switch (loader.getId()) { case LOADER_ID_USER_IDS: { - mUserIdsAdapter.swapCursor(data); + mUserIdsAdapter.swapCursor(cursor); break; } case LOADER_ID_LINKED_IDS: { - mLinkedIdsCard.setVisibility(data.getCount() > 0 ? View.VISIBLE : View.GONE); - mLinkedIdsAdapter.swapCursor(data); + mLinkedIdsCard.setVisibility(cursor.getCount() > 0 ? View.VISIBLE : View.GONE); + mLinkedIdsAdapter.swapCursor(cursor); break; } } 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 76cb63223..e0f28efb8 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 @@ -22,7 +22,6 @@ import android.app.Activity; import android.content.Context; import android.database.Cursor; import android.net.Uri; -import android.support.v4.app.Fragment; import android.support.v4.content.CursorLoader; import android.util.Log; import android.view.LayoutInflater; @@ -35,12 +34,12 @@ 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.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; +import org.sufficientlysecure.keychain.util.FilterCursorWrapper; import java.io.IOException; import java.util.WeakHashMap; @@ -57,6 +56,23 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { mShowCertification = showCertification; } + @Override + public Cursor swapCursor(Cursor cursor) { + if (cursor == null) { + return super.swapCursor(null); + } + + Cursor filteredCursor = new FilterCursorWrapper(cursor) { + @Override + public boolean isVisible(Cursor cursor) { + RawLinkedIdentity id = getItemAtPosition(cursor); + return id instanceof LinkedIdentity; + } + }; + + return super.swapCursor(filteredCursor); + } + @Override public void bindView(View view, Context context, Cursor cursor) { @@ -83,25 +99,26 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { holder.vVerified.setVisibility(View.GONE); } - RawLinkedIdentity id = getItem(cursor.getPosition()); + RawLinkedIdentity id = getItemAtPosition(cursor); holder.setData(mContext, id); } - @Override - public RawLinkedIdentity getItem(int position) { - RawLinkedIdentity ret = mLinkedIdentityCache.get(position); + public RawLinkedIdentity getItemAtPosition(Cursor cursor) { + int rank = cursor.getInt(INDEX_RANK); + Log.d(Constants.TAG, "requested rank: " + rank); + + RawLinkedIdentity ret = mLinkedIdentityCache.get(rank); if (ret != null) { + Log.d(Constants.TAG, "cached!"); return ret; } + Log.d(Constants.TAG, "not cached!"); - Cursor c = getCursor(); - c.moveToPosition(position); - - byte[] data = c.getBlob(INDEX_ATTRIBUTE_DATA); try { + byte[] data = cursor.getBlob(INDEX_ATTRIBUTE_DATA); ret = LinkedIdentity.fromAttributeData(data); - mLinkedIdentityCache.put(position, ret); + mLinkedIdentityCache.put(rank, ret); return ret; } catch (IOException e) { Log.e(Constants.TAG, "could not read linked identity subpacket data", e); @@ -109,6 +126,13 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { } } + @Override + public RawLinkedIdentity getItem(int position) { + Cursor cursor = getCursor(); + cursor.moveToPosition(position); + return getItemAtPosition(cursor); + } + @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { View v = mInflater.inflate(R.layout.linked_id_item, null); @@ -128,7 +152,6 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { public LinkedIdViewFragment getLinkedIdFragment(Uri baseUri, int position, byte[] fingerprint) throws IOException { - Cursor c = getCursor(); c.moveToPosition(position); int rank = c.getInt(UserIdsAdapter.INDEX_RANK); @@ -167,10 +190,4 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { } } - @Override - public void notifyDataSetChanged() { - mLinkedIdentityCache.clear(); - super.notifyDataSetChanged(); - } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FilterCursorWrapper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FilterCursorWrapper.java new file mode 100644 index 000000000..ab73f59b8 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FilterCursorWrapper.java @@ -0,0 +1,76 @@ +package org.sufficientlysecure.keychain.util; + + +import android.database.Cursor; +import android.database.CursorWrapper; + +public abstract class FilterCursorWrapper extends CursorWrapper { + private int[] mIndex; + private int mCount = 0; + private int mPos = 0; + + public abstract boolean isVisible(Cursor cursor); + + public FilterCursorWrapper(Cursor cursor) { + super(cursor); + mCount = super.getCount(); + mIndex = new int[mCount]; + for (int i = 0; i < mCount; i++) { + super.moveToPosition(i); + if (isVisible(cursor)) { + mIndex[mPos++] = i; + } + } + mCount = mPos; + mPos = 0; + super.moveToFirst(); + } + + @Override + public boolean move(int offset) { + return this.moveToPosition(mPos + offset); + } + + @Override + public boolean moveToNext() { + return this.moveToPosition(mPos + 1); + } + + @Override + public boolean moveToPrevious() { + return this.moveToPosition(mPos - 1); + } + + @Override + public boolean moveToFirst() { + return this.moveToPosition(0); + } + + @Override + public boolean moveToLast() { + return this.moveToPosition(mCount - 1); + } + + @Override + public boolean moveToPosition(int position) { + if (position >= mCount || position < 0) { + return false; + } + return super.moveToPosition(mIndex[position]); + } + + @Override + public int getCount() { + return mCount; + } + + public int getHiddenCount() { + return super.getCount() - mCount; + } + + @Override + public int getPosition() { + return mPos; + } + +} \ No newline at end of file -- cgit v1.2.3 From 3b7f21d98a667abec9cb9ddf855893b3a1ffdb8b Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Mar 2015 04:27:40 +0100 Subject: add expand item to linked id list --- .../keychain/ui/ViewKeyFragment.java | 7 +++- .../keychain/ui/adapter/LinkedIdsAdapter.java | 40 ++++++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index cfc1075af..153065bb7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -37,6 +37,7 @@ import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; +import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -70,6 +71,7 @@ public class ViewKeyFragment extends LoaderFragment implements private ListView mLinkedIds; private CardView mLinkedIdsCard; private byte[] mFingerprint; + private TextView mLinkedIdsExpander; /** * Creates new instance of this fragment @@ -113,6 +115,8 @@ public class ViewKeyFragment extends LoaderFragment implements mLinkedIds = (ListView) view.findViewById(R.id.view_key_linked_ids); + mLinkedIdsExpander = (TextView) view.findViewById(R.id.view_key_linked_ids_expander); + mUserIds.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { @@ -198,7 +202,8 @@ public class ViewKeyFragment extends LoaderFragment implements mUserIds.setAdapter(mUserIdsAdapter); getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); - mLinkedIdsAdapter = new LinkedIdsAdapter(getActivity(), null, 0, !mIsSecret); + mLinkedIdsAdapter = new LinkedIdsAdapter(getActivity(), null, 0, + !mIsSecret, mLinkedIdsExpander); mLinkedIds.setAdapter(mLinkedIdsAdapter); getLoaderManager().initLoader(LOADER_ID_LINKED_IDS, null, this); 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 e0f28efb8..c5bb58c3d 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 @@ -26,6 +26,7 @@ import android.support.v4.content.CursorLoader; import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; @@ -50,19 +51,36 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { protected LayoutInflater mInflater; WeakHashMap mLinkedIdentityCache = new WeakHashMap<>(); - public LinkedIdsAdapter(Context context, Cursor c, int flags, boolean showCertification) { + private Cursor mUnfilteredCursor; + + private TextView mExpander; + + public LinkedIdsAdapter(Context context, Cursor c, int flags, + boolean showCertification, TextView expander) { super(context, c, flags); mInflater = LayoutInflater.from(context); mShowCertification = showCertification; + + if (expander != null) { + mExpander = expander; + mExpander.setVisibility(View.GONE); + mExpander.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + showUnfiltered(); + } + }); + } } @Override public Cursor swapCursor(Cursor cursor) { if (cursor == null) { + mUnfilteredCursor = null; return super.swapCursor(null); } - - Cursor filteredCursor = new FilterCursorWrapper(cursor) { + mUnfilteredCursor = cursor; + FilterCursorWrapper filteredCursor = new FilterCursorWrapper(cursor) { @Override public boolean isVisible(Cursor cursor) { RawLinkedIdentity id = getItemAtPosition(cursor); @@ -70,9 +88,25 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { } }; + if (mExpander != null) { + int hidden = filteredCursor.getHiddenCount(); + if (hidden == 0) { + mExpander.setVisibility(View.GONE); + } else { + mExpander.setVisibility(View.VISIBLE); + mExpander.setText(mContext.getResources().getQuantityString( + R.plurals.linked_id_expand, hidden)); + } + } + return super.swapCursor(filteredCursor); } + private void showUnfiltered() { + mExpander.setVisibility(View.GONE); + super.swapCursor(mUnfilteredCursor); + } + @Override public void bindView(View view, Context context, Cursor cursor) { -- cgit v1.2.3 From f58ab3e439250cefdade219b0c8b89e60d471d0f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Mar 2015 05:11:46 +0100 Subject: hack loadSystemContact back in --- .../org/sufficientlysecure/keychain/ui/ViewKeyFragment.java | 10 +++++----- .../sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java | 8 ++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index c53f1b30a..2b497422f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -52,11 +52,7 @@ import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment; import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment.OnIdentityLoadedListener; -import org.sufficientlysecure.keychain.util.FilterCursorWrapper; -import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; -import org.sufficientlysecure.keychain.ui.dialog.UserIdInfoDialogFragment; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.ContactHelper; import org.sufficientlysecure.keychain.util.Log; @@ -295,6 +291,10 @@ public class ViewKeyFragment extends LoaderFragment implements switch (loader.getId()) { case LOADER_ID_USER_IDS: { mUserIdsAdapter.swapCursor(cursor); + + String guessedName = mUserIdsAdapter.getGuessedName(); + loadLinkedSystemContact(guessedName, + KeyFormattingUtils.convertFingerprintToKeyId(mFingerprint)); break; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java index 1cf3f4d38..7216bcfb3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/UserIdsAdapter.java @@ -177,6 +177,14 @@ public class UserIdsAdapter extends UserAttributesAdapter { return isRevokedPending; } + public String getGuessedName() { + Cursor cursor = getCursor(); + cursor.moveToFirst(); + String userId = cursor.getString(INDEX_USER_ID); + String[] splitUserId = KeyRing.splitUserId(userId); + return splitUserId[0]; + } + @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return mInflater.inflate(R.layout.view_key_adv_user_id_item, null); -- cgit v1.2.3 From 8d71a3fa92c9a9b690c547cbb970b2a5924c10d6 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 10 Mar 2015 19:02:51 +0100 Subject: better state management in LinkedIdView, move confirm progress into card --- .../keychain/ui/linked/LinkedIdViewFragment.java | 77 ++++++++++++++-------- 1 file changed, 50 insertions(+), 27 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 21c9abca3..742459ff8 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 @@ -47,6 +47,7 @@ import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; +import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment.ViewHolder.VerifyState; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; import org.sufficientlysecure.keychain.ui.widget.CertListWidget; @@ -242,7 +243,6 @@ public class LinkedIdViewFragment extends Fragment implements private final View vButtonBack; private final ViewAnimator vProgress; - private final ImageView vIcon; private final TextView vText; ViewHolder(View root) { @@ -261,12 +261,41 @@ public class LinkedIdViewFragment extends Fragment implements vVerifyingContainer = (ViewAnimator) root.findViewById(R.id.linked_verify_container); vProgress = (ViewAnimator) root.findViewById(R.id.linked_cert_progress); - vIcon = (ImageView) root.findViewById(R.id.linked_cert_icon); vText = (TextView) root.findViewById(R.id.linked_cert_text); } - void setShowProgress(boolean show) { - vProgress.setDisplayedChild(show ? 0 : 1); + enum VerifyState { + VERIFYING, VERIFY_OK, VERIFY_ERROR, CERTIFYING + } + + void setVerifyingState(VerifyState state) { + switch (state) { + case VERIFYING: + vProgress.setDisplayedChild(0); + vText.setText("Verifying…"); + vKeySpinner.setVisibility(View.GONE); + break; + + case VERIFY_OK: + showButton(2); + vText.setText("Ok"); + vProgress.setDisplayedChild(1); + vKeySpinner.setVisibility(View.VISIBLE); + break; + + case VERIFY_ERROR: + showButton(1); + vProgress.setDisplayedChild(2); + vText.setText("Error"); + vKeySpinner.setVisibility(View.GONE); + break; + + case CERTIFYING: + vProgress.setDisplayedChild(0); + vText.setText("Confirming…"); + vKeySpinner.setVisibility(View.GONE); + break; + } } void showButton(int which) { @@ -384,8 +413,7 @@ public class LinkedIdViewFragment extends Fragment implements setShowVerifying(true); mViewHolder.vKeySpinner.setVisibility(View.GONE); - mViewHolder.setShowProgress(true); - mViewHolder.vText.setText("Verifying…"); + mViewHolder.setVerifyingState(VerifyState.VERIFYING); mInProgress = new AsyncTask() { @Override @@ -406,16 +434,13 @@ public class LinkedIdViewFragment extends Fragment implements @Override protected void onPostExecute(LinkedVerifyResult result) { - mViewHolder.setShowProgress(false); if (isCancelled()) { return; } if (result.success()) { - mViewHolder.vText.setText("Ok"); - setupForConfirmation(); + mViewHolder.setVerifyingState(VerifyState.VERIFY_OK); } else { - mViewHolder.showButton(1); - mViewHolder.vText.setText("Error"); + mViewHolder.setVerifyingState(VerifyState.VERIFY_ERROR); } mInProgress = null; } @@ -423,15 +448,6 @@ public class LinkedIdViewFragment extends Fragment implements } - void setupForConfirmation() { - - // button is 'confirm' - mViewHolder.showButton(2); - - mViewHolder.vKeySpinner.setVisibility(View.VISIBLE); - - } - private void initiateCertifying() { // get the user's passphrase for this key (if required) String passphrase; @@ -478,6 +494,8 @@ public class LinkedIdViewFragment extends Fragment implements private void certifyResource(long certifyKeyId, String passphrase) { + mViewHolder.setVerifyingState(VerifyState.CERTIFYING); + Bundle data = new Bundle(); { @@ -502,16 +520,24 @@ public class LinkedIdViewFragment extends Fragment implements intent.putExtra(KeychainIntentService.EXTRA_DATA, data); // Message is received after signing is done in KeychainIntentService - KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity(), - getString(R.string.progress_certifying), ProgressDialog.STYLE_SPINNER, false) { + KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(getActivity()) { public void handleMessage(Message message) { // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); + Bundle data = message.getData(); + + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_UPDATE_PROGRESS) { + if (data.containsKey(DATA_MESSAGE)) { + mViewHolder.vText.setText(data.getString(DATA_MESSAGE)); + } else if (data.containsKey(DATA_MESSAGE_ID)) { + mViewHolder.vText.setText(data.getString(DATA_MESSAGE_ID)); + } + return; + } + if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { - Bundle data = message.getData(); CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT); - result.createNotify(getActivity()).show(); } } @@ -521,9 +547,6 @@ public class LinkedIdViewFragment extends Fragment implements Messenger messenger = new Messenger(saveHandler); intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - // show progress dialog - saveHandler.showProgressDialog(getActivity()); - // start service with intent getActivity().startService(intent); -- cgit v1.2.3 From a5e8825882a986bd25455a56e2eab2778fbdf75e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Mar 2015 20:46:50 +0100 Subject: finish implementing twitter resource --- .../keychain/pgp/linked/LinkedCookieResource.java | 2 +- .../keychain/pgp/linked/LinkedIdentity.java | 2 - .../pgp/linked/resources/TwitterResource.java | 229 +++++++++++++++------ .../keychain/ui/adapter/LinkedIdsAdapter.java | 1 - .../ui/linked/LinkedIdCreateFinalFragment.java | 14 +- .../linked/LinkedIdCreateTwitterStep1Fragment.java | 14 +- .../linked/LinkedIdCreateTwitterStep2Fragment.java | 31 +-- .../linked/LinkedIdCreateTwitterStep3Fragment.java | 29 +-- 8 files changed, 214 insertions(+), 108 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index 84b79920a..c92624f65 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java @@ -107,7 +107,7 @@ public abstract class LinkedCookieResource extends LinkedResource { String candidateFp = match.group(1).toLowerCase(); try { - int nonceCandidate = Integer.parseInt(match.group(2).toLowerCase(), 16); + int nonceCandidate = (int) Long.parseLong(match.group(2).toLowerCase(), 16); if (nonce != nonceCandidate) { log.add(LogType.MSG_LV_NONCE_ERROR, indent); 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 index ff5995329..d529a2a36 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java @@ -3,7 +3,6 @@ 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.R; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.util.Log; @@ -13,7 +12,6 @@ import java.nio.ByteBuffer; import java.util.Arrays; import android.content.Context; -import android.content.Intent; import android.support.annotation.DrawableRes; 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 index 3553ce740..d00fa7ece 100644 --- 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 @@ -1,22 +1,25 @@ 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.util.Base64; +import android.util.Log; import com.textuality.keybase.lib.JWalk; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; -import org.apache.http.client.ClientProtocolException; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; +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.OperationLog; import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; @@ -27,14 +30,47 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URI; -import java.net.URLEncoder; 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 { - TwitterResource(Set flags, HashMap params, URI uri) { + 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); + } public static String generateText (Context context, byte[] fingerprint, int nonce) { @@ -42,21 +78,127 @@ public class TwitterResource extends LinkedCookieResource { return LinkedCookieResource.generate(context, fingerprint, nonce); } - private String getTwitterStream(String screenName) { - String results = null; + @Override + protected String fetchResource(OperationLog log, int indent) { + + String authToken = getAuthToken(); + + if (authToken == null) { + 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"); - // Step 1: Encode consumer key and secret try { - // URL encode the consumer key and secret - String urlApiKey = URLEncoder.encode("6IhPnWbYxASAoAzH2QaUtHD0J", "UTF-8"); - String urlApiSecret = URLEncoder.encode("L0GnuiOnapWbSBbQtLIqtpeS5BTtvh06dmoMoKQfHQS8UwHuWm", "UTF-8"); + String response = getResponseBody(httpGet); + JSONObject obj = new JSONObject(response); + + if (!obj.has("text")) { + return null; + } + + JSONObject user = obj.getJSONObject("user"); + if (!mHandle.equalsIgnoreCase(user.getString("screen_name"))) { + return null; + } - // Concatenate the encoded consumer key, a colon character, and the - // encoded consumer secret - String combined = urlApiKey + ":" + urlApiSecret; + // update the results with the body of the response + return obj.getString("text"); + } catch (JSONException e) { + Log.e(Constants.TAG, "json error parsing stream", e); + return null; + } + + } + + @Override + public @DrawableRes int getDisplayIcon() { + return R.drawable.twitter; + } - // Base64 encode the string - String base64Encoded = Base64.encodeToString(combined.getBytes(), Base64.NO_WRAP); + @Override + public String getDisplayTitle(Context context) { + return "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) { + + String authToken = getAuthToken(); + + if (authToken == null) { + 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 + return null; + } catch (JSONException e) { + Log.e(Constants.TAG, "json error parsing stream", e); + return null; + } + } + + private static String authToken; + + private static String getAuthToken() { + if (authToken != null) { + return authToken; + } + try { + String base64Encoded = + "NkloUG5XYll4QVNBb0F6SDJRYVV0SEQwSjpMMEdudWlPbmFwV2JTQ" + + "mJRdExJcXRwZVM1QlR0dmgwNmRtb01vS1FmSFFTOFV3SHVXbQ=="; // Step 2: Obtain a bearer token HttpPost httpPost = new HttpPost("https://api.twitter.com/oauth2/token"); @@ -64,28 +206,21 @@ public class TwitterResource extends LinkedCookieResource { 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)); - String auth = JWalk.getString(rawAuthorization, "access_token"); // Applications should verify that the value associated with the // token_type key of the returned object is bearer - if (auth != null && JWalk.getString(rawAuthorization, "token_type").equals("bearer")) { - - // Step 3: Authenticate API requests with bearer token - HttpGet httpGet = - new HttpGet("https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=" + screenName); - - // construct a normal HTTPS request and include an Authorization - // header with the value of Bearer <> - httpGet.setHeader("Authorization", "Bearer " + auth); - httpGet.setHeader("Content-Type", "application/json"); - // update the results with the body of the response - results = getResponseBody(httpGet); + if (!"bearer".equals(JWalk.getString(rawAuthorization, "token_type"))) { + return null; } - } catch (UnsupportedEncodingException ex) { - } catch (JSONException ex) { - } catch (IllegalStateException ex1) { + + authToken = JWalk.getString(rawAuthorization, "access_token"); + return authToken; + + } catch (UnsupportedEncodingException | JSONException | IllegalStateException ex) { + Log.e(Constants.TAG, "auth token fetching error", ex); + return null; } - return results; + } private static String getResponseBody(HttpRequestBase request) { @@ -104,41 +239,17 @@ public class TwitterResource extends LinkedCookieResource { BufferedReader bReader = new BufferedReader( new InputStreamReader(inputStream, "UTF-8"), 8); - String line = null; + String line; while ((line = bReader.readLine()) != null) { sb.append(line); } } else { sb.append(reason); } - } catch (UnsupportedEncodingException ex) { - } catch (ClientProtocolException ex1) { - } catch (IOException ex2) { + } catch (IOException e) { + Log.e(Constants.TAG, "http request error", e); } return sb.toString(); } - @Override - protected String fetchResource(OperationLog log, int indent) { - return getTwitterStream("Valodim"); - } - - @Override - public @DrawableRes int getDisplayIcon() { - return R.drawable.twitter; - } - - @Override - public String getDisplayTitle(Context context) { - return "twitter"; - } - - @Override - public String getDisplayComment(Context context) { - return null; - } - - public static LinkedCookieResource create(Set flags, HashMap params, URI subUri) { - return null; - } } 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 c5bb58c3d..577fb1765 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 @@ -45,7 +45,6 @@ import org.sufficientlysecure.keychain.util.FilterCursorWrapper; import java.io.IOException; import java.util.WeakHashMap; - public class LinkedIdsAdapter extends UserAttributesAdapter { private final boolean mShowCertification; protected LayoutInflater mInflater; 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 ef76bc9d2..b8d36e0a1 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 @@ -33,7 +33,7 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { public static final String ARG_NONCE = "nonce"; protected static final int REQUEST_CODE_PASSPHRASE = 0x00007008; - private LinkedIdWizard mLinkedIdWizard; + protected LinkedIdWizard mLinkedIdWizard; private ImageView mVerifyImage; private View mVerifyProgress; @@ -113,16 +113,19 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { } } - private void proofVerify() { + protected void proofVerify() { setVerifyProgress(true, null); - final LinkedCookieResource resource = getResource(); - new AsyncTask() { @Override protected LinkedVerifyResult doInBackground(Void... params) { - return resource.verify(mLinkedIdWizard.mFingerprint, mResourceNonce); + LinkedCookieResource resource = getResource(); + LinkedVerifyResult result = resource.verify(mLinkedIdWizard.mFingerprint, mResourceNonce); + if (result.success()) { + mVerifiedResource = resource; + } + return result; } @Override @@ -130,7 +133,6 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { super.onPostExecute(result); if (result.success()) { setVerifyProgress(false, true); - mVerifiedResource = resource; } else { setVerifyProgress(false, false); // on error, show error message 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 e966fd71f..86579a132 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 @@ -75,7 +75,7 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { @Override protected Boolean doInBackground(Void... params) { - return true; // checkHandle(handle); + return true; // return checkHandle(handle); } @Override @@ -83,21 +83,21 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { super.onPostExecute(result); if (result == null) { - Notify.showNotify(getActivity(), "Connection error while checking username!", Notify.Style.ERROR); + Notify.showNotify(getActivity(), + "Connection error while checking username!", Notify.Style.ERROR); return; } if (!result) { - Notify.showNotify(getActivity(), "This handle does not exist on Twitter!", Notify.Style.ERROR); + Notify.showNotify(getActivity(), + "This handle does not exist on Twitter!", Notify.Style.ERROR); return; } int proofNonce = RawLinkedIdentity.generateNonce(); - String proofText = TwitterResource.generateText(getActivity(), - mLinkedIdWizard.mFingerprint, proofNonce); LinkedIdCreateTwitterStep2Fragment frag = - LinkedIdCreateTwitterStep2Fragment.newInstance(handle, proofNonce, proofText); + LinkedIdCreateTwitterStep2Fragment.newInstance(handle, proofNonce); mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); } @@ -114,7 +114,7 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { }); mEditHandle = (EditText) view.findViewById(R.id.linked_create_twitter_handle); - mEditHandle.setText("Valodim"); + mEditHandle.setText("v_debug"); return view; } 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 837b84d40..e2fa6d5f6 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 @@ -45,36 +45,46 @@ public class LinkedIdCreateTwitterStep2Fragment extends Fragment { TextView mVerifyStatus, mEditTweetTextLen; String mResourceHandle; - String mResourceNonce, mResourceString; + int mResourceNonce; + String mResourceString; String mCookiePreview; /** * Creates new instance of this fragment */ public static LinkedIdCreateTwitterStep2Fragment newInstance - (String handle, int proofNonce, String proofText) { + (String handle, int proofNonce) { LinkedIdCreateTwitterStep2Fragment frag = new LinkedIdCreateTwitterStep2Fragment(); Bundle args = new Bundle(); args.putString(HANDLE, handle); args.putInt(NONCE, proofNonce); - args.putString(TEXT, proofText); frag.setArguments(args); return frag; } + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + + mResourceHandle = getArguments().getString(HANDLE); + mResourceNonce = getArguments().getInt(NONCE); + + mResourceString = TwitterResource.generateText(getActivity(), + mLinkedIdWizard.mFingerprint, mResourceNonce); + + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { final View view = inflater.inflate(R.layout.linked_create_twitter_fragment_step2, container, false); mCookiePreview = TwitterResource.generatePreview(); - mResourceHandle = getArguments().getString(HANDLE); - mResourceNonce = getArguments().getString(NONCE); - mResourceString = getArguments().getString(TEXT); - view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { @@ -142,11 +152,4 @@ public class LinkedIdCreateTwitterStep2Fragment extends Fragment { return view; } - @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); - - mLinkedIdWizard = (LinkedIdWizard) getActivity(); - } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java index 0c317004b..cfa8d8d23 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java @@ -33,6 +33,7 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.pgp.linked.resources.TwitterResource; import org.sufficientlysecure.keychain.ui.util.Notify; import java.util.List; @@ -45,15 +46,16 @@ public class LinkedIdCreateTwitterStep3Fragment extends LinkedIdCreateFinalFragm String mResourceHandle, mCustom, mFullString; String mResourceString; + private int mNonce; public static LinkedIdCreateTwitterStep3Fragment newInstance - (String handle, String proofNonce, String proofText, String customText) { + (String handle, int proofNonce, String proofText, String customText) { LinkedIdCreateTwitterStep3Fragment frag = new LinkedIdCreateTwitterStep3Fragment(); Bundle args = new Bundle(); args.putString(ARG_HANDLE, handle); - args.putString(ARG_NONCE, proofNonce); + args.putInt(ARG_NONCE, proofNonce); args.putString(ARG_TEXT, proofText); args.putString(ARG_CUSTOM, customText); frag.setArguments(args); @@ -65,9 +67,11 @@ public class LinkedIdCreateTwitterStep3Fragment extends LinkedIdCreateFinalFragm public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mResourceHandle = getArguments().getString(ARG_HANDLE); - mResourceString = getArguments().getString(ARG_TEXT); - mCustom = getArguments().getString(ARG_CUSTOM); + Bundle args = getArguments(); + mResourceHandle = args.getString(ARG_HANDLE); + mResourceString = args.getString(ARG_TEXT); + mCustom = args.getString(ARG_CUSTOM); + mNonce = args.getInt(ARG_NONCE); mFullString = mCustom.isEmpty() ? mResourceString : (mCustom + " " + mResourceString); @@ -94,23 +98,12 @@ public class LinkedIdCreateTwitterStep3Fragment extends LinkedIdCreateFinalFragm } }); - view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - - // AffirmationCreateHttpsStep2Fragment frag = - // AffirmationCreateHttpsStep2Fragment.newInstance(); - - // mAffirmationWizard.loadFragment(null, frag, AffirmationWizard.FRAG_ACTION_TO_RIGHT); - } - }); - return view; } @Override LinkedCookieResource getResource() { - return null; + return TwitterResource.searchInTwitterStream(mResourceHandle, mFullString); } @Override @@ -137,7 +130,7 @@ public class LinkedIdCreateTwitterStep3Fragment extends LinkedIdCreateFinalFragm PackageManager.MATCH_DEFAULT_ONLY); boolean resolved = false; - for(ResolveInfo resolveInfo : resolvedInfoList){ + for(ResolveInfo resolveInfo : resolvedInfoList) { if(resolveInfo.activityInfo.packageName.startsWith("com.twitter.android")) { tweetIntent.setClassName( resolveInfo.activityInfo.packageName, -- cgit v1.2.3 From 4f31bbff0fec6e0ab2122cc0a85ba499e1610e6a Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Mar 2015 21:03:42 +0100 Subject: ditch custom text step for twitter --- .../linked/LinkedIdCreateTwitterStep1Fragment.java | 6 +- .../linked/LinkedIdCreateTwitterStep2Fragment.java | 155 --------------------- .../linked/LinkedIdCreateTwitterStep3Fragment.java | 31 +++-- 3 files changed, 18 insertions(+), 174 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep2Fragment.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 86579a132..06ca32e2e 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 @@ -94,10 +94,8 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { return; } - int proofNonce = RawLinkedIdentity.generateNonce(); - - LinkedIdCreateTwitterStep2Fragment frag = - LinkedIdCreateTwitterStep2Fragment.newInstance(handle, proofNonce); + LinkedIdCreateTwitterStep3Fragment frag = + LinkedIdCreateTwitterStep3Fragment.newInstance(handle); mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); } 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 e2fa6d5f6..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep2Fragment.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * 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.v4.app.Fragment; -import android.text.Editable; -import android.text.InputFilter; -import android.text.TextWatcher; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.linked.resources.TwitterResource; - -public class LinkedIdCreateTwitterStep2Fragment extends Fragment { - - public static final String HANDLE = "uri", NONCE = "nonce", TEXT = "text"; - - LinkedIdWizard mLinkedIdWizard; - - EditText mEditTweetCustom, mEditTweetPreview; - ImageView mVerifyImage; - View mVerifyProgress; - TextView mVerifyStatus, mEditTweetTextLen; - - String mResourceHandle; - int mResourceNonce; - String mResourceString; - String mCookiePreview; - - /** - * Creates new instance of this fragment - */ - public static LinkedIdCreateTwitterStep2Fragment newInstance - (String handle, int proofNonce) { - - LinkedIdCreateTwitterStep2Fragment frag = new LinkedIdCreateTwitterStep2Fragment(); - - Bundle args = new Bundle(); - args.putString(HANDLE, handle); - args.putInt(NONCE, proofNonce); - frag.setArguments(args); - - return frag; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mLinkedIdWizard = (LinkedIdWizard) getActivity(); - - mResourceHandle = getArguments().getString(HANDLE); - mResourceNonce = getArguments().getInt(NONCE); - - mResourceString = TwitterResource.generateText(getActivity(), - mLinkedIdWizard.mFingerprint, mResourceNonce); - - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - final View view = inflater.inflate(R.layout.linked_create_twitter_fragment_step2, container, false); - - mCookiePreview = TwitterResource.generatePreview(); - - view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - - LinkedIdCreateTwitterStep3Fragment frag = - LinkedIdCreateTwitterStep3Fragment.newInstance(mResourceHandle, - mResourceNonce, mResourceString, - mEditTweetCustom.getText().toString()); - - mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); - } - }); - - view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); - } - }); - - mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); - mVerifyProgress = view.findViewById(R.id.verify_progress); - mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); - - mEditTweetPreview = (EditText) view.findViewById(R.id.linked_create_twitter_preview); - mEditTweetPreview.setText(mCookiePreview); - - mEditTweetCustom = (EditText) view.findViewById(R.id.linked_create_twitter_custom); - mEditTweetCustom.setFilters(new InputFilter[] { - new InputFilter.LengthFilter(139 - mResourceString.length()) - }); - - mEditTweetTextLen = (TextView) view.findViewById(R.id.linked_create_twitter_textlen); - mEditTweetTextLen.setText(mResourceString.length() + "/140"); - - mEditTweetCustom.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) { - if (editable != null && editable.length() > 0) { - String str = editable + " " + mCookiePreview; - mEditTweetPreview.setText(str); - - mEditTweetTextLen.setText( - (editable.length() + mResourceString.length() + 1) + "/140"); - mEditTweetTextLen.setTextColor(getResources().getColor(str.length() == 140 - ? R.color.android_red_dark - : R.color.primary_dark_material_light)); - - - } else { - mEditTweetPreview.setText(mCookiePreview); - mEditTweetTextLen.setText(mResourceString.length() + "/140"); - } - } - }); - - return view; - } - -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java index cfa8d8d23..ce0526efc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java @@ -33,6 +33,9 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; 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.pgp.linked.resources.TwitterResource; import org.sufficientlysecure.keychain.ui.util.Notify; @@ -40,24 +43,24 @@ import java.util.List; public class LinkedIdCreateTwitterStep3Fragment extends LinkedIdCreateFinalFragment { - public static final String ARG_HANDLE = "uri", ARG_TEXT = "text", ARG_CUSTOM = "custom"; + public static final String ARG_HANDLE = "handle"; EditText mEditTweetPreview; - String mResourceHandle, mCustom, mFullString; + String mResourceHandle; String mResourceString; private int mNonce; public static LinkedIdCreateTwitterStep3Fragment newInstance - (String handle, int proofNonce, String proofText, String customText) { + (String handle) { LinkedIdCreateTwitterStep3Fragment frag = new LinkedIdCreateTwitterStep3Fragment(); + int proofNonce = RawLinkedIdentity.generateNonce(); + Bundle args = new Bundle(); args.putString(ARG_HANDLE, handle); args.putInt(ARG_NONCE, proofNonce); - args.putString(ARG_TEXT, proofText); - args.putString(ARG_CUSTOM, customText); frag.setArguments(args); return frag; @@ -67,13 +70,11 @@ public class LinkedIdCreateTwitterStep3Fragment extends LinkedIdCreateFinalFragm public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Bundle args = getArguments(); - mResourceHandle = args.getString(ARG_HANDLE); - mResourceString = args.getString(ARG_TEXT); - mCustom = args.getString(ARG_CUSTOM); - mNonce = args.getInt(ARG_NONCE); + mNonce = LinkedIdentity.generateNonce(); + mResourceString = + TwitterResource.generate(getActivity(), mLinkedIdWizard.mFingerprint, mNonce); - mFullString = mCustom.isEmpty() ? mResourceString : (mCustom + " " + mResourceString); + mResourceHandle = getArguments().getString(ARG_HANDLE); } @@ -82,7 +83,7 @@ public class LinkedIdCreateTwitterStep3Fragment extends LinkedIdCreateFinalFragm View view = super.onCreateView(inflater, container, savedInstanceState); mEditTweetPreview = (EditText) view.findViewById(R.id.linked_create_twitter_preview); - mEditTweetPreview.setText(mFullString); + mEditTweetPreview.setText(mResourceString); view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { @Override @@ -103,7 +104,7 @@ public class LinkedIdCreateTwitterStep3Fragment extends LinkedIdCreateFinalFragm @Override LinkedCookieResource getResource() { - return TwitterResource.searchInTwitterStream(mResourceHandle, mFullString); + return TwitterResource.searchInTwitterStream(mResourceHandle, mResourceString); } @Override @@ -114,7 +115,7 @@ public class LinkedIdCreateTwitterStep3Fragment extends LinkedIdCreateFinalFragm private void proofShare() { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, mFullString); + sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); sendIntent.setType("text/plain"); startActivity(sendIntent); } @@ -122,7 +123,7 @@ public class LinkedIdCreateTwitterStep3Fragment extends LinkedIdCreateFinalFragm private void proofSend() { Intent tweetIntent = new Intent(Intent.ACTION_SEND); - tweetIntent.putExtra(Intent.EXTRA_TEXT, mFullString); + tweetIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); tweetIntent.setType("text/plain"); PackageManager packManager = getActivity().getPackageManager(); -- cgit v1.2.3 From bebd72638460a2c6b27a9fb518326851d6019f23 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Mar 2015 21:07:43 +0100 Subject: twitter Step3 -> Step2 --- .../linked/LinkedIdCreateTwitterStep1Fragment.java | 6 +- .../linked/LinkedIdCreateTwitterStep2Fragment.java | 149 ++++++++++++++++++++ .../linked/LinkedIdCreateTwitterStep3Fragment.java | 154 --------------------- 3 files changed, 151 insertions(+), 158 deletions(-) create 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/LinkedIdCreateTwitterStep3Fragment.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 06ca32e2e..081b949d5 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 @@ -27,8 +27,6 @@ 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.TwitterResource; import org.sufficientlysecure.keychain.ui.util.Notify; import java.io.IOException; @@ -94,8 +92,8 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { return; } - LinkedIdCreateTwitterStep3Fragment frag = - LinkedIdCreateTwitterStep3Fragment.newInstance(handle); + LinkedIdCreateTwitterStep2Fragment frag = + LinkedIdCreateTwitterStep2Fragment.newInstance(handle); mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); } 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 new file mode 100644 index 000000000..1074a8e61 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep2Fragment.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.os.Bundle; +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.pgp.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; +import org.sufficientlysecure.keychain.pgp.linked.resources.TwitterResource; +import org.sufficientlysecure.keychain.ui.util.Notify; + +import java.util.List; + +public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragment { + + public static final String ARG_HANDLE = "handle"; + + EditText mEditTweetPreview; + + String mResourceHandle; + String mResourceString; + private int mNonce; + + public static LinkedIdCreateTwitterStep2Fragment newInstance + (String handle) { + + LinkedIdCreateTwitterStep2Fragment frag = new LinkedIdCreateTwitterStep2Fragment(); + + int proofNonce = RawLinkedIdentity.generateNonce(); + + Bundle args = new Bundle(); + args.putString(ARG_HANDLE, handle); + args.putInt(ARG_NONCE, proofNonce); + frag.setArguments(args); + + return frag; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mNonce = LinkedIdentity.generateNonce(); + mResourceString = + TwitterResource.generate(getActivity(), mLinkedIdWizard.mFingerprint, mNonce); + + mResourceHandle = getArguments().getString(ARG_HANDLE); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = super.onCreateView(inflater, container, savedInstanceState); + + mEditTweetPreview = (EditText) view.findViewById(R.id.linked_create_twitter_preview); + mEditTweetPreview.setText(mResourceString); + + 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; + } + + @Override + LinkedCookieResource getResource() { + return TwitterResource.searchInTwitterStream(mResourceHandle, mResourceString); + } + + @Override + protected View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.linked_create_twitter_fragment_step3, container, false); + } + + private void proofShare() { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); + sendIntent.setType("text/plain"); + startActivity(sendIntent); + } + + private void proofSend() { + + Intent tweetIntent = new Intent(Intent.ACTION_SEND); + tweetIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); + tweetIntent.setType("text/plain"); + + PackageManager packManager = getActivity().getPackageManager(); + List resolvedInfoList = packManager.queryIntentActivities(tweetIntent, + PackageManager.MATCH_DEFAULT_ONLY); + + boolean resolved = false; + for(ResolveInfo resolveInfo : resolvedInfoList) { + if(resolveInfo.activityInfo.packageName.startsWith("com.twitter.android")) { + tweetIntent.setClassName( + resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name ); + resolved = true; + break; + } + } + + if (resolved) { + startActivity(tweetIntent); + } else { + Notify.showNotify(getActivity(), + "Twitter app is not installed, please use the send intent!", + Notify.Style.ERROR); + } + + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java deleted file mode 100644 index ce0526efc..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateTwitterStep3Fragment.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2014 Dominik Schürmann - * - * 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.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.graphics.PorterDuff; -import android.os.Bundle; -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 android.widget.ImageView; -import android.widget.TextView; - -import org.sufficientlysecure.keychain.R; -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.pgp.linked.resources.TwitterResource; -import org.sufficientlysecure.keychain.ui.util.Notify; - -import java.util.List; - -public class LinkedIdCreateTwitterStep3Fragment extends LinkedIdCreateFinalFragment { - - public static final String ARG_HANDLE = "handle"; - - EditText mEditTweetPreview; - - String mResourceHandle; - String mResourceString; - private int mNonce; - - public static LinkedIdCreateTwitterStep3Fragment newInstance - (String handle) { - - LinkedIdCreateTwitterStep3Fragment frag = new LinkedIdCreateTwitterStep3Fragment(); - - int proofNonce = RawLinkedIdentity.generateNonce(); - - Bundle args = new Bundle(); - args.putString(ARG_HANDLE, handle); - args.putInt(ARG_NONCE, proofNonce); - frag.setArguments(args); - - return frag; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mNonce = LinkedIdentity.generateNonce(); - mResourceString = - TwitterResource.generate(getActivity(), mLinkedIdWizard.mFingerprint, mNonce); - - mResourceHandle = getArguments().getString(ARG_HANDLE); - - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = super.onCreateView(inflater, container, savedInstanceState); - - mEditTweetPreview = (EditText) view.findViewById(R.id.linked_create_twitter_preview); - mEditTweetPreview.setText(mResourceString); - - 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; - } - - @Override - LinkedCookieResource getResource() { - return TwitterResource.searchInTwitterStream(mResourceHandle, mResourceString); - } - - @Override - protected View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.linked_create_twitter_fragment_step3, container, false); - } - - private void proofShare() { - Intent sendIntent = new Intent(); - sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); - sendIntent.setType("text/plain"); - startActivity(sendIntent); - } - - private void proofSend() { - - Intent tweetIntent = new Intent(Intent.ACTION_SEND); - tweetIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); - tweetIntent.setType("text/plain"); - - PackageManager packManager = getActivity().getPackageManager(); - List resolvedInfoList = packManager.queryIntentActivities(tweetIntent, - PackageManager.MATCH_DEFAULT_ONLY); - - boolean resolved = false; - for(ResolveInfo resolveInfo : resolvedInfoList) { - if(resolveInfo.activityInfo.packageName.startsWith("com.twitter.android")) { - tweetIntent.setClassName( - resolveInfo.activityInfo.packageName, - resolveInfo.activityInfo.name ); - resolved = true; - break; - } - } - - if (resolved) { - startActivity(tweetIntent); - } else { - Notify.showNotify(getActivity(), - "Twitter app is not installed, please use the send intent!", - Notify.Style.ERROR); - } - - } - -} -- cgit v1.2.3 From 4adf2b343ce2fbf9749c413209e3c2fd96f14e44 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Mar 2015 22:06:30 +0100 Subject: small fix for CreateFinalFragment --- .../keychain/ui/linked/LinkedIdCreateFinalFragment.java | 4 ++-- .../keychain/ui/linked/LinkedIdCreateTwitterStep1Fragment.java | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 b8d36e0a1..07f47c31f 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 @@ -44,8 +44,8 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { LinkedCookieResource mVerifiedResource = null; @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); mLinkedIdWizard = (LinkedIdWizard) getActivity(); 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 081b949d5..c22351f87 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 @@ -39,9 +39,6 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { EditText mEditHandle; - /** - * Creates new instance of this fragment - */ public static LinkedIdCreateTwitterStep1Fragment newInstance() { LinkedIdCreateTwitterStep1Fragment frag = new LinkedIdCreateTwitterStep1Fragment(); -- cgit v1.2.3 From 706d33900432f5b6a4728bed3f154e78dba3086c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Mar 2015 22:09:05 +0100 Subject: regenerate twitter token --- .../pgp/linked/resources/TwitterResource.java | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index d00fa7ece..dc68be7f5 100644 --- 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 @@ -36,7 +36,6 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; - public class TwitterResource extends LinkedCookieResource { final String mHandle; @@ -196,9 +195,9 @@ public class TwitterResource extends LinkedCookieResource { return authToken; } try { - String base64Encoded = - "NkloUG5XYll4QVNBb0F6SDJRYVV0SEQwSjpMMEdudWlPbmFwV2JTQ" - + "mJRdExJcXRwZVM1QlR0dmgwNmRtb01vS1FmSFFTOFV3SHVXbQ=="; + + String base64Encoded = rot13("D293FQqanH0jH29KIaWJER5DomqSGRE2Ewc1LJACn3cbD1c" + + "Fq1bmqSAQAz5MI2cIHKOuo3cPoRAQI1OyqmIVFJS6LHMXq2g6MRLkIj") + "=="; // Step 2: Obtain a bearer token HttpPost httpPost = new HttpPost("https://api.twitter.com/oauth2/token"); @@ -252,4 +251,17 @@ public class TwitterResource extends LinkedCookieResource { return sb.toString(); } + 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(); + } + } -- cgit v1.2.3 From 163cbb04a00f298a26cd7a2ba19d107a5a8c233c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 12 Mar 2015 22:35:14 +0100 Subject: twitter layout step3 -> step2 --- .../keychain/ui/linked/LinkedIdCreateTwitterStep2Fragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 1074a8e61..262277700 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 @@ -104,7 +104,7 @@ public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragm @Override protected View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.linked_create_twitter_fragment_step3, container, false); + return inflater.inflate(R.layout.linked_create_twitter_fragment_step2, container, false); } private void proofShare() { -- cgit v1.2.3 From a9a5551d95d964b5037fb2e5308081618a33b802 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 13 Mar 2015 01:55:31 +0100 Subject: remove notiion of nonce/identifier --- .../operations/results/OperationResult.java | 2 -- .../keychain/pgp/WrappedUserAttribute.java | 5 +++-- .../keychain/pgp/linked/LinkedCookieResource.java | 26 +++++----------------- .../keychain/pgp/linked/LinkedIdentity.java | 20 +++++++---------- .../keychain/pgp/linked/LinkedResource.java | 4 +--- .../keychain/pgp/linked/RawLinkedIdentity.java | 25 ++------------------- .../keychain/pgp/linked/resources/DnsResource.java | 6 ++--- .../pgp/linked/resources/GenericHttpsResource.java | 4 ++-- .../pgp/linked/resources/TwitterResource.java | 5 ----- .../keychain/provider/KeychainProvider.java | 4 +++- .../ui/linked/LinkedIdCreateDnsStep1Fragment.java | 5 ++--- .../ui/linked/LinkedIdCreateDnsStep2Fragment.java | 3 +-- .../ui/linked/LinkedIdCreateFinalFragment.java | 8 ++----- .../linked/LinkedIdCreateHttpsStep1Fragment.java | 8 ++----- .../linked/LinkedIdCreateHttpsStep2Fragment.java | 3 +-- .../linked/LinkedIdCreateTwitterStep2Fragment.java | 9 +------- .../keychain/ui/linked/LinkedIdViewFragment.java | 4 +--- 17 files changed, 38 insertions(+), 103 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index 7999af667..fba52c4ad 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -770,8 +770,6 @@ public abstract class OperationResult implements Parcelable { MSG_LV_MATCH_ERROR (LogLevel.ERROR, R.string.msg_lv_match_error), MSG_LV_FP_OK (LogLevel.DEBUG, R.string.msg_lv_fp_ok), MSG_LV_FP_ERROR (LogLevel.ERROR, R.string.msg_lv_fp_error), - MSG_LV_NONCE_OK (LogLevel.OK, R.string.msg_lv_nonce_ok), - MSG_LV_NONCE_ERROR (LogLevel.ERROR, R.string.msg_lv_nonce_error), MSG_LV_FETCH (LogLevel.DEBUG, R.string.msg_lv_fetch), MSG_LV_FETCH_REDIR (LogLevel.DEBUG, R.string.msg_lv_fetch_redir), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java index 49e4d9793..2431cb743 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java @@ -41,7 +41,7 @@ public class WrappedUserAttribute implements Serializable { public static final int UAT_NONE = 0; public static final int UAT_IMAGE = UserAttributeSubpacketTags.IMAGE_ATTRIBUTE; - public static final int UAT_LINKED_ID = 100; + public static final int UAT_LINKED_ID = 101; private PGPUserAttributeSubpacketVector mVector; @@ -82,7 +82,7 @@ public class WrappedUserAttribute implements Serializable { public static WrappedUserAttribute fromData (byte[] data) throws IOException { UserAttributeSubpacketInputStream in = new UserAttributeSubpacketInputStream(new ByteArrayInputStream(data)); - ArrayList list = new ArrayList(); + ArrayList list = new ArrayList<>(); while (in.available() > 0) { list.add(in.readPacket()); } @@ -126,6 +126,7 @@ public class WrappedUserAttribute implements Serializable { private void readObjectNoData() throws ObjectStreamException { } + @SuppressWarnings("SimplifiableIfStatement") @Override public boolean equals(Object o) { if (!WrappedUserAttribute.class.isInstance(o)) { 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 index c92624f65..21a8c8f98 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java @@ -61,16 +61,16 @@ public abstract class LinkedCookieResource extends LinkedResource { return mSubUri; } - public static String generate (Context context, byte[] fingerprint, int nonce) { - return String.format("[Verifying my PGP key: openpgp4fpr:%s#%08x]", - KeyFormattingUtils.convertFingerprintToHex(fingerprint), nonce); + public static String generate (Context context, byte[] fingerprint) { + return String.format("[Verifying my PGP key: openpgp4fpr:%s]", + KeyFormattingUtils.convertFingerprintToHex(fingerprint)); } public static String generatePreview () { return "[Verifying my PGP key: openpgp4fpr:0x…]"; } - public LinkedVerifyResult verify(byte[] fingerprint, int nonce) { + public LinkedVerifyResult verify(byte[] fingerprint) { OperationLog log = new OperationLog(); log.add(LogType.MSG_LV, 0); @@ -84,7 +84,7 @@ public abstract class LinkedCookieResource extends LinkedResource { Log.d(Constants.TAG, "Resource data: '" + res + "'"); - return verifyString(log, 1, res, nonce, fingerprint); + return verifyString(log, 1, res, fingerprint); } @@ -96,7 +96,7 @@ public abstract class LinkedCookieResource extends LinkedResource { protected LinkedVerifyResult verifyString (OperationLog log, int indent, String res, - int nonce, byte[] fingerprint) { + byte[] fingerprint) { log.add(LogType.MSG_LV_MATCH, indent); Matcher match = matchResource(log, indent+1, res); @@ -106,27 +106,13 @@ public abstract class LinkedCookieResource extends LinkedResource { } String candidateFp = match.group(1).toLowerCase(); - try { - int nonceCandidate = (int) Long.parseLong(match.group(2).toLowerCase(), 16); - - if (nonce != nonceCandidate) { - log.add(LogType.MSG_LV_NONCE_ERROR, indent); - return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); - } - } catch (NumberFormatException e) { - log.add(LogType.MSG_LV_NONCE_ERROR, indent); - return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); - } - 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); - log.add(LogType.MSG_LV_NONCE_OK, indent); return new LinkedVerifyResult(LinkedVerifyResult.RESULT_OK, log); } 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 index d529a2a36..ed3031b84 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedIdentity.java @@ -8,8 +8,6 @@ import org.sufficientlysecure.keychain.util.Log; import java.io.IOException; import java.net.URI; -import java.nio.ByteBuffer; -import java.util.Arrays; import android.content.Context; import android.support.annotation.DrawableRes; @@ -19,8 +17,8 @@ public class LinkedIdentity extends RawLinkedIdentity { public final LinkedResource mResource; - protected LinkedIdentity(int nonce, URI uri, LinkedResource resource) { - super(nonce, uri); + protected LinkedIdentity(URI uri, LinkedResource resource) { + super(uri); if (resource == null) { throw new AssertionError("resource must not be null in a LinkedIdentity!"); } @@ -42,29 +40,27 @@ public class LinkedIdentity extends RawLinkedIdentity { * subpacket can not be parsed as a valid linked id. */ static RawLinkedIdentity fromAttributeSubpacket(UserAttributeSubpacket subpacket) { - if (subpacket.getType() != 100) { + if (subpacket.getType() != 101) { return null; } byte[] data = subpacket.getData(); return fromSubpacketData(data); - } static RawLinkedIdentity fromSubpacketData(byte[] data) { try { - int nonce = ByteBuffer.wrap(data).getInt(); - String uriStr = Strings.fromUTF8ByteArray(Arrays.copyOfRange(data, 4, data.length)); + String uriStr = Strings.fromUTF8ByteArray(data); URI uri = URI.create(uriStr); LinkedResource res = LinkedResource.fromUri(uri); if (res == null) { - return new RawLinkedIdentity(nonce, uri); + return new RawLinkedIdentity(uri); } - return new LinkedIdentity(nonce, uri, res); + return new LinkedIdentity(uri, res); } catch (IllegalArgumentException e) { Log.e(Constants.TAG, "error parsing uri in (suspected) linked id packet"); @@ -72,8 +68,8 @@ public class LinkedIdentity extends RawLinkedIdentity { } } - public static RawLinkedIdentity fromResource (LinkedCookieResource res, int nonce) { - return new RawLinkedIdentity(nonce, res.toUri()); + public static RawLinkedIdentity fromResource (LinkedCookieResource res) { + return new RawLinkedIdentity(res.toUri()); } 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 index 095fd4ac7..e954a514c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java @@ -23,7 +23,7 @@ public abstract class LinkedResource { protected final HashMap mParams; static Pattern magicPattern = - Pattern.compile("\\[Verifying my PGP key: openpgp4fpr:([a-zA-Z0-9]+)#([a-zA-Z0-9]+)\\]"); + Pattern.compile("\\[Verifying my PGP key: openpgp4fpr:([a-zA-Z0-9]+)]"); protected LinkedResource(Set flags, HashMap params, URI uri) { mFlags = flags; @@ -31,8 +31,6 @@ public abstract class LinkedResource { mSubUri = uri; } - public abstract URI toUri(); - public Set getFlags () { return new HashSet<>(mFlags); } 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 index 8f0467734..b3acc6790 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/RawLinkedIdentity.java @@ -5,48 +5,27 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import java.net.URI; -import java.nio.ByteBuffer; import android.content.Context; -import android.content.Intent; import android.support.annotation.DrawableRes; /** The RawLinkedIdentity contains raw parsed data from a Linked Identity subpacket. */ public class RawLinkedIdentity { - public final int mNonce; public final URI mUri; - protected RawLinkedIdentity(int nonce, URI uri) { - mNonce = nonce; + protected RawLinkedIdentity(URI uri) { mUri = uri; } public byte[] getEncoded() { - byte[] uriData = Strings.toUTF8ByteArray(mUri.toASCIIString()); - - ByteBuffer buf = ByteBuffer.allocate(4 + uriData.length); - - buf.putInt(mNonce); - buf.put(uriData); - - return buf.array(); + return Strings.toUTF8ByteArray(mUri.toASCIIString()); } public WrappedUserAttribute toUserAttribute () { return WrappedUserAttribute.fromSubpacket(WrappedUserAttribute.UAT_LINKED_ID, getEncoded()); } - public static int generateNonce() { - // TODO make this actually random - // byte[] data = new byte[4]; - // new SecureRandom().nextBytes(data); - // return Hex.toHexString(data); - - // debug for now - return 0x8a9bad32; - } - public @DrawableRes int getDisplayIcon() { return R.drawable.ic_warning_grey_24dp; } 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 index cd0706ff3..253e611a8 100644 --- 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 @@ -41,10 +41,10 @@ public class DnsResource extends LinkedCookieResource { mType = type; } - public static String generateText (Context context, byte[] fingerprint, int nonce) { + public static String generateText (Context context, byte[] fingerprint) { - return String.format("pgpid+cookie=%s;%08x", - KeyFormattingUtils.convertFingerprintToHex(fingerprint), nonce); + return String.format("pgpid+cookie=%s", + KeyFormattingUtils.convertFingerprintToHex(fingerprint)); } 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 index eebe0b5ec..1e872c6cb 100644 --- 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 @@ -31,8 +31,8 @@ public class GenericHttpsResource extends LinkedCookieResource { super(flags, params, uri); } - public static String generateText (Context context, byte[] fingerprint, int nonce) { - String cookie = LinkedCookieResource.generate(context, fingerprint, nonce); + 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)); 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 index dc68be7f5..32bf92a99 100644 --- 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 @@ -72,11 +72,6 @@ public class TwitterResource extends LinkedCookieResource { } - public static String generateText (Context context, byte[] fingerprint, int nonce) { - // nothing special here for now, might change this later - return LinkedCookieResource.generate(context, fingerprint, nonce); - } - @Override protected String fetchResource(OperationLog log, int indent) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 19634bed8..4a84397a4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -31,6 +31,7 @@ import android.net.Uri; import android.text.TextUtils; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAllowedKeys; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; @@ -508,7 +509,8 @@ public class KeychainProvider extends ContentProvider { + ", " + Tables.USER_PACKETS + "." + UserPackets.RANK; if (match == KEY_RING_LINKED_IDS) { - qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " = 100"); + qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " = " + + WrappedUserAttribute.UAT_LINKED_ID); } else { qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL"); } 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 26b0a0539..6b3eef26a 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 @@ -73,12 +73,11 @@ public class LinkedIdCreateDnsStep1Fragment extends Fragment { return; } - int proofNonce = RawLinkedIdentity.generateNonce(); String proofText = DnsResource.generateText(getActivity(), - mLinkedIdWizard.mFingerprint, proofNonce); + mLinkedIdWizard.mFingerprint); LinkedIdCreateDnsStep2Fragment frag = - LinkedIdCreateDnsStep2Fragment.newInstance(uri, proofNonce, proofText); + LinkedIdCreateDnsStep2Fragment.newInstance(uri, proofText); mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); 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 de3a63256..8715c0a2e 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 @@ -52,12 +52,11 @@ public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment String mResourceString; public static LinkedIdCreateDnsStep2Fragment newInstance - (String uri, int proofNonce, String proofText) { + (String uri, String proofText) { LinkedIdCreateDnsStep2Fragment frag = new LinkedIdCreateDnsStep2Fragment(); Bundle args = new Bundle(); - args.putInt(ARG_NONCE, proofNonce); args.putString(DOMAIN, uri); args.putString(TEXT, proofText); frag.setArguments(args); 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 07f47c31f..7be967907 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 @@ -30,7 +30,6 @@ import org.sufficientlysecure.keychain.ui.util.Notify; public abstract class LinkedIdCreateFinalFragment extends Fragment { - public static final String ARG_NONCE = "nonce"; protected static final int REQUEST_CODE_PASSPHRASE = 0x00007008; protected LinkedIdWizard mLinkedIdWizard; @@ -38,7 +37,6 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { private ImageView mVerifyImage; private View mVerifyProgress; private TextView mVerifyStatus; - private int mResourceNonce; // This is a resource, set AFTER it has been verified LinkedCookieResource mVerifiedResource = null; @@ -48,8 +46,6 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { super.onCreate(savedInstanceState); mLinkedIdWizard = (LinkedIdWizard) getActivity(); - - mResourceNonce = getArguments().getInt(ARG_NONCE); } protected abstract View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); @@ -121,7 +117,7 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { @Override protected LinkedVerifyResult doInBackground(Void... params) { LinkedCookieResource resource = getResource(); - LinkedVerifyResult result = resource.verify(mLinkedIdWizard.mFingerprint, mResourceNonce); + LinkedVerifyResult result = resource.verify(mLinkedIdWizard.mFingerprint); if (result.success()) { mVerifiedResource = resource; } @@ -195,7 +191,7 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint); WrappedUserAttribute ua = - LinkedIdentity.fromResource(mVerifiedResource, mResourceNonce).toUserAttribute(); + LinkedIdentity.fromResource(mVerifiedResource).toUserAttribute(); skp.mAddUserAttribute.add(ua); 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 78ca4cfe9..72669142b 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 @@ -38,9 +38,6 @@ public class LinkedIdCreateHttpsStep1Fragment extends Fragment { EditText mEditUri; - /** - * Creates new instance of this fragment - */ public static LinkedIdCreateHttpsStep1Fragment newInstance() { LinkedIdCreateHttpsStep1Fragment frag = new LinkedIdCreateHttpsStep1Fragment(); @@ -72,12 +69,11 @@ public class LinkedIdCreateHttpsStep1Fragment extends Fragment { return; } - int proofNonce = RawLinkedIdentity.generateNonce(); String proofText = GenericHttpsResource.generateText(getActivity(), - mLinkedIdWizard.mFingerprint, proofNonce); + mLinkedIdWizard.mFingerprint); LinkedIdCreateHttpsStep2Fragment frag = - LinkedIdCreateHttpsStep2Fragment.newInstance(uri, proofNonce, proofText); + LinkedIdCreateHttpsStep2Fragment.newInstance(uri, proofText); mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); 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 adae7eaf5..55ac6e075 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 @@ -53,12 +53,11 @@ public class LinkedIdCreateHttpsStep2Fragment extends LinkedIdCreateFinalFragmen String mResourceString; public static LinkedIdCreateHttpsStep2Fragment newInstance - (String uri, int proofNonce, String proofText) { + (String uri, String proofText) { LinkedIdCreateHttpsStep2Fragment frag = new LinkedIdCreateHttpsStep2Fragment(); Bundle args = new Bundle(); - args.putInt(ARG_NONCE, proofNonce); args.putString(ARG_URI, uri); args.putString(ARG_TEXT, proofText); frag.setArguments(args); 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 262277700..fa0b2911c 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 @@ -29,8 +29,6 @@ import android.widget.EditText; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; -import org.sufficientlysecure.keychain.pgp.linked.LinkedIdentity; -import org.sufficientlysecure.keychain.pgp.linked.RawLinkedIdentity; import org.sufficientlysecure.keychain.pgp.linked.resources.TwitterResource; import org.sufficientlysecure.keychain.ui.util.Notify; @@ -44,18 +42,14 @@ public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragm String mResourceHandle; String mResourceString; - private int mNonce; public static LinkedIdCreateTwitterStep2Fragment newInstance (String handle) { LinkedIdCreateTwitterStep2Fragment frag = new LinkedIdCreateTwitterStep2Fragment(); - int proofNonce = RawLinkedIdentity.generateNonce(); - Bundle args = new Bundle(); args.putString(ARG_HANDLE, handle); - args.putInt(ARG_NONCE, proofNonce); frag.setArguments(args); return frag; @@ -65,9 +59,8 @@ public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragm public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mNonce = LinkedIdentity.generateNonce(); mResourceString = - TwitterResource.generate(getActivity(), mLinkedIdWizard.mFingerprint, mNonce); + TwitterResource.generate(getActivity(), mLinkedIdWizard.mFingerprint); mResourceHandle = getArguments().getString(ARG_HANDLE); 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 742459ff8..98adb5889 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 @@ -4,7 +4,6 @@ import java.io.IOException; import java.util.Arrays; import android.app.Activity; -import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.database.Cursor; @@ -24,7 +23,6 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; -import android.widget.ImageView; import android.widget.TextView; import android.widget.ViewAnimator; @@ -419,7 +417,7 @@ public class LinkedIdViewFragment extends Fragment implements @Override protected LinkedVerifyResult doInBackground(Void... params) { long timer = System.currentTimeMillis(); - LinkedVerifyResult result = mLinkedResource.verify(mFingerprint, mLinkedId.mNonce); + LinkedVerifyResult result = mLinkedResource.verify(mFingerprint); // ux flow: this operation should take at last a second timer = System.currentTimeMillis() -timer; -- cgit v1.2.3 From 8fcffdd7cd1732b10ed6872fc88aead356fd7dbd Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 13 Mar 2015 13:54:52 +0100 Subject: use ViewAnimator for progress, streamline text on first step --- .../keychain/ui/linked/LinkedIdCreateFinalFragment.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 7be967907..38dce0c17 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 @@ -15,6 +15,7 @@ import android.view.View.OnClickListener; 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.operations.results.LinkedVerifyResult; @@ -35,8 +36,8 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { protected LinkedIdWizard mLinkedIdWizard; private ImageView mVerifyImage; - private View mVerifyProgress; private TextView mVerifyStatus; + private ViewAnimator mVerifyAnimator; // This is a resource, set AFTER it has been verified LinkedCookieResource mVerifiedResource = null; @@ -69,7 +70,7 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { }); mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); - mVerifyProgress = view.findViewById(R.id.verify_progress); + mVerifyAnimator = (ViewAnimator) view.findViewById(R.id.verify_progress); mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { @@ -89,13 +90,8 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { abstract LinkedCookieResource getResource(); private void setVerifyProgress(boolean on, Boolean success) { - mVerifyProgress.setVisibility(on ? View.VISIBLE : View.GONE); - mVerifyImage.setVisibility(on ? View.GONE : View.VISIBLE); if (success == null) { mVerifyStatus.setText(R.string.linked_verifying); - mVerifyImage.setImageResource(R.drawable.status_signature_unverified_cutout_24dp); - mVerifyImage.setColorFilter(getResources().getColor(R.color.tertiary_text_light), - PorterDuff.Mode.SRC_IN); } else if (success) { mVerifyStatus.setText(R.string.linked_verify_success); mVerifyImage.setImageResource(R.drawable.status_signature_verified_cutout_24dp); @@ -107,6 +103,7 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { mVerifyImage.setColorFilter(getResources().getColor(R.color.android_red_dark), PorterDuff.Mode.SRC_IN); } + mVerifyAnimator.setDisplayedChild(on ? 1 : 0); } protected void proofVerify() { -- cgit v1.2.3 From 4c122dfb601e65a145019d26270ab4ec6c56df04 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 13 Mar 2015 14:07:37 +0100 Subject: verification takes a second at least --- .../keychain/ui/linked/LinkedIdCreateFinalFragment.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 38dce0c17..040c26aab 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 @@ -86,7 +86,6 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { return view; } - abstract LinkedCookieResource getResource(); private void setVerifyProgress(boolean on, Boolean success) { @@ -113,8 +112,19 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { @Override protected LinkedVerifyResult doInBackground(Void... params) { + long timer = System.currentTimeMillis(); + LinkedCookieResource resource = getResource(); LinkedVerifyResult result = resource.verify(mLinkedIdWizard.mFingerprint); + + // 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; } -- cgit v1.2.3 From 9e379fd19c2b6caf4cfcba2c485a0380eb971e88 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 13 Mar 2015 14:37:52 +0100 Subject: improve error handling in LinkedIdViewFragment, disable unknown identity types (for now?) --- .../keychain/ui/adapter/LinkedIdsAdapter.java | 4 ++- .../keychain/ui/linked/LinkedIdViewFragment.java | 31 +++++++++++++++++----- 2 files changed, 27 insertions(+), 8 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 577fb1765..9eb919c85 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 @@ -61,14 +61,16 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { mShowCertification = showCertification; if (expander != null) { + expander.setVisibility(View.GONE); + /* don't show an expander (maybe in some sort of advanced view?) mExpander = expander; - mExpander.setVisibility(View.GONE); mExpander.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showUnfiltered(); } }); + */ } } 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 98adb5889..9501e454c 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 @@ -48,6 +48,8 @@ import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment.ViewHolder.VerifyState; 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.widget.CertListWidget; import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner; import org.sufficientlysecure.keychain.util.Log; @@ -127,10 +129,10 @@ public class LinkedIdViewFragment extends Fragment implements switch (loader.getId()) { case LOADER_ID_LINKED_ID: - // TODO proper error reporting and null checks here! - if (!cursor.moveToFirst()) { - Log.e(Constants.TAG, "error"); + Notify.createNotify(getActivity(), "Error loading identity!", + Notify.LENGTH_LONG, Style.ERROR).show(); + finishFragment(); break; } @@ -148,14 +150,22 @@ public class LinkedIdViewFragment extends Fragment implements } } catch (IOException e) { - e.printStackTrace(); - throw new AssertionError("reconstruction of user attribute must succeed!"); + Log.e(Constants.TAG, "error parsing identity", e); + Notify.createNotify(getActivity(), "Error parsing identity!", + Notify.LENGTH_LONG, Style.ERROR).show(); + finishFragment(); } break; } } + public void finishFragment() { + FragmentManager manager = getFragmentManager(); + manager.removeOnBackStackChangedListener(this); + manager.popBackStack("linked_id", FragmentManager.POP_BACK_STACK_INCLUSIVE); + } + public interface OnIdentityLoadedListener { public void onIdentityLoaded(); } @@ -296,6 +306,13 @@ public class LinkedIdViewFragment extends Fragment implements } } + void showVerifyingContainer(boolean show) { + if (vVerifyingContainer.getDisplayedChild() == (show ? 1 : 0)) { + return; + } + vVerifyingContainer.setDisplayedChild(show ? 1 : 0); + } + void showButton(int which) { if (vButtonSwitcher.getDisplayedChild() == which) { return; @@ -335,7 +352,7 @@ public class LinkedIdViewFragment extends Fragment implements mViewHolder.showButton(0); mViewHolder.vKeySpinner.setVisibility(View.GONE); - mViewHolder.vVerifyingContainer.setDisplayedChild(0); + mViewHolder.showVerifyingContainer(false); return; } @@ -348,7 +365,7 @@ public class LinkedIdViewFragment extends Fragment implements manager.beginTransaction().addToBackStack("verification").commit(); manager.executePendingTransactions(); manager.addOnBackStackChangedListener(this); - mViewHolder.vVerifyingContainer.setDisplayedChild(1); + mViewHolder.showVerifyingContainer(true); } -- cgit v1.2.3 From 9e3125c15eea3a9df33577ef613086b1f0efea27 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 13 Mar 2015 14:58:06 +0100 Subject: don't filter tweet intent --- .../linked/LinkedIdCreateTwitterStep2Fragment.java | 37 ++++------------------ 1 file changed, 6 insertions(+), 31 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 fa0b2911c..f8322d3c4 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 @@ -18,8 +18,7 @@ package org.sufficientlysecure.keychain.ui.linked; import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; +import android.net.Uri; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -30,9 +29,6 @@ import android.widget.EditText; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; import org.sufficientlysecure.keychain.pgp.linked.resources.TwitterResource; -import org.sufficientlysecure.keychain.ui.util.Notify; - -import java.util.List; public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragment { @@ -110,33 +106,12 @@ public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragm private void proofSend() { - Intent tweetIntent = new Intent(Intent.ACTION_SEND); - tweetIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); - tweetIntent.setType("text/plain"); - - PackageManager packManager = getActivity().getPackageManager(); - List resolvedInfoList = packManager.queryIntentActivities(tweetIntent, - PackageManager.MATCH_DEFAULT_ONLY); - - boolean resolved = false; - for(ResolveInfo resolveInfo : resolvedInfoList) { - if(resolveInfo.activityInfo.packageName.startsWith("com.twitter.android")) { - tweetIntent.setClassName( - resolveInfo.activityInfo.packageName, - resolveInfo.activityInfo.name ); - resolved = true; - break; - } - } - - if (resolved) { - startActivity(tweetIntent); - } else { - Notify.showNotify(getActivity(), - "Twitter app is not installed, please use the send intent!", - Notify.Style.ERROR); - } + Uri.Builder builder = Uri.parse("https://twitter.com/intent/tweet").buildUpon(); + builder.appendQueryParameter("text", mResourceString); + Uri uri = builder.build(); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + getActivity().startActivity(intent); } } -- cgit v1.2.3 From 3f844cc70b487507f1f41f50b1d9867463b2161b Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 13 Mar 2015 16:30:49 +0100 Subject: work on second step of identity creation --- .../ui/linked/LinkedIdCreateDnsStep2Fragment.java | 22 +++++----------------- .../ui/linked/LinkedIdCreateFinalFragment.java | 21 ++++++++++++++++++++- .../linked/LinkedIdCreateTwitterStep2Fragment.java | 5 ----- 3 files changed, 25 insertions(+), 23 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 8715c0a2e..2e6181f07 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 @@ -30,6 +30,7 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; import org.sufficientlysecure.keychain.pgp.linked.resources.DnsResource; import org.sufficientlysecure.keychain.ui.util.Notify; @@ -92,7 +93,7 @@ public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment view.findViewById(R.id.button_save).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - proofSave(); + proofToClipboard(); } }); @@ -115,22 +116,9 @@ public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment startActivity(sendIntent); } - private void proofSave () { - String state = Environment.getExternalStorageState(); - if (!Environment.MEDIA_MOUNTED.equals(state)) { - Notify.showNotify(getActivity(), "External storage not available!", Style.ERROR); - return; - } - - String targetName = "pgpkey.txt"; - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - File targetFile = new File(Constants.Path.APP_DIR, targetName); - FileHelper.saveFile(this, getString(R.string.title_decrypt_to_file), - getString(R.string.specify_file_to_decrypt_to), targetFile, REQUEST_CODE_OUTPUT); - } else { - FileHelper.saveDocument(this, "text/plain", targetName, REQUEST_CODE_OUTPUT); - } + private void proofToClipboard() { + ClipboardReflection.copyToClipboard(getActivity(), mResourceString); + Notify.showNotify(getActivity(), R.string.linked_text_clipboard, Notify.Style.OK); } private void saveFile(Uri uri) { 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 040c26aab..30dfd8dfd 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 @@ -41,6 +41,7 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { // This is a resource, set AFTER it has been verified LinkedCookieResource mVerifiedResource = null; + private ViewAnimator mVerifyButtonAnimator; @Override public void onCreate(Bundle savedInstanceState) { @@ -69,9 +70,10 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { } }); - mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); mVerifyAnimator = (ViewAnimator) view.findViewById(R.id.verify_progress); + mVerifyImage = (ImageView) view.findViewById(R.id.verify_image); mVerifyStatus = (TextView) view.findViewById(R.id.verify_status); + mVerifyButtonAnimator = (ViewAnimator) view.findViewById(R.id.verify_buttons); view.findViewById(R.id.button_verify).setOnClickListener(new OnClickListener() { @Override @@ -80,6 +82,13 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { } }); + view.findViewById(R.id.button_retry).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + proofVerify(); + } + }); + setVerifyProgress(false, null); mVerifyStatus.setText(R.string.linked_verify_pending); @@ -91,20 +100,30 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { 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); 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 f8322d3c4..2de9b54c9 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 @@ -34,8 +34,6 @@ public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragm public static final String ARG_HANDLE = "handle"; - EditText mEditTweetPreview; - String mResourceHandle; String mResourceString; @@ -66,9 +64,6 @@ public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragm public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); - mEditTweetPreview = (EditText) view.findViewById(R.id.linked_create_twitter_preview); - mEditTweetPreview.setText(mResourceString); - view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { -- cgit v1.2.3 From ba7d8a58673d55660bc7faf68970229f17458514 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 14 Mar 2015 23:10:58 +0100 Subject: show certificates for secret keys differently --- .../keychain/ui/ViewKeyFragment.java | 2 +- .../keychain/ui/adapter/LinkedIdsAdapter.java | 11 +++--- .../keychain/ui/linked/LinkedIdViewFragment.java | 41 +++++++++++++++------- .../keychain/ui/widget/CertListWidget.java | 20 ++++++----- 4 files changed, 46 insertions(+), 28 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index 2b497422f..06db6eadc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -262,7 +262,7 @@ public class ViewKeyFragment extends LoaderFragment implements getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); mLinkedIdsAdapter = new LinkedIdsAdapter(getActivity(), null, 0, - !mIsSecret, mLinkedIdsExpander); + mIsSecret, mLinkedIdsExpander); mLinkedIds.setAdapter(mLinkedIdsAdapter); getLoaderManager().initLoader(LOADER_ID_LINKED_IDS, null, this); 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 9eb919c85..9727fab1d 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 @@ -26,7 +26,6 @@ import android.support.v4.content.CursorLoader; import android.util.Log; import android.view.LayoutInflater; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; @@ -46,7 +45,7 @@ import java.io.IOException; import java.util.WeakHashMap; public class LinkedIdsAdapter extends UserAttributesAdapter { - private final boolean mShowCertification; + private final boolean mIsSecret; protected LayoutInflater mInflater; WeakHashMap mLinkedIdentityCache = new WeakHashMap<>(); @@ -55,10 +54,10 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { private TextView mExpander; public LinkedIdsAdapter(Context context, Cursor c, int flags, - boolean showCertification, TextView expander) { + boolean isSecret, TextView expander) { super(context, c, flags); mInflater = LayoutInflater.from(context); - mShowCertification = showCertification; + mIsSecret = isSecret; if (expander != null) { expander.setVisibility(View.GONE); @@ -113,7 +112,7 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { ViewHolder holder = (ViewHolder) view.getTag(); - if (mShowCertification) { + if (!mIsSecret) { holder.vVerified.setVisibility(View.VISIBLE); int isVerified = cursor.getInt(INDEX_VERIFIED); switch (isVerified) { @@ -192,7 +191,7 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { int rank = c.getInt(UserIdsAdapter.INDEX_RANK); Uri dataUri = UserPackets.buildLinkedIdsUri(baseUri); - return LinkedIdViewFragment.newInstance(dataUri, rank, mShowCertification, fingerprint); + return LinkedIdViewFragment.newInstance(dataUri, rank, mIsSecret, fingerprint); } public static class ViewHolder { 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 9501e454c..1cec12b26 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 @@ -62,13 +62,13 @@ public class LinkedIdViewFragment extends Fragment implements private static final String ARG_DATA_URI = "data_uri"; private static final String ARG_LID_RANK = "rank"; - private static final String ARG_SHOWCERT = "verified"; + private static final String ARG_IS_SECRET = "verified"; private static final String ARG_FINGERPRINT = "fingerprint"; private static final int LOADER_ID_LINKED_ID = 1; private RawLinkedIdentity mLinkedId; private LinkedCookieResource mLinkedResource; - private boolean mShowCert; + private boolean mIsSecret; private Context mContext; private byte[] mFingerprint; @@ -81,13 +81,13 @@ public class LinkedIdViewFragment extends Fragment implements private OnIdentityLoadedListener mIdLoadedListener; public static LinkedIdViewFragment newInstance(Uri dataUri, int rank, - boolean showCertified, byte[] fingerprint) throws IOException { + boolean isSecret, byte[] fingerprint) throws IOException { LinkedIdViewFragment frag = new LinkedIdViewFragment(); Bundle args = new Bundle(); args.putParcelable(ARG_DATA_URI, dataUri); args.putInt(ARG_LID_RANK, rank); - args.putBoolean(ARG_SHOWCERT, showCertified); + args.putBoolean(ARG_IS_SECRET, isSecret); args.putByteArray(ARG_FINGERPRINT, fingerprint); frag.setArguments(args); @@ -102,7 +102,7 @@ public class LinkedIdViewFragment extends Fragment implements mDataUri = args.getParcelable(ARG_DATA_URI); mLidRank = args.getInt(ARG_LID_RANK); - mShowCert = args.getBoolean(ARG_SHOWCERT); + mIsSecret = args.getBoolean(ARG_IS_SECRET); mFingerprint = args.getByteArray(ARG_FINGERPRINT); mContext = getActivity(); @@ -184,7 +184,7 @@ public class LinkedIdViewFragment extends Fragment implements mLinkedResource = (LinkedCookieResource) res; } - if (mShowCert) { + if (!mIsSecret) { mViewHolder.mLinkedIdHolder.vVerified.setVisibility(View.VISIBLE); switch (certStatus) { @@ -276,7 +276,7 @@ public class LinkedIdViewFragment extends Fragment implements VERIFYING, VERIFY_OK, VERIFY_ERROR, CERTIFYING } - void setVerifyingState(VerifyState state) { + void setVerifyingState(VerifyState state, boolean isSecret) { switch (state) { case VERIFYING: vProgress.setDisplayedChild(0); @@ -285,10 +285,15 @@ public class LinkedIdViewFragment extends Fragment implements break; case VERIFY_OK: - showButton(2); vText.setText("Ok"); vProgress.setDisplayedChild(1); - vKeySpinner.setVisibility(View.VISIBLE); + if (!isSecret) { + showButton(2); + vKeySpinner.setVisibility(View.VISIBLE); + } else { + showButton(1); + vKeySpinner.setVisibility(View.GONE); + } break; case VERIFY_ERROR: @@ -411,6 +416,7 @@ public class LinkedIdViewFragment extends Fragment implements { Bundle args = new Bundle(); args.putParcelable(CertListWidget.ARG_URI, Certs.buildLinkedIdCertsUri(mDataUri, mLidRank)); + args.putBoolean(CertListWidget.ARG_IS_SECRET, mIsSecret); getLoaderManager().initLoader(CertListWidget.LOADER_ID_LINKED_CERTS, args, mViewHolder.vLinkedCerts); } @@ -428,7 +434,7 @@ public class LinkedIdViewFragment extends Fragment implements setShowVerifying(true); mViewHolder.vKeySpinner.setVisibility(View.GONE); - mViewHolder.setVerifyingState(VerifyState.VERIFYING); + mViewHolder.setVerifyingState(VerifyState.VERIFYING, mIsSecret); mInProgress = new AsyncTask() { @Override @@ -453,9 +459,9 @@ public class LinkedIdViewFragment extends Fragment implements return; } if (result.success()) { - mViewHolder.setVerifyingState(VerifyState.VERIFY_OK); + mViewHolder.setVerifyingState(VerifyState.VERIFY_OK, mIsSecret); } else { - mViewHolder.setVerifyingState(VerifyState.VERIFY_ERROR); + mViewHolder.setVerifyingState(VerifyState.VERIFY_ERROR, mIsSecret); } mInProgress = null; } @@ -464,6 +470,11 @@ public class LinkedIdViewFragment extends Fragment implements } private void initiateCertifying() { + + if (mIsSecret) { + return; + } + // get the user's passphrase for this key (if required) String passphrase; long certifyKeyId = mViewHolder.vKeySpinner.getSelectedItemId(); @@ -509,7 +520,11 @@ public class LinkedIdViewFragment extends Fragment implements private void certifyResource(long certifyKeyId, String passphrase) { - mViewHolder.setVerifyingState(VerifyState.CERTIFYING); + if (mIsSecret) { + return; + } + + mViewHolder.setVerifyingState(VerifyState.CERTIFYING, mIsSecret); Bundle data = new Bundle(); { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java index 094a84820..c8f80f4a5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java @@ -1,7 +1,5 @@ package org.sufficientlysecure.keychain.ui.widget; -import java.text.DateFormat; -import java.text.SimpleDateFormat; import java.util.Date; import android.content.Context; @@ -11,7 +9,6 @@ import android.os.Bundle; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; -import android.text.SpannableStringBuilder; import android.util.AttributeSet; import android.view.View; import android.widget.ListView; @@ -29,7 +26,7 @@ public class CertListWidget extends ViewAnimator public static final int LOADER_ID_LINKED_CERTS = 38572; public static final String ARG_URI = "uri"; - public static final String ARG_RANK = "rank"; + public static final String ARG_IS_SECRET = "is_secret"; // These are the rows that we will retrieve. @@ -56,6 +53,7 @@ public class CertListWidget extends ViewAnimator private TextView vCollapsed; private ListView vExpanded; private View vExpandButton; + private boolean mIsSecret; public CertListWidget(Context context, AttributeSet attrs) { super(context, attrs); @@ -94,6 +92,7 @@ public class CertListWidget extends ViewAnimator @Override public Loader onCreateLoader(int id, Bundle args) { Uri uri = args.getParcelable(ARG_URI); + mIsSecret = args.getBoolean(ARG_IS_SECRET, false); return new CursorLoader(getContext(), uri, CERTS_PROJECTION, null, null, null); } @@ -105,8 +104,6 @@ public class CertListWidget extends ViewAnimator return; } - setVisibility(View.VISIBLE); - // TODO support external certificates Date userCert = null; while (!data.isAfterLast()) { @@ -125,12 +122,19 @@ public class CertListWidget extends ViewAnimator if (userCert != null) { PrettyTime format = new PrettyTime(); - vCollapsed.setText("You verified and confirmed this identity " - + format.format(userCert) + "."); + if (mIsSecret) { + vCollapsed.setText("You created this identity " + + format.format(userCert) + "."); + } else { + vCollapsed.setText("You verified and confirmed this identity " + + format.format(userCert) + "."); + } } else { vCollapsed.setText("This identity is not yet verified or confirmed."); } + setVisibility(View.VISIBLE); + } @Override -- cgit v1.2.3 From 538be96e61f5b6c2c39b02ed2f1550a1a6a9a2c9 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 15 Mar 2015 21:41:04 +0100 Subject: work on certification ui --- .../keychain/pgp/linked/LinkedResource.java | 3 +++ .../keychain/pgp/linked/resources/DnsResource.java | 7 +++++ .../pgp/linked/resources/GenericHttpsResource.java | 9 +++++++ .../pgp/linked/resources/TwitterResource.java | 7 +++++ .../keychain/ui/adapter/LinkedIdsAdapter.java | 2 +- .../keychain/ui/linked/LinkedIdViewFragment.java | 30 ++++++++++++++-------- 6 files changed, 46 insertions(+), 12 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index e954a514c..f91a24d57 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java @@ -15,6 +15,8 @@ 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 { @@ -102,6 +104,7 @@ public abstract class LinkedResource { } public abstract @DrawableRes int getDisplayIcon(); + public abstract @StringRes int getVerifiedText(); public abstract String getDisplayTitle(Context context); public abstract String getDisplayComment(Context context); public boolean isViewable() { 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 index 253e611a8..21c3a3eef 100644 --- 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 @@ -2,6 +2,7 @@ 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; @@ -103,6 +104,12 @@ public class DnsResource extends LinkedCookieResource { return magicPattern.matcher(res); } + @Override + public @StringRes + int getVerifiedText() { + return R.string.linked_verified_dns; + } + @Override public @DrawableRes int getDisplayIcon() { return R.drawable.dns; 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 index 1e872c6cb..c6d5883ee 100644 --- 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 @@ -4,6 +4,7 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.support.annotation.DrawableRes; +import android.support.annotation.StringRes; import com.textuality.keybase.lib.Search; @@ -81,6 +82,8 @@ public class GenericHttpsResource extends LinkedCookieResource { log.add(LogType.MSG_LV_FETCH_ERROR_URL, indent); return null; } catch (IOException e) { + Log.e(Constants.TAG, "io error", e); + e.printStackTrace(); log.add(LogType.MSG_LV_FETCH_ERROR_IO, indent); return null; } @@ -109,6 +112,12 @@ public class GenericHttpsResource extends LinkedCookieResource { return R.drawable.ssl_lock; } + @Override + public @StringRes + int getVerifiedText() { + return R.string.linked_verified_https; + } + @Override public String getDisplayTitle(Context context) { return "Website (HTTPS)"; 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 index 32bf92a99..8bc872f51 100644 --- 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 @@ -4,6 +4,7 @@ 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; @@ -118,6 +119,12 @@ public class TwitterResource extends LinkedCookieResource { return R.drawable.twitter; } + @Override + public @StringRes + int getVerifiedText() { + return R.string.linked_verified_twitter; + } + @Override public String getDisplayTitle(Context context) { return "Twitter"; 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 9727fab1d..e94ea5189 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 @@ -201,7 +201,7 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { final public TextView vComment; public ViewHolder(View view) { - vVerified = (ImageView) view.findViewById(R.id.user_id_item_certified); + vVerified = (ImageView) view.findViewById(R.id.linked_id_certified_icon); vIcon = (ImageView) view.findViewById(R.id.linked_id_type_icon); vTitle = (TextView) view.findViewById(R.id.linked_id_title); vComment = (TextView) view.findViewById(R.id.linked_id_comment); 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 c39e40761..502abe8e6 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 @@ -7,6 +7,7 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.database.Cursor; +import android.graphics.PorterDuff; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; @@ -23,6 +24,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.widget.ImageView; import android.widget.TextView; import android.widget.ViewAnimator; @@ -240,6 +242,7 @@ public class LinkedIdViewFragment extends Fragment implements static class ViewHolder { private final View vButtonView; private final ViewAnimator vVerifyingContainer; + private final ViewAnimator vItemCertified; LinkedIdsAdapter.ViewHolder mLinkedIdHolder; private ViewAnimator vButtonSwitcher; @@ -267,6 +270,7 @@ public class LinkedIdViewFragment extends Fragment implements vButtonView = root.findViewById(R.id.button_view); vVerifyingContainer = (ViewAnimator) root.findViewById(R.id.linked_verify_container); + vItemCertified = (ViewAnimator) root.findViewById(R.id.linked_id_certified); vProgress = (ViewAnimator) root.findViewById(R.id.linked_cert_progress); vText = (TextView) root.findViewById(R.id.linked_cert_text); @@ -285,7 +289,6 @@ public class LinkedIdViewFragment extends Fragment implements break; case VERIFY_OK: - vText.setText("Ok"); vProgress.setDisplayedChild(1); if (!isSecret) { showButton(2); @@ -315,7 +318,9 @@ public class LinkedIdViewFragment extends Fragment implements if (vVerifyingContainer.getDisplayedChild() == (show ? 1 : 0)) { return; } + vVerifyingContainer.setDisplayedChild(show ? 1 : 0); + vItemCertified.setDisplayedChild(show ? 1 : 0); } void showButton(int which) { @@ -386,6 +391,15 @@ public class LinkedIdViewFragment extends Fragment implements mViewHolder = new ViewHolder(root); root.setTag(mViewHolder); + ((ImageView) root.findViewById(R.id.status_icon_verified)) + .setColorFilter(mContext.getResources().getColor(R.color.android_green_light), + PorterDuff.Mode.SRC_IN); + ((ImageView) root.findViewById(R.id.status_icon_invalid)) + .setColorFilter(mContext.getResources().getColor(R.color.android_red_light), + PorterDuff.Mode.SRC_IN); + + + mViewHolder.vButtonBack.setClickable(true); mViewHolder.vButtonBack.findViewById(R.id.back_button).setOnClickListener(new OnClickListener() { @Override @@ -459,9 +473,11 @@ public class LinkedIdViewFragment extends Fragment implements return; } if (result.success()) { + mViewHolder.vText.setText(mLinkedResource.getVerifiedText()); mViewHolder.setVerifyingState(VerifyState.VERIFY_OK, mIsSecret); } else { mViewHolder.setVerifyingState(VerifyState.VERIFY_ERROR, mIsSecret); + result.createNotify(getActivity()).show(); } mInProgress = null; } @@ -524,7 +540,7 @@ public class LinkedIdViewFragment extends Fragment implements return; } - mViewHolder.setVerifyingState(VerifyState.CERTIFYING, mIsSecret); + mViewHolder.setVerifyingState(VerifyState.CERTIFYING, false); Bundle data = new Bundle(); { @@ -557,19 +573,11 @@ public class LinkedIdViewFragment extends Fragment implements Bundle data = message.getData(); - if (message.arg1 == MessageStatus.UPDATE_PROGRESS.ordinal()) { - if (data.containsKey(DATA_MESSAGE)) { - mViewHolder.vText.setText(data.getString(DATA_MESSAGE)); - } else if (data.containsKey(DATA_MESSAGE_ID)) { - mViewHolder.vText.setText(data.getString(DATA_MESSAGE_ID)); - } - return; - } - if (message.arg1 == MessageStatus.OKAY.ordinal()) { CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT); result.createNotify(getActivity()).show(); } + } }; -- cgit v1.2.3 From 94dbeaeaf03ae16637a3cb8593850d4326458924 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 24 Mar 2015 01:52:54 +0100 Subject: support github resource (pa) --- .../operations/results/OperationResult.java | 1 + .../keychain/pgp/linked/LinkedCookieResource.java | 55 ++++++++ .../pgp/linked/resources/GithubResource.java | 146 +++++++++++++++++++++ .../pgp/linked/resources/TwitterResource.java | 65 +++------ 4 files changed, 221 insertions(+), 46 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/GithubResource.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index fba52c4ad..7cb5e4904 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -777,6 +777,7 @@ public abstract class OperationResult implements Parcelable { MSG_LV_FETCH_ERROR (LogLevel.ERROR, R.string.msg_lv_fetch_error), MSG_LV_FETCH_ERROR_URL (LogLevel.ERROR, R.string.msg_lv_fetch_error_url), MSG_LV_FETCH_ERROR_IO (LogLevel.ERROR, R.string.msg_lv_fetch_error_io), + MSG_LV_FETCH_ERROR_FORMAT(LogLevel.ERROR, R.string.msg_lv_fetch_error_format), //export log MSG_EXPORT_LOG(LogLevel.START,R.string.msg_export_log_start), 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 index 21a8c8f98..2b9156417 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java @@ -2,6 +2,12 @@ package org.sufficientlysecure.keychain.pgp.linked; import android.content.Context; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.params.BasicHttpParams; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; @@ -9,6 +15,10 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat 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.URI; import java.util.HashMap; import java.util.Map.Entry; @@ -117,4 +127,49 @@ public abstract class LinkedCookieResource extends LinkedResource { } + public static String getResponseBody(HttpRequestBase request) throws IOException, HttpStatusException { + StringBuilder sb = new StringBuilder(); + + 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) { + 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/resources/GithubResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/GithubResource.java new file mode 100644 index 000000000..de313e14e --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/resources/GithubResource.java @@ -0,0 +1,146 @@ +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.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.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 generateText (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) { + + log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); + indent += 1; + + try { + + 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_FETCH_ERROR_FORMAT, 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"); + } + + } 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, indent, Integer.toString(e.getStatus())); + } catch (MalformedURLException e) { + log.add(LogType.MSG_LV_FETCH_ERROR_URL, indent); + } catch (IOException e) { + Log.e(Constants.TAG, "io error", e); + log.add(LogType.MSG_LV_FETCH_ERROR_IO, indent); + } catch (JSONException e) { + Log.e(Constants.TAG, "json error", e); + log.add(LogType.MSG_LV_FETCH_ERROR_FORMAT, indent); + } + return null; + + } + + 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.github; + } + + @Override + public @StringRes + int getVerifiedText() { + return R.string.linked_verified_github; + } + + @Override + public String getDisplayTitle(Context context) { + return "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 index 8bc872f51..136d87d03 100644 --- 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 @@ -9,27 +9,19 @@ import android.util.Log; import com.textuality.keybase.lib.JWalk; -import org.apache.http.HttpEntity; -import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.entity.StringEntity; -import org.apache.http.impl.client.DefaultHttpClient; -import org.apache.http.params.BasicHttpParams; 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.BufferedReader; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; import java.net.URI; import java.util.HashMap; import java.util.HashSet; @@ -61,7 +53,7 @@ public class TwitterResource extends LinkedCookieResource { return null; } - Pattern p = Pattern.compile("https://twitter.com/([a-zA-Z0-9_]+)/status/([0-9]+)"); + 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; @@ -102,16 +94,25 @@ public class TwitterResource extends LinkedCookieResource { JSONObject user = obj.getJSONObject("user"); if (!mHandle.equalsIgnoreCase(user.getString("screen_name"))) { + log.add(LogType.MSG_LV_FETCH_ERROR_FORMAT, indent); return null; } // update the results with the body of the response return obj.getString("text"); + } 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, indent, Integer.toString(e.getStatus())); + } catch (IOException e) { + Log.e(Constants.TAG, "io error", e); + log.add(LogType.MSG_LV_FETCH_ERROR_IO, indent); } catch (JSONException e) { - Log.e(Constants.TAG, "json error parsing stream", e); - return null; + Log.e(Constants.TAG, "json error", e); + log.add(LogType.MSG_LV_FETCH_ERROR_FORMAT, indent); } + return null; } @Override @@ -184,10 +185,11 @@ public class TwitterResource extends LinkedCookieResource { // update the results with the body of the response return null; - } catch (JSONException e) { - Log.e(Constants.TAG, "json error parsing stream", e); - return null; + } catch (JSONException | HttpStatusException | IOException e) { + Log.e(Constants.TAG, "exception parsing stream", e); } + + return null; } private static String authToken; @@ -217,42 +219,13 @@ public class TwitterResource extends LinkedCookieResource { authToken = JWalk.getString(rawAuthorization, "access_token"); return authToken; - } catch (UnsupportedEncodingException | JSONException | IllegalStateException ex) { - Log.e(Constants.TAG, "auth token fetching error", ex); + } catch (JSONException | IllegalStateException | HttpStatusException | IOException ex) { + Log.e(Constants.TAG, "exception fetching auth token", ex); return null; } } - private static String getResponseBody(HttpRequestBase request) { - StringBuilder sb = new StringBuilder(); - try { - - DefaultHttpClient httpClient = new DefaultHttpClient(new BasicHttpParams()); - HttpResponse response = httpClient.execute(request); - int statusCode = response.getStatusLine().getStatusCode(); - String reason = response.getStatusLine().getReasonPhrase(); - - if (statusCode == 200) { - - 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); - } - } else { - sb.append(reason); - } - } catch (IOException e) { - Log.e(Constants.TAG, "http request error", e); - } - return sb.toString(); - } - public static String rot13(String input) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < input.length(); i++) { -- cgit v1.2.3 From b25371fc1bd604dd15f3f42bda7227e3f84d4515 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 24 Mar 2015 02:30:39 +0100 Subject: support github resource (re) --- .../pgp/linked/resources/GithubResource.java | 11 ++ .../linked/LinkedIdCreateGithubStep1Fragment.java | 129 +++++++++++++++++++++ .../linked/LinkedIdCreateGithubStep2Fragment.java | 111 ++++++++++++++++++ .../keychain/ui/linked/LinkedIdSelectFragment.java | 12 ++ 4 files changed, 263 insertions(+) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep1Fragment.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep2Fragment.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index de313e14e..43cbed958 100644 --- 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 @@ -7,6 +7,7 @@ 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; @@ -20,6 +21,7 @@ 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; @@ -90,6 +92,15 @@ public class GithubResource extends LinkedCookieResource { } + public static GithubResource searchInGithubStream(String screenName, String needle) { + // TODO implement + 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 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 new file mode 100644 index 000000000..86a514fa9 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep1Fragment.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.IOException; +import java.net.HttpURLConnection; +import java.net.URL; + +import android.os.AsyncTask; +import android.os.Bundle; +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 LinkedIdCreateGithubStep1Fragment extends Fragment { + + LinkedIdWizard mLinkedIdWizard; + + EditText mEditHandle; + + public static LinkedIdCreateGithubStep1Fragment newInstance() { + LinkedIdCreateGithubStep1Fragment frag = new LinkedIdCreateGithubStep1Fragment(); + + Bundle args = new Bundle(); + frag.setArguments(args); + + return frag; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + mLinkedIdWizard = (LinkedIdWizard) getActivity(); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + final View view = inflater.inflate(R.layout.linked_create_github_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(); + + 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.showNotify(getActivity(), + "Connection error while checking username!", Notify.Style.ERROR); + return; + } + + if (!result) { + Notify.showNotify(getActivity(), + "This handle does not exist on Github!", Notify.Style.ERROR); + return; + } + + LinkedIdCreateGithubStep2Fragment frag = + LinkedIdCreateGithubStep2Fragment.newInstance(handle); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + } + }.execute(); + + } + }); + + view.findViewById(R.id.back_button).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + mLinkedIdWizard.loadFragment(null, null, LinkedIdWizard.FRAG_ACTION_TO_LEFT); + } + }); + + mEditHandle = (EditText) view.findViewById(R.id.linked_create_github_handle); + mEditHandle.setText("Valodim"); + + return view; + } + + private static Boolean checkHandle(String handle) { + try { + HttpURLConnection nection = + (HttpURLConnection) new URL("https://api.github.com/" + handle).openConnection(); + nection.setRequestMethod("HEAD"); + return nection.getResponseCode() == 200; + } catch (IOException e) { + e.printStackTrace(); + 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 new file mode 100644 index 000000000..05bdf89a4 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep2Fragment.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2014 Dominik Schürmann + * + * 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.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.pgp.linked.resources.GithubResource; + + +public class LinkedIdCreateGithubStep2Fragment extends LinkedIdCreateFinalFragment { + + public static final String ARG_HANDLE = "handle"; + + String mResourceHandle; + String mResourceString; + + public static LinkedIdCreateGithubStep2Fragment newInstance + (String handle) { + + LinkedIdCreateGithubStep2Fragment frag = new LinkedIdCreateGithubStep2Fragment(); + + Bundle args = new Bundle(); + args.putString(ARG_HANDLE, handle); + frag.setArguments(args); + + return frag; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mResourceString = + GithubResource.generate(getActivity(), mLinkedIdWizard.mFingerprint); + + mResourceHandle = getArguments().getString(ARG_HANDLE); + + } + + @Override + 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(); + } + }); + + return view; + } + + @Override + LinkedCookieResource getResource() { + return GithubResource.searchInGithubStream(mResourceHandle, mResourceString); + } + + @Override + protected View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.linked_create_github_fragment_step2, container, false); + } + + private void proofShare() { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_TEXT, mResourceString); + sendIntent.setType("text/plain"); + startActivity(sendIntent); + } + + private void proofSend() { + Uri.Builder builder = Uri.parse("https://gist.github.com/").buildUpon(); + builder.appendQueryParameter("text", mResourceString); + Uri uri = builder.build(); + + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + getActivity().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 index abe7dbaf1..8249f0bb6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdSelectFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdSelectFragment.java @@ -78,6 +78,18 @@ public class LinkedIdSelectFragment extends Fragment { } }); + view.findViewById(R.id.linked_create_github_button) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + LinkedIdCreateGithubStep1Fragment frag = + LinkedIdCreateGithubStep1Fragment.newInstance(); + + mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); + } + }); + + return view; } -- cgit v1.2.3 From fe32e7bff4e724d37903d07cd3b4f0287ec85879 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 24 Mar 2015 03:49:28 +0100 Subject: support github resource (ci) --- .../keychain/pgp/linked/LinkedCookieResource.java | 1 + .../keychain/pgp/linked/LinkedResource.java | 7 ++- .../pgp/linked/resources/GithubResource.java | 65 +++++++++++++++++++++- 3 files changed, 70 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index 2b9156417..940e0f7eb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java @@ -158,6 +158,7 @@ public abstract class LinkedCookieResource extends LinkedResource { private final String mReason; HttpStatusException(int statusCode, String reason) { + super("http status " + statusCode + ": " + reason); mStatusCode = statusCode; mReason = reason; } 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 index f91a24d57..476d0b21e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java @@ -3,6 +3,7 @@ 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; @@ -24,7 +25,7 @@ public abstract class LinkedResource { protected final Set mFlags; protected final HashMap mParams; - static Pattern magicPattern = + public static Pattern magicPattern = Pattern.compile("\\[Verifying my PGP key: openpgp4fpr:([a-zA-Z0-9]+)]"); protected LinkedResource(Set flags, HashMap params, URI uri) { @@ -98,6 +99,10 @@ public abstract class LinkedResource { if (res != null) { return res; } + res = GithubResource.create(flags, params, subUri); + if (res != null) { + return res; + } return null; 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 index 43cbed958..400a0a678 100644 --- 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 @@ -41,7 +41,7 @@ public class GithubResource extends LinkedCookieResource { mGistId = gistId; } - public static String generateText (Context context, byte[] fingerprint) { + 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); @@ -56,6 +56,7 @@ public class GithubResource extends LinkedCookieResource { try { HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + mGistId); + httpGet.setHeader("User-Agent", "OpenKeychain"); String response = getResponseBody(httpGet); @@ -93,7 +94,67 @@ public class GithubResource extends LinkedCookieResource { } public static GithubResource searchInGithubStream(String screenName, String needle) { - // TODO implement + + // narrow the needle down to important part + Matcher matcher = magicPattern.matcher(needle); + if (!matcher.find()) { + Log.e(Constants.TAG, "needle didn't contain cookie!"); + return null; + } + 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 + return null; + } catch (JSONException | HttpStatusException | IOException e) { + Log.e(Constants.TAG, "exception parsing stream", e); + } + return null; } -- cgit v1.2.3 From e573cd774a4a41234c229d9c49ef7a5656445f93 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 24 Mar 2015 16:41:01 +0100 Subject: work on cookie scanning during creation --- .../operations/results/OperationResult.java | 10 +- .../keychain/pgp/linked/LinkedCookieResource.java | 26 ++++- .../pgp/linked/resources/GenericHttpsResource.java | 49 ++-------- .../pgp/linked/resources/GithubResource.java | 73 +++++++------- .../pgp/linked/resources/TwitterResource.java | 107 ++++++++++----------- .../ui/linked/LinkedIdCreateDnsStep2Fragment.java | 3 +- .../ui/linked/LinkedIdCreateFinalFragment.java | 20 ++-- .../linked/LinkedIdCreateGithubStep2Fragment.java | 5 +- .../linked/LinkedIdCreateHttpsStep2Fragment.java | 3 +- .../linked/LinkedIdCreateTwitterStep2Fragment.java | 5 +- .../keychain/ui/linked/LinkedIdViewFragment.java | 2 +- 11 files changed, 150 insertions(+), 153 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java index 616b6f062..3270d12d5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/results/OperationResult.java @@ -771,6 +771,12 @@ public abstract class OperationResult implements Parcelable { MSG_LV_FP_OK (LogLevel.DEBUG, R.string.msg_lv_fp_ok), MSG_LV_FP_ERROR (LogLevel.ERROR, R.string.msg_lv_fp_error), + MSG_LV_ERROR_TWITTER_AUTH (LogLevel.ERROR, R.string.msg_lv_error_twitter_auth), + MSG_LV_ERROR_TWITTER_HANDLE (LogLevel.ERROR, R.string.msg_lv_error_twitter_handle), + MSG_LV_ERROR_TWITTER_RESPONSE (LogLevel.ERROR, R.string.msg_lv_error_twitter_response), + MSG_LV_ERROR_GITHUB_HANDLE (LogLevel.ERROR, R.string.msg_lv_error_github_handle), + MSG_LV_ERROR_GITHUB_NOT_FOUND (LogLevel.ERROR, R.string.msg_lv_error_github_not_found), + MSG_LV_FETCH (LogLevel.DEBUG, R.string.msg_lv_fetch), MSG_LV_FETCH_REDIR (LogLevel.DEBUG, R.string.msg_lv_fetch_redir), MSG_LV_FETCH_OK (LogLevel.DEBUG, R.string.msg_lv_fetch_ok), @@ -778,14 +784,14 @@ public abstract class OperationResult implements Parcelable { MSG_LV_FETCH_ERROR_URL (LogLevel.ERROR, R.string.msg_lv_fetch_error_url), MSG_LV_FETCH_ERROR_IO (LogLevel.ERROR, R.string.msg_lv_fetch_error_io), MSG_LV_FETCH_ERROR_FORMAT(LogLevel.ERROR, R.string.msg_lv_fetch_error_format), + MSG_LV_FETCH_ERROR_NOTHING (LogLevel.ERROR, R.string.msg_lv_fetch_error_nothing), //export log MSG_EXPORT_LOG(LogLevel.START,R.string.msg_export_log_start), MSG_EXPORT_LOG_EXPORT_ERROR_NO_FILE(LogLevel.ERROR,R.string.msg_export_log_error_no_file), MSG_EXPORT_LOG_EXPORT_ERROR_FOPEN(LogLevel.ERROR,R.string.msg_export_log_error_fopen), MSG_EXPORT_LOG_EXPORT_ERROR_WRITING(LogLevel.ERROR,R.string.msg_export_log_error_writing), - MSG_EXPORT_LOG_EXPORT_SUCCESS (LogLevel.OK, R.string.msg_export_log_success), - ; + MSG_EXPORT_LOG_EXPORT_SUCCESS (LogLevel.OK, R.string.msg_export_log_success); public final int mMsgId; public final LogLevel mLevel; 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 index 940e0f7eb..606c951b8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java @@ -4,10 +4,10 @@ import android.content.Context; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; 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; @@ -19,6 +19,7 @@ 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; @@ -86,7 +87,23 @@ public abstract class LinkedCookieResource extends LinkedResource { log.add(LogType.MSG_LV, 0); // Try to fetch resource. Logs for itself - String res = fetchResource(log, 1); + 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); @@ -98,7 +115,8 @@ public abstract class LinkedCookieResource extends LinkedResource { } - protected abstract String fetchResource (OperationLog log, int indent); + 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); @@ -130,6 +148,8 @@ public abstract class LinkedCookieResource extends LinkedResource { 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(); 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 index c6d5883ee..4cf56fe67 100644 --- 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 @@ -8,6 +8,7 @@ import android.support.annotation.StringRes; import com.textuality.keybase.lib.Search; +import org.apache.http.client.methods.HttpGet; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; @@ -40,53 +41,15 @@ public class GenericHttpsResource extends LinkedCookieResource { } @Override - protected String fetchResource (OperationLog log, int indent) { + protected String fetchResource (OperationLog log, int indent) throws HttpStatusException, IOException { log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); indent += 1; - try { - - HttpsURLConnection conn = null; - URL url = mSubUri.toURL(); - int status = 0; - int redirects = 0; - - while (redirects < 5) { - conn = (HttpsURLConnection) url.openConnection(); - conn.addRequestProperty("User-Agent", "OpenKeychain"); - conn.setConnectTimeout(5000); - conn.setReadTimeout(25000); - conn.connect(); - status = conn.getResponseCode(); - if (status == 301) { - redirects++; - url = new URL(conn.getHeaderFields().get("Location").get(0)); - log.add(LogType.MSG_LV_FETCH_REDIR, indent, url.toString()); - } else { - break; - } - } - - if (status >= 200 && status < 300) { - log.add(LogType.MSG_LV_FETCH_OK, indent, Integer.toString(status)); - return Search.snarf(conn.getInputStream()); - } else { - // log verbose output to logcat - Log.e(Constants.TAG, Search.snarf(conn.getErrorStream())); - log.add(LogType.MSG_LV_FETCH_ERROR, indent, Integer.toString(status)); - return null; - } - - } catch (MalformedURLException e) { - log.add(LogType.MSG_LV_FETCH_ERROR_URL, indent); - return null; - } catch (IOException e) { - Log.e(Constants.TAG, "io error", e); - e.printStackTrace(); - log.add(LogType.MSG_LV_FETCH_ERROR_IO, indent); - return null; - } + HttpGet httpGet = new HttpGet(mSubUri); + return getResponseBody(httpGet); + + // log.add(LogType.MSG_LV_FETCH_REDIR, indent, url.toString()); } 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 index 400a0a678..9300b67e7 100644 --- 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 @@ -48,58 +48,43 @@ public class GithubResource extends LinkedCookieResource { } @Override - protected String fetchResource (OperationLog log, int indent) { + protected String fetchResource (OperationLog log, int indent) + throws HttpStatusException, IOException, JSONException { log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); indent += 1; - try { - - HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + mGistId); - httpGet.setHeader("User-Agent", "OpenKeychain"); + HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + mGistId); + String response = getResponseBody(httpGet); - String response = getResponseBody(httpGet); + JSONObject obj = new JSONObject(response); - JSONObject obj = new JSONObject(response); - - JSONObject owner = obj.getJSONObject("owner"); - if (!mHandle.equals(owner.getString("login"))) { - log.add(LogType.MSG_LV_FETCH_ERROR_FORMAT, 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"); - } + JSONObject owner = obj.getJSONObject("owner"); + if (!mHandle.equals(owner.getString("login"))) { + log.add(LogType.MSG_LV_ERROR_GITHUB_HANDLE, indent); + 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, indent, Integer.toString(e.getStatus())); - } catch (MalformedURLException e) { - log.add(LogType.MSG_LV_FETCH_ERROR_URL, indent); - } catch (IOException e) { - Log.e(Constants.TAG, "io error", e); - log.add(LogType.MSG_LV_FETCH_ERROR_IO, indent); - } catch (JSONException e) { - Log.e(Constants.TAG, "json error", e); - log.add(LogType.MSG_LV_FETCH_ERROR_FORMAT, indent); + 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) { + 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()) { - Log.e(Constants.TAG, "needle didn't contain cookie!"); - return null; + throw new AssertionError("Needle must contain cookie pattern! This is a programming error, please report."); } needle = matcher.group(); @@ -150,9 +135,21 @@ public class GithubResource extends LinkedCookieResource { } // update the results with the body of the response + log.add(LogType.MSG_LV_FETCH_ERROR_NOTHING, 2); return null; - } catch (JSONException | HttpStatusException | IOException e) { - Log.e(Constants.TAG, "exception parsing stream", e); + + } 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; 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 index 136d87d03..e56d281b7 100644 --- 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 @@ -22,6 +22,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.Operat 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; @@ -66,11 +67,14 @@ public class TwitterResource extends LinkedCookieResource { } @Override - protected String fetchResource(OperationLog log, int indent) { + protected String fetchResource(OperationLog log, int indent) throws IOException, HttpStatusException, + JSONException { - String authToken = getAuthToken(); - - if (authToken == null) { + String authToken; + try { + authToken = getAuthToken(); + } catch (IOException | HttpStatusException | JSONException e) { + log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, indent); return null; } @@ -87,32 +91,19 @@ public class TwitterResource extends LinkedCookieResource { try { String response = getResponseBody(httpGet); JSONObject obj = new JSONObject(response); - - if (!obj.has("text")) { - return null; - } - JSONObject user = obj.getJSONObject("user"); if (!mHandle.equalsIgnoreCase(user.getString("screen_name"))) { - log.add(LogType.MSG_LV_FETCH_ERROR_FORMAT, indent); + 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 (HttpStatusException e) { - // log verbose output to logcat - Log.e(Constants.TAG, "http error (" + e.getStatus() + "): " + e.getReason()); - log.add(LogType.MSG_LV_FETCH_ERROR, indent, Integer.toString(e.getStatus())); - } catch (IOException e) { - Log.e(Constants.TAG, "io error", e); - log.add(LogType.MSG_LV_FETCH_ERROR_IO, indent); } catch (JSONException e) { - Log.e(Constants.TAG, "json error", e); - log.add(LogType.MSG_LV_FETCH_ERROR_FORMAT, indent); + log.add(LogType.MSG_LV_ERROR_TWITTER_RESPONSE, indent); + return null; } - return null; } @Override @@ -148,11 +139,14 @@ public class TwitterResource extends LinkedCookieResource { return intent; } - public static TwitterResource searchInTwitterStream(String screenName, String needle) { - - String authToken = getAuthToken(); + public static TwitterResource searchInTwitterStream( + String screenName, String needle, OperationLog log) { - if (authToken == null) { + String authToken; + try { + authToken = getAuthToken(); + } catch (IOException | HttpStatusException | JSONException e) { + log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, 1); return null; } @@ -184,46 +178,51 @@ public class TwitterResource extends LinkedCookieResource { } // update the results with the body of the response + log.add(LogType.MSG_LV_FETCH_ERROR_NOTHING, 1); return null; - } catch (JSONException | HttpStatusException | IOException e) { - Log.e(Constants.TAG, "exception parsing stream", e); + + } 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 authToken; + private static String cachedAuthToken; - private static String getAuthToken() { - if (authToken != null) { - return authToken; + private static String getAuthToken() throws IOException, HttpStatusException, JSONException { + if (cachedAuthToken != null) { + return cachedAuthToken; } - try { - - 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"))) { - return null; - } - - authToken = JWalk.getString(rawAuthorization, "access_token"); - return authToken; - - } catch (JSONException | IllegalStateException | HttpStatusException | IOException ex) { - Log.e(Constants.TAG, "exception fetching auth token", ex); - return null; + 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) { 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 c0e2fd29c..e8668dc90 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 @@ -31,6 +31,7 @@ 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.ui.util.Notify; @@ -104,7 +105,7 @@ public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment } @Override - LinkedCookieResource getResource() { + LinkedCookieResource getResource(OperationLog log) { return DnsResource.createNew(mResourceDomain); } 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 99e770857..4f10102ae 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,6 +20,7 @@ import android.widget.ViewAnimator; import org.sufficientlysecure.keychain.R; 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; @@ -28,6 +29,8 @@ import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.util.Passphrase; + public abstract class LinkedIdCreateFinalFragment extends Fragment { @@ -95,7 +98,7 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { return view; } - abstract LinkedCookieResource getResource(); + abstract LinkedCookieResource getResource(OperationLog log); private void setVerifyProgress(boolean on, Boolean success) { if (success == null) { @@ -133,7 +136,12 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { protected LinkedVerifyResult doInBackground(Void... params) { long timer = System.currentTimeMillis(); - LinkedCookieResource resource = getResource(); + OperationLog log = new OperationLog(); + LinkedCookieResource resource = getResource(log); + if (resource == null) { + return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); + } + LinkedVerifyResult result = resource.verify(mLinkedIdWizard.mFingerprint); // ux flow: this operation should take at last a second @@ -178,7 +186,7 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { } - private void certifyLinkedIdentity (String passphrase) { + private void certifyLinkedIdentity (Passphrase passphrase) { KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( getActivity(), getString(R.string.progress_saving), @@ -227,7 +235,7 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { // fill values for this action Bundle data = new Bundle(); - data.putString(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); + data.putParcelable(KeychainIntentService.EDIT_KEYRING_PASSPHRASE, passphrase); data.putParcelable(KeychainIntentService.EDIT_KEYRING_PARCEL, skp); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); @@ -249,8 +257,8 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { switch (requestCode) { case REQUEST_CODE_PASSPHRASE: if (resultCode == Activity.RESULT_OK && data != null) { - String passphrase = - data.getStringExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); + Passphrase passphrase = + data.getParcelableExtra(PassphraseDialogActivity.MESSAGE_DATA_PASSPHRASE); certifyLinkedIdentity(passphrase); } break; 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 05bdf89a4..cc1052b1f 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 @@ -26,6 +26,7 @@ import android.view.View.OnClickListener; 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; @@ -82,8 +83,8 @@ public class LinkedIdCreateGithubStep2Fragment extends LinkedIdCreateFinalFragme } @Override - LinkedCookieResource getResource() { - return GithubResource.searchInGithubStream(mResourceHandle, mResourceString); + LinkedCookieResource getResource(OperationLog log) { + return GithubResource.searchInGithubStream(mResourceHandle, mResourceString, log); } @Override 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 5559c0daf..6bb555105 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 @@ -30,6 +30,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.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; @@ -66,7 +67,7 @@ public class LinkedIdCreateHttpsStep2Fragment extends LinkedIdCreateFinalFragmen } @Override - GenericHttpsResource getResource() { + GenericHttpsResource getResource(OperationLog log) { return GenericHttpsResource.createNew(mResourceUri); } 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 2de9b54c9..66e86f7d4 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 @@ -27,6 +27,7 @@ import android.view.ViewGroup; import android.widget.EditText; 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; @@ -82,8 +83,8 @@ public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragm } @Override - LinkedCookieResource getResource() { - return TwitterResource.searchInTwitterStream(mResourceHandle, mResourceString); + LinkedCookieResource getResource(OperationLog log) { + return TwitterResource.searchInTwitterStream(mResourceHandle, mResourceString, log); } @Override 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 41d0178a3..4a3c6f4d0 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 @@ -133,7 +133,7 @@ public class LinkedIdViewFragment extends Fragment implements case LOADER_ID_LINKED_ID: if (!cursor.moveToFirst()) { - Notify.createNotify(getActivity(), "Error loading identity!", + Notify.create(getActivity(), "Error loading identity!", Notify.LENGTH_LONG, Style.ERROR).show(); finishFragment(); break; -- cgit v1.2.3 From 3edf47c5decdc547724bc84628ddfa25bf8ae53a Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 24 Mar 2015 19:47:54 +0100 Subject: secret/public distinction in LinkedIdView --- .../keychain/pgp/linked/LinkedResource.java | 2 +- .../keychain/pgp/linked/resources/DnsResource.java | 6 +++--- .../keychain/pgp/linked/resources/GenericHttpsResource.java | 13 +++---------- .../keychain/pgp/linked/resources/GithubResource.java | 6 +++--- .../keychain/pgp/linked/resources/TwitterResource.java | 6 +++--- .../keychain/ui/linked/LinkedIdViewFragment.java | 10 +++++----- 6 files changed, 18 insertions(+), 25 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index 476d0b21e..5a0fc6e47 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java @@ -109,7 +109,7 @@ public abstract class LinkedResource { } public abstract @DrawableRes int getDisplayIcon(); - public abstract @StringRes int getVerifiedText(); + public abstract @StringRes int getVerifiedText(boolean isSecret); public abstract String getDisplayTitle(Context context); public abstract String getDisplayComment(Context context); public boolean isViewable() { 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 index 21c3a3eef..368d0f4da 100644 --- 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 @@ -106,8 +106,8 @@ public class DnsResource extends LinkedCookieResource { @Override public @StringRes - int getVerifiedText() { - return R.string.linked_verified_dns; + int getVerifiedText(boolean isSecret) { + return isSecret ? R.string.linked_verified_secret_dns : R.string.linked_verified_dns; } @Override @@ -117,7 +117,7 @@ public class DnsResource extends LinkedCookieResource { @Override public String getDisplayTitle(Context context) { - return "Domain Name"; + return context.getString(R.string.linked_title_dns); } @Override 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 index 4cf56fe67..597d5aee0 100644 --- 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 @@ -6,26 +6,19 @@ import android.net.Uri; import android.support.annotation.DrawableRes; import android.support.annotation.StringRes; -import com.textuality.keybase.lib.Search; - import org.apache.http.client.methods.HttpGet; -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.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.Log; import java.io.IOException; -import java.net.MalformedURLException; import java.net.URI; -import java.net.URL; import java.util.HashMap; import java.util.HashSet; import java.util.Set; -import javax.net.ssl.HttpsURLConnection; public class GenericHttpsResource extends LinkedCookieResource { @@ -77,13 +70,13 @@ public class GenericHttpsResource extends LinkedCookieResource { @Override public @StringRes - int getVerifiedText() { - return R.string.linked_verified_https; + int getVerifiedText(boolean isSecret) { + return isSecret ? R.string.linked_verified_secret_https : R.string.linked_verified_https; } @Override public String getDisplayTitle(Context context) { - return "Website (HTTPS)"; + return context.getString(R.string.linked_title_https); } @Override 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 index 9300b67e7..723898d20 100644 --- 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 @@ -187,13 +187,13 @@ public class GithubResource extends LinkedCookieResource { @Override public @StringRes - int getVerifiedText() { - return R.string.linked_verified_github; + int getVerifiedText(boolean isSecret) { + return isSecret ? R.string.linked_verified_secret_github : R.string.linked_verified_github; } @Override public String getDisplayTitle(Context context) { - return "Github"; + return context.getString(R.string.linked_title_github); } @Override 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 index e56d281b7..54444ee5e 100644 --- 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 @@ -113,13 +113,13 @@ public class TwitterResource extends LinkedCookieResource { @Override public @StringRes - int getVerifiedText() { - return R.string.linked_verified_twitter; + int getVerifiedText(boolean isSecret) { + return isSecret ? R.string.linked_verified_secret_twitter : R.string.linked_verified_twitter; } @Override public String getDisplayTitle(Context context) { - return "Twitter"; + return context.getString(R.string.linked_title_twitter); } @Override 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 4a3c6f4d0..e0db0174e 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 @@ -315,13 +315,13 @@ public class LinkedIdViewFragment extends Fragment implements } } - void showVerifyingContainer(boolean show) { + void showVerifyingContainer(boolean show, boolean isSecret) { if (vVerifyingContainer.getDisplayedChild() == (show ? 1 : 0)) { return; } vVerifyingContainer.setDisplayedChild(show ? 1 : 0); - vItemCertified.setDisplayedChild(show ? 1 : 0); + vItemCertified.setDisplayedChild(show && !isSecret ? 1 : 0); } void showButton(int which) { @@ -363,7 +363,7 @@ public class LinkedIdViewFragment extends Fragment implements mViewHolder.showButton(0); mViewHolder.vKeySpinner.setVisibility(View.GONE); - mViewHolder.showVerifyingContainer(false); + mViewHolder.showVerifyingContainer(false, mIsSecret); return; } @@ -376,7 +376,7 @@ public class LinkedIdViewFragment extends Fragment implements manager.beginTransaction().addToBackStack("verification").commit(); manager.executePendingTransactions(); manager.addOnBackStackChangedListener(this); - mViewHolder.showVerifyingContainer(true); + mViewHolder.showVerifyingContainer(true, mIsSecret); } @@ -474,7 +474,7 @@ public class LinkedIdViewFragment extends Fragment implements return; } if (result.success()) { - mViewHolder.vText.setText(mLinkedResource.getVerifiedText()); + mViewHolder.vText.setText(mLinkedResource.getVerifiedText(mIsSecret)); mViewHolder.setVerifyingState(VerifyState.VERIFY_OK, mIsSecret); } else { mViewHolder.setVerifyingState(VerifyState.VERIFY_ERROR, mIsSecret); -- cgit v1.2.3 From aa6d0dd86773eda7d0a5a6855b40c2e1f95e2b45 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 26 Mar 2015 13:53:43 +0100 Subject: some linked id ui fine tuning --- .../keychain/pgp/linked/resources/DnsResource.java | 2 +- .../pgp/linked/resources/GenericHttpsResource.java | 2 +- .../pgp/linked/resources/GithubResource.java | 2 +- .../pgp/linked/resources/TwitterResource.java | 2 +- .../keychain/ui/linked/LinkedIdViewFragment.java | 53 ++++++++++++---------- 5 files changed, 34 insertions(+), 27 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index 368d0f4da..a8faa435d 100644 --- 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 @@ -112,7 +112,7 @@ public class DnsResource extends LinkedCookieResource { @Override public @DrawableRes int getDisplayIcon() { - return R.drawable.dns; + return R.drawable.linked_dns; } @Override 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 index 597d5aee0..8f5c0f8c2 100644 --- 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 @@ -65,7 +65,7 @@ public class GenericHttpsResource extends LinkedCookieResource { @Override public @DrawableRes int getDisplayIcon() { - return R.drawable.ssl_lock; + return R.drawable.linked_https; } @Override 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 index 723898d20..d411395a3 100644 --- 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 @@ -182,7 +182,7 @@ public class GithubResource extends LinkedCookieResource { @Override public @DrawableRes int getDisplayIcon() { - return R.drawable.github; + return R.drawable.linked_github; } @Override 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 index 54444ee5e..935268da6 100644 --- 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 @@ -108,7 +108,7 @@ public class TwitterResource extends LinkedCookieResource { @Override public @DrawableRes int getDisplayIcon() { - return R.drawable.twitter; + return R.drawable.linked_twitter; } @Override 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 e0db0174e..aa99a2907 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 @@ -25,10 +25,11 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ImageView; -import android.widget.TextView; +import android.widget.TextSwitcher; import android.widget.ViewAnimator; import org.sufficientlysecure.keychain.Constants; +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; @@ -180,16 +181,12 @@ public class LinkedIdViewFragment extends Fragment implements private void loadIdentity(RawLinkedIdentity linkedId, int certStatus) { mLinkedId = linkedId; - setShowVerifying(false); - if (mLinkedId instanceof LinkedIdentity) { LinkedResource res = ((LinkedIdentity) mLinkedId).mResource; mLinkedResource = (LinkedCookieResource) res; } if (!mIsSecret) { - mViewHolder.mLinkedIdHolder.vVerified.setVisibility(View.VISIBLE); - switch (certStatus) { case Certs.VERIFIED_SECRET: KeyFormattingUtils.setStatusImage(mContext, mViewHolder.mLinkedIdHolder.vVerified, @@ -204,12 +201,12 @@ public class LinkedIdViewFragment extends Fragment implements null, State.INVALID, KeyFormattingUtils.DEFAULT_COLOR); break; } - } else { - mViewHolder.mLinkedIdHolder.vVerified.setVisibility(View.GONE); } mViewHolder.mLinkedIdHolder.setData(mContext, mLinkedId); + setShowVerifying(false); + // no resource, nothing further we can do… if (mLinkedResource == null) { mViewHolder.vButtonView.setVisibility(View.GONE); @@ -255,7 +252,7 @@ public class LinkedIdViewFragment extends Fragment implements private final View vButtonBack; private final ViewAnimator vProgress; - private final TextView vText; + private final TextSwitcher vText; ViewHolder(View root) { vLinkedCerts = (CertListWidget) root.findViewById(R.id.linked_id_certs); @@ -274,18 +271,18 @@ public class LinkedIdViewFragment extends Fragment implements vItemCertified = (ViewAnimator) root.findViewById(R.id.linked_id_certified); vProgress = (ViewAnimator) root.findViewById(R.id.linked_cert_progress); - vText = (TextView) root.findViewById(R.id.linked_cert_text); + vText = (TextSwitcher) root.findViewById(R.id.linked_cert_text); } enum VerifyState { VERIFYING, VERIFY_OK, VERIFY_ERROR, CERTIFYING } - void setVerifyingState(VerifyState state, boolean isSecret) { + void setVerifyingState(Context context, VerifyState state, boolean isSecret) { switch (state) { case VERIFYING: vProgress.setDisplayedChild(0); - vText.setText("Verifying…"); + vText.setText(context.getString(R.string.linked_text_verifying)); vKeySpinner.setVisibility(View.GONE); break; @@ -303,25 +300,30 @@ public class LinkedIdViewFragment extends Fragment implements case VERIFY_ERROR: showButton(1); vProgress.setDisplayedChild(2); - vText.setText("Error"); + vText.setText(context.getString(R.string.linked_text_error)); vKeySpinner.setVisibility(View.GONE); break; case CERTIFYING: vProgress.setDisplayedChild(0); - vText.setText("Confirming…"); + vText.setText(context.getString(R.string.linked_text_confirming)); vKeySpinner.setVisibility(View.GONE); break; } } - void showVerifyingContainer(boolean show, boolean isSecret) { + 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.setDisplayedChild(show && !isSecret ? 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) { @@ -363,7 +365,7 @@ public class LinkedIdViewFragment extends Fragment implements mViewHolder.showButton(0); mViewHolder.vKeySpinner.setVisibility(View.GONE); - mViewHolder.showVerifyingContainer(false, mIsSecret); + mViewHolder.showVerifyingContainer(mContext, false, mIsSecret); return; } @@ -376,7 +378,7 @@ public class LinkedIdViewFragment extends Fragment implements manager.beginTransaction().addToBackStack("verification").commit(); manager.executePendingTransactions(); manager.addOnBackStackChangedListener(this); - mViewHolder.showVerifyingContainer(true, mIsSecret); + mViewHolder.showVerifyingContainer(mContext, true, mIsSecret); } @@ -449,7 +451,7 @@ public class LinkedIdViewFragment extends Fragment implements setShowVerifying(true); mViewHolder.vKeySpinner.setVisibility(View.GONE); - mViewHolder.setVerifyingState(VerifyState.VERIFYING, mIsSecret); + mViewHolder.setVerifyingState(mContext, VerifyState.VERIFYING, mIsSecret); mInProgress = new AsyncTask() { @Override @@ -474,10 +476,10 @@ public class LinkedIdViewFragment extends Fragment implements return; } if (result.success()) { - mViewHolder.vText.setText(mLinkedResource.getVerifiedText(mIsSecret)); - mViewHolder.setVerifyingState(VerifyState.VERIFY_OK, mIsSecret); + mViewHolder.vText.setText(getString(mLinkedResource.getVerifiedText(mIsSecret))); + mViewHolder.setVerifyingState(mContext, VerifyState.VERIFY_OK, mIsSecret); } else { - mViewHolder.setVerifyingState(VerifyState.VERIFY_ERROR, mIsSecret); + mViewHolder.setVerifyingState(mContext, VerifyState.VERIFY_ERROR, mIsSecret); result.createNotify(getActivity()).show(); } mInProgress = null; @@ -493,8 +495,12 @@ public class LinkedIdViewFragment extends Fragment implements } // get the user's passphrase for this key (if required) - Passphrase passphrase; long certifyKeyId = mViewHolder.vKeySpinner.getSelectedItemId(); + if (certifyKeyId == key.none || certifyKeyId == key.symmetric) { + Notify.create(getActivity(), R.string.select_key_to_certify, Style.ERROR).show(); + } + + Passphrase passphrase; try { passphrase = PassphraseCacheService.getCachedPassphrase( getActivity(), certifyKeyId, certifyKeyId); @@ -541,7 +547,7 @@ public class LinkedIdViewFragment extends Fragment implements return; } - mViewHolder.setVerifyingState(VerifyState.CERTIFYING, false); + mViewHolder.setVerifyingState(mContext, VerifyState.CERTIFYING, false); Bundle data = new Bundle(); { @@ -577,6 +583,7 @@ public class LinkedIdViewFragment extends Fragment implements if (message.arg1 == MessageStatus.OKAY.ordinal()) { CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT); result.createNotify(getActivity()).show(); + // no need to do anything else, we will get a loader refresh! } } -- cgit v1.2.3 From dc8b04a79333d13f3e9903757502521348ccec57 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 26 Mar 2015 16:30:49 +0100 Subject: more linked id ui fine tuning --- .../org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java | 2 ++ 1 file changed, 2 insertions(+) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 aa99a2907..2efafc086 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 @@ -478,6 +478,7 @@ public class LinkedIdViewFragment extends Fragment implements if (result.success()) { mViewHolder.vText.setText(getString(mLinkedResource.getVerifiedText(mIsSecret))); mViewHolder.setVerifyingState(mContext, VerifyState.VERIFY_OK, mIsSecret); + } else { mViewHolder.setVerifyingState(mContext, VerifyState.VERIFY_ERROR, mIsSecret); result.createNotify(getActivity()).show(); @@ -498,6 +499,7 @@ public class LinkedIdViewFragment extends Fragment implements long certifyKeyId = mViewHolder.vKeySpinner.getSelectedItemId(); if (certifyKeyId == key.none || certifyKeyId == key.symmetric) { Notify.create(getActivity(), R.string.select_key_to_certify, Style.ERROR).show(); + return; } Passphrase passphrase; -- cgit v1.2.3 From ab5a7d409450d6e7451ec8ea62ca091df83e2933 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 26 Mar 2015 17:52:18 +0100 Subject: more (subtle) ui work --- .../keychain/ui/linked/LinkedIdViewFragment.java | 29 ++++++++++++++++------ .../keychain/ui/util/SubtleAttentionSeeker.java | 18 +++++++++++++- 2 files changed, 38 insertions(+), 9 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 2efafc086..5c1389573 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 @@ -3,13 +3,18 @@ package org.sufficientlysecure.keychain.ui.linked; import java.io.IOException; import java.util.Arrays; +import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.animation.ObjectAnimator; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -53,6 +58,7 @@ 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.CertifyKeySpinner; import org.sufficientlysecure.keychain.util.Log; @@ -241,6 +247,7 @@ public class LinkedIdViewFragment extends Fragment implements private final View vButtonView; private final ViewAnimator vVerifyingContainer; private final ViewAnimator vItemCertified; + private final View vKeySpinnerContainer; LinkedIdsAdapter.ViewHolder mLinkedIdHolder; private ViewAnimator vButtonSwitcher; @@ -257,6 +264,7 @@ public class LinkedIdViewFragment extends Fragment implements ViewHolder(View root) { vLinkedCerts = (CertListWidget) root.findViewById(R.id.linked_id_certs); vKeySpinner = (CertifyKeySpinner) root.findViewById(R.id.cert_key_spinner); + vKeySpinnerContainer = root.findViewById(R.id.cert_key_spincontainer); vButtonSwitcher = (ViewAnimator) root.findViewById(R.id.button_animator); mLinkedIdHolder = new LinkedIdsAdapter.ViewHolder(root); @@ -283,17 +291,17 @@ public class LinkedIdViewFragment extends Fragment implements case VERIFYING: vProgress.setDisplayedChild(0); vText.setText(context.getString(R.string.linked_text_verifying)); - vKeySpinner.setVisibility(View.GONE); + vKeySpinnerContainer.setVisibility(View.GONE); break; case VERIFY_OK: vProgress.setDisplayedChild(1); if (!isSecret) { showButton(2); - vKeySpinner.setVisibility(View.VISIBLE); + vKeySpinnerContainer.setVisibility(View.VISIBLE); } else { showButton(1); - vKeySpinner.setVisibility(View.GONE); + vKeySpinnerContainer.setVisibility(View.GONE); } break; @@ -301,13 +309,13 @@ public class LinkedIdViewFragment extends Fragment implements showButton(1); vProgress.setDisplayedChild(2); vText.setText(context.getString(R.string.linked_text_error)); - vKeySpinner.setVisibility(View.GONE); + vKeySpinnerContainer.setVisibility(View.GONE); break; case CERTIFYING: vProgress.setDisplayedChild(0); vText.setText(context.getString(R.string.linked_text_confirming)); - vKeySpinner.setVisibility(View.GONE); + vKeySpinnerContainer.setVisibility(View.GONE); break; } } @@ -364,7 +372,7 @@ public class LinkedIdViewFragment extends Fragment implements mVerificationState = false; mViewHolder.showButton(0); - mViewHolder.vKeySpinner.setVisibility(View.GONE); + mViewHolder.vKeySpinnerContainer.setVisibility(View.GONE); mViewHolder.showVerifyingContainer(mContext, false, mIsSecret); return; } @@ -450,7 +458,7 @@ public class LinkedIdViewFragment extends Fragment implements setShowVerifying(true); - mViewHolder.vKeySpinner.setVisibility(View.GONE); + mViewHolder.vKeySpinnerContainer.setVisibility(View.GONE); mViewHolder.setVerifyingState(mContext, VerifyState.VERIFYING, mIsSecret); mInProgress = new AsyncTask() { @@ -478,7 +486,9 @@ public class LinkedIdViewFragment extends Fragment implements if (result.success()) { mViewHolder.vText.setText(getString(mLinkedResource.getVerifiedText(mIsSecret))); mViewHolder.setVerifyingState(mContext, VerifyState.VERIFY_OK, mIsSecret); - + ObjectAnimator anim = SubtleAttentionSeeker.tada(mViewHolder.vButtonConfirm, 0.6f, 1000); + anim.setStartDelay(800); + anim.start(); } else { mViewHolder.setVerifyingState(mContext, VerifyState.VERIFY_ERROR, mIsSecret); result.createNotify(getActivity()).show(); @@ -498,6 +508,9 @@ public class LinkedIdViewFragment extends Fragment implements // get the user's passphrase for this key (if required) long certifyKeyId = mViewHolder.vKeySpinner.getSelectedItemId(); if (certifyKeyId == key.none || certifyKeyId == key.symmetric) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + SubtleAttentionSeeker.tint(mViewHolder.vKeySpinnerContainer, 600).start(); + } Notify.create(getActivity(), R.string.select_key_to_certify, Style.ERROR).show(); return; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/SubtleAttentionSeeker.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/SubtleAttentionSeeker.java index 87444c226..bf4211899 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/SubtleAttentionSeeker.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/SubtleAttentionSeeker.java @@ -17,13 +17,19 @@ package org.sufficientlysecure.keychain.ui.util; +import android.animation.AnimatorInflater; +import android.animation.ArgbEvaluator; import android.animation.Keyframe; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.annotation.TargetApi; +import android.content.Context; import android.os.Build.VERSION_CODES; import android.view.View; +import org.sufficientlysecure.keychain.R; + + @TargetApi(VERSION_CODES.ICE_CREAM_SANDWICH) /** Simple animation helper for subtle attention seeker stuff. * @@ -36,6 +42,10 @@ public class SubtleAttentionSeeker { } public static ObjectAnimator tada(View view, float shakeFactor) { + return tada(view, shakeFactor, 1400); + } + + public static ObjectAnimator tada(View view, float shakeFactor, int duration) { PropertyValuesHolder pvhScaleX = PropertyValuesHolder.ofKeyframe(View.SCALE_X, Keyframe.ofFloat(0f, 1f), @@ -80,7 +90,13 @@ public class SubtleAttentionSeeker { ); return ObjectAnimator.ofPropertyValuesHolder(view, pvhScaleX, pvhScaleY, pvhRotate). - setDuration(1400); + setDuration(duration); + } + + @TargetApi(VERSION_CODES.LOLLIPOP) + public static ObjectAnimator tint(View view, int duration) { + return ObjectAnimator.ofArgb(view, "backgroundColor", + 0x00FF0000, 0x33FF0000, 0x00FF0000).setDuration(duration); } } -- cgit v1.2.3 From 01c444d63788b78781132ef5cc9998b0b4352e71 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 29 Mar 2015 23:38:01 +0200 Subject: even more linked id ui work --- .../keychain/ui/adapter/LinkedIdsAdapter.java | 3 -- .../keychain/ui/linked/LinkedIdViewFragment.java | 39 ++++++++++------------ .../keychain/ui/widget/CertListWidget.java | 2 -- .../keychain/ui/widget/CertifyKeySpinner.java | 6 ++++ .../keychain/ui/widget/KeySpinner.java | 7 +++- 5 files changed, 29 insertions(+), 28 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 e94ea5189..480421499 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 @@ -113,7 +113,6 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { ViewHolder holder = (ViewHolder) view.getTag(); if (!mIsSecret) { - holder.vVerified.setVisibility(View.VISIBLE); int isVerified = cursor.getInt(INDEX_VERIFIED); switch (isVerified) { case Certs.VERIFIED_SECRET: @@ -129,8 +128,6 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { null, State.INVALID, KeyFormattingUtils.DEFAULT_COLOR); break; } - } else { - holder.vVerified.setVisibility(View.GONE); } RawLinkedIdentity id = getItemAtPosition(cursor); 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 5c1389573..3a83939e7 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 @@ -3,15 +3,12 @@ package org.sufficientlysecure.keychain.ui.linked; import java.io.IOException; import java.util.Arrays; -import android.animation.Animator; -import android.animation.Animator.AnimatorListener; import android.animation.ObjectAnimator; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.PorterDuff; -import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; @@ -139,10 +136,14 @@ public class LinkedIdViewFragment extends Fragment implements switch (loader.getId()) { case LOADER_ID_LINKED_ID: + // Nothing to load means break if we are *expected* to load if (!cursor.moveToFirst()) { - Notify.create(getActivity(), "Error loading identity!", - Notify.LENGTH_LONG, Style.ERROR).show(); - finishFragment(); + if (mIdLoadedListener != null) { + Notify.create(getActivity(), "Error loading identity!", + Notify.LENGTH_LONG, Style.ERROR).show(); + finishFragment(); + } + // Or just ignore, this is probably some intermediate state during certify break; } @@ -171,9 +172,14 @@ public class LinkedIdViewFragment extends Fragment implements } public void finishFragment() { - FragmentManager manager = getFragmentManager(); - manager.removeOnBackStackChangedListener(this); - manager.popBackStack("linked_id", FragmentManager.POP_BACK_STACK_INCLUSIVE); + new Handler().post(new Runnable() { + @Override + public void run() { + FragmentManager manager = getFragmentManager(); + manager.removeOnBackStackChangedListener(LinkedIdViewFragment.this); + manager.popBackStack("linked_id", FragmentManager.POP_BACK_STACK_INCLUSIVE); + } + }); } public interface OnIdentityLoadedListener { @@ -256,7 +262,6 @@ public class LinkedIdViewFragment extends Fragment implements private final View vButtonVerify; private final View vButtonRetry; private final View vButtonConfirm; - private final View vButtonBack; private final ViewAnimator vProgress; private final TextSwitcher vText; @@ -269,7 +274,6 @@ public class LinkedIdViewFragment extends Fragment implements mLinkedIdHolder = new LinkedIdsAdapter.ViewHolder(root); - vButtonBack = root.findViewById(R.id.back_button); vButtonVerify = root.findViewById(R.id.button_verify); vButtonRetry = root.findViewById(R.id.button_retry); vButtonConfirm = root.findViewById(R.id.button_confirm); @@ -409,16 +413,6 @@ public class LinkedIdViewFragment extends Fragment implements .setColorFilter(mContext.getResources().getColor(R.color.android_red_light), PorterDuff.Mode.SRC_IN); - - - mViewHolder.vButtonBack.setClickable(true); - mViewHolder.vButtonBack.findViewById(R.id.back_button).setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - getFragmentManager().popBackStack(); - } - }); - mViewHolder.vButtonVerify.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { @@ -510,8 +504,9 @@ public class LinkedIdViewFragment extends Fragment implements if (certifyKeyId == key.none || certifyKeyId == key.symmetric) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { SubtleAttentionSeeker.tint(mViewHolder.vKeySpinnerContainer, 600).start(); + } else { + Notify.create(getActivity(), R.string.select_key_to_certify, Style.ERROR).show(); } - Notify.create(getActivity(), R.string.select_key_to_certify, Style.ERROR).show(); return; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java index c8f80f4a5..0f33a3b8a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertListWidget.java @@ -133,8 +133,6 @@ public class CertListWidget extends ViewAnimator vCollapsed.setText("This identity is not yet verified or confirmed."); } - setVisibility(View.VISIBLE); - } @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java index 460163a47..714933b58 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java @@ -21,6 +21,7 @@ import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; +import android.support.annotation.StringRes; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.util.AttributeSet; @@ -138,4 +139,9 @@ public class CertifyKeySpinner extends KeySpinner { return true; } + public @StringRes int getNoneString() { + return R.string.choice_select_cert; + } + + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java index 70f4e7792..5a6c61f7d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeySpinner.java @@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui.widget; import android.content.Context; import android.database.Cursor; import android.graphics.Color; +import android.support.annotation.StringRes; import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; @@ -277,7 +278,7 @@ public abstract class KeySpinner extends TintSpinner implements LoaderManager.Lo TextView vKeyEmail = (TextView) view.findViewById(R.id.keyspinner_key_email); TextView vKeyDuplicate = (TextView) view.findViewById(R.id.keyspinner_duplicate); - vKeyName.setText(R.string.choice_none); + vKeyName.setText(getNoneString()); vKeyEmail.setVisibility(View.GONE); vKeyDuplicate.setVisibility(View.GONE); vKeyStatus.setVisibility(View.GONE); @@ -296,4 +297,8 @@ public abstract class KeySpinner extends TintSpinner implements LoaderManager.Lo return true; } + public @StringRes int getNoneString() { + return R.string.choice_none; + } + } -- cgit v1.2.3 From d27925ad19b6800658a69670ceaed261776ba6d3 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 30 Mar 2015 02:42:16 +0200 Subject: rewrite EncryptKeyCompletionView with generalized KeyAdapter --- .../keychain/ui/EncryptAsymmetricFragment.java | 25 +- .../keychain/ui/KeyListFragment.java | 211 +++------------- .../keychain/ui/adapter/KeyAdapter.java | 279 +++++++++++++++++++++ .../ui/widget/EncryptKeyCompletionView.java | 278 ++++++-------------- 4 files changed, 400 insertions(+), 393 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java index c5404094a..b242381b1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptAsymmetricFragment.java @@ -28,10 +28,14 @@ import com.tokenautocomplete.TokenCompleteTextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey; +import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.provider.ProviderHelper.NotFoundException; +import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter.KeyItem; import org.sufficientlysecure.keychain.ui.widget.EncryptKeyCompletionView; import org.sufficientlysecure.keychain.ui.widget.KeySpinner; import org.sufficientlysecure.keychain.util.Log; @@ -63,7 +67,7 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi try { mEncryptInterface = (EncryptActivityInterface) activity; } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() + " must implement EncryptActivityInterface"); + throw new ClassCastException(activity + " must implement EncryptActivityInterface"); } } @@ -110,14 +114,14 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi mEncryptKeyView.setTokenListener(new TokenCompleteTextView.TokenListener() { @Override public void onTokenAdded(Object token) { - if (token instanceof EncryptKeyCompletionView.EncryptionKey) { + if (token instanceof KeyItem) { updateEncryptionKeys(); } } @Override public void onTokenRemoved(Object token) { - if (token instanceof EncryptKeyCompletionView.EncryptionKey) { + if (token instanceof KeyItem) { updateEncryptionKeys(); } } @@ -148,10 +152,10 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi if (encryptionKeyIds != null) { for (long preselectedId : encryptionKeyIds) { try { - CachedPublicKeyRing ring = mProviderHelper.getCachedPublicKeyRing( - KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(preselectedId)); - mEncryptKeyView.addObject(mEncryptKeyView.new EncryptionKey(ring)); - } catch (PgpKeyNotFoundException e) { + CanonicalizedPublicKeyRing ring = + mProviderHelper.getCanonicalizedPublicKeyRing(preselectedId); + mEncryptKeyView.addObject(new KeyItem(ring)); + } catch (NotFoundException e) { Log.e(Constants.TAG, "key not found!", e); } } @@ -159,6 +163,7 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi mEncryptKeyView.requestFocus(); updateEncryptionKeys(); } + } private void updateEncryptionKeys() { @@ -166,9 +171,9 @@ public class EncryptAsymmetricFragment extends Fragment implements EncryptActivi List keyIds = new ArrayList<>(); List userIds = new ArrayList<>(); for (Object object : objects) { - if (object instanceof EncryptKeyCompletionView.EncryptionKey) { - keyIds.add(((EncryptKeyCompletionView.EncryptionKey) object).getKeyId()); - userIds.add(((EncryptKeyCompletionView.EncryptionKey) object).getUserId()); + if (object instanceof KeyItem) { + keyIds.add(((KeyItem) object).mKeyId); + userIds.add(((KeyItem) object).mUserIdFull); } } long[] keyIdsArr = new long[keyIds.size()]; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 5f1189deb..d9156e628 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -1,6 +1,6 @@ /* - * Copyright (C) 2013-2014 Dominik Schürmann - * Copyright (C) 2014 Vincent Breitmoser + * Copyright (C) 2013-2015 Dominik Schürmann + * Copyright (C) 2014-2015 Vincent Breitmoser * * 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 @@ -25,7 +25,6 @@ import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.Color; -import android.graphics.PorterDuff; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -36,7 +35,6 @@ import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.view.MenuItemCompat; -import android.support.v4.widget.CursorAdapter; import android.support.v7.widget.SearchView; import android.view.ActionMode; import android.view.LayoutInflater; @@ -48,8 +46,6 @@ import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AbsListView.MultiChoiceModeListener; import android.widget.AdapterView; -import android.widget.ImageButton; -import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; @@ -61,16 +57,13 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.ConsolidateResult; import org.sufficientlysecure.keychain.operations.results.DeleteResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.pgp.KeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; +import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; -import org.sufficientlysecure.keychain.ui.util.Highlighter; -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.util.ExportHelper; import org.sufficientlysecure.keychain.util.FabContainer; @@ -278,26 +271,6 @@ public class KeyListFragment extends LoaderFragment getLoaderManager().initLoader(0, null, this); } - // These are the rows that we will retrieve. - static final String[] PROJECTION = new String[]{ - KeyRings._ID, - KeyRings.MASTER_KEY_ID, - KeyRings.USER_ID, - KeyRings.IS_REVOKED, - KeyRings.IS_EXPIRED, - KeyRings.VERIFIED, - KeyRings.HAS_ANY_SECRET, - KeyRings.HAS_DUPLICATE_USER_ID, - }; - - static final int INDEX_MASTER_KEY_ID = 1; - static final int INDEX_USER_ID = 2; - static final int INDEX_IS_REVOKED = 3; - static final int INDEX_IS_EXPIRED = 4; - static final int INDEX_VERIFIED = 5; - static final int INDEX_HAS_ANY_SECRET = 6; - static final int INDEX_HAS_DUPLICATE_USER_ID = 7; - static final String ORDER = KeyRings.HAS_ANY_SECRET + " DESC, UPPER(" + KeyRings.USER_ID + ") ASC"; @@ -325,7 +298,8 @@ public class KeyListFragment extends LoaderFragment // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. - return new CursorLoader(getActivity(), baseUri, PROJECTION, where, whereArgs, ORDER); + return new CursorLoader(getActivity(), baseUri, + KeyListAdapter.PROJECTION, where, whereArgs, ORDER); } @Override @@ -637,148 +611,54 @@ public class KeyListFragment extends LoaderFragment anim.start(); } - /** - * Implements StickyListHeadersAdapter from library - */ - private class KeyListAdapter extends CursorAdapter implements StickyListHeadersAdapter { - private String mQuery; - private LayoutInflater mInflater; + public class KeyListAdapter extends KeyAdapter implements StickyListHeadersAdapter { private HashMap mSelection = new HashMap<>(); public KeyListAdapter(Context context, Cursor c, int flags) { super(context, c, flags); - - mInflater = LayoutInflater.from(context); - } - - public void setSearchQuery(String query) { - mQuery = query; } @Override - public Cursor swapCursor(Cursor newCursor) { - return super.swapCursor(newCursor); - } + public View newView(Context context, Cursor cursor, ViewGroup parent) { + View view = super.newView(context, cursor, parent); - private class ItemViewHolder { - Long mMasterKeyId; - TextView mMainUserId; - TextView mMainUserIdRest; - ImageView mStatus; - View mSlinger; - ImageButton mSlingerButton; - } + final KeyItemViewHolder holder = (KeyItemViewHolder) view.getTag(); - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - View view = mInflater.inflate(R.layout.key_list_item, parent, false); - final ItemViewHolder holder = new ItemViewHolder(); - holder.mMainUserId = (TextView) view.findViewById(R.id.key_list_item_name); - holder.mMainUserIdRest = (TextView) view.findViewById(R.id.key_list_item_email); - holder.mStatus = (ImageView) view.findViewById(R.id.key_list_item_status_icon); - holder.mSlinger = view.findViewById(R.id.key_list_item_slinger_view); - holder.mSlingerButton = (ImageButton) view.findViewById(R.id.key_list_item_slinger_button); - holder.mSlingerButton.setColorFilter(context.getResources().getColor(R.color.tertiary_text_light), - PorterDuff.Mode.SRC_IN); - view.setTag(holder); - view.findViewById(R.id.key_list_item_slinger_button).setOnClickListener(new OnClickListener() { + holder.mSlinger.setVisibility(View.VISIBLE); + holder.mSlingerButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (holder.mMasterKeyId != null) { - Intent safeSlingerIntent = new Intent(getActivity(), SafeSlingerActivity.class); + Intent safeSlingerIntent = new Intent(mContext, SafeSlingerActivity.class); safeSlingerIntent.putExtra(SafeSlingerActivity.EXTRA_MASTER_KEY_ID, holder.mMasterKeyId); startActivityForResult(safeSlingerIntent, 0); } } }); + return view; } - /** - * Bind cursor data to the item list view - */ @Override - public void bindView(View view, Context context, Cursor cursor) { - Highlighter highlighter = new Highlighter(context, mQuery); - ItemViewHolder h = (ItemViewHolder) view.getTag(); - - { // set name and stuff, common to both key types - String userId = cursor.getString(INDEX_USER_ID); - KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId); - if (userIdSplit.name != null) { - h.mMainUserId.setText(highlighter.highlight(userIdSplit.name)); - } else { - h.mMainUserId.setText(R.string.user_id_no_name); - } - if (userIdSplit.email != null) { - h.mMainUserIdRest.setText(highlighter.highlight(userIdSplit.email)); - h.mMainUserIdRest.setVisibility(View.VISIBLE); - } else { - h.mMainUserIdRest.setVisibility(View.GONE); - } - } - - { // set edit button and status, specific by key type - - long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID); - boolean isSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) != 0; - boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; - boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0; - boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0; - boolean hasDuplicate = cursor.getInt(INDEX_HAS_DUPLICATE_USER_ID) == 1; - - h.mMasterKeyId = masterKeyId; - - // Note: order is important! - if (isRevoked) { - KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, null, State.REVOKED, R.color.bg_gray); - h.mStatus.setVisibility(View.VISIBLE); - h.mSlinger.setVisibility(View.GONE); - h.mMainUserId.setTextColor(context.getResources().getColor(R.color.bg_gray)); - h.mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.bg_gray)); - } else if (isExpired) { - KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, null, State.EXPIRED, R.color.bg_gray); - h.mStatus.setVisibility(View.VISIBLE); - h.mSlinger.setVisibility(View.GONE); - h.mMainUserId.setTextColor(context.getResources().getColor(R.color.bg_gray)); - h.mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.bg_gray)); - } else if (isSecret) { - h.mStatus.setVisibility(View.GONE); - h.mSlinger.setVisibility(View.VISIBLE); - h.mMainUserId.setTextColor(context.getResources().getColor(R.color.black)); - h.mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.black)); - } else { - // this is a public key - show if it's verified - if (isVerified) { - KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, State.VERIFIED); - h.mStatus.setVisibility(View.VISIBLE); - } else { - KeyFormattingUtils.setStatusImage(getActivity(), h.mStatus, State.UNVERIFIED); - h.mStatus.setVisibility(View.VISIBLE); - } - h.mSlinger.setVisibility(View.GONE); - h.mMainUserId.setTextColor(context.getResources().getColor(R.color.black)); - h.mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.black)); - } - } - - } + public View getView(int position, View convertView, ViewGroup parent) { + // let the adapter handle setting up the row views + View v = super.getView(position, convertView, parent); - public boolean isSecretAvailable(int id) { - if (!mCursor.moveToPosition(id)) { - throw new IllegalStateException("couldn't move cursor to position " + id); + if (mSelection.get(position) != null) { + // selected position color + v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis)); + } else { + // default color + v.setBackgroundColor(Color.TRANSPARENT); } - return mCursor.getInt(INDEX_HAS_ANY_SECRET) != 0; + return v; } - public long getMasterKeyId(int id) { - if (!mCursor.moveToPosition(id)) { - throw new IllegalStateException("couldn't move cursor to position " + id); - } - - return mCursor.getLong(INDEX_MASTER_KEY_ID); + private class HeaderViewHolder { + TextView mText; + TextView mCount; } /** @@ -811,10 +691,10 @@ public class KeyListFragment extends LoaderFragment throw new IllegalStateException("couldn't move cursor to position " + position); } - if (mCursor.getInt(KeyListFragment.INDEX_HAS_ANY_SECRET) != 0) { + if (mCursor.getInt(INDEX_HAS_ANY_SECRET) != 0) { { // set contact count int num = mCursor.getCount(); - String contactsTotal = getResources().getQuantityString(R.plurals.n_keys, num, num); + String contactsTotal = mContext.getResources().getQuantityString(R.plurals.n_keys, num, num); holder.mCount.setText(contactsTotal); holder.mCount.setVisibility(View.VISIBLE); } @@ -824,7 +704,7 @@ public class KeyListFragment extends LoaderFragment } // set header text as first char in user id - String userId = mCursor.getString(KeyListFragment.INDEX_USER_ID); + String userId = mCursor.getString(INDEX_USER_ID); String headerText = convertView.getResources().getString(R.string.user_id_no_name); if (userId != null && userId.length() > 0) { headerText = "" + userId.charAt(0); @@ -850,11 +730,11 @@ public class KeyListFragment extends LoaderFragment } // early breakout: all secret keys are assigned id 0 - if (mCursor.getInt(KeyListFragment.INDEX_HAS_ANY_SECRET) != 0) { + if (mCursor.getInt(INDEX_HAS_ANY_SECRET) != 0) { return 1L; } // otherwise, return the first character of the name as ID - String userId = mCursor.getString(KeyListFragment.INDEX_USER_ID); + String userId = mCursor.getString(INDEX_USER_ID); if (userId != null && userId.length() > 0) { return Character.toUpperCase(userId.charAt(0)); } else { @@ -862,11 +742,6 @@ public class KeyListFragment extends LoaderFragment } } - private class HeaderViewHolder { - TextView mText; - TextView mCount; - } - /** * -------------------------- MULTI-SELECTION METHODS -------------- */ @@ -877,7 +752,7 @@ public class KeyListFragment extends LoaderFragment public boolean isAnySecretSelected() { for (int pos : mSelection.keySet()) { - if (mAdapter.isSecretAvailable(pos)) + if (isSecretAvailable(pos)) return true; } return false; @@ -888,7 +763,7 @@ public class KeyListFragment extends LoaderFragment int i = 0; // get master key ids for (int pos : mSelection.keySet()) { - ids[i++] = mAdapter.getMasterKeyId(pos); + ids[i++] = getMasterKeyId(pos); } return ids; } @@ -903,26 +778,6 @@ public class KeyListFragment extends LoaderFragment notifyDataSetChanged(); } - @Override - public View getView(int position, View convertView, ViewGroup parent) { - // let the adapter handle setting up the row views - View v = super.getView(position, convertView, parent); - - /** - * Change color for multi-selection - */ - if (mSelection.get(position) != null) { - // selected position color - v.setBackgroundColor(parent.getResources().getColor(R.color.emphasis)); - } else { - // default color - v.setBackgroundColor(Color.TRANSPARENT); - } - - return v; - } - } - } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java new file mode 100644 index 000000000..9304b14f1 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2015 Vincent Breitmoser + * + * 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.adapter; + + +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +import android.content.Context; +import android.database.Cursor; +import android.graphics.PorterDuff; +import android.support.v4.widget.CursorAdapter; +import android.text.format.DateFormat; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKey; +import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing; +import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.ui.util.Highlighter; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; + +public class KeyAdapter extends CursorAdapter { + + protected String mQuery; + protected LayoutInflater mInflater; + + // These are the rows that we will retrieve. + public static final String[] PROJECTION = new String[]{ + KeyRings._ID, + KeyRings.MASTER_KEY_ID, + KeyRings.USER_ID, + KeyRings.IS_REVOKED, + KeyRings.IS_EXPIRED, + KeyRings.VERIFIED, + KeyRings.HAS_ANY_SECRET, + KeyRings.HAS_DUPLICATE_USER_ID, + KeyRings.HAS_ENCRYPT, + KeyRings.FINGERPRINT, + KeyRings.CREATION, + }; + + public static final int INDEX_MASTER_KEY_ID = 1; + public static final int INDEX_USER_ID = 2; + public static final int INDEX_IS_REVOKED = 3; + public static final int INDEX_IS_EXPIRED = 4; + public static final int INDEX_VERIFIED = 5; + public static final int INDEX_HAS_ANY_SECRET = 6; + public static final int INDEX_HAS_DUPLICATE_USER_ID = 7; + public static final int INDEX_HAS_ENCRYPT = 8; + public static final int INDEX_FINGERPRINT = 9; + public static final int INDEX_CREATION = 10; + + public KeyAdapter(Context context, Cursor c, int flags) { + super(context, c, flags); + + mInflater = LayoutInflater.from(context); + } + + public void setSearchQuery(String query) { + mQuery = query; + } + + public static class KeyItemViewHolder { + public Long mMasterKeyId; + public TextView mMainUserId; + public TextView mMainUserIdRest; + public ImageView mStatus; + public View mSlinger; + public ImageButton mSlingerButton; + + public KeyItemViewHolder(View view) { + mMainUserId = (TextView) view.findViewById(R.id.key_list_item_name); + mMainUserIdRest = (TextView) view.findViewById(R.id.key_list_item_email); + mStatus = (ImageView) view.findViewById(R.id.key_list_item_status_icon); + mSlinger = view.findViewById(R.id.key_list_item_slinger_view); + mSlingerButton = (ImageButton) view.findViewById(R.id.key_list_item_slinger_button); + } + + public void setData(Context context, Cursor cursor, Highlighter highlighter) { + + { // set name and stuff, common to both key types + String userId = cursor.getString(INDEX_USER_ID); + KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId); + if (userIdSplit.name != null) { + mMainUserId.setText(highlighter.highlight(userIdSplit.name)); + } else { + mMainUserId.setText(R.string.user_id_no_name); + } + if (userIdSplit.email != null) { + mMainUserIdRest.setText(highlighter.highlight(userIdSplit.email)); + mMainUserIdRest.setVisibility(View.VISIBLE); + } else { + mMainUserIdRest.setVisibility(View.GONE); + } + } + + { // set edit button and status, specific by key type + + long masterKeyId = cursor.getLong(INDEX_MASTER_KEY_ID); + boolean isSecret = cursor.getInt(INDEX_HAS_ANY_SECRET) != 0; + boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; + boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0; + boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0; + // boolean hasDuplicate = cursor.getInt(INDEX_HAS_DUPLICATE_USER_ID) == 1; + + mMasterKeyId = masterKeyId; + + // Note: order is important! + if (isRevoked) { + KeyFormattingUtils + .setStatusImage(context, mStatus, null, State.REVOKED, R.color.bg_gray); + mStatus.setVisibility(View.VISIBLE); + mSlinger.setVisibility(View.GONE); + mMainUserId.setTextColor(context.getResources().getColor(R.color.bg_gray)); + mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.bg_gray)); + } else if (isExpired) { + KeyFormattingUtils.setStatusImage(context, mStatus, null, State.EXPIRED, R.color.bg_gray); + mStatus.setVisibility(View.VISIBLE); + mSlinger.setVisibility(View.GONE); + mMainUserId.setTextColor(context.getResources().getColor(R.color.bg_gray)); + mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.bg_gray)); + } else if (isSecret) { + mStatus.setVisibility(View.GONE); + if (mSlingerButton.hasOnClickListeners()) { + mSlinger.setVisibility(View.VISIBLE); + } + mMainUserId.setTextColor(context.getResources().getColor(R.color.black)); + mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.black)); + } else { + // this is a public key - show if it's verified + if (isVerified) { + KeyFormattingUtils.setStatusImage(context, mStatus, State.VERIFIED); + mStatus.setVisibility(View.VISIBLE); + } else { + KeyFormattingUtils.setStatusImage(context, mStatus, State.UNVERIFIED); + mStatus.setVisibility(View.VISIBLE); + } + mSlinger.setVisibility(View.GONE); + mMainUserId.setTextColor(context.getResources().getColor(R.color.black)); + mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.black)); + } + } + + } + + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + View view = mInflater.inflate(R.layout.key_list_item, parent, false); + KeyItemViewHolder holder = new KeyItemViewHolder(view); + view.setTag(holder); + holder.mSlingerButton.setColorFilter(context.getResources().getColor(R.color.tertiary_text_light), + PorterDuff.Mode.SRC_IN); + return view; + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + Highlighter highlighter = new Highlighter(context, mQuery); + KeyItemViewHolder h = (KeyItemViewHolder) view.getTag(); + h.setData(context, cursor, highlighter); + } + + public boolean isSecretAvailable(int id) { + if (!mCursor.moveToPosition(id)) { + throw new IllegalStateException("couldn't move cursor to position " + id); + } + + return mCursor.getInt(INDEX_HAS_ANY_SECRET) != 0; + } + + public long getMasterKeyId(int id) { + if (!mCursor.moveToPosition(id)) { + throw new IllegalStateException("couldn't move cursor to position " + id); + } + + return mCursor.getLong(INDEX_MASTER_KEY_ID); + } + + @Override + public KeyItem getItem(int position) { + Cursor c = getCursor(); + if (c.isClosed() || !c.moveToPosition(position)) { + return null; + } + return new KeyItem(c); + } + + @Override + public long getItemId(int position) { + // prevent a crash on rapid cursor changes + if (getCursor().isClosed()) { + return 0L; + } + return super.getItemId(position); + } + + public static class KeyItem { + + public final String mUserIdFull; + public final KeyRing.UserId mUserId; + public final long mKeyId; + public final boolean mHasDuplicate; + public final Date mCreation; + public final String mFingerprint; + + private KeyItem(Cursor cursor) { + String userId = cursor.getString(INDEX_USER_ID); + mUserId = KeyRing.splitUserId(userId); + mUserIdFull = userId; + mKeyId = cursor.getLong(INDEX_MASTER_KEY_ID); + mHasDuplicate = cursor.getLong(INDEX_HAS_DUPLICATE_USER_ID) > 0; + mCreation = new Date(cursor.getLong(INDEX_CREATION) * 1000); + mFingerprint = KeyFormattingUtils.convertFingerprintToHex( + cursor.getBlob(INDEX_FINGERPRINT)); + } + + public KeyItem(CanonicalizedPublicKeyRing ring) { + CanonicalizedPublicKey key = ring.getPublicKey(); + String userId = key.getPrimaryUserIdWithFallback(); + mUserId = KeyRing.splitUserId(userId); + mUserIdFull = userId; + mKeyId = ring.getMasterKeyId(); + mHasDuplicate = false; + mCreation = key.getCreationTime(); + mFingerprint = KeyFormattingUtils.convertFingerprintToHex( + ring.getFingerprint()); + } + + public String getReadableName() { + if (mUserId.name != null) { + return mUserId.name; + } else { + return mUserId.email; + } + } + + public boolean hasDuplicate() { + return mHasDuplicate; + } + + public String getCreationDate(Context context) { + Calendar creationCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + creationCal.setTime(mCreation); + // convert from UTC to time zone of device + creationCal.setTimeZone(TimeZone.getDefault()); + + return context.getString(R.string.label_creation) + ": " + + DateFormat.getDateFormat(context).format(creationCal.getTime()); + } + + } + +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java index ceace1d26..b2dfb2493 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java @@ -1,5 +1,6 @@ /* - * Copyright (C) 2014 Dominik Schürmann + * Copyright (C) 2014-2015 Dominik Schürmann + * Copyright (C) 2015 Vincent Breitmoser * * 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 @@ -17,49 +18,42 @@ package org.sufficientlysecure.keychain.ui.widget; -import android.app.Activity; import android.content.Context; import android.database.Cursor; -import android.graphics.Bitmap; import android.graphics.Rect; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; +import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; -import android.text.format.DateFormat; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; -import android.widget.ImageView; import android.widget.TextView; -import com.tokenautocomplete.FilteredArrayAdapter; import com.tokenautocomplete.TokenCompleteTextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.pgp.KeyRing; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; -import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.ContactHelper; +import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter; +import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter.KeyItem; import org.sufficientlysecure.keychain.util.Log; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.TimeZone; -public class EncryptKeyCompletionView extends TokenCompleteTextView { +public class EncryptKeyCompletionView extends TokenCompleteTextView + implements LoaderCallbacks { + + public static final String ARG_QUERY = "query"; + + private KeyAdapter mAdapter; + private LoaderManager mLoaderManager; + private String mPrefix; + public EncryptKeyCompletionView(Context context) { super(context); initView(); @@ -76,33 +70,31 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView { } private void initView() { - swapCursor(null); setPrefix(getContext().getString(R.string.label_to) + " "); + allowDuplicates(false); + mAdapter = new KeyAdapter(getContext(), null, 0); + setAdapter(mAdapter); + } + + @Override + public void setPrefix(String p) { + // this one is private in the superclass, but we need it here + mPrefix = p; + super.setPrefix(p); } @Override protected View getViewForObject(Object object) { - if (object instanceof EncryptionKey) { - LayoutInflater l = (LayoutInflater) getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE); + if (object instanceof KeyItem) { + LayoutInflater l = LayoutInflater.from(getContext()); View view = l.inflate(R.layout.recipient_box_entry, null); - ((TextView) view.findViewById(android.R.id.text1)).setText(((EncryptionKey) object).getPrimary()); - setImageByKey((ImageView) view.findViewById(android.R.id.icon), (EncryptionKey) object); + ((TextView) view.findViewById(android.R.id.text1)).setText(((KeyItem) object).getReadableName()); return view; } return null; } - private void setImageByKey(ImageView view, EncryptionKey key) { - Bitmap photo = ContactHelper.getCachedPhotoByMasterKeyId(getContext().getContentResolver(), key.getKeyId()); - - if (photo != null) { - view.setImageBitmap(photo); - } else { - view.setImageResource(R.drawable.ic_generic_man); - } - } - @Override protected Object defaultObject(String completionText) { // TODO: We could try to automagically download the key if it's unknown but a key id @@ -115,196 +107,72 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); + + mLoaderManager = ((FragmentActivity) getContext()).getSupportLoaderManager(); + if (getContext() instanceof FragmentActivity) { - ((FragmentActivity) getContext()).getSupportLoaderManager().initLoader(hashCode(), null, new LoaderManager.LoaderCallbacks() { - @Override - public Loader onCreateLoader(int id, Bundle args) { - // These are the rows that we will retrieve. - Uri baseUri = KeyRings.buildUnifiedKeyRingsUri(); - - String[] projection = new String[]{ - KeyRings._ID, - KeyRings.MASTER_KEY_ID, - KeyRings.KEY_ID, - KeyRings.USER_ID, - KeyRings.FINGERPRINT, - KeyRings.IS_EXPIRED, - KeyRings.HAS_ENCRYPT, - KeyRings.HAS_DUPLICATE_USER_ID, - KeyRings.CREATION - }; - - String where = KeyRings.HAS_ENCRYPT + " NOT NULL AND " + KeyRings.IS_EXPIRED + " = 0 AND " - + Tables.KEYS + "." + KeyRings.IS_REVOKED + " = 0"; - - return new CursorLoader(getContext(), baseUri, projection, where, null, null); - } - - @Override - public void onLoadFinished(Loader loader, Cursor data) { - swapCursor(data); - } - - @Override - public void onLoaderReset(Loader loader) { - swapCursor(null); - } - }); + mLoaderManager.initLoader(hashCode(), null, this); } else { Log.e(Constants.TAG, "EncryptKeyCompletionView must be attached to a FragmentActivity, this is " + getContext().getClass()); } } @Override - public void onFocusChanged(boolean hasFocus, int direction, Rect previous) { - super.onFocusChanged(hasFocus, direction, previous); - if (hasFocus) { - ((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE)) - .showSoftInput(this, InputMethodManager.SHOW_IMPLICIT); - } + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mLoaderManager = null; } - public void swapCursor(Cursor cursor) { - if (cursor == null) { - setAdapter(new EncryptKeyAdapter(Collections.emptyList())); - return; - } - ArrayList keys = new ArrayList<>(); - while (cursor.moveToNext()) { - try { - EncryptionKey key = new EncryptionKey(cursor); - keys.add(key); - } catch (Exception e) { - Log.w(Constants.TAG, e); - return; - } - } - setAdapter(new EncryptKeyAdapter(keys)); - } - - public class EncryptionKey { - private String mUserIdFull; - private KeyRing.UserId mUserId; - private long mKeyId; - private boolean mHasDuplicate; - private Date mCreation; - private String mFingerprint; - - public EncryptionKey(String userId, long keyId, boolean hasDuplicate, Date creation, String fingerprint) { - mUserId = KeyRing.splitUserId(userId); - mUserIdFull = userId; - mKeyId = keyId; - mHasDuplicate = hasDuplicate; - mCreation = creation; - mFingerprint = fingerprint; - } - - public EncryptionKey(Cursor cursor) { - this(cursor.getString(cursor.getColumnIndexOrThrow(KeyRings.USER_ID)), - cursor.getLong(cursor.getColumnIndexOrThrow(KeyRings.KEY_ID)), - cursor.getLong(cursor.getColumnIndexOrThrow(KeyRings.HAS_DUPLICATE_USER_ID)) > 0, - new Date(cursor.getLong(cursor.getColumnIndexOrThrow(KeyRings.CREATION)) * 1000), - KeyFormattingUtils.convertFingerprintToHex( - cursor.getBlob(cursor.getColumnIndexOrThrow(KeyRings.FINGERPRINT)))); - } - - public EncryptionKey(CachedPublicKeyRing ring) throws PgpKeyNotFoundException { - this(ring.getPrimaryUserId(), ring.extractOrGetMasterKeyId(), false, null, - KeyFormattingUtils.convertFingerprintToHex(ring.getFingerprint())); - } - - public String getUserId() { - return mUserIdFull; - } - - public String getFingerprint() { - return mFingerprint; - } - - public String getPrimary() { - if (mUserId.name != null) { - return mUserId.name; - } else { - return mUserId.email; - } - } - - public String getSecondary() { - if (mUserId.email != null) { - return mUserId.email; - } else { - return getCreationDate(); - } - } - - public String getTertiary() { - if (mUserId.name != null) { - return getCreationDate(); - } else { - return null; - } - } + @Override + public Loader onCreateLoader(int id, Bundle args) { + // These are the rows that we will retrieve. + Uri baseUri = KeyRings.buildUnifiedKeyRingsUri(); + String where = KeyRings.HAS_ENCRYPT + " NOT NULL AND " + KeyRings.IS_EXPIRED + " = 0 AND " + + Tables.KEYS + "." + KeyRings.IS_REVOKED + " = 0"; - public long getKeyId() { - return mKeyId; - } + if (args != null && args.containsKey(ARG_QUERY)) { + String query = args.getString(ARG_QUERY); + mAdapter.setSearchQuery(query); - public String getCreationDate() { - if (mHasDuplicate) { - Calendar creationCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - creationCal.setTime(mCreation); - // convert from UTC to time zone of device - creationCal.setTimeZone(TimeZone.getDefault()); - - return getContext().getString(R.string.label_creation) + ": " - + DateFormat.getDateFormat(getContext()).format(creationCal.getTime()); - } else { - return null; - } - } + where += " AND " + KeyRings.USER_ID + " LIKE ?"; - public String getKeyIdHex() { - return KeyFormattingUtils.beautifyKeyIdWithPrefix(getContext(), mKeyId); + return new CursorLoader(getContext(), baseUri, KeyAdapter.PROJECTION, where, + new String[] { "%" + query + "%" }, null); } - public String getKeyIdHexShort() { - return KeyFormattingUtils.convertKeyIdToHexShort(mKeyId); - } + mAdapter.setSearchQuery(null); + return new CursorLoader(getContext(), baseUri, KeyAdapter.PROJECTION, where, null, null); - @Override - public String toString() { - return Long.toString(mKeyId); - } } - private class EncryptKeyAdapter extends FilteredArrayAdapter { + @Override + public void onLoadFinished(Loader loader, Cursor data) { + mAdapter.swapCursor(data); + } - public EncryptKeyAdapter(List objs) { - super(EncryptKeyCompletionView.this.getContext(), 0, 0, objs); - } + @Override + public void onLoaderReset(Loader loader) { + mAdapter.swapCursor(null); + } - @Override - public View getView(int position, View convertView, ViewGroup parent) { - LayoutInflater l = (LayoutInflater) getContext().getSystemService(Activity.LAYOUT_INFLATER_SERVICE); - View view; - if (convertView != null) { - view = convertView; - } else { - view = l.inflate(R.layout.recipient_selection_list_entry, null); - } - ((TextView) view.findViewById(android.R.id.title)).setText(getItem(position).getPrimary()); - ((TextView) view.findViewById(android.R.id.text1)).setText(getItem(position).getSecondary()); - ((TextView) view.findViewById(android.R.id.text2)).setText(getItem(position).getTertiary()); - setImageByKey((ImageView) view.findViewById(android.R.id.icon), getItem(position)); - return view; + @Override + public void onFocusChanged(boolean hasFocus, int direction, Rect previous) { + super.onFocusChanged(hasFocus, direction, previous); + if (hasFocus) { + ((InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE)) + .showSoftInput(this, InputMethodManager.SHOW_IMPLICIT); } + } - @Override - protected boolean keepObject(EncryptionKey obj, String mask) { - String m = mask.toLowerCase(Locale.ENGLISH); - return obj.getUserId().toLowerCase(Locale.ENGLISH).contains(m) || - obj.getKeyIdHex().contains(m) || - obj.getKeyIdHexShort().startsWith(m); - } + @Override + protected void performFiltering(CharSequence text, int start, int end, int keyCode) { + super.performFiltering(text, start, end, keyCode); + if (start < mPrefix.length()) { + start = mPrefix.length(); + } + Bundle args = new Bundle(); + args.putString(ARG_QUERY, text.subSequence(start, end).toString()); + mLoaderManager.restartLoader(hashCode(), args, this); } + } -- cgit v1.2.3 From b12aa63cb35e88df66ad424e12586c02217df246 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 24 Apr 2015 15:44:33 +0200 Subject: use CryptoOperationFragment for linked id fragments, and some warning cleanup --- .../keychain/ui/CertifyKeyFragment.java | 2 + .../keychain/ui/ViewKeyActivity.java | 6 +-- .../ui/linked/LinkedIdCreateFinalFragment.java | 42 ++++------------ .../keychain/ui/linked/LinkedIdViewFragment.java | 56 ++++++---------------- 4 files changed, 30 insertions(+), 76 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java index 59623a610..51b6e824d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyFragment.java @@ -88,7 +88,9 @@ public class CertifyKeyFragment extends CryptoOperationFragment }; private static final int INDEX_MASTER_KEY_ID = 1; private static final int INDEX_USER_ID = 2; + @SuppressWarnings("unused") private static final int INDEX_IS_PRIMARY = 3; + @SuppressWarnings("unused") private static final int INDEX_IS_REVOKED = 4; private MultiUserIdsAdapter mUserIdsAdapter; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 4ec3c28da..9a48201b6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -312,10 +312,10 @@ public class ViewKeyActivity extends BaseNfcActivity implements .replace(R.id.view_key_fragment, frag) .commit(); manager.popBackStack(); - } else { + } /* else { // not sure yet if we actually want this! - // manager.popBackStack(); - } + manager.popBackStack(); + } */ } }); 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 0767aabd2..b98fc6238 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 @@ -1,6 +1,5 @@ package org.sufficientlysecure.keychain.ui.linked; -import android.app.Activity; import android.app.ProgressDialog; import android.content.Intent; import android.graphics.PorterDuff; @@ -8,7 +7,6 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.Message; import android.os.Messenger; -import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -28,14 +26,11 @@ import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; -import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment.ServiceType; import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.util.Passphrase; -public abstract class LinkedIdCreateFinalFragment extends Fragment { - - protected static final int REQUEST_CODE_PASSPHRASE = 0x00007008; +public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragment { protected LinkedIdWizard mLinkedIdWizard; @@ -63,7 +58,7 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { view.findViewById(R.id.next_button).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - startCertify(); + cryptoOperation(); } }); @@ -174,29 +169,28 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { } - private void startCertify() { + protected void cryptoOperation(CryptoInputParcel cryptoInput) { if (mVerifiedResource == null) { Notify.create(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR).show(); return; } - Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); - intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mLinkedIdWizard.mMasterKeyId); - startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); - - } - - private void certifyLinkedIdentity (CryptoInputParcel cryptoInput) { ServiceProgressHandler saveHandler = new ServiceProgressHandler( getActivity(), getString(R.string.progress_saving), ProgressDialog.STYLE_HORIZONTAL, true, ServiceType.KEYCHAIN_INTENT) { + public void handleMessage(Message message) { // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); + // handle pending messages + if (handlePendingMessage(message)) { + return; + } + if (message.arg1 == MessageStatus.OKAY.ordinal()) { // get returned data bundle @@ -252,20 +246,4 @@ public abstract class LinkedIdCreateFinalFragment extends Fragment { } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case REQUEST_CODE_PASSPHRASE: - if (resultCode == Activity.RESULT_OK && data != null) { - CryptoInputParcel cryptoInput = - data.getParcelableExtra(PassphraseDialogActivity.RESULT_CRYPTO_INPUT); - certifyLinkedIdentity(cryptoInput); - } - break; - default: - super.onActivityResult(requestCode, resultCode, data); - } - } - } 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 1944146dd..152cb8988 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 @@ -4,7 +4,6 @@ import java.io.IOException; import java.util.Collections; import android.animation.ObjectAnimator; -import android.app.Activity; import android.content.Context; import android.content.Intent; import android.database.Cursor; @@ -16,7 +15,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.Messenger; -import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager.OnBackStackChangedListener; import android.support.v4.app.LoaderManager; @@ -45,12 +43,11 @@ import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.service.CertifyActionsParcel; import org.sufficientlysecure.keychain.service.CertifyActionsParcel.CertifyAction; import org.sufficientlysecure.keychain.service.KeychainIntentService; -import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; -import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter; import org.sufficientlysecure.keychain.ui.adapter.UserIdsAdapter; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment.ViewHolder.VerifyState; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; @@ -60,14 +57,11 @@ import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker; import org.sufficientlysecure.keychain.ui.widget.CertListWidget; import org.sufficientlysecure.keychain.ui.widget.CertifyKeySpinner; import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.Passphrase; -public class LinkedIdViewFragment extends Fragment implements +public class LinkedIdViewFragment extends CryptoOperationFragment implements LoaderManager.LoaderCallbacks, OnBackStackChangedListener { - public static final int REQUEST_CODE_PASSPHRASE = 0x00008001; - private static final String ARG_DATA_URI = "data_uri"; private static final String ARG_LID_RANK = "rank"; private static final String ARG_IS_SECRET = "verified"; @@ -512,44 +506,20 @@ public class LinkedIdViewFragment extends Fragment implements return; } - Passphrase passphrase; - try { - passphrase = PassphraseCacheService.getCachedPassphrase( - getActivity(), mCertifyKeyId, mCertifyKeyId); - } catch (PassphraseCacheService.KeyNotFoundException e) { - Log.e(Constants.TAG, "Key not found!", e); - getActivity().finish(); - return; - } - if (passphrase == null) { - Intent intent = new Intent(getActivity(), PassphraseDialogActivity.class); - intent.putExtra(PassphraseDialogActivity.EXTRA_SUBKEY_ID, mCertifyKeyId); - startActivityForResult(intent, REQUEST_CODE_PASSPHRASE); - // bail out; need to wait until the user has entered the passphrase before trying again - } else { - certifyResource(new CryptoInputParcel()); - } + cryptoOperation(); } @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - case REQUEST_CODE_PASSPHRASE: { - if (resultCode == Activity.RESULT_OK && data != null) { - CryptoInputParcel cryptoInput = data.getParcelableExtra( - PassphraseDialogActivity.RESULT_CRYPTO_INPUT); - certifyResource(cryptoInput); - } - return; - } + protected void onCryptoOperationCancelled() { + super.onCryptoOperationCancelled(); + + // go back to 'verified ok' + setShowVerifying(false); - default: { - super.onActivityResult(requestCode, resultCode, data); - } - } } - private void certifyResource(CryptoInputParcel cryptoInput) { + @Override + protected void cryptoOperation(CryptoInputParcel cryptoInput) { if (mIsSecret) { return; @@ -588,9 +558,13 @@ public class LinkedIdViewFragment extends Fragment implements // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); - Bundle data = message.getData(); + // handle pending messages + if (handlePendingMessage(message)) { + return; + } if (message.arg1 == MessageStatus.OKAY.ordinal()) { + Bundle data = message.getData(); CertifyResult result = data.getParcelable(CertifyResult.EXTRA_RESULT); result.createNotify(getActivity()).show(); // no need to do anything else, we will get a loader refresh! -- cgit v1.2.3 From ead7597afc95706b04430e9b4171acdfa3ca4fef Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 24 Apr 2015 15:44:53 +0200 Subject: don't show key spinner for a single entry --- .../keychain/ui/linked/LinkedIdViewFragment.java | 4 +++- .../sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 152cb8988..f618181c6 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 @@ -298,7 +298,9 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements vProgress.setDisplayedChild(1); if (!isSecret) { showButton(2); - vKeySpinnerContainer.setVisibility(View.VISIBLE); + if (!vKeySpinner.isSingleEntry()) { + vKeySpinnerContainer.setVisibility(View.VISIBLE); + } } else { showButton(1); vKeySpinnerContainer.setVisibility(View.GONE); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java index 714933b58..e5b3df8c9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/CertifyKeySpinner.java @@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; public class CertifyKeySpinner extends KeySpinner { private long mHiddenMasterKeyId = Constants.key.none; + private boolean mIsSingle; public CertifyKeySpinner(Context context) { super(context); @@ -99,6 +100,7 @@ public class CertifyKeySpinner extends KeySpinner { // - there are actually keys (not just "none" entry) // Then: // - select key that is capable of certifying, but only if there is only one key capable of it + mIsSingle = false; if (mSelectedKeyId == Constants.key.none && mAdapter.getCount() > 1) { // preselect if key can certify int selection = -1; @@ -106,9 +108,11 @@ public class CertifyKeySpinner extends KeySpinner { if (!data.isNull(mIndexHasCertify)) { if (selection == -1) { selection = data.getPosition() + 1; + mIsSingle = true; } else { // if selection is already set, we have more than one certify key! // get back to "none"! + mIsSingle = false; selection = 0; } } @@ -118,6 +122,9 @@ public class CertifyKeySpinner extends KeySpinner { } } + public boolean isSingleEntry() { + return mIsSingle && getSelectedItemPosition() != 0; + } @Override boolean setStatus(Context context, Cursor cursor, ImageView statusView) { -- cgit v1.2.3 From ca9ab02b7b9074bd28e4cd8414b50032b4525eb3 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 24 Apr 2015 19:00:33 +0200 Subject: revamp decrypt ui --- .../keychain/ui/DecryptFilesFragment.java | 6 +- .../keychain/ui/DecryptFragment.java | 416 +++++++++++++++------ .../keychain/ui/DecryptTextFragment.java | 74 ++-- 3 files changed, 347 insertions(+), 149 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java index 766e65e8b..6c1902af1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFilesFragment.java @@ -240,7 +240,7 @@ public class DecryptFilesFragment extends DecryptFragment { } case KeychainIntentService.ACTION_DECRYPT_VERIFY: { // display signature result in activity - onResult(pgpResult); + loadVerifyResult(pgpResult); if (mDeleteAfter.isChecked()) { // Create and show dialog to delete original file @@ -308,4 +308,8 @@ public class DecryptFilesFragment extends DecryptFragment { } } + @Override + protected void onVerifyLoaded(boolean verified) { + + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java index 33209be86..d641f02f9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java @@ -17,26 +17,49 @@ package org.sufficientlysecure.keychain.ui; +import java.util.ArrayList; + import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.util.Log; import android.view.View; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import org.openintents.openpgp.OpenPgpSignatureResult; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.pgp.KeyRing; +import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.KeychainIntentService; +import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; +import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.ui.util.Notify.Style; +import org.sufficientlysecure.keychain.util.Preferences; + -public abstract class DecryptFragment extends CryptoOperationFragment { - private static final int RESULT_CODE_LOOKUP_KEY = 0x00007006; +public abstract class DecryptFragment extends CryptoOperationFragment implements + LoaderManager.LoaderCallbacks { - protected long mSignatureKeyId = 0; + public static final int LOADER_ID_UNIFIED = 0; protected LinearLayout mResultLayout; @@ -46,155 +69,118 @@ public abstract class DecryptFragment extends CryptoOperationFragment { protected TextView mSignatureText; protected View mSignatureLayout; - protected View mSignatureDivider1; - protected View mSignatureDivider2; protected TextView mSignatureName; protected TextView mSignatureEmail; protected TextView mSignatureAction; + private OpenPgpSignatureResult mSignatureResult; + @Override - public void onActivityCreated(Bundle savedInstanceState) { - super.onActivityCreated(savedInstanceState); + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); - mResultLayout = (LinearLayout) getView().findViewById(R.id.result_main_layout); + mResultLayout = (LinearLayout) view.findViewById(R.id.result_main_layout); mResultLayout.setVisibility(View.GONE); - mEncryptionIcon = (ImageView) getView().findViewById(R.id.result_encryption_icon); - mEncryptionText = (TextView) getView().findViewById(R.id.result_encryption_text); - mSignatureIcon = (ImageView) getView().findViewById(R.id.result_signature_icon); - mSignatureText = (TextView) getView().findViewById(R.id.result_signature_text); - mSignatureLayout = getView().findViewById(R.id.result_signature_layout); - mSignatureDivider1 = getView().findViewById(R.id.result_signature_divider1); - mSignatureDivider2 = getView().findViewById(R.id.result_signature_divider2); - mSignatureName = (TextView) getView().findViewById(R.id.result_signature_name); - mSignatureEmail = (TextView) getView().findViewById(R.id.result_signature_email); - mSignatureAction = (TextView) getView().findViewById(R.id.result_signature_action); + mEncryptionIcon = (ImageView) view.findViewById(R.id.result_encryption_icon); + mEncryptionText = (TextView) view.findViewById(R.id.result_encryption_text); + mSignatureIcon = (ImageView) view.findViewById(R.id.result_signature_icon); + mSignatureText = (TextView) view.findViewById(R.id.result_signature_text); + mSignatureLayout = view.findViewById(R.id.result_signature_layout); + mSignatureName = (TextView) view.findViewById(R.id.result_signature_name); + mSignatureEmail = (TextView) view.findViewById(R.id.result_signature_email); + mSignatureAction = (TextView) view.findViewById(R.id.result_signature_action); } private void lookupUnknownKey(long unknownKeyId) { - Intent intent = new Intent(getActivity(), ImportKeysActivity.class); - intent.setAction(ImportKeysActivity.ACTION_IMPORT_KEY_FROM_KEYSERVER); - intent.putExtra(ImportKeysActivity.EXTRA_KEY_ID, unknownKeyId); - startActivityForResult(intent, RESULT_CODE_LOOKUP_KEY); - } - - private void showKey(long keyId) { - Intent viewKeyIntent = new Intent(getActivity(), ViewKeyActivity.class); - viewKeyIntent.setData(KeychainContract.KeyRings - .buildGenericKeyRingUri(keyId)); - startActivity(viewKeyIntent); - } - /** - * - * @return returns false if signature is invalid, key is revoked or expired. - */ - protected boolean onResult(DecryptVerifyResult decryptVerifyResult) { - final OpenPgpSignatureResult signatureResult = decryptVerifyResult.getSignatureResult(); + // Message is received after importing is done in KeychainIntentService + ServiceProgressHandler serviceHandler = new ServiceProgressHandler(getActivity()) { + public void handleMessage(Message message) { + // handle messages by standard KeychainIntentServiceHandler first + super.handleMessage(message); - boolean valid = false; + if (message.arg1 == MessageStatus.OKAY.ordinal()) { + // get returned data bundle + Bundle returnData = message.getData(); - mSignatureKeyId = 0; - mResultLayout.setVisibility(View.VISIBLE); - if (signatureResult != null) { - mSignatureKeyId = signatureResult.getKeyId(); - - String userId = signatureResult.getPrimaryUserId(); - KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId); - if (userIdSplit.name != null) { - mSignatureName.setText(userIdSplit.name); - } else { - mSignatureName.setText(R.string.user_id_no_name); - } - if (userIdSplit.email != null) { - mSignatureEmail.setText(userIdSplit.email); - } else { - mSignatureEmail.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix(getActivity(), mSignatureKeyId)); - } + if (returnData == null) { + return; + } - if (signatureResult.isSignatureOnly()) { - mEncryptionText.setText(R.string.decrypt_result_not_encrypted); - KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.NOT_ENCRYPTED); - } else { - mEncryptionText.setText(R.string.decrypt_result_encrypted); - KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.ENCRYPTED); - } + final ImportKeyResult result = + returnData.getParcelable(OperationResult.EXTRA_RESULT); - switch (signatureResult.getStatus()) { - case OpenPgpSignatureResult.SIGNATURE_SUCCESS_CERTIFIED: { - mSignatureText.setText(R.string.decrypt_result_signature_certified); - KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.VERIFIED); + // if (!result.success()) { + result.createNotify(getActivity()).show(); + // } - setSignatureLayoutVisibility(View.VISIBLE); - setShowAction(mSignatureKeyId); + getLoaderManager().restartLoader(LOADER_ID_UNIFIED, null, DecryptFragment.this); - valid = true; - break; } + } + }; - case OpenPgpSignatureResult.SIGNATURE_SUCCESS_UNCERTIFIED: { - mSignatureText.setText(R.string.decrypt_result_signature_uncertified); - KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.UNVERIFIED); + // fill values for this action + Bundle data = new Bundle(); - setSignatureLayoutVisibility(View.VISIBLE); - setShowAction(mSignatureKeyId); + // search config + { + Preferences prefs = Preferences.getPreferences(getActivity()); + Preferences.CloudSearchPrefs cloudPrefs = + new Preferences.CloudSearchPrefs(true, true, prefs.getPreferredKeyserver()); + data.putString(KeychainIntentService.IMPORT_KEY_SERVER, cloudPrefs.keyserver); + } - valid = true; - break; - } + { + ParcelableKeyRing keyEntry = new ParcelableKeyRing(null, + KeyFormattingUtils.convertKeyIdToHex(unknownKeyId), null); + ArrayList selectedEntries = new ArrayList<>(); + selectedEntries.add(keyEntry); - case OpenPgpSignatureResult.SIGNATURE_KEY_MISSING: { - mSignatureText.setText(R.string.decrypt_result_signature_missing_key); - KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.UNKNOWN_KEY); - - setSignatureLayoutVisibility(View.VISIBLE); - mSignatureAction.setText(R.string.decrypt_result_action_Lookup); - mSignatureAction.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_file_download_grey_24dp, 0); - mSignatureLayout.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - lookupUnknownKey(mSignatureKeyId); - } - }); - - valid = true; - break; - } + data.putParcelableArrayList(KeychainIntentService.IMPORT_KEY_LIST, selectedEntries); + } - case OpenPgpSignatureResult.SIGNATURE_KEY_EXPIRED: { - mSignatureText.setText(R.string.decrypt_result_signature_expired_key); - KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.EXPIRED); + // Send all information needed to service to query keys in other thread + Intent intent = new Intent(getActivity(), KeychainIntentService.class); + intent.setAction(KeychainIntentService.ACTION_IMPORT_KEYRING); + intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - setSignatureLayoutVisibility(View.VISIBLE); - setShowAction(mSignatureKeyId); + // Create a new Messenger for the communication back + Messenger messenger = new Messenger(serviceHandler); + intent.putExtra(KeychainIntentService.EXTRA_MESSENGER, messenger); - valid = false; - break; - } + getActivity().startService(intent); - case OpenPgpSignatureResult.SIGNATURE_KEY_REVOKED: { - mSignatureText.setText(R.string.decrypt_result_signature_revoked_key); - KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.REVOKED); + } - setSignatureLayoutVisibility(View.VISIBLE); - setShowAction(mSignatureKeyId); + private void showKey(long keyId) { + try { - valid = false; - break; - } + Intent viewKeyIntent = new Intent(getActivity(), ViewKeyActivity.class); + long masterKeyId = new ProviderHelper(getActivity()).getCachedPublicKeyRing( + KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(keyId) + ).getMasterKeyId(); + viewKeyIntent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId)); + startActivity(viewKeyIntent); + + } catch (PgpKeyNotFoundException e) { + Notify.create(getActivity(), R.string.error_key_not_found, Style.ERROR); + } + } + + /** + * @return returns false if signature is invalid, key is revoked or expired. + */ + protected void loadVerifyResult(DecryptVerifyResult decryptVerifyResult) { - case OpenPgpSignatureResult.SIGNATURE_ERROR: { - mSignatureText.setText(R.string.decrypt_result_invalid_signature); - KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.INVALID); + mSignatureResult = decryptVerifyResult.getSignatureResult(); + mResultLayout.setVisibility(View.VISIBLE); - setSignatureLayoutVisibility(View.GONE); + // unsigned data + if (mSignatureResult == null) { - valid = false; - break; - } - } - } else { setSignatureLayoutVisibility(View.GONE); mSignatureText.setText(R.string.decrypt_result_no_signature); @@ -202,16 +188,27 @@ public abstract class DecryptFragment extends CryptoOperationFragment { mEncryptionText.setText(R.string.decrypt_result_encrypted); KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.ENCRYPTED); - valid = true; + getLoaderManager().destroyLoader(LOADER_ID_UNIFIED); + + onVerifyLoaded(true); + + return; + } + + if (mSignatureResult.isSignatureOnly()) { + mEncryptionText.setText(R.string.decrypt_result_not_encrypted); + KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.NOT_ENCRYPTED); + } else { + mEncryptionText.setText(R.string.decrypt_result_encrypted); + KeyFormattingUtils.setStatusImage(getActivity(), mEncryptionIcon, mEncryptionText, State.ENCRYPTED); } - return valid; + getLoaderManager().restartLoader(LOADER_ID_UNIFIED, null, this); + } private void setSignatureLayoutVisibility(int visibility) { mSignatureLayout.setVisibility(visibility); - mSignatureDivider1.setVisibility(visibility); - mSignatureDivider2.setVisibility(visibility); } private void setShowAction(final long signatureKeyId) { @@ -225,4 +222,177 @@ public abstract class DecryptFragment extends CryptoOperationFragment { }); } + // These are the rows that we will retrieve. + static final String[] UNIFIED_PROJECTION = new String[]{ + KeychainContract.KeyRings._ID, + KeychainContract.KeyRings.MASTER_KEY_ID, + KeychainContract.KeyRings.USER_ID, + KeychainContract.KeyRings.IS_REVOKED, + KeychainContract.KeyRings.IS_EXPIRED, + KeychainContract.KeyRings.VERIFIED, + }; + + @SuppressWarnings("unused") + static final int INDEX_MASTER_KEY_ID = 1; + static final int INDEX_USER_ID = 2; + static final int INDEX_IS_REVOKED = 3; + static final int INDEX_IS_EXPIRED = 4; + static final int INDEX_VERIFIED = 5; + + @Override + public Loader onCreateLoader(int id, Bundle args) { + if (id != LOADER_ID_UNIFIED) { + return null; + } + + Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingsFindBySubkeyUri( + mSignatureResult.getKeyId()); + return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + + if (loader.getId() != LOADER_ID_UNIFIED) { + return; + } + + // If the key is unknown, show it as such + if (data.getCount() == 0 || !data.moveToFirst()) { + showUnknownKeyStatus(); + return; + } + + long signatureKeyId = mSignatureResult.getKeyId(); + + String userId = data.getString(INDEX_USER_ID); + KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId); + if (userIdSplit.name != null) { + mSignatureName.setText(userIdSplit.name); + } else { + mSignatureName.setText(R.string.user_id_no_name); + } + if (userIdSplit.email != null) { + mSignatureEmail.setText(userIdSplit.email); + } else { + mSignatureEmail.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix( + getActivity(), mSignatureResult.getKeyId())); + } + + boolean isRevoked = data.getInt(INDEX_IS_REVOKED) != 0; + boolean isExpired = data.getInt(INDEX_IS_EXPIRED) != 0; + boolean isVerified = data.getInt(INDEX_VERIFIED) > 0; + + if (isRevoked) { + mSignatureText.setText(R.string.decrypt_result_signature_revoked_key); + KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.REVOKED); + + setSignatureLayoutVisibility(View.VISIBLE); + setShowAction(signatureKeyId); + + onVerifyLoaded(false); + + } else if (isExpired) { + mSignatureText.setText(R.string.decrypt_result_signature_expired_key); + KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.EXPIRED); + + setSignatureLayoutVisibility(View.VISIBLE); + setShowAction(signatureKeyId); + + onVerifyLoaded(true); + + } else if (isVerified) { + mSignatureText.setText(R.string.decrypt_result_signature_certified); + KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.VERIFIED); + + setSignatureLayoutVisibility(View.VISIBLE); + setShowAction(signatureKeyId); + + onVerifyLoaded(true); + + } else { + mSignatureText.setText(R.string.decrypt_result_signature_uncertified); + KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.UNVERIFIED); + + setSignatureLayoutVisibility(View.VISIBLE); + setShowAction(signatureKeyId); + + onVerifyLoaded(true); + } + + } + + @Override + public void onLoaderReset(Loader loader) { + + if (loader.getId() != LOADER_ID_UNIFIED) { + return; + } + + setSignatureLayoutVisibility(View.GONE); + + } + + private void showUnknownKeyStatus() { + + final long signatureKeyId = mSignatureResult.getKeyId(); + + int result = mSignatureResult.getStatus(); + if (result != OpenPgpSignatureResult.SIGNATURE_KEY_MISSING + && result != OpenPgpSignatureResult.SIGNATURE_ERROR) { + Log.e(Constants.TAG, "got missing status for non-missing key, shouldn't happen!"); + } + + String userId = mSignatureResult.getPrimaryUserId(); + KeyRing.UserId userIdSplit = KeyRing.splitUserId(userId); + if (userIdSplit.name != null) { + mSignatureName.setText(userIdSplit.name); + } else { + mSignatureName.setText(R.string.user_id_no_name); + } + if (userIdSplit.email != null) { + mSignatureEmail.setText(userIdSplit.email); + } else { + mSignatureEmail.setText(KeyFormattingUtils.beautifyKeyIdWithPrefix( + getActivity(), mSignatureResult.getKeyId())); + } + + switch (mSignatureResult.getStatus()) { + + case OpenPgpSignatureResult.SIGNATURE_KEY_MISSING: { + mSignatureText.setText(R.string.decrypt_result_signature_missing_key); + KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.UNKNOWN_KEY); + + setSignatureLayoutVisibility(View.VISIBLE); + mSignatureAction.setText(R.string.decrypt_result_action_Lookup); + mSignatureAction + .setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_file_download_grey_24dp, 0); + mSignatureLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + lookupUnknownKey(signatureKeyId); + } + }); + + onVerifyLoaded(true); + + break; + } + + case OpenPgpSignatureResult.SIGNATURE_ERROR: { + mSignatureText.setText(R.string.decrypt_result_invalid_signature); + KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.INVALID); + + setSignatureLayoutVisibility(View.GONE); + + onVerifyLoaded(false); + break; + } + + } + + } + + protected abstract void onVerifyLoaded(boolean verified); + } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java index 9c6c89c43..1b9ae917f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptTextFragment.java @@ -23,6 +23,9 @@ import android.os.Bundle; import android.os.Message; import android.os.Messenger; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Button; @@ -39,7 +42,6 @@ import org.sufficientlysecure.keychain.service.ServiceProgressHandler; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.dialog.ProgressDialogFragment; import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ShareHelper; import java.io.UnsupportedEncodingException; @@ -54,6 +56,7 @@ public class DecryptTextFragment extends DecryptFragment { // model private String mCiphertext; + private boolean mShowMenuOptions = false; /** * Creates new instance of this fragment @@ -79,22 +82,6 @@ public class DecryptTextFragment extends DecryptFragment { mInvalidLayout = (LinearLayout) view.findViewById(R.id.decrypt_text_invalid); mText = (TextView) view.findViewById(R.id.decrypt_text_plaintext); - View vShareButton = view.findViewById(R.id.action_decrypt_share_plaintext); - vShareButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - startActivity(sendWithChooserExcludingEncrypt(mText.getText().toString())); - } - }); - - View vCopyButton = view.findViewById(R.id.action_decrypt_copy_plaintext); - vCopyButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - copyToClipboard(mText.getText().toString()); - } - }); - Button vInvalidButton = (Button) view.findViewById(R.id.decrypt_text_invalid_button); vInvalidButton.setOnClickListener(new View.OnClickListener() { @Override @@ -139,6 +126,8 @@ public class DecryptTextFragment extends DecryptFragment { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setHasOptionsMenu(true); + String ciphertext = getArguments().getString(ARG_CIPHERTEXT); if (ciphertext != null) { mCiphertext = ciphertext; @@ -146,6 +135,33 @@ public class DecryptTextFragment extends DecryptFragment { } } + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + if (mShowMenuOptions) { + inflater.inflate(R.menu.decrypt_menu, menu); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.decrypt_share: { + startActivity(sendWithChooserExcludingEncrypt(mText.getText().toString())); + break; + } + case R.id.decrypt_copy: { + copyToClipboard(mText.getText().toString()); + break; + } + default: { + return super.onOptionsItemSelected(item); + } + } + + return true; + } + @Override protected void cryptoOperation(CryptoInputParcel cryptoInput) { // Send all information needed to service to decrypt in other thread @@ -206,15 +222,8 @@ public class DecryptTextFragment extends DecryptFragment { pgpResult.createNotify(getActivity()).show(); // display signature result in activity - boolean valid = onResult(pgpResult); + loadVerifyResult(pgpResult); - if (valid) { - mInvalidLayout.setVisibility(View.GONE); - mValidLayout.setVisibility(View.VISIBLE); - } else { - mInvalidLayout.setVisibility(View.VISIBLE); - mValidLayout.setVisibility(View.GONE); - } } else { pgpResult.createNotify(getActivity()).show(); // TODO: show also invalid layout with different text? @@ -234,4 +243,19 @@ public class DecryptTextFragment extends DecryptFragment { getActivity().startService(intent); } + @Override + protected void onVerifyLoaded(boolean verified) { + + mShowMenuOptions = verified; + getActivity().supportInvalidateOptionsMenu(); + + if (verified) { + mInvalidLayout.setVisibility(View.GONE); + mValidLayout.setVisibility(View.VISIBLE); + } else { + mInvalidLayout.setVisibility(View.VISIBLE); + mValidLayout.setVisibility(View.GONE); + } + + } } -- cgit v1.2.3 From 6de403c21a1cf8d2ec4e62debd8eec620c63328e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 24 Apr 2015 19:16:15 +0200 Subject: fix encrypt activity layout --- .../keychain/ui/widget/EncryptKeyCompletionView.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java index b2dfb2493..3d2e8b9df 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java @@ -108,9 +108,8 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView protected void onAttachedToWindow() { super.onAttachedToWindow(); - mLoaderManager = ((FragmentActivity) getContext()).getSupportLoaderManager(); - if (getContext() instanceof FragmentActivity) { + mLoaderManager = ((FragmentActivity) getContext()).getSupportLoaderManager(); mLoaderManager.initLoader(hashCode(), null, this); } else { Log.e(Constants.TAG, "EncryptKeyCompletionView must be attached to a FragmentActivity, this is " + getContext().getClass()); -- cgit v1.2.3 From 608b66d192fcab62ab44d28c34171f9ca1aff38b Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 25 Apr 2015 03:39:40 +0200 Subject: change HttpsEditText to generic PrefixEditText --- .../linked/LinkedIdCreateTwitterStep1Fragment.java | 2 +- .../keychain/ui/widget/HttpsPrefixedText.java | 38 ------------------ .../keychain/ui/widget/PrefixedEditText.java | 45 ++++++++++++++++++++++ 3 files changed, 46 insertions(+), 39 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/HttpsPrefixedText.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PrefixedEditText.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 c36f98058..d7c7a6f2e 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 @@ -107,7 +107,7 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { }); mEditHandle = (EditText) view.findViewById(R.id.linked_create_twitter_handle); - mEditHandle.setText("v_debug"); + mEditHandle.setText(""); return view; } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/HttpsPrefixedText.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/HttpsPrefixedText.java deleted file mode 100644 index 292343eb7..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/HttpsPrefixedText.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.sufficientlysecure.keychain.ui.widget; - -import android.content.Context; -import android.graphics.*; -import android.support.annotation.NonNull; -import android.util.AttributeSet; -import android.widget.EditText; - -/** */ -public class HttpsPrefixedText extends EditText { - - private String mPrefix; // can be hardcoded for demo purposes - private Rect mPrefixRect = new Rect(); - - public HttpsPrefixedText(Context context, AttributeSet attrs) { - super(context, attrs); - mPrefix = "https://"; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - getPaint().getTextBounds(mPrefix, 0, mPrefix.length(), mPrefixRect); - - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - } - - @Override - protected void onDraw(@NonNull Canvas canvas) { - super.onDraw(canvas); - canvas.drawText(mPrefix, super.getCompoundPaddingLeft(), getBaseline(), getPaint()); - } - - @Override - public int getCompoundPaddingLeft() { - return super.getCompoundPaddingLeft() + mPrefixRect.width(); - } - -} \ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PrefixedEditText.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PrefixedEditText.java new file mode 100644 index 000000000..3cbb114e8 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/PrefixedEditText.java @@ -0,0 +1,45 @@ +package org.sufficientlysecure.keychain.ui.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.*; +import android.support.annotation.NonNull; +import android.util.AttributeSet; +import android.widget.EditText; + +import org.sufficientlysecure.keychain.R; + +public class PrefixedEditText extends EditText { + + private String mPrefix; + private Rect mPrefixRect = new Rect(); + + public PrefixedEditText(Context context, AttributeSet attrs) { + super(context, attrs); + TypedArray style = context.getTheme().obtainStyledAttributes( + attrs, R.styleable.PrefixedEditText, 0, 0); + mPrefix = style.getString(R.styleable.PrefixedEditText_prefix); + if (mPrefix == null) { + mPrefix = ""; + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + getPaint().getTextBounds(mPrefix, 0, mPrefix.length(), mPrefixRect); + + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + + @Override + protected void onDraw(@NonNull Canvas canvas) { + super.onDraw(canvas); + canvas.drawText(mPrefix, super.getCompoundPaddingLeft(), getBaseline(), getPaint()); + } + + @Override + public int getCompoundPaddingLeft() { + return super.getCompoundPaddingLeft() + mPrefixRect.width(); + } + +} \ No newline at end of file -- cgit v1.2.3 From d6d678dae3ed5d794a9aa5e289197d264f6a7ff9 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 25 Apr 2015 03:40:38 +0200 Subject: update uris and cookie patterns, plus some stylings --- .../keychain/pgp/linked/LinkedCookieResource.java | 6 ++--- .../keychain/pgp/linked/LinkedResource.java | 4 ++-- .../keychain/pgp/linked/resources/DnsResource.java | 4 ++-- .../ui/linked/LinkedIdCreateFinalFragment.java | 7 +++--- .../linked/LinkedIdCreateTwitterStep1Fragment.java | 17 ++++++++++---- .../linked/LinkedIdCreateTwitterStep2Fragment.java | 5 ++++ .../keychain/ui/linked/LinkedIdWizard.java | 27 ++++++++++++++++++++-- 7 files changed, 53 insertions(+), 17 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 index 606c951b8..37265b2b5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedCookieResource.java @@ -35,7 +35,7 @@ public abstract class LinkedCookieResource extends LinkedResource { public URI toUri () { StringBuilder b = new StringBuilder(); - b.append("pgpid+cookie:"); + b.append("openpgpid+cookie:"); // add flags if (mFlags != null) { @@ -73,12 +73,12 @@ public abstract class LinkedCookieResource extends LinkedResource { } public static String generate (Context context, byte[] fingerprint) { - return String.format("[Verifying my PGP key: openpgp4fpr:%s]", + return String.format("[Verifying my OpenPGP key: openpgp4fpr:%s]", KeyFormattingUtils.convertFingerprintToHex(fingerprint)); } public static String generatePreview () { - return "[Verifying my PGP key: openpgp4fpr:0x…]"; + return "[Verifying my OpenPGP key: openpgp4fpr:0x…]"; } public LinkedVerifyResult verify(byte[] fingerprint) { 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 index 5a0fc6e47..26fc9f4cf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/linked/LinkedResource.java @@ -26,7 +26,7 @@ public abstract class LinkedResource { protected final HashMap mParams; public static Pattern magicPattern = - Pattern.compile("\\[Verifying my PGP key: openpgp4fpr:([a-zA-Z0-9]+)]"); + Pattern.compile("\\[Verifying my (?:Open)?PGP key: openpgp4fpr:([a-zA-Z0-9]+)]"); protected LinkedResource(Set flags, HashMap params, URI uri) { mFlags = flags; @@ -44,7 +44,7 @@ public abstract class LinkedResource { protected static LinkedCookieResource fromUri (URI uri) { - if (!"pgpid+cookie".equals(uri.getScheme())) { + if (!"openpgpid+cookie".equals(uri.getScheme())) { Log.e(Constants.TAG, "unknown uri scheme in (suspected) linked id packet"); 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 index a8faa435d..3ca71c74b 100644 --- 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 @@ -27,7 +27,7 @@ import de.measite.minidns.record.TXT; public class DnsResource extends LinkedCookieResource { final static Pattern magicPattern = - Pattern.compile("pgpid\\+cookie=([a-zA-Z0-9]+)(?:#|;)([a-zA-Z0-9]+)"); + Pattern.compile("openpgpid\\+cookie=([a-zA-Z0-9]+)(?:#|;)([a-zA-Z0-9]+)"); String mFqdn; CLASS mClass; @@ -44,7 +44,7 @@ public class DnsResource extends LinkedCookieResource { public static String generateText (Context context, byte[] fingerprint) { - return String.format("pgpid+cookie=%s", + return String.format("openpgpid+cookie=%s", KeyFormattingUtils.convertFingerprintToHex(fingerprint)); } 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 b98fc6238..ef9ae9ac4 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 @@ -162,7 +162,7 @@ public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragmen } else { setVerifyProgress(false, false); // on error, show error message - result.createNotify(getActivity()).show(); + result.createNotify(getActivity()).show(LinkedIdCreateFinalFragment.this); } } }.execute(); @@ -172,7 +172,8 @@ public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragmen protected void cryptoOperation(CryptoInputParcel cryptoInput) { if (mVerifiedResource == null) { - Notify.create(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR).show(); + Notify.create(getActivity(), R.string.linked_need_verify, Notify.Style.ERROR) + .show(LinkedIdCreateFinalFragment.this); return; } @@ -206,7 +207,7 @@ public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragmen // if bad -> display here! if (!result.success()) { - result.createNotify(getActivity()).show(); + result.createNotify(getActivity()).show(LinkedIdCreateFinalFragment.this); return; } 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 d7c7a6f2e..0d6d36ca4 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 @@ -66,11 +66,17 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { 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); + return true; + // return checkHandle(handle); } @Override @@ -79,13 +85,15 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { if (result == null) { Notify.create(getActivity(), - "Connection error while checking username!", Notify.Style.ERROR); + "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); + "This handle does not exist on Twitter!", + Notify.Style.ERROR).show(LinkedIdCreateTwitterStep1Fragment.this); return; } @@ -107,7 +115,6 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { }); mEditHandle = (EditText) view.findViewById(R.id.linked_create_twitter_handle); - mEditHandle.setText(""); return view; } @@ -117,9 +124,9 @@ public class LinkedIdCreateTwitterStep1Fragment extends Fragment { HttpURLConnection nection = (HttpURLConnection) new URL("https://twitter.com/" + handle).openConnection(); nection.setRequestMethod("HEAD"); + nection.setRequestProperty("User-Agent", "OpenKeychain"); return nection.getResponseCode() == 200; } catch (IOException e) { - e.printStackTrace(); 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 66e86f7d4..295ef4aef 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,6 +25,7 @@ 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; @@ -79,6 +80,10 @@ public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragm } }); + ((TextView) view.findViewById(R.id.linked_tweet_published)).setText( + getString(R.string.linked_create_twitter_2_3, mResourceHandle) + ); + 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 index 161efc8fb..6165efd90 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 @@ -17,11 +17,14 @@ package org.sufficientlysecure.keychain.ui.linked; +import android.content.Context; 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; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -29,9 +32,10 @@ import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.CachedPublicKeyRing; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.base.BaseActivity; import org.sufficientlysecure.keychain.util.Log; -public class LinkedIdWizard extends ActionBarActivity { +public class LinkedIdWizard extends BaseActivity { public static final int FRAG_ACTION_START = 0; public static final int FRAG_ACTION_TO_RIGHT = 1; @@ -44,7 +48,7 @@ public class LinkedIdWizard extends ActionBarActivity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.create_key_activity); + setTitle(getString(R.string.title_linked_id_create)); try { Uri uri = getIntent().getData(); @@ -69,6 +73,11 @@ public class LinkedIdWizard extends ActionBarActivity { loadFragment(null, frag, FRAG_ACTION_START); } + @Override + protected void initLayout() { + setContentView(R.layout.create_key_activity); + } + public void loadFragment(Bundle savedInstanceState, Fragment fragment, int action) { // However, if we're being restored from a previous state, // then we don't need to do anything and should return or else @@ -77,6 +86,8 @@ public class LinkedIdWizard extends ActionBarActivity { return; } + hideKeyboard(); + // Add the fragment to the 'fragment_container' FrameLayout // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); @@ -103,4 +114,16 @@ public class LinkedIdWizard extends ActionBarActivity { getSupportFragmentManager().executePendingTransactions(); } + private void hideKeyboard() { + InputMethodManager inputManager = (InputMethodManager) + getSystemService(Context.INPUT_METHOD_SERVICE); + + // check if no view has focus + View v = getCurrentFocus(); + if (v == null) + return; + + inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0); + } + } -- cgit v1.2.3 From 18a88d35beaf44a1a5ff1a15f226fb7a9e13d075 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 26 Apr 2015 12:57:04 +0200 Subject: show linked ids card only if unfiltered ids are available --- .../main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java index 046de6c0c..d9b1fa279 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -423,8 +423,8 @@ public class ViewKeyFragment extends LoaderFragment implements } case LOADER_ID_LINKED_IDS: { - mLinkedIdsCard.setVisibility(data.getCount() > 0 ? View.VISIBLE : View.GONE); mLinkedIdsAdapter.swapCursor(data); + mLinkedIdsCard.setVisibility(mLinkedIdsAdapter.getCount() > 0 ? View.VISIBLE : View.GONE); break; } -- cgit v1.2.3 From c08fe2b50da5c753715b7d21b4e97a01e4a89bfc Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 27 Apr 2015 13:51:39 +0200 Subject: profiling says: caching qrCode bitmaps is a good idea --- .../keychain/KeychainApplication.java | 14 +++++++ .../keychain/ui/util/QrCodeUtils.java | 48 +++++++++++++--------- 2 files changed, 43 insertions(+), 19 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java index d26ccbe57..710dbf8aa 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java @@ -24,6 +24,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.res.Resources; +import android.graphics.Bitmap; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.os.Build; @@ -40,6 +41,8 @@ import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.TlsHelper; import java.security.Security; +import java.util.HashMap; + public class KeychainApplication extends Application { @@ -100,6 +103,17 @@ public class KeychainApplication extends Application { checkConsolidateRecovery(); } + public static HashMap qrCodeCache = new HashMap<>(); + + @Override + public void onTrimMemory(int level) { + super.onTrimMemory(level); + + if (level >= TRIM_MEMORY_UI_HIDDEN) { + qrCodeCache.clear(); + } + } + /** * Restart consolidate process if it has been interruped before */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java index b8d4ea7d2..5f71abdab 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/QrCodeUtils.java @@ -29,6 +29,7 @@ import com.google.zxing.qrcode.QRCodeWriter; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.KeychainApplication; import org.sufficientlysecure.keychain.util.Log; import java.util.Hashtable; @@ -40,36 +41,45 @@ public class QrCodeUtils { /** * Generate Bitmap with QR Code based on input. - * - * @param input - * @param size * @return QR Code as Bitmap */ public static Bitmap getQRCodeBitmap(final String input, final int size) { + try { - final Hashtable hints = new Hashtable<>(); - hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); - final BitMatrix result = new QRCodeWriter().encode(input, BarcodeFormat.QR_CODE, size, - size, hints); - - final int width = result.getWidth(); - final int height = result.getHeight(); - final int[] pixels = new int[width * height]; - - for (int y = 0; y < height; y++) { - final int offset = y * width; - for (int x = 0; x < width; x++) { - pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.TRANSPARENT; + + // the qrCodeCache is handled in KeychainApplication so we can + // properly react to onTrimMemory calls + Bitmap bitmap = KeychainApplication.qrCodeCache.get(input); + if (bitmap == null) { + + Hashtable hints = new Hashtable<>(); + hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.M); + BitMatrix result = new QRCodeWriter().encode(input, BarcodeFormat.QR_CODE, size, + size, hints); + + int width = result.getWidth(); + int height = result.getHeight(); + int[] pixels = new int[width * height]; + + for (int y = 0; y < height; y++) { + final int offset = y * width; + for (int x = 0; x < width; x++) { + pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.TRANSPARENT; + } } + + bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + bitmap.setPixels(pixels, 0, width, 0, 0, width, height); + + KeychainApplication.qrCodeCache.put(input, bitmap); } - final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - bitmap.setPixels(pixels, 0, width, 0, 0, width, height); return bitmap; - } catch (final WriterException e) { + } catch (WriterException e) { Log.e(Constants.TAG, "QrCodeUtils", e); return null; } + } } -- cgit v1.2.3 From 0ad66eb510772a5731fa97d1745082b43a71215e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 27 Apr 2015 14:40:52 +0200 Subject: add distinct status for messages signed by "your" key --- .../org/sufficientlysecure/keychain/ui/DecryptFragment.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java index 651b56ab0..9c51893ce 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptFragment.java @@ -231,6 +231,7 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements KeychainContract.KeyRings.IS_REVOKED, KeychainContract.KeyRings.IS_EXPIRED, KeychainContract.KeyRings.VERIFIED, + KeychainContract.KeyRings.HAS_ANY_SECRET, }; @SuppressWarnings("unused") @@ -239,6 +240,7 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements static final int INDEX_IS_REVOKED = 3; static final int INDEX_IS_EXPIRED = 4; static final int INDEX_VERIFIED = 5; + static final int INDEX_HAS_ANY_SECRET = 6; @Override public Loader onCreateLoader(int id, Bundle args) { @@ -283,6 +285,7 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements boolean isRevoked = data.getInt(INDEX_IS_REVOKED) != 0; boolean isExpired = data.getInt(INDEX_IS_EXPIRED) != 0; boolean isVerified = data.getInt(INDEX_VERIFIED) > 0; + boolean isYours = data.getInt(INDEX_HAS_ANY_SECRET) != 0; if (isRevoked) { mSignatureText.setText(R.string.decrypt_result_signature_revoked_key); @@ -302,6 +305,16 @@ public abstract class DecryptFragment extends CryptoOperationFragment implements onVerifyLoaded(true); + } else if (isYours) { + + mSignatureText.setText(R.string.decrypt_result_signature_secret); + KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.VERIFIED); + + setSignatureLayoutVisibility(View.VISIBLE); + setShowAction(signatureKeyId); + + onVerifyLoaded(true); + } else if (isVerified) { mSignatureText.setText(R.string.decrypt_result_signature_certified); KeyFormattingUtils.setStatusImage(getActivity(), mSignatureIcon, mSignatureText, State.VERIFIED); -- cgit v1.2.3 From 32db4e393b823759380ebdcdac28bfd950cd41e4 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 27 Apr 2015 17:45:50 +0200 Subject: display creation dates for ambiguous user ids --- .../keychain/ui/adapter/KeyAdapter.java | 21 ++++++++++++++++++++- .../ui/adapter/SelectKeyCursorAdapter.java | 22 +++++++++------------- 2 files changed, 29 insertions(+), 14 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java index 6f19fc6ed..f09dc1a4f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/KeyAdapter.java @@ -27,6 +27,7 @@ import android.database.Cursor; import android.graphics.PorterDuff; import android.support.v4.widget.CursorAdapter; import android.text.format.DateFormat; +import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -88,6 +89,7 @@ public class KeyAdapter extends CursorAdapter { public Long mMasterKeyId; public TextView mMainUserId; public TextView mMainUserIdRest; + public TextView mCreationDate; public ImageView mStatus; public View mSlinger; public ImageButton mSlingerButton; @@ -98,6 +100,7 @@ public class KeyAdapter extends CursorAdapter { mStatus = (ImageView) view.findViewById(R.id.key_list_item_status_icon); mSlinger = view.findViewById(R.id.key_list_item_slinger_view); mSlingerButton = (ImageButton) view.findViewById(R.id.key_list_item_slinger_button); + mCreationDate = (TextView) view.findViewById(R.id.key_list_item_creation); } public void setData(Context context, Cursor cursor, Highlighter highlighter) { @@ -125,7 +128,7 @@ public class KeyAdapter extends CursorAdapter { boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; boolean isExpired = cursor.getInt(INDEX_IS_EXPIRED) != 0; boolean isVerified = cursor.getInt(INDEX_VERIFIED) > 0; - // boolean hasDuplicate = cursor.getInt(INDEX_HAS_DUPLICATE_USER_ID) == 1; + boolean hasDuplicate = cursor.getInt(INDEX_HAS_DUPLICATE_USER_ID) != 0; mMasterKeyId = masterKeyId; @@ -165,6 +168,22 @@ public class KeyAdapter extends CursorAdapter { mMainUserId.setTextColor(context.getResources().getColor(R.color.black)); mMainUserIdRest.setTextColor(context.getResources().getColor(R.color.black)); } + + if (hasDuplicate) { + String dateTime = DateUtils.formatDateTime(context, + cursor.getLong(INDEX_CREATION) * 1000, + DateUtils.FORMAT_SHOW_DATE + | DateUtils.FORMAT_SHOW_TIME + | DateUtils.FORMAT_SHOW_YEAR + | DateUtils.FORMAT_ABBREV_MONTH); + + mCreationDate.setText(context.getString(R.string.label_creation, + dateTime)); + mCreationDate.setVisibility(View.VISIBLE); + } else { + mCreationDate.setVisibility(View.GONE); + } + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java index 3308a4500..1ccb910d0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java @@ -20,7 +20,7 @@ package org.sufficientlysecure.keychain.ui.adapter; import android.content.Context; import android.database.Cursor; import android.support.v4.widget.CursorAdapter; -import android.text.format.DateFormat; +import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -36,10 +36,6 @@ import org.sufficientlysecure.keychain.ui.util.Highlighter; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; -import java.util.Calendar; -import java.util.Date; -import java.util.TimeZone; - /** * Yes this class is abstract! @@ -138,14 +134,14 @@ abstract public class SelectKeyCursorAdapter extends CursorAdapter { boolean duplicate = cursor.getLong(mIndexDuplicateUserId) > 0; if (duplicate) { - Date creationDate = new Date(cursor.getLong(mIndexCreation) * 1000); - Calendar creationCal = Calendar.getInstance(TimeZone.getTimeZone("UTC")); - creationCal.setTime(creationDate); - // convert from UTC to time zone of device - creationCal.setTimeZone(TimeZone.getDefault()); - - h.creation.setText(context.getString(R.string.label_creation) + ": " - + DateFormat.getDateFormat(context).format(creationCal.getTime())); + String dateTime = DateUtils.formatDateTime(context, + cursor.getLong(mIndexCreation) * 1000, + DateUtils.FORMAT_SHOW_DATE + | DateUtils.FORMAT_SHOW_TIME + | DateUtils.FORMAT_SHOW_YEAR + | DateUtils.FORMAT_ABBREV_MONTH); + + h.creation.setText(context.getString(R.string.label_creation, dateTime)); h.creation.setVisibility(View.VISIBLE); } else { h.creation.setVisibility(View.GONE); -- cgit v1.2.3 From dd1243c31289973e238d36ca35b10539eb909239 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 27 Apr 2015 19:48:58 +0200 Subject: prevent crashes in EncryptKeyCompletionView --- .../keychain/ui/widget/EncryptKeyCompletionView.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java index 3d2e8b9df..4e691d962 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java @@ -110,7 +110,7 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView if (getContext() instanceof FragmentActivity) { mLoaderManager = ((FragmentActivity) getContext()).getSupportLoaderManager(); - mLoaderManager.initLoader(hashCode(), null, this); + mLoaderManager.initLoader(0, null, this); } else { Log.e(Constants.TAG, "EncryptKeyCompletionView must be attached to a FragmentActivity, this is " + getContext().getClass()); } @@ -154,6 +154,14 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView mAdapter.swapCursor(null); } + @Override + public void showDropDown() { + if (mAdapter.getCursor().isClosed()) { + return; + } + super.showDropDown(); + } + @Override public void onFocusChanged(boolean hasFocus, int direction, Rect previous) { super.onFocusChanged(hasFocus, direction, previous); @@ -171,7 +179,7 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView } Bundle args = new Bundle(); args.putString(ARG_QUERY, text.subSequence(start, end).toString()); - mLoaderManager.restartLoader(hashCode(), args, this); + mLoaderManager.restartLoader(0, args, this); } } -- cgit v1.2.3 From fc61208ce78e1a70dbd407bb554ff3e3edd91cea Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 27 Apr 2015 19:49:42 +0200 Subject: small ui niceties --- .../keychain/ui/adapter/LinkedIdsAdapter.java | 9 +++++++++ .../ui/linked/LinkedIdCreateTwitterStep2Fragment.java | 3 ++- .../keychain/ui/linked/LinkedIdViewFragment.java | 11 ++++++----- .../keychain/ui/util/SubtleAttentionSeeker.java | 8 +++++++- 4 files changed, 24 insertions(+), 7 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 480421499..9455f9489 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 @@ -18,6 +18,7 @@ package org.sufficientlysecure.keychain.ui.adapter; +import android.animation.ObjectAnimator; import android.app.Activity; import android.content.Context; import android.database.Cursor; @@ -39,6 +40,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.State; +import org.sufficientlysecure.keychain.ui.util.SubtleAttentionSeeker; import org.sufficientlysecure.keychain.util.FilterCursorWrapper; import java.io.IOException; @@ -219,6 +221,13 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { vIcon.setImageResource(id.getDisplayIcon()); } + + public void seekAttention() { + ObjectAnimator anim = SubtleAttentionSeeker.tintText(vComment, 1000); + anim.setStartDelay(200); + anim.start(); + } + } } 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 295ef4aef..89d72faf3 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 @@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui.linked; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.text.Html; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -81,7 +82,7 @@ public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragm }); ((TextView) view.findViewById(R.id.linked_tweet_published)).setText( - getString(R.string.linked_create_twitter_2_3, mResourceHandle) + 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 f618181c6..f4987d137 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 @@ -3,7 +3,6 @@ package org.sufficientlysecure.keychain.ui.linked; import java.io.IOException; import java.util.Collections; -import android.animation.ObjectAnimator; import android.content.Context; import android.content.Intent; import android.database.Cursor; @@ -26,6 +25,7 @@ import android.view.View.OnClickListener; 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; @@ -477,10 +477,11 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements } if (result.success()) { mViewHolder.vText.setText(getString(mLinkedResource.getVerifiedText(mIsSecret))); + // hack to preserve bold text + ((TextView) mViewHolder.vText.getCurrentView()).setText( + mLinkedResource.getVerifiedText(mIsSecret)); mViewHolder.setVerifyingState(mContext, VerifyState.VERIFY_OK, mIsSecret); - ObjectAnimator anim = SubtleAttentionSeeker.tada(mViewHolder.vButtonConfirm, 0.6f, 1000); - anim.setStartDelay(800); - anim.start(); + mViewHolder.mLinkedIdHolder.seekAttention(); } else { mViewHolder.setVerifyingState(mContext, VerifyState.VERIFY_ERROR, mIsSecret); result.createNotify(getActivity()).show(); @@ -501,7 +502,7 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements mCertifyKeyId = mViewHolder.vKeySpinner.getSelectedItemId(); if (mCertifyKeyId == key.none || mCertifyKeyId == key.symmetric) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - SubtleAttentionSeeker.tint(mViewHolder.vKeySpinnerContainer, 600).start(); + SubtleAttentionSeeker.tintBackground(mViewHolder.vKeySpinnerContainer, 600).start(); } else { Notify.create(getActivity(), R.string.select_key_to_certify, Style.ERROR).show(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/SubtleAttentionSeeker.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/SubtleAttentionSeeker.java index bf4211899..4549e8993 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/SubtleAttentionSeeker.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/SubtleAttentionSeeker.java @@ -94,9 +94,15 @@ public class SubtleAttentionSeeker { } @TargetApi(VERSION_CODES.LOLLIPOP) - public static ObjectAnimator tint(View view, int duration) { + public static ObjectAnimator tintBackground(View view, int duration) { return ObjectAnimator.ofArgb(view, "backgroundColor", 0x00FF0000, 0x33FF0000, 0x00FF0000).setDuration(duration); } + @TargetApi(VERSION_CODES.LOLLIPOP) + public static ObjectAnimator tintText(View view, int duration) { + return ObjectAnimator.ofArgb(view, "backgroundColor", + 0x00FF7F00, 0x33FF7F00, 0x00FF7F00).setDuration(duration); + } + } -- cgit v1.2.3 From 6b940d42d98e126eebae2af95a4d36def4a5c0c6 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 28 Apr 2015 18:18:14 +0200 Subject: fix "select all" in key list multi-select --- .../main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index edaea539b..8c4ea1884 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -241,7 +241,7 @@ public class KeyListFragment extends LoaderFragment } case R.id.menu_key_list_multi_select_all: { // select all - for (int i = 0; i < mStickyList.getCount(); i++) { + for (int i = 0; i < mAdapter.getCount(); i++) { mStickyList.setItemChecked(i, true); } break; -- cgit v1.2.3 From efb87baf479c1d749555bb97f544cbc350e9d77c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 28 Apr 2015 18:18:42 +0200 Subject: display unknown keys red rather than orange For an unknown key, there is no indication of the state the key is in. To indicate both immediate action required, and to make this status equal to its worst case (rather than *better* than its worst case), the status is displayed in red. At some point, we will probably want to download unknown keys automatically, at which point an unknown key will actually be an error state. This is an intermediate solution until then. --- .../org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java index aaf127614..ac7e7a487 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/KeyFormattingUtils.java @@ -479,7 +479,7 @@ public class KeyFormattingUtils { statusIcon.setImageDrawable( context.getResources().getDrawable(R.drawable.status_signature_unknown_cutout_24dp)); if (color == KeyFormattingUtils.DEFAULT_COLOR) { - color = R.color.android_orange_light; + color = R.color.android_red_light; } statusIcon.setColorFilter(context.getResources().getColor(color), PorterDuff.Mode.SRC_IN); -- cgit v1.2.3 From 1fd35cc23b4882e41488263f2eefd9e9d8b8335f Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 28 Apr 2015 18:21:51 +0200 Subject: fix crash when moving focus from EncryptKeyCompletionView with unknown text --- .../sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java index 4e691d962..525bc26ca 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/EncryptKeyCompletionView.java @@ -101,7 +101,7 @@ public class EncryptKeyCompletionView extends TokenCompleteTextView /*if (completionText.startsWith("0x")) { }*/ - return null; + return ""; } @Override -- cgit v1.2.3 From 39382e978f554a8da0ee7698bd84cbb2023b186d Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 6 May 2015 11:25:29 +0200 Subject: check for fingerprint of any subkey (arguable?) --- .../keychain/operations/ImportExportOperation.java | 13 +++++++------ .../sufficientlysecure/keychain/pgp/UncachedKeyRing.java | 11 +++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java index ff0b545cd..b48a1da91 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportExportOperation.java @@ -275,7 +275,7 @@ public class ImportExportOperation extends BaseOperation { // If we have an expected fingerprint, make sure it matches if (entry.mExpectedFingerprint != null) { - if(!KeyFormattingUtils.convertFingerprintToHex(key.getFingerprint()).equals(entry.mExpectedFingerprint)) { + if (!key.containsSubkey(entry.mExpectedFingerprint)) { log.add(LogType.MSG_IMPORT_FINGERPRINT_ERROR, 2); badKeys += 1; continue; @@ -314,10 +314,7 @@ public class ImportExportOperation extends BaseOperation { log.add(result, 2); - } catch (IOException e) { - Log.e(Constants.TAG, "Encountered bad key on import!", e); - ++badKeys; - } catch (PgpGeneralException e) { + } catch (IOException | PgpGeneralException e) { Log.e(Constants.TAG, "Encountered bad key on import!", e); ++badKeys; } @@ -460,6 +457,7 @@ public class ImportExportOperation extends BaseOperation { int okSecret = 0, okPublic = 0, progress = 0; + Cursor cursor = null; try { String selection = null, ids[] = null; @@ -480,7 +478,7 @@ public class ImportExportOperation extends BaseOperation { + " IN (" + placeholders + ")"; } - Cursor cursor = mProviderHelper.getContentResolver().query( + cursor = mProviderHelper.getContentResolver().query( KeyRings.buildUnifiedKeyRingsUri(), new String[]{ KeyRings.MASTER_KEY_ID, KeyRings.PUBKEY_DATA, KeyRings.PRIVKEY_DATA, KeyRings.HAS_ANY_SECRET @@ -569,6 +567,9 @@ public class ImportExportOperation extends BaseOperation { } catch (Exception e) { Log.e(Constants.TAG, "error closing stream", e); } + if (cursor != null) { + cursor.close(); + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java index b86618a9a..2bb4f7dc4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -215,6 +215,17 @@ public class UncachedKeyRing { } + public boolean containsSubkey(String expectedFingerprint) { + Iterator it = mRing.getPublicKeys(); + while (it.hasNext()) { + if (KeyFormattingUtils.convertFingerprintToHex( + it.next().getFingerprint()).equals(expectedFingerprint)) { + return true; + } + } + return false; + } + public interface IteratorWithIOThrow { public boolean hasNext() throws IOException; public E next() throws IOException; -- cgit v1.2.3 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 (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 From 57f72996e8e0db35e629ffd48f58cb5812f47788 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 9 May 2015 12:29:43 +0200 Subject: linked-ids: rename "cookie" to "token" --- .../keychain/linked/LinkedCookieResource.java | 282 --------------------- .../keychain/linked/LinkedTokenResource.java | 282 +++++++++++++++++++++ .../keychain/linked/RawLinkedIdentity.java | 4 +- .../keychain/linked/resources/DnsResource.java | 8 +- .../linked/resources/GenericHttpsResource.java | 8 +- .../keychain/linked/resources/GithubResource.java | 10 +- .../keychain/linked/resources/TwitterResource.java | 4 +- .../ui/linked/LinkedIdCreateDnsStep2Fragment.java | 4 +- .../ui/linked/LinkedIdCreateFinalFragment.java | 8 +- .../linked/LinkedIdCreateGithubStep2Fragment.java | 4 +- .../linked/LinkedIdCreateTwitterStep2Fragment.java | 4 +- .../keychain/ui/linked/LinkedIdViewFragment.java | 6 +- 12 files changed, 312 insertions(+), 312 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedCookieResource.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedCookieResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedCookieResource.java deleted file mode 100644 index d09854583..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedCookieResource.java +++ /dev/null @@ -1,282 +0,0 @@ -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/LinkedTokenResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java new file mode 100644 index 000000000..7eec2a66a --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.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 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())) { + 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 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(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/RawLinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/RawLinkedIdentity.java index 0bdc1b280..5310322f1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/RawLinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/RawLinkedIdentity.java @@ -42,7 +42,7 @@ public class RawLinkedIdentity { String uriStr = Strings.fromUTF8ByteArray(data); URI uri = URI.create(uriStr); - LinkedResource res = LinkedCookieResource.fromUri(uri); + LinkedResource res = LinkedTokenResource.fromUri(uri); if (res == null) { return new RawLinkedIdentity(uri); } @@ -55,7 +55,7 @@ public class RawLinkedIdentity { } } - public static RawLinkedIdentity fromResource (LinkedCookieResource res) { + public static RawLinkedIdentity fromResource (LinkedTokenResource res) { return new RawLinkedIdentity(res.toUri()); } 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 index d63cafcc2..1e55b0fc3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java @@ -6,7 +6,7 @@ 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.linked.LinkedTokenResource; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import java.net.URI; @@ -24,10 +24,10 @@ import de.measite.minidns.Record.CLASS; import de.measite.minidns.Record.TYPE; import de.measite.minidns.record.TXT; -public class DnsResource extends LinkedCookieResource { +public class DnsResource extends LinkedTokenResource { final static Pattern magicPattern = - Pattern.compile("openpgpid\\+cookie=([a-zA-Z0-9]+)(?:#|;)([a-zA-Z0-9]+)"); + Pattern.compile("openpgpid\\+token=([a-zA-Z0-9]+)(?:#|;)([a-zA-Z0-9]+)"); String mFqdn; CLASS mClass; @@ -44,7 +44,7 @@ public class DnsResource extends LinkedCookieResource { public static String generateText(byte[] fingerprint) { - return String.format("openpgpid+cookie=%s", + return String.format("openpgpid+token=%s", KeyFormattingUtils.convertFingerprintToHex(fingerprint)); } 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 index 55f998952..05939a2db 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java @@ -10,7 +10,7 @@ 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.linked.LinkedTokenResource; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import java.io.IOException; @@ -19,17 +19,17 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Set; -public class GenericHttpsResource extends LinkedCookieResource { +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 cookie = LinkedCookieResource.generate(fingerprint); + String token = LinkedTokenResource.generate(fingerprint); return String.format(context.getResources().getString(R.string.linked_id_generic_text), - cookie, "0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprint).substring(24)); + token, "0x" + KeyFormattingUtils.convertFingerprintToHex(fingerprint).substring(24)); } @SuppressWarnings("deprecation") // HttpGet is deprecated 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 index 078328198..ef042d540 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java @@ -14,7 +14,7 @@ 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.linked.LinkedTokenResource; import org.sufficientlysecure.keychain.util.Log; import java.io.IOException; @@ -28,7 +28,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -public class GithubResource extends LinkedCookieResource { +public class GithubResource extends LinkedTokenResource { final String mHandle; final String mGistId; @@ -42,9 +42,9 @@ public class GithubResource extends LinkedCookieResource { } public static String generate(Context context, byte[] fingerprint) { - String cookie = LinkedCookieResource.generate(fingerprint); + String token = LinkedTokenResource.generate(fingerprint); - return String.format(context.getResources().getString(R.string.linked_id_github_text), cookie); + return String.format(context.getResources().getString(R.string.linked_id_github_text), token); } @SuppressWarnings("deprecation") // HttpGet is deprecated @@ -86,7 +86,7 @@ public class GithubResource extends LinkedCookieResource { // 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."); + throw new AssertionError("Needle must contain token pattern! This is a programming error, please report."); } needle = matcher.group(); 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 index c981e2bd6..36100fe58 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java @@ -19,7 +19,7 @@ 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.linked.LinkedTokenResource; import java.io.IOException; import java.net.MalformedURLException; @@ -30,7 +30,7 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; -public class TwitterResource extends LinkedCookieResource { +public class TwitterResource extends LinkedTokenResource { final String mHandle; final String mTweetId; 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 e72366fe1..e0e6976ee 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 @@ -29,7 +29,7 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.ClipboardReflection; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.linked.LinkedTokenResource; import org.sufficientlysecure.keychain.linked.resources.DnsResource; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify.Style; @@ -104,7 +104,7 @@ public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment } @Override - LinkedCookieResource getResource(OperationLog log) { + LinkedTokenResource getResource(OperationLog log) { return DnsResource.createNew(mResourceDomain); } 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 5b3250644..985cb6f18 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,7 +20,7 @@ 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.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.linked.LinkedTokenResource; import org.sufficientlysecure.keychain.linked.LinkedIdentity; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; @@ -39,7 +39,7 @@ public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragmen private ViewAnimator mVerifyAnimator; // This is a resource, set AFTER it has been verified - LinkedCookieResource mVerifiedResource = null; + LinkedTokenResource mVerifiedResource = null; private ViewAnimator mVerifyButtonAnimator; @Override @@ -94,7 +94,7 @@ public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragmen return view; } - abstract LinkedCookieResource getResource(OperationLog log); + abstract LinkedTokenResource getResource(OperationLog log); private void setVerifyProgress(boolean on, Boolean success) { if (success == null) { @@ -133,7 +133,7 @@ public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragmen long timer = System.currentTimeMillis(); OperationLog log = new OperationLog(); - LinkedCookieResource resource = getResource(log); + LinkedTokenResource resource = getResource(log); if (resource == null) { return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); } 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 95c029738..6e85193a7 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,7 +27,7 @@ import android.view.ViewGroup; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.linked.LinkedTokenResource; import org.sufficientlysecure.keychain.linked.resources.GithubResource; @@ -87,7 +87,7 @@ public class LinkedIdCreateGithubStep2Fragment extends LinkedIdCreateFinalFragme } @Override - LinkedCookieResource getResource(OperationLog log) { + LinkedTokenResource getResource(OperationLog log) { return GithubResource.searchInGithubStream(mResourceHandle, mResourceString, log); } 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 ebfbfd16b..aa9354a04 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 @@ -29,7 +29,7 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.linked.LinkedTokenResource; import org.sufficientlysecure.keychain.linked.resources.TwitterResource; public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragment { @@ -90,7 +90,7 @@ public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragm } @Override - LinkedCookieResource getResource(OperationLog log) { + LinkedTokenResource getResource(OperationLog log) { return TwitterResource.searchInTwitterStream(mResourceHandle, mResourceString, log); } 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 d2ae69a24..c3dcaf239 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,7 +33,7 @@ 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.linked.LinkedCookieResource; +import org.sufficientlysecure.keychain.linked.LinkedTokenResource; import org.sufficientlysecure.keychain.linked.LinkedIdentity; import org.sufficientlysecure.keychain.linked.LinkedResource; import org.sufficientlysecure.keychain.linked.RawLinkedIdentity; @@ -69,7 +69,7 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements private static final int LOADER_ID_LINKED_ID = 1; private RawLinkedIdentity mLinkedId; - private LinkedCookieResource mLinkedResource; + private LinkedTokenResource mLinkedResource; private boolean mIsSecret; private Context mContext; @@ -191,7 +191,7 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements if (mLinkedId instanceof LinkedIdentity) { LinkedResource res = ((LinkedIdentity) mLinkedId).mResource; - mLinkedResource = (LinkedCookieResource) res; + mLinkedResource = (LinkedTokenResource) res; } if (!mIsSecret) { -- cgit v1.2.3 From 3600cda3bcf1797db60c870a5fa582a27856e666 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 9 May 2015 19:09:09 +0200 Subject: linked-ids: rename RawLinkedIdentity to UriAttribute --- .../keychain/linked/LinkedIdentity.java | 2 +- .../keychain/linked/RawLinkedIdentity.java | 79 ---------------------- .../keychain/linked/UriAttribute.java | 79 ++++++++++++++++++++++ .../keychain/linked/resources/DnsResource.java | 4 +- .../keychain/pgp/WrappedUserAttribute.java | 2 +- .../keychain/provider/KeychainProvider.java | 2 +- .../keychain/ui/adapter/LinkedIdsAdapter.java | 16 ++--- .../keychain/ui/linked/LinkedIdViewFragment.java | 8 +-- 8 files changed, 96 insertions(+), 96 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/RawLinkedIdentity.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/UriAttribute.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedIdentity.java index af19aefdd..11ddb2cea 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedIdentity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedIdentity.java @@ -5,7 +5,7 @@ import java.net.URI; import android.content.Context; import android.support.annotation.DrawableRes; -public class LinkedIdentity extends RawLinkedIdentity { +public class LinkedIdentity extends UriAttribute { public final LinkedResource mResource; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/RawLinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/RawLinkedIdentity.java deleted file mode 100644 index 5310322f1..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/RawLinkedIdentity.java +++ /dev/null @@ -1,79 +0,0 @@ -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 = LinkedTokenResource.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 (LinkedTokenResource 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/UriAttribute.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/UriAttribute.java new file mode 100644 index 000000000..9bcf84994 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/UriAttribute.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 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 LinkedIdentity(uri, res); + + } catch (IllegalArgumentException e) { + Log.e(Constants.TAG, "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"; + } + + 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 index 1e55b0fc3..dd7662c6e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java @@ -44,7 +44,7 @@ public class DnsResource extends LinkedTokenResource { public static String generateText(byte[] fingerprint) { - return String.format("openpgpid+token=%s", + return String.format("openpgp4fpr=%s", KeyFormattingUtils.convertFingerprintToHex(fingerprint)); } @@ -52,7 +52,7 @@ public class DnsResource extends LinkedTokenResource { public static DnsResource createNew (String domain) { HashSet flags = new HashSet<>(); HashMap params = new HashMap<>(); - URI uri = URI.create("dns:" + domain); + URI uri = URI.create("dns:" + domain + "?TYPE=TXT"); return create(flags, params, uri); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java index 2431cb743..535314607 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedUserAttribute.java @@ -41,7 +41,7 @@ public class WrappedUserAttribute implements Serializable { public static final int UAT_NONE = 0; public static final int UAT_IMAGE = UserAttributeSubpacketTags.IMAGE_ATTRIBUTE; - public static final int UAT_LINKED_ID = 101; + public static final int UAT_URI_ATTRIBUTE = 101; private PGPUserAttributeSubpacketVector mVector; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 380c5cc46..ecb26b56a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -518,7 +518,7 @@ public class KeychainProvider extends ContentProvider { if (match == KEY_RING_LINKED_IDS) { qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " = " - + WrappedUserAttribute.UAT_LINKED_ID); + + WrappedUserAttribute.UAT_URI_ATTRIBUTE); } else { qb.appendWhere(Tables.USER_PACKETS + "." + UserPackets.TYPE + " IS NULL"); } 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 bc49e9a78..d50c3fd51 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 @@ -34,7 +34,7 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.linked.LinkedIdentity; -import org.sufficientlysecure.keychain.linked.RawLinkedIdentity; +import org.sufficientlysecure.keychain.linked.UriAttribute; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment; @@ -49,7 +49,7 @@ import java.util.WeakHashMap; public class LinkedIdsAdapter extends UserAttributesAdapter { private final boolean mIsSecret; protected LayoutInflater mInflater; - WeakHashMap mLinkedIdentityCache = new WeakHashMap<>(); + WeakHashMap mLinkedIdentityCache = new WeakHashMap<>(); private Cursor mUnfilteredCursor; @@ -85,7 +85,7 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { FilterCursorWrapper filteredCursor = new FilterCursorWrapper(cursor) { @Override public boolean isVisible(Cursor cursor) { - RawLinkedIdentity id = getItemAtPosition(cursor); + UriAttribute id = getItemAtPosition(cursor); return id instanceof LinkedIdentity; } }; @@ -132,16 +132,16 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { } } - RawLinkedIdentity id = getItemAtPosition(cursor); + UriAttribute id = getItemAtPosition(cursor); holder.setData(mContext, id); } - public RawLinkedIdentity getItemAtPosition(Cursor cursor) { + public UriAttribute getItemAtPosition(Cursor cursor) { int rank = cursor.getInt(INDEX_RANK); Log.d(Constants.TAG, "requested rank: " + rank); - RawLinkedIdentity ret = mLinkedIdentityCache.get(rank); + UriAttribute ret = mLinkedIdentityCache.get(rank); if (ret != null) { Log.d(Constants.TAG, "cached!"); return ret; @@ -160,7 +160,7 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { } @Override - public RawLinkedIdentity getItem(int position) { + public UriAttribute getItem(int position) { Cursor cursor = getCursor(); cursor.moveToPosition(position); return getItemAtPosition(cursor); @@ -206,7 +206,7 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { vComment = (TextView) view.findViewById(R.id.linked_id_comment); } - public void setData(Context context, RawLinkedIdentity id) { + public void setData(Context context, UriAttribute id) { vTitle.setText(id.getDisplayTitle(context)); 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 c3dcaf239..00eeb5de6 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 @@ -36,7 +36,7 @@ import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; import org.sufficientlysecure.keychain.linked.LinkedTokenResource; import org.sufficientlysecure.keychain.linked.LinkedIdentity; import org.sufficientlysecure.keychain.linked.LinkedResource; -import org.sufficientlysecure.keychain.linked.RawLinkedIdentity; +import org.sufficientlysecure.keychain.linked.UriAttribute; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; @@ -68,7 +68,7 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements private static final String ARG_FINGERPRINT = "fingerprint"; private static final int LOADER_ID_LINKED_ID = 1; - private RawLinkedIdentity mLinkedId; + private UriAttribute mLinkedId; private LinkedTokenResource mLinkedResource; private boolean mIsSecret; @@ -147,7 +147,7 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements int certStatus = cursor.getInt(UserIdsAdapter.INDEX_VERIFIED); byte[] data = cursor.getBlob(UserIdsAdapter.INDEX_ATTRIBUTE_DATA); - RawLinkedIdentity linkedId = LinkedIdentity.fromAttributeData(data); + UriAttribute linkedId = LinkedIdentity.fromAttributeData(data); loadIdentity(linkedId, certStatus); @@ -186,7 +186,7 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements mIdLoadedListener = listener; } - private void loadIdentity(RawLinkedIdentity linkedId, int certStatus) { + private void loadIdentity(UriAttribute linkedId, int certStatus) { mLinkedId = linkedId; if (mLinkedId instanceof LinkedIdentity) { -- cgit v1.2.3 From 9aff6c7f8527f3eb78a14c62a677a2fd0631130e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 9 May 2015 19:26:11 +0200 Subject: linked-ids: add certificate pinning, pin twitter api cert --- .../keychain/linked/LinkedTokenResource.java | 30 +++++++++++++++++----- .../keychain/linked/resources/DnsResource.java | 2 +- .../linked/resources/GenericHttpsResource.java | 5 ++-- .../keychain/linked/resources/GithubResource.java | 12 ++++----- .../keychain/linked/resources/TwitterResource.java | 24 ++++++++++------- .../ui/linked/LinkedIdCreateFinalFragment.java | 2 +- .../linked/LinkedIdCreateGithubStep2Fragment.java | 2 +- .../linked/LinkedIdCreateTwitterStep2Fragment.java | 3 ++- 8 files changed, 53 insertions(+), 27 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java index 7eec2a66a..3f42355fc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java @@ -2,6 +2,7 @@ package org.sufficientlysecure.keychain.linked; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; @@ -16,6 +17,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; +import org.thoughtcrime.ssl.pinning.util.PinningHelper; import java.io.BufferedReader; import java.io.IOException; @@ -30,6 +32,8 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import android.content.Context; + public abstract class LinkedTokenResource extends LinkedResource { @@ -166,7 +170,7 @@ public abstract class LinkedTokenResource extends LinkedResource { } - public LinkedVerifyResult verify(byte[] fingerprint) { + public LinkedVerifyResult verify(Context context, byte[] fingerprint) { OperationLog log = new OperationLog(); log.add(LogType.MSG_LV, 0); @@ -174,7 +178,7 @@ public abstract class LinkedTokenResource extends LinkedResource { // Try to fetch resource. Logs for itself String res = null; try { - res = fetchResource(log, 1); + res = fetchResource(context, log, 1); } catch (HttpStatusException e) { // log verbose output to logcat Log.e(Constants.TAG, "http error (" + e.getStatus() + "): " + e.getReason()); @@ -200,8 +204,8 @@ public abstract class LinkedTokenResource extends LinkedResource { } - protected abstract String fetchResource (OperationLog log, int indent) throws HttpStatusException, IOException, - JSONException; + 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); @@ -231,12 +235,26 @@ public abstract class LinkedTokenResource extends LinkedResource { } @SuppressWarnings("deprecation") // HttpRequestBase is deprecated - public static String getResponseBody(HttpRequestBase request) throws IOException, HttpStatusException { + public static String getResponseBody(Context context, HttpRequestBase request) + throws IOException, HttpStatusException { + return getResponseBody(context, request, null); + } + + @SuppressWarnings("deprecation") // HttpRequestBase is deprecated + public static String getResponseBody(Context context, HttpRequestBase request, String[] pins) + throws IOException, HttpStatusException { StringBuilder sb = new StringBuilder(); request.setHeader("User-Agent", "Open Keychain"); - DefaultHttpClient httpClient = new DefaultHttpClient(new BasicHttpParams()); + + HttpClient httpClient; + if (pins == null) { + httpClient = new DefaultHttpClient(new BasicHttpParams()); + } else { + httpClient = PinningHelper.getPinnedHttpClient(context, pins); + } + HttpResponse response = httpClient.execute(request); int statusCode = response.getStatusLine().getStatusCode(); String reason = response.getStatusLine().getReasonPhrase(); 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 index dd7662c6e..86b672cc1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java @@ -92,7 +92,7 @@ public class DnsResource extends LinkedTokenResource { } @Override - protected String fetchResource (OperationLog log, int indent) { + protected String fetchResource (Context context, OperationLog log, int indent) { Client c = new Client(); DNSMessage msg = c.query(new Question(mFqdn, mType, mClass)); 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 index 05939a2db..82240c405 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java @@ -34,11 +34,12 @@ public class GenericHttpsResource extends LinkedTokenResource { @SuppressWarnings("deprecation") // HttpGet is deprecated @Override - protected String fetchResource (OperationLog log, int indent) throws HttpStatusException, IOException { + protected String fetchResource (Context context, OperationLog log, int indent) + throws HttpStatusException, IOException { log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); HttpGet httpGet = new HttpGet(mSubUri); - return getResponseBody(httpGet); + return getResponseBody(context, httpGet); } 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 index ef042d540..30ce075b4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java @@ -49,14 +49,14 @@ public class GithubResource extends LinkedTokenResource { @SuppressWarnings("deprecation") // HttpGet is deprecated @Override - protected String fetchResource (OperationLog log, int indent) + protected String fetchResource (Context context, 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); + String response = getResponseBody(context, httpGet); JSONObject obj = new JSONObject(response); @@ -80,8 +80,8 @@ public class GithubResource extends LinkedTokenResource { } @SuppressWarnings("deprecation") - public static GithubResource searchInGithubStream(String screenName, String needle, - OperationLog log) { + public static GithubResource searchInGithubStream( + Context context, String screenName, String needle, OperationLog log) { // narrow the needle down to important part Matcher matcher = magicPattern.matcher(needle); @@ -98,7 +98,7 @@ public class GithubResource extends LinkedTokenResource { httpGet.setHeader("Content-Type", "application/json"); httpGet.setHeader("User-Agent", "OpenKeychain"); - String response = getResponseBody(httpGet); + String response = getResponseBody(context, httpGet); array = new JSONArray(response); } @@ -118,7 +118,7 @@ public class GithubResource extends LinkedTokenResource { HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + id); httpGet.setHeader("User-Agent", "OpenKeychain"); - JSONObject gistObj = new JSONObject(getResponseBody(httpGet)); + JSONObject gistObj = new JSONObject(getResponseBody(context, httpGet)); JSONObject gistFiles = gistObj.getJSONObject("files"); Iterator gistIt = gistFiles.keys(); if (!gistIt.hasNext()) { 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 index 36100fe58..d6b806ee6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java @@ -32,6 +32,11 @@ import java.util.regex.Pattern; public class TwitterResource extends LinkedTokenResource { + public static final String[] CERT_PINS = new String[] { + // antec Class 3 Secure Server CA - G4 + "513fb9743870b73440418d30930699ff" + }; + final String mHandle; final String mTweetId; @@ -68,12 +73,12 @@ public class TwitterResource extends LinkedTokenResource { @SuppressWarnings("deprecation") @Override - protected String fetchResource(OperationLog log, int indent) throws IOException, HttpStatusException, - JSONException { + protected String fetchResource(Context context, OperationLog log, int indent) + throws IOException, HttpStatusException, JSONException { String authToken; try { - authToken = getAuthToken(); + authToken = getAuthToken(context); } catch (IOException | HttpStatusException | JSONException e) { log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, indent); return null; @@ -90,7 +95,7 @@ public class TwitterResource extends LinkedTokenResource { httpGet.setHeader("Content-Type", "application/json"); try { - String response = getResponseBody(httpGet); + String response = getResponseBody(context, httpGet, CERT_PINS); JSONObject obj = new JSONObject(response); JSONObject user = obj.getJSONObject("user"); if (!mHandle.equalsIgnoreCase(user.getString("screen_name"))) { @@ -142,11 +147,11 @@ public class TwitterResource extends LinkedTokenResource { @SuppressWarnings("deprecation") public static TwitterResource searchInTwitterStream( - String screenName, String needle, OperationLog log) { + Context context, String screenName, String needle, OperationLog log) { String authToken; try { - authToken = getAuthToken(); + authToken = getAuthToken(context); } catch (IOException | HttpStatusException | JSONException e) { log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, 1); return null; @@ -166,7 +171,7 @@ public class TwitterResource extends LinkedTokenResource { httpGet.setHeader("Content-Type", "application/json"); try { - String response = getResponseBody(httpGet); + String response = getResponseBody(context, httpGet, CERT_PINS); JSONArray array = new JSONArray(response); for (int i = 0; i < array.length(); i++) { @@ -203,7 +208,8 @@ public class TwitterResource extends LinkedTokenResource { private static String cachedAuthToken; @SuppressWarnings("deprecation") - private static String getAuthToken() throws IOException, HttpStatusException, JSONException { + private static String getAuthToken(Context context) + throws IOException, HttpStatusException, JSONException { if (cachedAuthToken != null) { return cachedAuthToken; } @@ -215,7 +221,7 @@ public class TwitterResource extends LinkedTokenResource { 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)); + JSONObject rawAuthorization = new JSONObject(getResponseBody(context, httpPost, CERT_PINS)); // Applications should verify that the value associated with the // token_type key of the returned object is bearer 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 985cb6f18..4927064e4 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 @@ -138,7 +138,7 @@ public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragmen return new LinkedVerifyResult(LinkedVerifyResult.RESULT_ERROR, log); } - LinkedVerifyResult result = resource.verify(mLinkedIdWizard.mFingerprint); + LinkedVerifyResult result = resource.verify(getActivity(), mLinkedIdWizard.mFingerprint); // ux flow: this operation should take at last a second timer = System.currentTimeMillis() -timer; 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 6e85193a7..a3a8c779f 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 @@ -88,7 +88,7 @@ public class LinkedIdCreateGithubStep2Fragment extends LinkedIdCreateFinalFragme @Override LinkedTokenResource getResource(OperationLog log) { - return GithubResource.searchInGithubStream(mResourceHandle, mResourceString, log); + return GithubResource.searchInGithubStream(getActivity(), mResourceHandle, mResourceString, log); } @Override 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 aa9354a04..362798bc8 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 @@ -91,7 +91,8 @@ public class LinkedIdCreateTwitterStep2Fragment extends LinkedIdCreateFinalFragm @Override LinkedTokenResource getResource(OperationLog log) { - return TwitterResource.searchInTwitterStream(mResourceHandle, mResourceString, log); + return TwitterResource.searchInTwitterStream(getActivity(), + mResourceHandle, mResourceString, log); } @Override -- cgit v1.2.3 From 92ac2ded10548756922a4c22ebae0a19b8862426 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 11 May 2015 17:00:45 +0200 Subject: linked-ids: small compatibility changes --- .../org/sufficientlysecure/keychain/linked/LinkedTokenResource.java | 3 ++- .../sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java index 3f42355fc..b5a3a6be6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java @@ -70,7 +70,8 @@ public abstract class LinkedTokenResource extends LinkedResource { protected static LinkedTokenResource fromUri (URI uri) { - if (!"openpgpid+token".equals(uri.getScheme())) { + if (!"openpgpid+token".equals(uri.getScheme()) + && !"openpgpid+cookie".equals(uri.getScheme())) { Log.e(Constants.TAG, "unknown uri scheme in (suspected) linked id packet"); return null; } 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 00eeb5de6..7e9aaf25e 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 @@ -457,7 +457,7 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements @Override protected LinkedVerifyResult doInBackground(Void... params) { long timer = System.currentTimeMillis(); - LinkedVerifyResult result = mLinkedResource.verify(mFingerprint); + LinkedVerifyResult result = mLinkedResource.verify(getActivity(), mFingerprint); // ux flow: this operation should take at last a second timer = System.currentTimeMillis() -timer; -- cgit v1.2.3 From 6fe0a555228bd6fee95cc6f992986f91392ff736 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 12 May 2015 11:56:39 +0200 Subject: rename LinkedIdentity to LinkedAttribute --- .../keychain/linked/LinkedAttribute.java | 32 ++++++++++++++++++++++ .../keychain/linked/LinkedIdentity.java | 32 ---------------------- .../keychain/linked/UriAttribute.java | 4 +-- .../keychain/ui/adapter/LinkedIdsAdapter.java | 6 ++-- .../ui/linked/LinkedIdCreateFinalFragment.java | 4 +-- .../keychain/ui/linked/LinkedIdViewFragment.java | 8 +++--- 6 files changed, 43 insertions(+), 43 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedAttribute.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedIdentity.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedAttribute.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedAttribute.java new file mode 100644 index 000000000..3b05afbb3 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedAttribute.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 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/LinkedIdentity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedIdentity.java deleted file mode 100644 index 11ddb2cea..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedIdentity.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.sufficientlysecure.keychain.linked; - -import java.net.URI; - -import android.content.Context; -import android.support.annotation.DrawableRes; - -public class LinkedIdentity extends UriAttribute { - - 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/UriAttribute.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/UriAttribute.java index 9bcf84994..7a8ece2cb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/UriAttribute.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/UriAttribute.java @@ -47,7 +47,7 @@ public class UriAttribute { return new UriAttribute(uri); } - return new LinkedIdentity(uri, res); + return new LinkedAttribute(uri, res); } catch (IllegalArgumentException e) { Log.e(Constants.TAG, "error parsing uri in (suspected) linked id packet"); @@ -69,7 +69,7 @@ public class UriAttribute { } public String getDisplayTitle(Context context) { - return "unknown"; + return "Unknown Identity"; } public String getDisplayComment(Context context) { 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 d50c3fd51..805666bb2 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,7 +33,7 @@ import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.linked.LinkedAttribute; import org.sufficientlysecure.keychain.linked.UriAttribute; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; @@ -86,7 +86,7 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { @Override public boolean isVisible(Cursor cursor) { UriAttribute id = getItemAtPosition(cursor); - return id instanceof LinkedIdentity; + return id instanceof LinkedAttribute; } }; @@ -150,7 +150,7 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { try { byte[] data = cursor.getBlob(INDEX_ATTRIBUTE_DATA); - ret = LinkedIdentity.fromAttributeData(data); + ret = LinkedAttribute.fromAttributeData(data); mLinkedIdentityCache.put(rank, ret); return ret; } catch (IOException e) { 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 4927064e4..eedc7cdd9 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 @@ -21,7 +21,7 @@ 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.linked.LinkedTokenResource; -import org.sufficientlysecure.keychain.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.linked.LinkedAttribute; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.service.ServiceProgressHandler; @@ -221,7 +221,7 @@ public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragmen new SaveKeyringParcel(mLinkedIdWizard.mMasterKeyId, mLinkedIdWizard.mFingerprint); WrappedUserAttribute ua = - LinkedIdentity.fromResource(mVerifiedResource).toUserAttribute(); + LinkedAttribute.fromResource(mVerifiedResource).toUserAttribute(); skp.mAddUserAttribute.add(ua); 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 7e9aaf25e..82aaa51c4 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 @@ -34,7 +34,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.CertifyResult; import org.sufficientlysecure.keychain.operations.results.LinkedVerifyResult; import org.sufficientlysecure.keychain.linked.LinkedTokenResource; -import org.sufficientlysecure.keychain.linked.LinkedIdentity; +import org.sufficientlysecure.keychain.linked.LinkedAttribute; import org.sufficientlysecure.keychain.linked.LinkedResource; import org.sufficientlysecure.keychain.linked.UriAttribute; import org.sufficientlysecure.keychain.provider.KeychainContract.Certs; @@ -147,7 +147,7 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements int certStatus = cursor.getInt(UserIdsAdapter.INDEX_VERIFIED); byte[] data = cursor.getBlob(UserIdsAdapter.INDEX_ATTRIBUTE_DATA); - UriAttribute linkedId = LinkedIdentity.fromAttributeData(data); + UriAttribute linkedId = LinkedAttribute.fromAttributeData(data); loadIdentity(linkedId, certStatus); @@ -189,8 +189,8 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements private void loadIdentity(UriAttribute linkedId, int certStatus) { mLinkedId = linkedId; - if (mLinkedId instanceof LinkedIdentity) { - LinkedResource res = ((LinkedIdentity) mLinkedId).mResource; + if (mLinkedId instanceof LinkedAttribute) { + LinkedResource res = ((LinkedAttribute) mLinkedId).mResource; mLinkedResource = (LinkedTokenResource) res; } -- cgit v1.2.3 From df44d9b65cfe11910ab23b2b2fe82697f7af99b0 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 12 May 2015 11:57:07 +0200 Subject: allow linked token without one colon --- .../org/sufficientlysecure/keychain/linked/LinkedTokenResource.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java index b5a3a6be6..61e275166 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java @@ -42,7 +42,7 @@ public abstract class LinkedTokenResource extends LinkedResource { protected final HashMap mParams; public static Pattern magicPattern = - Pattern.compile("\\[Verifying my (?:Open)?PGP key: openpgp4fpr:([a-zA-Z0-9]+)]"); + Pattern.compile("\\[Verifying my (?:Open|)?PGP key(?::|) openpgp4fpr:([a-zA-Z0-9]+)]"); protected LinkedTokenResource(Set flags, HashMap params, URI uri) { mFlags = flags; @@ -109,8 +109,7 @@ public abstract class LinkedTokenResource extends LinkedResource { } protected static LinkedTokenResource findResourceType (Set flags, - HashMap params, - URI subUri) { + HashMap params, URI subUri) { LinkedTokenResource res; -- cgit v1.2.3 From f188b5b9b744016312a2cfb3ee4ea4f8edf459c0 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 15 May 2015 01:05:10 +0200 Subject: linked-ids: remove debug uri --- .../keychain/ui/linked/LinkedIdCreateHttpsStep1Fragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') 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 5a8b4a72a..7bc33c93b 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 @@ -115,7 +115,7 @@ public class LinkedIdCreateHttpsStep1Fragment extends Fragment { } }); - mEditUri.setText("mugenguild.com/pgpkey.txt"); + // mEditUri.setText("mugenguild.com/pgpkey.txt"); return view; } -- cgit v1.2.3 From aa133574b00d65c698d52d815db566c8536195fa Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 16 May 2015 23:05:58 +0200 Subject: fix fragment handling in ViewKeyActivity --- .../keychain/ui/ViewKeyActivity.java | 49 ++++++---------------- 1 file changed, 12 insertions(+), 37 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 9a48201b6..302236570 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -275,9 +275,19 @@ public class ViewKeyActivity extends BaseNfcActivity implements result.createNotify(this).show(); } - startFragment(savedInstanceState, mDataUri); + // Fragments are stored, no need to recreate those + if (savedInstanceState != null) { + return; + } - if (savedInstanceState == null && getIntent().hasExtra(EXTRA_NFC_AID)) { + FragmentManager manager = getSupportFragmentManager(); + // Create an instance of the fragment + final ViewKeyFragment frag = ViewKeyFragment.newInstance(mDataUri); + manager.beginTransaction() + .replace(R.id.view_key_fragment, frag) + .commit(); + + if (getIntent().hasExtra(EXTRA_NFC_AID)) { Intent intent = getIntent(); byte[] nfcFingerprints = intent.getByteArrayExtra(EXTRA_NFC_FINGERPRINTS); String nfcUserId = intent.getStringExtra(EXTRA_NFC_USER_ID); @@ -292,35 +302,6 @@ public class ViewKeyActivity extends BaseNfcActivity implements setContentView(R.layout.view_key_activity); } - private void startFragment(Bundle savedInstanceState, final Uri dataUri) { - // If we're being restored from a previous state, then we don't - // need to do anything and should return or else we could end - // up with overlapping fragments. - if (savedInstanceState != null) { - return; - } - - new Handler().post(new Runnable() { - @Override - public void run() { - - FragmentManager manager = getSupportFragmentManager(); - if (manager.getBackStackEntryCount() == 0) { - // Create an instance of the fragment - final ViewKeyFragment frag = ViewKeyFragment.newInstance(dataUri); - manager.beginTransaction() - .replace(R.id.view_key_fragment, frag) - .commit(); - manager.popBackStack(); - } /* else { - // not sure yet if we actually want this! - manager.popBackStack(); - } */ - - } - }); - } - @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); @@ -455,12 +436,6 @@ public class ViewKeyActivity extends BaseNfcActivity implements startActivityForResult(intent, 0); } - @Override - protected void onSaveInstanceState(Bundle outState) { - //Note:-Done due to the same weird crashes as for commitAllowingStateLoss() - //super.onSaveInstanceState(outState); - } - private void showQrCodeDialog() { Intent qrCodeIntent = new Intent(this, QrCodeViewActivity.class); -- cgit v1.2.3 From 5b75b542e8d2d467ce9e34bd9df2038d6c88885e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 15 May 2015 01:04:25 +0200 Subject: yubikey: don't assume signing key is masterKeyId in ViewKeyActivity --- .../ui/CreateKeyYubiKeyImportFragment.java | 10 ++- .../keychain/ui/ViewKeyActivity.java | 86 ++++++++++++---------- 2 files changed, 54 insertions(+), 42 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java index 4c7d1dfbd..f8d79d33b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CreateKeyYubiKeyImportFragment.java @@ -193,13 +193,19 @@ public class CreateKeyYubiKeyImportFragment extends Fragment implements NfcListe ImportKeyResult result = returnData.getParcelable(DecryptVerifyResult.EXTRA_RESULT); - if (!result.success()) { + long[] masterKeyIds = result.getImportedMasterKeyIds(); + + // TODO handle masterKeyIds.length != 1...? sorta outlandish scenario + + if (!result.success() || masterKeyIds.length == 0) { result.createNotify(getActivity()).show(); return; } Intent intent = new Intent(getActivity(), ViewKeyActivity.class); - intent.setData(KeyRings.buildGenericKeyRingUri(mNfcMasterKeyId)); + // use the imported masterKeyId, not the one from the yubikey, because + // that one might* just have been a subkey of the imported key + intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyIds[0])); intent.putExtra(ViewKeyActivity.EXTRA_DISPLAY_RESULT, result); intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, mNfcAid); intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, mNfcUserId); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 302236570..7ae4f1be3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -546,49 +546,54 @@ public class ViewKeyActivity extends BaseNfcActivity implements final String nfcUserId = nfcGetUserId(); final byte[] nfcAid = nfcGetAid(); - String fp = KeyFormattingUtils.convertFingerprintToHex(nfcFingerprints); - final long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(nfcFingerprints); - - if (!mFingerprint.equals(fp)) { - try { - CachedPublicKeyRing ring = mProviderHelper.getCachedPublicKeyRing(masterKeyId); - ring.getMasterKeyId(); - - Notify.create(this, R.string.snack_yubi_other, Notify.LENGTH_LONG, - Style.WARN, new ActionListener() { - @Override - public void onAction() { - Intent intent = new Intent( - ViewKeyActivity.this, ViewKeyActivity.class); - intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId)); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints); - startActivity(intent); - finish(); - } - }, R.string.snack_yubikey_view).show(); - return; + long yubiKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(nfcFingerprints); - } catch (PgpKeyNotFoundException e) { - Notify.create(this, R.string.snack_yubi_other, Notify.LENGTH_LONG, - Style.WARN, new ActionListener() { - @Override - public void onAction() { - Intent intent = new Intent( - ViewKeyActivity.this, CreateKeyActivity.class); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId); - intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints); - startActivity(intent); - finish(); - } - }, R.string.snack_yubikey_import).show(); + try { + + // if the yubikey matches a subkey in any key + CachedPublicKeyRing ring = mProviderHelper.getCachedPublicKeyRing( + KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(yubiKeyId)); + byte[] candidateFp = ring.getFingerprint(); + + // if the master key of that key matches this one, just show the yubikey dialog + if (KeyFormattingUtils.convertFingerprintToHex(candidateFp).equals(mFingerprint)) { + showYubiKeyFragment(nfcFingerprints, nfcUserId, nfcAid); return; } - } - showYubiKeyFragment(nfcFingerprints, nfcUserId, nfcAid); + // otherwise, offer to go to that key + final long masterKeyId = KeyFormattingUtils.getKeyIdFromFingerprint(candidateFp); + Notify.create(this, R.string.snack_yubi_other, Notify.LENGTH_LONG, + Style.WARN, new ActionListener() { + @Override + public void onAction() { + Intent intent = new Intent( + ViewKeyActivity.this, ViewKeyActivity.class); + intent.setData(KeyRings.buildGenericKeyRingUri(masterKeyId)); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints); + startActivity(intent); + finish(); + } + }, R.string.snack_yubikey_view).show(); + + // and if it's not found, offer import + } catch (PgpKeyNotFoundException e) { + Notify.create(this, R.string.snack_yubi_other, Notify.LENGTH_LONG, + Style.WARN, new ActionListener() { + @Override + public void onAction() { + Intent intent = new Intent( + ViewKeyActivity.this, CreateKeyActivity.class); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_AID, nfcAid); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_USER_ID, nfcUserId); + intent.putExtra(ViewKeyActivity.EXTRA_NFC_FINGERPRINTS, nfcFingerprints); + startActivity(intent); + finish(); + } + }, R.string.snack_yubikey_import).show(); + } } @@ -820,7 +825,8 @@ public class ViewKeyActivity extends BaseNfcActivity implements } mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID); - mFingerprint = KeyFormattingUtils.convertFingerprintToHex(data.getBlob(INDEX_FINGERPRINT)); + mFingerprint = KeyFormattingUtils.convertFingerprintToHex( + data.getBlob(INDEX_FINGERPRINT)); mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; mHasEncrypt = data.getInt(INDEX_HAS_ENCRYPT) != 0; -- cgit v1.2.3