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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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') 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 From a3395112e091eca4d3efafc1b9f29d782298b588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 17 Aug 2015 10:25:02 +0200 Subject: Revoke master key only --- .../keychain/operations/RevokeOperation.java | 48 +++------------------- 1 file changed, 5 insertions(+), 43 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java index ecf64e1af..9aceb946c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java @@ -20,7 +20,6 @@ package org.sufficientlysecure.keychain.operations; import android.content.Context; -import android.database.Cursor; import android.net.Uri; import android.support.annotation.NonNull; @@ -39,7 +38,7 @@ import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; -public class RevokeOperation extends BaseOperation { +public class RevokeOperation extends BaseOperation { public RevokeOperation(Context context, ProviderHelper providerHelper, Progressable progressable) { super(context, providerHelper, progressable); @@ -71,13 +70,15 @@ public class RevokeOperation extends BaseOperation { return new RevokeResult(RevokeResult.RESULT_ERROR, log, masterKeyId); } - SaveKeyringParcel saveKeyringParcel = getRevokedSaveKeyringParcel(masterKeyId, - keyRing.getFingerprint()); + SaveKeyringParcel saveKeyringParcel = + new SaveKeyringParcel(masterKeyId, keyRing.getFingerprint()); // all revoke operations are made atomic as of now saveKeyringParcel.setUpdateOptions(revokeKeyringParcel.mUpload, true, revokeKeyringParcel.mKeyserver); + saveKeyringParcel.mRevokeSubKeys.add(masterKeyId); + InputPendingResult revokeAndUploadResult = new EditKeyOperation(mContext, mProviderHelper, mProgressable, mCancelled) .execute(saveKeyringParcel, cryptoInputParcel); @@ -103,43 +104,4 @@ public class RevokeOperation extends BaseOperation { } } - private SaveKeyringParcel getRevokedSaveKeyringParcel(long masterKeyId, byte[] fingerprint) { - final String[] SUBKEYS_PROJECTION = new String[]{ - KeychainContract.Keys.KEY_ID - }; - final int INDEX_KEY_ID = 0; - - Uri keysUri = KeychainContract.Keys.buildKeysUri(masterKeyId); - Cursor subKeyCursor = - mContext.getContentResolver().query(keysUri, SUBKEYS_PROJECTION, null, null, null); - - SaveKeyringParcel saveKeyringParcel = - new SaveKeyringParcel(masterKeyId, fingerprint); - - // add all subkeys, for revocation - while (subKeyCursor != null && subKeyCursor.moveToNext()) { - saveKeyringParcel.mRevokeSubKeys.add(subKeyCursor.getLong(INDEX_KEY_ID)); - } - if (subKeyCursor != null) { - subKeyCursor.close(); - } - - final String[] USER_IDS_PROJECTION = new String[]{ - KeychainContract.UserPackets.USER_ID - }; - final int INDEX_USER_ID = 0; - - Uri userIdsUri = KeychainContract.UserPackets.buildUserIdsUri(masterKeyId); - Cursor userIdCursor = mContext.getContentResolver().query( - userIdsUri, USER_IDS_PROJECTION, null, null, null); - - while (userIdCursor != null && userIdCursor.moveToNext()) { - saveKeyringParcel.mRevokeUserIds.add(userIdCursor.getString(INDEX_USER_ID)); - } - if (userIdCursor != null) { - userIdCursor.close(); - } - - return saveKeyringParcel; - } } -- cgit v1.2.3 From 3c8028fc88af0b6af09d453fddc779e5972a59f9 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 17 Aug 2015 12:38:46 +0200 Subject: apply fixPgpMessage to plaintext/armored decrypt input --- .../sufficientlysecure/keychain/pgp/PgpHelper.java | 32 ++++++++++------------ .../keychain/ui/DecryptActivity.java | 25 +++++++++++++---- 2 files changed, 34 insertions(+), 23 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java index 32718fb4e..e8d1d3111 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpHelper.java @@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.pgp; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; +import android.support.annotation.NonNull; import android.text.TextUtils; import org.sufficientlysecure.keychain.Constants; @@ -116,32 +117,27 @@ public class PgpHelper { } } - public static String getPgpContent(CharSequence input) { - // only decrypt if clipboard content is available and a pgp message or cleartext signature - if (!TextUtils.isEmpty(input)) { - Log.dEscaped(Constants.TAG, "input: " + input); + public static String getPgpContent(@NonNull CharSequence input) { + Log.dEscaped(Constants.TAG, "input: " + input); + + Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(input); + if (matcher.matches()) { + String text = matcher.group(1); + text = fixPgpMessage(text); - Matcher matcher = PgpHelper.PGP_MESSAGE.matcher(input); + Log.dEscaped(Constants.TAG, "input fixed: " + text); + return text; + } else { + matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(input); if (matcher.matches()) { String text = matcher.group(1); - text = fixPgpMessage(text); + text = fixPgpCleartextSignature(text); Log.dEscaped(Constants.TAG, "input fixed: " + text); return text; } else { - matcher = PgpHelper.PGP_CLEARTEXT_SIGNATURE.matcher(input); - if (matcher.matches()) { - String text = matcher.group(1); - text = fixPgpCleartextSignature(text); - - Log.dEscaped(Constants.TAG, "input fixed: " + text); - return text; - } else { - return null; - } + return null; } - } else { - return null; } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java index e581e069b..881190ae2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java @@ -29,13 +29,14 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.support.annotation.Nullable; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; -import android.view.View; import android.widget.Toast; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.intents.OpenKeychainIntents; +import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider; import org.sufficientlysecure.keychain.ui.base.BaseActivity; @@ -93,7 +94,9 @@ public class DecryptActivity extends BaseActivity { } else if (intent.hasExtra(Intent.EXTRA_TEXT)) { String text = intent.getStringExtra(Intent.EXTRA_TEXT); Uri uri = readToTempFile(text); - uris.add(uri); + if (uri != null) { + uris.add(uri); + } } break; @@ -105,7 +108,9 @@ public class DecryptActivity extends BaseActivity { } else if (intent.hasExtra(Intent.EXTRA_TEXT)) { for (String text : intent.getStringArrayListExtra(Intent.EXTRA_TEXT)) { Uri uri = readToTempFile(text); - uris.add(uri); + if (uri != null) { + uris.add(uri); + } } } @@ -139,7 +144,9 @@ public class DecryptActivity extends BaseActivity { String text = clip.getItemAt(0).coerceToText(this).toString(); uri = readToTempFile(text); } - uris.add(uri); + if (uri != null) { + uris.add(uri); + } break; } @@ -170,9 +177,17 @@ public class DecryptActivity extends BaseActivity { } - public Uri readToTempFile(String text) throws IOException { + @Nullable public Uri readToTempFile(String text) throws IOException { Uri tempFile = TemporaryStorageProvider.createFile(this); OutputStream outStream = getContentResolver().openOutputStream(tempFile); + + // clean up ascii armored message, fixing newlines and stuff + String cleanedText = PgpHelper.getPgpContent(text); + if (cleanedText == null) { + return null; + } + + // if cleanup didn't work, just try the raw data outStream.write(text.getBytes()); outStream.close(); return tempFile; -- cgit v1.2.3 From 979605b5de92314736e43506ca262d5f57675485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 18 Aug 2015 09:59:31 +0200 Subject: Fix CryptoInputParcel handling for YubiKeys --- .../keychain/ui/NfcOperationActivity.java | 25 +++++----------------- 1 file changed, 5 insertions(+), 20 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java index e6591595e..6b48dbb4b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java @@ -21,7 +21,6 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; -import android.content.pm.ActivityInfo; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; @@ -50,15 +49,11 @@ import org.sufficientlysecure.keychain.util.Preferences; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Arrays; -import java.util.Date; /** * This class provides a communication interface to OpenPGP applications on ISO SmartCard compliant * NFC devices. - *

* For the full specs, see http://g10code.com/docs/openpgp-card-2.0.pdf - * NOTE: If no CryptoInputParcel is passed via EXTRA_CRYPTO_INPUT, the CryptoInputParcel is created - * internally and is NOT meant to be used by signing operations before adding signature time */ public class NfcOperationActivity extends BaseNfcActivity { @@ -101,13 +96,6 @@ public class NfcOperationActivity extends BaseNfcActivity { mInputParcel = getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT); - if (mInputParcel == null) { - // for compatibility when used from OpenPgpService - // (or any place other than CryptoOperationHelper) - // NOTE: This CryptoInputParcel cannot be used for signing without adding signature time - mInputParcel = new CryptoInputParcel(); - } - setTitle(R.string.nfc_text); vAnimator = (ViewAnimator) findViewById(R.id.view_animator); @@ -163,9 +151,8 @@ public class NfcOperationActivity extends BaseNfcActivity { break; } case NFC_SIGN: { - if (mInputParcel.getSignatureTime() == null) { - mInputParcel.addSignatureTime(new Date()); - } + mInputParcel.addSignatureTime(mRequiredInput.mSignatureTime); + for (int i = 0; i < mRequiredInput.mInputData.length; i++) { byte[] hash = mRequiredInput.mInputData[i]; int algo = mRequiredInput.mSignAlgos[i]; @@ -240,7 +227,7 @@ public class NfcOperationActivity extends BaseNfcActivity { throw new IOException("Inappropriate key flags for smart card key."); } - // TODO: Is this really needed? + // TODO: Is this really used anywhere? mInputParcel.addCryptoData(subkeyBytes, cardSerialNumber); } @@ -261,15 +248,13 @@ public class NfcOperationActivity extends BaseNfcActivity { protected void onNfcPostExecute() throws IOException { if (mServiceIntent != null) { // if we're triggered by OpenPgpService + // save updated cryptoInputParcel in cache CryptoInputParcelCacheService.addCryptoInputParcel(this, mServiceIntent, mInputParcel); - mServiceIntent.putExtra(EXTRA_CRYPTO_INPUT, - getIntent().getParcelableExtra(EXTRA_CRYPTO_INPUT)); setResult(RESULT_OK, mServiceIntent); } else { Intent result = new Intent(); + // send back the CryptoInputParcel we received result.putExtra(RESULT_CRYPTO_INPUT, mInputParcel); - // send back the CryptoInputParcel we receive, to conform with the pattern in - // CryptoOperationHelper setResult(RESULT_OK, result); } -- cgit v1.2.3 From 2ef691b264fcc985dfd94708c185ea6715a1972d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 18 Aug 2015 11:19:50 +0200 Subject: Fix theme problems --- .../java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java | 4 ++-- .../java/org/sufficientlysecure/keychain/ui/util/ThemeChanger.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java index 6b48dbb4b..b811b218e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/NfcOperationActivity.java @@ -79,8 +79,8 @@ public class NfcOperationActivity extends BaseNfcActivity { @Override protected void initTheme() { mThemeChanger = new ThemeChanger(this); - mThemeChanger.setThemes(R.style.Theme_Keychain_Light_Dialog_SecurityToken, - R.style.Theme_Keychain_Dark_Dialog_SecurityToken); + mThemeChanger.setThemes(R.style.Theme_Keychain_Light_Dialog, + R.style.Theme_Keychain_Dark_Dialog); mThemeChanger.changeTheme(); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ThemeChanger.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ThemeChanger.java index 75a0d1ea5..375483d89 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ThemeChanger.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ThemeChanger.java @@ -40,9 +40,9 @@ public class ThemeChanger { // hack to get holo design (which is not automatically applied due to activity's // Theme.NoDisplay) if (Constants.Pref.Theme.DARK.equals(preferences.getTheme())) { - return new ContextThemeWrapper(context, R.style.Theme_AppCompat_Dialog); + return new ContextThemeWrapper(context, R.style.Theme_Keychain_Dark); } else { - return new ContextThemeWrapper(context, R.style.Theme_AppCompat_Light_Dialog); + return new ContextThemeWrapper(context, R.style.Theme_Keychain_Light); } } -- cgit v1.2.3 From 1ef6f883e34a08dc916ad7dfabd7f892964aff54 Mon Sep 17 00:00:00 2001 From: Adithya Abraham Philip Date: Mon, 27 Jul 2015 14:10:26 +0530 Subject: introduced keyserver sync adapter --- .../org/sufficientlysecure/keychain/Constants.java | 5 + .../keychain/keyimport/HkpKeyserver.java | 6 +- .../keychain/operations/ImportOperation.java | 37 +++- .../keychain/provider/KeychainContract.java | 17 ++ .../keychain/provider/KeychainDatabase.java | 18 +- .../keychain/provider/KeychainProvider.java | 37 ++++ .../keychain/provider/ProviderHelper.java | 71 +++++++ .../service/KeyserverSyncAdapterService.java | 234 +++++++++++++++++++++ .../keychain/service/PassphraseCacheService.java | 4 +- .../keychain/ui/DeleteKeyDialogActivity.java | 1 - .../keychain/ui/KeyListFragment.java | 24 ++- 11 files changed, 428 insertions(+), 26 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index 3d58602ab..c66c33453 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -81,6 +81,11 @@ public final class Constants { public static final File APP_DIR_FILE = new File(APP_DIR, "export.asc"); } + public static final class Notification { + public static final int PASSPHRASE_CACHE = 1; + public static final int KEYSERVER_SYNC_FAIL_ORBOT = 2; + } + public static final class Pref { public static final String PASSPHRASE_CACHE_TTL = "passphraseCacheTtl"; public static final String PASSPHRASE_CACHE_SUBS = "passphraseCacheSubs"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java index 2cf6d8b34..5aac05712 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -196,9 +196,9 @@ public class HkpKeyserver extends Keyserver { /** * returns a client with pinned certificate if necessary * - * @param url - * @param proxy - * @return + * @param url url to be queried by client + * @param proxy proxy to be used by client + * @return client with a pinned certificate if necesary */ public static OkHttpClient getClient(URL url, Proxy proxy) throws IOException { OkHttpClient client = new OkHttpClient(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java index 4acfd6e30..9513f58ee 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java @@ -22,6 +22,7 @@ package org.sufficientlysecure.keychain.operations; import java.io.IOException; import java.net.Proxy; import java.util.ArrayList; +import java.util.GregorianCalendar; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -305,15 +306,25 @@ public class ImportOperation extends BaseOperation { } if (!result.success()) { badKeys += 1; - } else if (result.updated()) { - updatedKeys += 1; - importedMasterKeyIds.add(key.getMasterKeyId()); } else { - newKeys += 1; - if (key.isSecret()) { - secret += 1; + if (result.updated()) { + updatedKeys += 1; + importedMasterKeyIds.add(key.getMasterKeyId()); + } else { + newKeys += 1; + if (key.isSecret()) { + secret += 1; + } + importedMasterKeyIds.add(key.getMasterKeyId()); + } + if (entry.mBytes == null) { + // synonymous to isDownloadFromKeyserver. + // If no byte data was supplied, import from keyserver took place + // this prevents file imports being noted as keyserver imports + mProviderHelper.renewKeyLastUpdatedTime(key.getMasterKeyId(), + GregorianCalendar.getInstance().getTimeInMillis(), + TimeUnit.MILLISECONDS); } - importedMasterKeyIds.add(key.getMasterKeyId()); } log.add(result, 2); @@ -386,7 +397,7 @@ public class ImportOperation extends BaseOperation { @NonNull @Override - public OperationResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) { + public ImportKeyResult execute(ImportKeyringParcel importInput, CryptoInputParcel cryptoInput) { ArrayList keyList = importInput.mKeyList; String keyServer = importInput.mKeyserver; @@ -497,7 +508,7 @@ public class ImportOperation extends BaseOperation { Progressable mProgressable; private int mTotalKeys; private int mImportedKeys = 0; - ArrayList mImportedMasterKeyIds = new ArrayList(); + ArrayList mImportedMasterKeyIds = new ArrayList<>(); private int mBadKeys = 0; private int mNewKeys = 0; private int mUpdatedKeys = 0; @@ -515,7 +526,9 @@ public class ImportOperation extends BaseOperation { public KeyImportAccumulator(int totalKeys, Progressable externalProgressable) { mTotalKeys = totalKeys; mProgressable = externalProgressable; - mProgressable.setProgress(0, totalKeys); + if (mProgressable != null) { + mProgressable.setProgress(0, totalKeys); + } } public int getTotalKeys() { @@ -529,7 +542,9 @@ public class ImportOperation extends BaseOperation { public synchronized void accumulateKeyImport(ImportKeyResult result) { mImportedKeys++; - mProgressable.setProgress(mImportedKeys, mTotalKeys); + if (mProgressable != null) { + mProgressable.setProgress(mImportedKeys, mTotalKeys); + } mImportLog.addAll(result.getLog().toList());//accumulates log mBadKeys += result.mBadKeys; 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 0d9a4ac16..73a687efe 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -51,6 +51,11 @@ public class KeychainContract { String EXPIRY = "expiry"; } + interface UpdatedKeysColumns { + String MASTER_KEY_ID = "master_key_id"; // not a database id + String LAST_UPDATED = "last_updated"; // time since epoch in seconds + } + interface UserPacketsColumns { String MASTER_KEY_ID = "master_key_id"; // foreign key to key_rings._ID String TYPE = "type"; // not a database id @@ -97,6 +102,8 @@ public class KeychainContract { public static final String BASE_KEY_RINGS = "key_rings"; + public static final String BASE_UPDATED_KEYS = "updated_keys"; + public static final String PATH_UNIFIED = "unified"; public static final String PATH_FIND = "find"; @@ -234,6 +241,16 @@ public class KeychainContract { } + public static class UpdatedKeys implements UpdatedKeysColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_UPDATED_KEYS).build(); + + public static final String CONTENT_TYPE + = "vnd.android.cursor.dir/vnd.org.sufficientlysecure.keychain.provider.updated_keys"; + public static final String CONTENT_ITEM_TYPE + = "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.provider.updated_keys"; + } + public static class UserPackets implements UserPacketsColumns, BaseColumns { public static final String VERIFIED = "verified"; public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() 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 4d16d44c5..d7fb738fc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -34,6 +34,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.UpdatedKeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns; import org.sufficientlysecure.keychain.ui.ConsolidateDialogActivity; import org.sufficientlysecure.keychain.util.Log; @@ -53,7 +54,7 @@ import java.io.IOException; */ public class KeychainDatabase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "openkeychain.db"; - private static final int DATABASE_VERSION = 11; + private static final int DATABASE_VERSION = 12; static Boolean apgHack = false; private Context mContext; @@ -61,6 +62,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { String KEY_RINGS_PUBLIC = "keyrings_public"; String KEY_RINGS_SECRET = "keyrings_secret"; String KEYS = "keys"; + String UPDATED_KEYS = "updated_keys"; String USER_PACKETS = "user_packets"; String CERTS = "certs"; String API_APPS = "api_apps"; @@ -144,6 +146,14 @@ public class KeychainDatabase extends SQLiteOpenHelper { + Tables.USER_PACKETS + "(" + UserPacketsColumns.MASTER_KEY_ID + ", " + UserPacketsColumns.RANK + ") ON DELETE CASCADE" + ")"; + private static final String CREATE_UPDATE_KEYS = + "CREATE TABLE IF NOT EXISTS " + Tables.UPDATED_KEYS + " (" + + UpdatedKeysColumns.MASTER_KEY_ID + " INTEGER PRIMARY KEY, " + + UpdatedKeysColumns.LAST_UPDATED + " INTEGER, " + + "FOREIGN KEY(" + UpdatedKeysColumns.MASTER_KEY_ID + ") REFERENCES " + + Tables.KEY_RINGS_PUBLIC + "(" + KeyRingsColumns.MASTER_KEY_ID + ") ON DELETE CASCADE" + + ")"; + private static final String CREATE_API_APPS = "CREATE TABLE IF NOT EXISTS " + Tables.API_APPS + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " @@ -206,6 +216,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { db.execSQL(CREATE_KEYS); db.execSQL(CREATE_USER_PACKETS); db.execSQL(CREATE_CERTS); + db.execSQL(CREATE_UPDATE_KEYS); db.execSQL(CREATE_API_APPS); db.execSQL(CREATE_API_APPS_ACCOUNTS); db.execSQL(CREATE_API_APPS_ALLOWED_KEYS); @@ -278,8 +289,11 @@ public class KeychainDatabase extends SQLiteOpenHelper { // fix problems in database, see #1402 for details // https://github.com/open-keychain/open-keychain/issues/1402 db.execSQL("DELETE FROM api_accounts WHERE key_id BETWEEN 0 AND 3"); + case 12: + db.execSQL(CREATE_UPDATE_KEYS); if (oldVersion == 10) { - // no consolidate if we are updating from 10, we're just here for the api_accounts fix + // no consolidate if we are updating from 10, we're just here for + // the api_accounts fix and the new update keys table return; } 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 3ed9b1da9..178199805 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -38,6 +38,7 @@ 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.UpdatedKeys; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPackets; import org.sufficientlysecure.keychain.provider.KeychainContract.UserPacketsColumns; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; @@ -72,6 +73,9 @@ public class KeychainProvider extends ContentProvider { private static final int KEY_RINGS_FIND_BY_EMAIL = 400; private static final int KEY_RINGS_FIND_BY_SUBKEY = 401; + private static final int UPDATED_KEYS = 500; + private static final int UPDATED_KEYS_SPECIFIC = 501; + protected UriMatcher mUriMatcher; /** @@ -179,6 +183,12 @@ public class KeychainProvider extends ContentProvider { matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/" + KeychainContract.PATH_ALLOWED_KEYS, API_ALLOWED_KEYS); + /** + * to access table containing last updated dates of keys + */ + matcher.addURI(authority, KeychainContract.BASE_UPDATED_KEYS, UPDATED_KEYS); + matcher.addURI(authority, KeychainContract.BASE_UPDATED_KEYS + "/*", UPDATED_KEYS_SPECIFIC); + return matcher; } @@ -218,6 +228,11 @@ public class KeychainProvider extends ContentProvider { case KEY_RING_SECRET: return KeyRings.CONTENT_ITEM_TYPE; + case UPDATED_KEYS: + return UpdatedKeys.CONTENT_TYPE; + case UPDATED_KEYS_SPECIFIC: + return UpdatedKeys.CONTENT_ITEM_TYPE; + case API_APPS: return ApiApps.CONTENT_TYPE; @@ -606,6 +621,22 @@ public class KeychainProvider extends ContentProvider { break; } + case UPDATED_KEYS: + case UPDATED_KEYS_SPECIFIC: { + HashMap projectionMap = new HashMap<>(); + qb.setTables(Tables.UPDATED_KEYS); + projectionMap.put(UpdatedKeys.MASTER_KEY_ID, Tables.UPDATED_KEYS + "." + + UpdatedKeys.MASTER_KEY_ID); + projectionMap.put(UpdatedKeys.LAST_UPDATED, Tables.UPDATED_KEYS + "." + + UpdatedKeys.LAST_UPDATED); + qb.setProjectionMap(projectionMap); + if (match == UPDATED_KEYS_SPECIFIC) { + qb.appendWhere(UpdatedKeys.MASTER_KEY_ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); + } + break; + } + case API_APPS: { qb.setTables(Tables.API_APPS); @@ -726,6 +757,12 @@ public class KeychainProvider extends ContentProvider { keyId = values.getAsLong(Certs.MASTER_KEY_ID); break; } + case UPDATED_KEYS: { + long updatedKeyId = db.replace(Tables.UPDATED_KEYS, null, values); + rowUri = UpdatedKeys.CONTENT_URI.buildUpon().appendPath("" + updatedKeyId) + .build(); + break; + } case API_APPS: { db.insertOrThrow(Tables.API_APPS, null, values); break; 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 0c37bfc2a..e8eea6831 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -59,6 +59,7 @@ 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.provider.KeychainContract.UpdatedKeys; import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AppSettings; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; @@ -82,6 +83,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.concurrent.TimeUnit; /** * This class contains high level methods for database access. Despite its @@ -685,6 +687,41 @@ public class ProviderHelper { mIndent -= 1; } + // before deleting key, retrieve it's last updated time + final int INDEX_MASTER_KEY_ID = 0; + final int INDEX_LAST_UPDATED = 1; + Cursor lastUpdatedCursor = mContentResolver.query( + UpdatedKeys.CONTENT_URI, + new String[]{ + UpdatedKeys.MASTER_KEY_ID, + UpdatedKeys.LAST_UPDATED + }, + UpdatedKeys.MASTER_KEY_ID + " = ?", + new String[]{"" + masterKeyId}, + null + ); + ContentValues lastUpdatedEntry = null; + if (lastUpdatedCursor.moveToNext()) { + lastUpdatedEntry = new ContentValues(2); + lastUpdatedEntry.put(UpdatedKeys.MASTER_KEY_ID, + lastUpdatedCursor.getLong(INDEX_MASTER_KEY_ID)); + lastUpdatedEntry.put(UpdatedKeys.LAST_UPDATED, + lastUpdatedCursor.getLong(INDEX_LAST_UPDATED)); + Log.e("PHILIP", "cv: " + lastUpdatedEntry + " actual: " + masterKeyId); + } + lastUpdatedCursor.close(); + + if (lastUpdatedEntry != null) { + // there was an entry to re-insert + // this operation must happen after the new key is inserted + operations.add( + ContentProviderOperation + .newInsert(UpdatedKeys.CONTENT_URI) + .withValues(lastUpdatedEntry) + .build() + ); + } + try { // delete old version of this keyRing, which also deletes all keys and userIds on cascade int deleted = mContentResolver.delete( @@ -1239,6 +1276,28 @@ public class ProviderHelper { } // 2. wipe database (IT'S DANGEROUS) + + // first, backup our list of updated key times + ArrayList updatedKeysValues = new ArrayList<>(); + final int INDEX_MASTER_KEY_ID = 0; + final int INDEX_LAST_UPDATED = 1; + Cursor lastUpdatedCursor = mContentResolver.query( + UpdatedKeys.CONTENT_URI, + new String[]{ + UpdatedKeys.MASTER_KEY_ID, + UpdatedKeys.LAST_UPDATED + }, + null, null, null); + while (lastUpdatedCursor.moveToNext()) { + ContentValues values = new ContentValues(); + values.put(UpdatedKeys.MASTER_KEY_ID, + lastUpdatedCursor.getLong(INDEX_MASTER_KEY_ID)); + values.put(UpdatedKeys.LAST_UPDATED, + lastUpdatedCursor.getLong(INDEX_LAST_UPDATED)); + updatedKeysValues.add(values); + } + lastUpdatedCursor.close(); + log.add(LogType.MSG_CON_DB_CLEAR, indent); mContentResolver.delete(KeyRings.buildUnifiedKeyRingsUri(), null, null); @@ -1288,6 +1347,10 @@ public class ProviderHelper { new ProgressFixedScaler(progress, 25, 99, 100, R.string.progress_con_reimport)) .serialKeyRingImport(itPublics, numPublics, null, null); log.add(result, indent); + // re-insert our backed up list of updated key times + // TODO: can this cause issues in case a public key re-import failed? + mContentResolver.bulkInsert(UpdatedKeys.CONTENT_URI, + updatedKeysValues.toArray(new ContentValues[updatedKeysValues.size()])); } else { log.add(LogType.MSG_CON_REIMPORT_PUBLIC_SKIP, indent); } @@ -1397,6 +1460,14 @@ public class ProviderHelper { return getKeyRingAsArmoredString(data); } + public Uri renewKeyLastUpdatedTime(long masterKeyId, long time, TimeUnit timeUnit) { + ContentValues values = new ContentValues(); + values.put(UpdatedKeys.MASTER_KEY_ID, masterKeyId); + values.put(UpdatedKeys.LAST_UPDATED, timeUnit.toSeconds(time)); + + return mContentResolver.insert(UpdatedKeys.CONTENT_URI, values); + } + public ArrayList getRegisteredApiApps() { Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java new file mode 100644 index 000000000..4a7e2942e --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java @@ -0,0 +1,234 @@ +package org.sufficientlysecure.keychain.service; + +import android.accounts.Account; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.AbstractThreadedSyncAdapter; +import android.content.ContentProviderClient; +import android.content.Context; +import android.content.Intent; +import android.content.SyncResult; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.os.Build; +import android.os.Bundle; +import android.os.IBinder; +import android.support.v4.app.NotificationCompat; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; +import org.sufficientlysecure.keychain.operations.ImportOperation; +import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.ui.MainActivity; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.ParcelableProxy; +import org.sufficientlysecure.keychain.util.Preferences; + +import java.util.ArrayList; +import java.util.GregorianCalendar; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +public class KeyserverSyncAdapterService extends Service { + + private static final String ACTION_IGNORE_TOR = "ignore_tor"; + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.e("PHILIP", "Sync adapter service starting"); + switch (intent.getAction()) { + case ACTION_IGNORE_TOR: { + updateKeysFromKeyserver(this, + new CryptoInputParcel(ParcelableProxy.getForNoProxy())); + break; + } + } + // TODO: correct flag? + return START_NOT_STICKY; + } + + private static AtomicBoolean sCancelled = new AtomicBoolean(false); + + private class KeyserverSyncAdapter extends AbstractThreadedSyncAdapter { + + public KeyserverSyncAdapter() { + super(KeyserverSyncAdapterService.this, true); + } + + @Override + public void onPerformSync(Account account, Bundle extras, String authority, + ContentProviderClient provider, SyncResult syncResult) { + Log.d(Constants.TAG, "Performing a keyserver sync!"); + + updateKeysFromKeyserver(KeyserverSyncAdapterService.this, new CryptoInputParcel()); + } + } + + @Override + public IBinder onBind(Intent intent) { + return new KeyserverSyncAdapter().getSyncAdapterBinder(); + } + + public static void updateKeysFromKeyserver(Context context, + CryptoInputParcel cryptoInputParcel) { + /** + * 1. Get keys which have been updated recently and therefore do not need to + * be updated now + * 2. Get list of all keys and filter out ones that don't need to be updated + * 3. Update the remaining keys. + * At any time, if the operation is to be cancelled, the sCancelled AtomicBoolean may be set + */ + + // 1. Get keys which have been updated recently and don't need to updated now + final int INDEX_UPDATED_KEYS_MASTER_KEY_ID = 0; + final int INDEX_LAST_UPDATED = 1; + + final long TIME_DAY = TimeUnit.DAYS.toSeconds(1); + final long CURRENT_TIME = GregorianCalendar.getInstance().get(GregorianCalendar.SECOND); + Log.e("PHILIP", "day: " + TIME_DAY); + Cursor updatedKeysCursor = context.getContentResolver().query( + KeychainContract.UpdatedKeys.CONTENT_URI, + new String[]{ + KeychainContract.UpdatedKeys.MASTER_KEY_ID, + KeychainContract.UpdatedKeys.LAST_UPDATED + }, + "? - " + KeychainContract.UpdatedKeys.LAST_UPDATED + " < " + TIME_DAY, + new String[]{"" + CURRENT_TIME}, + null + ); + + ArrayList ignoreMasterKeyIds = new ArrayList<>(); + while (updatedKeysCursor.moveToNext()) { + long masterKeyId = updatedKeysCursor.getLong(INDEX_UPDATED_KEYS_MASTER_KEY_ID); + Log.d(Constants.TAG, "Keyserver sync: {" + masterKeyId + "} last updated at {" + + updatedKeysCursor.getLong(INDEX_LAST_UPDATED) + "}s"); + ignoreMasterKeyIds.add(masterKeyId); + } + updatedKeysCursor.close(); + + // 2. Make a list of public keys which should be updated + final int INDEX_MASTER_KEY_ID = 0; + final int INDEX_FINGERPRINT = 1; + Cursor keyCursor = context.getContentResolver().query( + KeychainContract.KeyRings.buildUnifiedKeyRingsUri(), + new String[]{ + KeychainContract.KeyRings.MASTER_KEY_ID, + KeychainContract.KeyRings.FINGERPRINT + }, + null, + null, + null + ); + + if (keyCursor == null) { + return; + } + + ArrayList keyList = new ArrayList<>(); + while (keyCursor.moveToNext()) { + long keyId = keyCursor.getLong(INDEX_MASTER_KEY_ID); + if (ignoreMasterKeyIds.contains(keyId)) { + continue; + } + String fingerprint = KeyFormattingUtils + .convertFingerprintToHex(keyCursor.getBlob(INDEX_FINGERPRINT)); + String hexKeyId = KeyFormattingUtils + .convertKeyIdToHex(keyId); + // we aren't updating from keybase as of now + keyList.add(new ParcelableKeyRing(fingerprint, hexKeyId, null)); + } + keyCursor.close(); + + if (sCancelled.get()) { + // if we've already been cancelled + return; + } + + // 3. Actually update the keys + Log.e("PHILIP", keyList.toString()); + ImportOperation importOp = new ImportOperation(context, new ProviderHelper(context), null); + ImportKeyResult result = importOp.execute( + new ImportKeyringParcel(keyList, + Preferences.getPreferences(context).getPreferredKeyserver()), + cryptoInputParcel + ); + if (result.isPending()) { + NotificationManager manager = + (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE); + manager.notify(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT, + getOrbotNoification(context)); + + Log.d(Constants.TAG, "Keyserver sync failed due to pending result " + + result.getRequiredInputParcel().mType); + } else { + Log.d(Constants.TAG, "Background keyserver sync completed: new " + result.mNewKeys + + " updated " + result.mUpdatedKeys + " bad " + result.mBadKeys); + } + } + + public static void preventAndCancelUpdates() { + // TODO: PHILIP uncomment! + // sCancelled.set(true); + } + + public static void allowUpdates() { + sCancelled.set(false); + } + + private static Notification getOrbotNoification(Context context) { + // TODO: PHILIP work in progress + NotificationCompat.Builder builder = new NotificationCompat.Builder(context); + builder.setSmallIcon(R.drawable.ic_stat_notify_24dp) + .setLargeIcon(getBitmap(R.drawable.ic_launcher, context)) + .setContentTitle(context.getString(R.string.keyserver_sync_orbot_notif_title)) + .setContentText(context.getString(R.string.keyserver_sync_orbot_notif_msg)); + + // In case the user decides to not use tor + Intent ignoreTorIntent = new Intent(context, KeyserverSyncAdapterService.class); + ignoreTorIntent.setAction(ACTION_IGNORE_TOR); + PendingIntent ignoreTorPi = PendingIntent.getService( + context, + Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT, + ignoreTorIntent, + PendingIntent.FLAG_CANCEL_CURRENT + ); + + builder.addAction(R.drawable.abc_ic_clear_mtrl_alpha, + context.getString(R.string.keyserver_sync_orbot_notif_ignore), + ignoreTorPi); + + return builder.build(); + } + + // from de.azapps.mirakel.helper.Helpers from https://github.com/MirakelX/mirakel-android + private static Bitmap getBitmap(int resId, Context context) { + int mLargeIconWidth = (int) context.getResources().getDimension( + android.R.dimen.notification_large_icon_width); + int mLargeIconHeight = (int) context.getResources().getDimension( + android.R.dimen.notification_large_icon_height); + Drawable d; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + // noinspection deprecation (can't help it at this api level) + d = context.getResources().getDrawable(resId); + } else { + d = context.getDrawable(resId); + } + if (d == null) { + return null; + } + Bitmap b = Bitmap.createBitmap(mLargeIconWidth, mLargeIconHeight, Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(b); + d.setBounds(0, 0, mLargeIconWidth, mLargeIconHeight); + d.draw(c); + return b; + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java index 2a258b7e3..be269c66d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -101,8 +101,6 @@ public class PassphraseCacheService extends Service { private static final long DEFAULT_TTL = 15; - private static final int NOTIFICATION_ID = 1; - private static final int MSG_PASSPHRASE_CACHE_GET_OKAY = 1; private static final int MSG_PASSPHRASE_CACHE_GET_KEY_NOT_FOUND = 2; @@ -477,7 +475,7 @@ public class PassphraseCacheService extends Service { private void updateService() { if (mPassphraseCache.size() > 0) { - startForeground(NOTIFICATION_ID, getNotification()); + startForeground(Constants.Notification.PASSPHRASE_CACHE, getNotification()); } else { // stop whole service if no cached passphrases remaining Log.d(Constants.TAG, "PassphraseCacheService: No passphrases remaining in memory, stopping service!"); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java index b22053df1..478259133 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DeleteKeyDialogActivity.java @@ -48,7 +48,6 @@ import org.sufficientlysecure.keychain.service.RevokeKeyringParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.dialog.CustomAlertDialogBuilder; -import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.ThemeChanger; import org.sufficientlysecure.keychain.util.Log; 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 8c46876be..4997d3fab 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -25,7 +25,6 @@ import java.util.HashMap; import android.animation.ObjectAnimator; import android.annotation.TargetApi; -import android.app.Activity; import android.content.Context; import android.content.Intent; import android.database.Cursor; @@ -65,15 +64,14 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainDatabase; -import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.ConsolidateInputParcel; import org.sufficientlysecure.keychain.service.ImportKeyringParcel; +import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.util.FabContainer; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Preferences; @@ -522,7 +520,9 @@ public class KeyListFragment extends LoaderFragment } private void updateAllKeys() { - Activity activity = getActivity(); + // TODO: PHILIP just for testing, remove! + KeyserverSyncAdapterService.updateKeysFromKeyserver(getActivity(), new CryptoInputParcel()); + /*Activity activity = getActivity(); if (activity == null) { return; } @@ -563,7 +563,7 @@ public class KeyListFragment extends LoaderFragment mImportOpHelper = new CryptoOperationHelper<>(1, this, this, R.string.progress_updating); - mImportOpHelper.cryptoOperation(); + mImportOpHelper.cryptoOperation();*/ } private void consolidate() { @@ -647,6 +647,18 @@ public class KeyListFragment extends LoaderFragment } } + @Override + public void onResume() { + super.onResume(); + KeyserverSyncAdapterService.preventAndCancelUpdates(); + } + + @Override + public void onPause() { + super.onPause(); + KeyserverSyncAdapterService.allowUpdates(); + } + @Override public void fabMoveUp(int height) { ObjectAnimator anim = ObjectAnimator.ofFloat(mFab, "translationY", 0, -height); -- cgit v1.2.3 From 448657602ce5baa5b2a99e26ab2eb2fb8061778f Mon Sep 17 00:00:00 2001 From: Adithya Abraham Philip Date: Thu, 30 Jul 2015 04:39:38 +0530 Subject: improved orbot notify, added periodic sync --- .../org/sufficientlysecure/keychain/Constants.java | 7 +- .../service/KeyserverSyncAdapterService.java | 88 ++++++++++++++++++++-- .../keychain/ui/MainActivity.java | 19 +++++ 3 files changed, 100 insertions(+), 14 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index c66c33453..03d39f6be 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -19,14 +19,9 @@ package org.sufficientlysecure.keychain; import android.os.Environment; -import org.spongycastle.bcpg.HashAlgorithmTags; -import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; import org.spongycastle.jce.provider.BouncyCastleProvider; -import org.sufficientlysecure.keychain.BuildConfig; - import java.io.File; -import java.net.InetSocketAddress; import java.net.Proxy; public final class Constants { @@ -43,7 +38,7 @@ public final class Constants { public static final String ACCOUNT_TYPE = BuildConfig.ACCOUNT_TYPE; public static final String CUSTOM_CONTACT_DATA_MIME_TYPE = "vnd.android.cursor.item/vnd.org.sufficientlysecure.keychain.key"; - public static final String PROVIDER_AUTHORITY = BuildConfig.APPLICATION_ID + ".provider"; + public static final String PROVIDER_AUTHORITY = BuildConfig.PROVIDER_CONTENT_AUTHORITY; public static final String TEMPSTORAGE_AUTHORITY = BuildConfig.APPLICATION_ID + ".tempstorage"; public static final String CLIPBOARD_LABEL = "Keychain"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java index 4a7e2942e..a08797e6d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java @@ -16,6 +16,7 @@ import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; import android.support.v4.app.NotificationCompat; @@ -27,7 +28,7 @@ import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; -import org.sufficientlysecure.keychain.ui.MainActivity; +import org.sufficientlysecure.keychain.ui.OrbotRequiredDialogActivity; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ParcelableProxy; @@ -41,19 +42,49 @@ import java.util.concurrent.atomic.AtomicBoolean; public class KeyserverSyncAdapterService extends Service { private static final String ACTION_IGNORE_TOR = "ignore_tor"; + private static final String ACTION_DISMISS_NOTIFICATION = "cancel_sync"; + private static final String ACTION_START_ORBOT = "start_orbot"; @Override - public int onStartCommand(Intent intent, int flags, int startId) { + public int onStartCommand(Intent intent, int flags, final int startId) { Log.e("PHILIP", "Sync adapter service starting"); + NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); switch (intent.getAction()) { case ACTION_IGNORE_TOR: { updateKeysFromKeyserver(this, new CryptoInputParcel(ParcelableProxy.getForNoProxy())); + manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); + stopSelf(startId); + break; + } + case ACTION_START_ORBOT: { + Intent startOrbot = new Intent(this, OrbotRequiredDialogActivity.class); + startOrbot.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startOrbot.setAction(OrbotRequiredDialogActivity.ACTION_START_ORBOT); + startActivity(startOrbot); + new Handler().postDelayed( + new Runnable() { + @Override + public void run() { + updateKeysFromKeyserver(KeyserverSyncAdapterService.this, + new CryptoInputParcel()); + stopSelf(startId); + } + }, + 30000 // shouldn't take longer for orbot to start + ); + + manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); + break; + } + case ACTION_DISMISS_NOTIFICATION: { + manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); + stopSelf(startId); break; } } // TODO: correct flag? - return START_NOT_STICKY; + return START_REDELIVER_INTENT; } private static AtomicBoolean sCancelled = new AtomicBoolean(false); @@ -92,16 +123,16 @@ public class KeyserverSyncAdapterService extends Service { final int INDEX_UPDATED_KEYS_MASTER_KEY_ID = 0; final int INDEX_LAST_UPDATED = 1; - final long TIME_DAY = TimeUnit.DAYS.toSeconds(1); + final long TIME_MAX = TimeUnit.DAYS.toSeconds(7); final long CURRENT_TIME = GregorianCalendar.getInstance().get(GregorianCalendar.SECOND); - Log.e("PHILIP", "day: " + TIME_DAY); + Log.e("PHILIP", "week: " + TIME_MAX); Cursor updatedKeysCursor = context.getContentResolver().query( KeychainContract.UpdatedKeys.CONTENT_URI, new String[]{ KeychainContract.UpdatedKeys.MASTER_KEY_ID, KeychainContract.UpdatedKeys.LAST_UPDATED }, - "? - " + KeychainContract.UpdatedKeys.LAST_UPDATED + " < " + TIME_DAY, + "? - " + KeychainContract.UpdatedKeys.LAST_UPDATED + " < " + TIME_MAX, new String[]{"" + CURRENT_TIME}, null ); @@ -110,7 +141,7 @@ public class KeyserverSyncAdapterService extends Service { while (updatedKeysCursor.moveToNext()) { long masterKeyId = updatedKeysCursor.getLong(INDEX_UPDATED_KEYS_MASTER_KEY_ID); Log.d(Constants.TAG, "Keyserver sync: {" + masterKeyId + "} last updated at {" - + updatedKeysCursor.getLong(INDEX_LAST_UPDATED) + "}s"); + + updatedKeysCursor.getLong(INDEX_LAST_UPDATED) + "}s"); ignoreMasterKeyIds.add(masterKeyId); } updatedKeysCursor.close(); @@ -197,7 +228,7 @@ public class KeyserverSyncAdapterService extends Service { ignoreTorIntent.setAction(ACTION_IGNORE_TOR); PendingIntent ignoreTorPi = PendingIntent.getService( context, - Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT, + 0, // security not issue since we're giving this pending intent to Notification Manager ignoreTorIntent, PendingIntent.FLAG_CANCEL_CURRENT ); @@ -206,9 +237,50 @@ public class KeyserverSyncAdapterService extends Service { context.getString(R.string.keyserver_sync_orbot_notif_ignore), ignoreTorPi); + // not enough space to show it + Intent dismissIntent = new Intent(context, KeyserverSyncAdapterService.class); + dismissIntent.setAction(ACTION_DISMISS_NOTIFICATION); + PendingIntent dismissPi = PendingIntent.getService( + context, + 0, // security not issue since we're giving this pending intent to Notification Manager + dismissIntent, + PendingIntent.FLAG_CANCEL_CURRENT + ); + + /*builder.addAction(R.drawable.abc_ic_clear_mtrl_alpha, + context.getString(android.R.string.cancel), + dismissPi + );*/ + + Intent startOrbotIntent = new Intent(context, KeyserverSyncAdapterService.class); + startOrbotIntent.setAction(ACTION_START_ORBOT); + PendingIntent startOrbotPi = PendingIntent.getService( + context, + 0, // security not issue since we're giving this pending intent to Notification Manager + startOrbotIntent, + PendingIntent.FLAG_CANCEL_CURRENT + ); + + builder.addAction(R.drawable.abc_ic_clear_mtrl_alpha, + context.getString(R.string.keyserver_sync_orbot_notif_start), + startOrbotPi + ); + builder.setContentIntent(startOrbotPi); + return builder.build(); } + /** + * will perform a staggered update of user's keys using delays to ensure new Tor circuits, as + * performed by parcimonie + * + * @return result of the sync + */ + private static ImportKeyResult staggeredSync() { + // TODO: WIP + return null; + } + // from de.azapps.mirakel.helper.Helpers from https://github.com/MirakelX/mirakel-android private static Bitmap getBitmap(int resId, Context context) { int mLargeIconWidth = (int) context.getResources().getDimension( diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java index 6f5d98afd..0fa6566f8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -19,6 +19,9 @@ package org.sufficientlysecure.keychain.ui; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.content.ContentResolver; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -37,11 +40,14 @@ import com.mikepenz.materialdrawer.DrawerBuilder; import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.remote.ui.AppsListFragment; +import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService; import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity; import org.sufficientlysecure.keychain.util.FabContainer; +import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Preferences; public class MainActivity extends BaseNfcActivity implements FabContainer, OnBackStackChangedListener { @@ -164,6 +170,19 @@ public class MainActivity extends BaseNfcActivity implements FabContainer, OnBac } } + enablePeriodicKeyserverSync(); + + } + + private void enablePeriodicKeyserverSync() { + // TODO: Increase periodic update time after testing + Log.e("PHILIP", "enabled periodic keyserversybc"); + ContentResolver.addPeriodicSync( + new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE), + Constants.PROVIDER_AUTHORITY, + new Bundle(), + 2*60 + ); } private void setFragment(Fragment fragment, boolean addToBackStack) { -- cgit v1.2.3 From 65a993446c8905ca5e77fefe4b7f1c4d0aee74d8 Mon Sep 17 00:00:00 2001 From: Adithya Abraham Philip Date: Mon, 10 Aug 2015 06:17:29 +0530 Subject: reworked keyserversyncadapterservice flow --- .../keychain/KeychainApplication.java | 20 +- .../keychain/operations/ImportOperation.java | 10 +- .../service/KeyserverSyncAdapterService.java | 368 +++++++++++++++------ .../keychain/ui/KeyListFragment.java | 33 +- .../keychain/ui/MainActivity.java | 19 -- .../keychain/ui/OrbotRequiredDialogActivity.java | 60 +++- .../keychain/ui/base/BaseActivity.java | 2 + .../keychain/util/orbot/OrbotHelper.java | 36 +- 8 files changed, 385 insertions(+), 163 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java index cd24394d7..1d6de260d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java @@ -28,6 +28,7 @@ import android.graphics.Bitmap; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.os.Build; +import android.os.Bundle; import android.os.Environment; import android.provider.ContactsContract; import android.widget.Toast; @@ -44,6 +45,7 @@ import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.TlsHelper; import java.security.Security; +import java.util.Arrays; import java.util.HashMap; @@ -136,17 +138,31 @@ public class KeychainApplication extends Application { } /** - * Add OpenKeychain account to Android to link contacts with keys + * Add OpenKeychain account to Android to link contacts with keys and keyserver sync */ public static void setupAccountAsNeeded(Context context) { try { AccountManager manager = AccountManager.get(context); Account[] accounts = manager.getAccountsByType(Constants.ACCOUNT_TYPE); - if (accounts == null || accounts.length == 0) { + + if (accounts.length == 0) { Account account = new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE); if (manager.addAccountExplicitly(account, null, null)) { + // for contact sync ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1); ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true); + // for keyserver sync + ContentResolver.setIsSyncable(account, Constants.PROVIDER_AUTHORITY, 1); + ContentResolver.setSyncAutomatically(account, Constants.PROVIDER_AUTHORITY, + true); + // TODO: Increase periodic update time after testing + Log.e("PHILIP", "enabled periodic keyserversync"); + ContentResolver.addPeriodicSync( + new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE), + Constants.PROVIDER_AUTHORITY, + new Bundle(), + 60 + ); } else { Log.e(Constants.TAG, "Adding account failed!"); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java index 9513f58ee..bde161db7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java @@ -503,7 +503,7 @@ public class ImportOperation extends BaseOperation { /** * Used to accumulate the results of individual key imports */ - private class KeyImportAccumulator { + public static class KeyImportAccumulator { private OperationResult.OperationLog mImportLog = new OperationResult.OperationLog(); Progressable mProgressable; private int mTotalKeys; @@ -531,14 +531,6 @@ public class ImportOperation extends BaseOperation { } } - public int getTotalKeys() { - return mTotalKeys; - } - - public int getImportedKeys() { - return mImportedKeys; - } - public synchronized void accumulateKeyImport(ImportKeyResult result) { mImportedKeys++; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java index a08797e6d..11d543f49 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java @@ -1,12 +1,14 @@ package org.sufficientlysecure.keychain.service; import android.accounts.Account; +import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.AbstractThreadedSyncAdapter; import android.content.ContentProviderClient; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.SyncResult; @@ -18,6 +20,10 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.PowerManager; +import android.os.SystemClock; import android.support.v4.app.NotificationCompat; import org.sufficientlysecure.keychain.Constants; @@ -25,6 +31,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; import org.sufficientlysecure.keychain.operations.ImportOperation; import org.sufficientlysecure.keychain.operations.results.ImportKeyResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; @@ -33,62 +40,100 @@ import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.Preferences; +import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; import java.util.ArrayList; import java.util.GregorianCalendar; +import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public class KeyserverSyncAdapterService extends Service { private static final String ACTION_IGNORE_TOR = "ignore_tor"; + private static final String ACTION_UPDATE_ALL = "update_all"; + private static final String ACTION_SYNC_NOW = "sync_now"; private static final String ACTION_DISMISS_NOTIFICATION = "cancel_sync"; private static final String ACTION_START_ORBOT = "start_orbot"; + private static final String ACTION_CANCEL = "cancel"; + + private AtomicBoolean mCancelled = new AtomicBoolean(false); @Override - public int onStartCommand(Intent intent, int flags, final int startId) { - Log.e("PHILIP", "Sync adapter service starting"); + public int onStartCommand(final Intent intent, int flags, final int startId) { + Log.e("PHILIP", "Sync adapter service starting" + intent.getAction()); + NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); switch (intent.getAction()) { + case ACTION_CANCEL: { + mCancelled.set(true); + break; + } + // the reason for the separation betweyeen SYNC_NOW and UPDATE_ALL is so that starting + // the sync directly from the notification is possible while the screen is on with + // UPDATE_ALL, but a postponed sync is only started if screen is off + case ACTION_SYNC_NOW: { + // this checks for screen on/off before sync, and postpones the sync if on + ContentResolver.requestSync( + new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE), + Constants.PROVIDER_AUTHORITY, + new Bundle() + ); + break; + } + case ACTION_UPDATE_ALL: { + // does not check for screen on/off + asyncKeyUpdate(this, new CryptoInputParcel()); + break; + } case ACTION_IGNORE_TOR: { - updateKeysFromKeyserver(this, - new CryptoInputParcel(ParcelableProxy.getForNoProxy())); - manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); - stopSelf(startId); + asyncKeyUpdate(this, new CryptoInputParcel(ParcelableProxy.getForNoProxy())); break; } case ACTION_START_ORBOT: { Intent startOrbot = new Intent(this, OrbotRequiredDialogActivity.class); startOrbot.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startOrbot.setAction(OrbotRequiredDialogActivity.ACTION_START_ORBOT); - startActivity(startOrbot); - new Handler().postDelayed( - new Runnable() { + startOrbot.putExtra(OrbotRequiredDialogActivity.EXTRA_START_ORBOT, true); + Messenger messenger = new Messenger( + new Handler() { @Override - public void run() { - updateKeysFromKeyserver(KeyserverSyncAdapterService.this, - new CryptoInputParcel()); - stopSelf(startId); + public void handleMessage(Message msg) { + switch (msg.what) { + case OrbotRequiredDialogActivity.MESSAGE_ORBOT_STARTED: { + Log.e("PHILIP", "orbot activity returned"); + asyncKeyUpdate(KeyserverSyncAdapterService.this, + new CryptoInputParcel()); + break; + } + case OrbotRequiredDialogActivity.MESSAGE_ORBOT_IGNORE: { + asyncKeyUpdate(KeyserverSyncAdapterService.this, + new CryptoInputParcel( + ParcelableProxy.getForNoProxy())); + break; + } + case OrbotRequiredDialogActivity.MESSAGE_DIALOG_CANCEL: { + // just stop service + stopSelf(); + break; + } + } } - }, - 30000 // shouldn't take longer for orbot to start + } ); - - manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); + startOrbot.putExtra(OrbotRequiredDialogActivity.EXTRA_MESSENGER, messenger); + startActivity(startOrbot); break; } case ACTION_DISMISS_NOTIFICATION: { - manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); + // notification is dismissed at the beginning stopSelf(startId); break; } } - // TODO: correct flag? - return START_REDELIVER_INTENT; + return START_NOT_STICKY; } - private static AtomicBoolean sCancelled = new AtomicBoolean(false); - private class KeyserverSyncAdapter extends AbstractThreadedSyncAdapter { public KeyserverSyncAdapter() { @@ -100,7 +145,19 @@ public class KeyserverSyncAdapterService extends Service { ContentProviderClient provider, SyncResult syncResult) { Log.d(Constants.TAG, "Performing a keyserver sync!"); - updateKeysFromKeyserver(KeyserverSyncAdapterService.this, new CryptoInputParcel()); + PowerManager pm = (PowerManager) KeyserverSyncAdapterService.this + .getSystemService(Context.POWER_SERVICE); + @SuppressWarnings("deprecation") // our min is API 15, deprecated only in 20 + boolean isScreenOn = pm.isScreenOn(); + + if (!isScreenOn) { + Intent serviceIntent = new Intent(KeyserverSyncAdapterService.this, + KeyserverSyncAdapterService.class); + serviceIntent.setAction(ACTION_UPDATE_ALL); + startService(serviceIntent); + } else { + postponeSync(); + } } } @@ -109,23 +166,178 @@ public class KeyserverSyncAdapterService extends Service { return new KeyserverSyncAdapter().getSyncAdapterBinder(); } - public static void updateKeysFromKeyserver(Context context, - CryptoInputParcel cryptoInputParcel) { - /** - * 1. Get keys which have been updated recently and therefore do not need to - * be updated now - * 2. Get list of all keys and filter out ones that don't need to be updated - * 3. Update the remaining keys. - * At any time, if the operation is to be cancelled, the sCancelled AtomicBoolean may be set - */ + private void handleUpdateResult(ImportKeyResult result) { + if (result.isPending()) { + Log.e(Constants.TAG, "Keyserver sync pending result: " + + result.getRequiredInputParcel().mType); + // result is pending due to Orbot not being started + // try to start it silently, if disabled show notificationaa + new OrbotHelper.SilentStartManager() { + @Override + protected void onOrbotStarted() { + // retry the update + asyncKeyUpdate(KeyserverSyncAdapterService.this, + new CryptoInputParcel()); + } + + @Override + protected void onSilentStartDisabled() { + // show notification + NotificationManager manager = + (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + manager.notify(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT, + getOrbotNoification(KeyserverSyncAdapterService.this)); + } + }.startOrbotAndListen(this, false); + } else if (isUpdateCancelled()) { + Log.d(Constants.TAG, "Keyserver sync cancelled"); + postponeSync(); + } else { + Log.d(Constants.TAG, "Keyserver sync completed: Updated: " + result.mUpdatedKeys + + " Failed: " + result.mBadKeys); + stopSelf(); + } + } + + private void postponeSync() { + AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + Intent serviceIntent = new Intent(this, KeyserverSyncAdapterService.class); + serviceIntent.setAction(ACTION_SYNC_NOW); + PendingIntent pi = PendingIntent.getService(this, 0, serviceIntent, + PendingIntent.FLAG_UPDATE_CURRENT); + alarmManager.set( + AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + 30 * 1000, + pi + ); + } + + private void asyncKeyUpdate(final Context context, + final CryptoInputParcel cryptoInputParcel) { + Log.e("PHILIP", "async key update starting"); + new Thread(new Runnable() { + @Override + public void run() { + ImportKeyResult result = updateKeysFromKeyserver(context, cryptoInputParcel); + handleUpdateResult(result); + } + }).start(); + } + + private synchronized ImportKeyResult updateKeysFromKeyserver(final Context context, + final CryptoInputParcel cryptoInputParcel) { + mCancelled.set(false); + + ArrayList keyList = getKeysToUpdate(context); + + if (isUpdateCancelled()) { // if we've already been cancelled + return new ImportKeyResult(OperationResult.RESULT_CANCELLED, + new OperationResult.OperationLog()); + } + + if (cryptoInputParcel.getParcelableProxy() == null) { + // no explicit proxy, retrieve from preferences. Check if we should do a staggered sync + if (Preferences.getPreferences(context).getProxyPrefs().torEnabled) { + return staggeredUpdate(context, keyList, cryptoInputParcel); + } else { + return directUpdate(context, keyList, cryptoInputParcel); + } + } else { + return directUpdate(context, keyList, cryptoInputParcel); + } + } + + private ImportKeyResult directUpdate(Context context, ArrayList keyList, + CryptoInputParcel cryptoInputParcel) { + Log.d(Constants.TAG, "Starting normal update"); + ImportOperation importOp = new ImportOperation(context, new ProviderHelper(context), null); + return importOp.execute( + new ImportKeyringParcel(keyList, + Preferences.getPreferences(context).getPreferredKeyserver()), + cryptoInputParcel + ); + } + + + /** + * will perform a staggered update of user's keys using delays to ensure new Tor circuits, as + * performed by parcimonie. Relevant issue and method at: + * https://github.com/open-keychain/open-keychain/issues/1337 + * + * @return result of the sync + */ + private ImportKeyResult staggeredUpdate(Context context, ArrayList keyList, + CryptoInputParcel cryptoInputParcel) { + Log.d(Constants.TAG, "Starting staggered update"); + // assuming maxCircuitDirtiness is 10min in Tor + // final int MAX_CIRCUIT_DIRTINESS = (int) TimeUnit.MINUTES.toSeconds(10); + // TODO: PHILIP remove after testing + final int MAX_CIRCUIT_DIRTINESS = (int) TimeUnit.MINUTES.toSeconds(1); + // final int WEEK_IN_SECONDS = (int) TimeUnit.DAYS.toSeconds(7); + final int WEEK_IN_SECONDS = 0; + ImportOperation.KeyImportAccumulator accumulator + = new ImportOperation.KeyImportAccumulator(keyList.size(), null); + for (ParcelableKeyRing keyRing : keyList) { + int waitTime; + int staggeredTime = new Random().nextInt(1 + 2 * (WEEK_IN_SECONDS / keyList.size())); + if (staggeredTime >= MAX_CIRCUIT_DIRTINESS) { + waitTime = staggeredTime; + } else { + waitTime = MAX_CIRCUIT_DIRTINESS + new Random().nextInt(MAX_CIRCUIT_DIRTINESS); + } + Log.d(Constants.TAG, "Updating key with fingerprint " + keyRing.mExpectedFingerprint + + " with a wait time of " + waitTime + "s"); + try { + Thread.sleep(waitTime * 1000); + } catch (InterruptedException e) { + Log.e(Constants.TAG, "Exception during sleep between key updates", e); + // skip this one + continue; + } + ArrayList keyWrapper = new ArrayList<>(); + keyWrapper.add(keyRing); + if (isUpdateCancelled()) { + return new ImportKeyResult(ImportKeyResult.RESULT_CANCELLED, + new OperationResult.OperationLog()); + } + ImportKeyResult result = + new ImportOperation(context, new ProviderHelper(context), null, mCancelled) + .execute( + new ImportKeyringParcel( + keyWrapper, + Preferences.getPreferences(context) + .getPreferredKeyserver() + ), + cryptoInputParcel + ); + if (result.isPending()) { + return result; + } + accumulator.accumulateKeyImport(result); + } + return accumulator.getConsolidatedResult(); + } + + /** + * 1. Get keys which have been updated recently and therefore do not need to + * be updated now + * 2. Get list of all keys and filter out ones that don't need to be updated + * 3. Return keys to be updated + * + * @return list of keys that require update + */ + private ArrayList getKeysToUpdate(Context context) { // 1. Get keys which have been updated recently and don't need to updated now final int INDEX_UPDATED_KEYS_MASTER_KEY_ID = 0; final int INDEX_LAST_UPDATED = 1; - final long TIME_MAX = TimeUnit.DAYS.toSeconds(7); - final long CURRENT_TIME = GregorianCalendar.getInstance().get(GregorianCalendar.SECOND); - Log.e("PHILIP", "week: " + TIME_MAX); + // all time in seconds not milliseconds + // TODO: PHILIP correct TIME_MAX after testing + // final long TIME_MAX = TimeUnit.DAYS.toSeconds(7); + final long TIME_MAX = 1; + final long CURRENT_TIME = GregorianCalendar.getInstance().getTimeInMillis() / 1000; + Log.e("PHILIP", "week: " + TIME_MAX + " current: " + CURRENT_TIME); Cursor updatedKeysCursor = context.getContentResolver().query( KeychainContract.UpdatedKeys.CONTENT_URI, new String[]{ @@ -161,7 +373,7 @@ public class KeyserverSyncAdapterService extends Service { ); if (keyCursor == null) { - return; + return new ArrayList<>(); } ArrayList keyList = new ArrayList<>(); @@ -179,49 +391,39 @@ public class KeyserverSyncAdapterService extends Service { } keyCursor.close(); - if (sCancelled.get()) { - // if we've already been cancelled - return; - } - - // 3. Actually update the keys - Log.e("PHILIP", keyList.toString()); - ImportOperation importOp = new ImportOperation(context, new ProviderHelper(context), null); - ImportKeyResult result = importOp.execute( - new ImportKeyringParcel(keyList, - Preferences.getPreferences(context).getPreferredKeyserver()), - cryptoInputParcel - ); - if (result.isPending()) { - NotificationManager manager = - (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE); - manager.notify(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT, - getOrbotNoification(context)); + return keyList; + } - Log.d(Constants.TAG, "Keyserver sync failed due to pending result " + - result.getRequiredInputParcel().mType); - } else { - Log.d(Constants.TAG, "Background keyserver sync completed: new " + result.mNewKeys - + " updated " + result.mUpdatedKeys + " bad " + result.mBadKeys); - } + private boolean isUpdateCancelled() { + return mCancelled.get(); } - public static void preventAndCancelUpdates() { - // TODO: PHILIP uncomment! - // sCancelled.set(true); + /** + * will cancel an update already in progress + * + * @param context used to send an Intent to the service requesting cancellation. + */ + public static void cancelUpdates(Context context) { + Intent intent = new Intent(context, KeyserverSyncAdapterService.class); + intent.setAction(ACTION_CANCEL); + context.startService(intent); } - public static void allowUpdates() { - sCancelled.set(false); + // TODO: PHILIP remove! + @Override + public void onDestroy() { + Log.e("PHILIP", "onDestroy"); + super.onDestroy(); } - private static Notification getOrbotNoification(Context context) { + private Notification getOrbotNoification(Context context) { // TODO: PHILIP work in progress NotificationCompat.Builder builder = new NotificationCompat.Builder(context); builder.setSmallIcon(R.drawable.ic_stat_notify_24dp) .setLargeIcon(getBitmap(R.drawable.ic_launcher, context)) .setContentTitle(context.getString(R.string.keyserver_sync_orbot_notif_title)) - .setContentText(context.getString(R.string.keyserver_sync_orbot_notif_msg)); + .setContentText(context.getString(R.string.keyserver_sync_orbot_notif_msg)) + .setAutoCancel(true); // In case the user decides to not use tor Intent ignoreTorIntent = new Intent(context, KeyserverSyncAdapterService.class); @@ -237,21 +439,6 @@ public class KeyserverSyncAdapterService extends Service { context.getString(R.string.keyserver_sync_orbot_notif_ignore), ignoreTorPi); - // not enough space to show it - Intent dismissIntent = new Intent(context, KeyserverSyncAdapterService.class); - dismissIntent.setAction(ACTION_DISMISS_NOTIFICATION); - PendingIntent dismissPi = PendingIntent.getService( - context, - 0, // security not issue since we're giving this pending intent to Notification Manager - dismissIntent, - PendingIntent.FLAG_CANCEL_CURRENT - ); - - /*builder.addAction(R.drawable.abc_ic_clear_mtrl_alpha, - context.getString(android.R.string.cancel), - dismissPi - );*/ - Intent startOrbotIntent = new Intent(context, KeyserverSyncAdapterService.class); startOrbotIntent.setAction(ACTION_START_ORBOT); PendingIntent startOrbotPi = PendingIntent.getService( @@ -261,7 +448,7 @@ public class KeyserverSyncAdapterService extends Service { PendingIntent.FLAG_CANCEL_CURRENT ); - builder.addAction(R.drawable.abc_ic_clear_mtrl_alpha, + builder.addAction(R.drawable.abc_ic_commit_search_api_mtrl_alpha, context.getString(R.string.keyserver_sync_orbot_notif_start), startOrbotPi ); @@ -270,19 +457,8 @@ public class KeyserverSyncAdapterService extends Service { return builder.build(); } - /** - * will perform a staggered update of user's keys using delays to ensure new Tor circuits, as - * performed by parcimonie - * - * @return result of the sync - */ - private static ImportKeyResult staggeredSync() { - // TODO: WIP - return null; - } - // from de.azapps.mirakel.helper.Helpers from https://github.com/MirakelX/mirakel-android - private static Bitmap getBitmap(int resId, Context context) { + private Bitmap getBitmap(int resId, Context context) { int mLargeIconWidth = (int) context.getResources().getDimension( android.R.dimen.notification_large_icon_width); int mLargeIconHeight = (int) context.getResources().getDimension( 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 4997d3fab..9630463fc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -18,13 +18,9 @@ package org.sufficientlysecure.keychain.ui; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; - import android.animation.ObjectAnimator; import android.annotation.TargetApi; +import android.app.Activity; import android.content.Context; import android.content.Intent; import android.database.Cursor; @@ -64,13 +60,14 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainDatabase; +import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.ConsolidateInputParcel; import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService; -import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.util.FabContainer; import org.sufficientlysecure.keychain.util.Log; @@ -78,6 +75,10 @@ import org.sufficientlysecure.keychain.util.Preferences; import se.emilsjolander.stickylistheaders.StickyListHeadersAdapter; import se.emilsjolander.stickylistheaders.StickyListHeadersListView; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; + /** * Public key list with sticky list headers. It does _not_ extend ListFragment because it uses * StickyListHeaders library which does not extend upon ListView. @@ -520,9 +521,7 @@ public class KeyListFragment extends LoaderFragment } private void updateAllKeys() { - // TODO: PHILIP just for testing, remove! - KeyserverSyncAdapterService.updateKeysFromKeyserver(getActivity(), new CryptoInputParcel()); - /*Activity activity = getActivity(); + Activity activity = getActivity(); if (activity == null) { return; } @@ -536,7 +535,7 @@ public class KeyListFragment extends LoaderFragment ); if (cursor == null) { - Notify.create(activity, R.string.error_loading_keys, Style.ERROR); + Notify.create(activity, R.string.error_loading_keys, Notify.Style.ERROR); return; } @@ -563,7 +562,7 @@ public class KeyListFragment extends LoaderFragment mImportOpHelper = new CryptoOperationHelper<>(1, this, this, R.string.progress_updating); - mImportOpHelper.cryptoOperation();*/ + mImportOpHelper.cryptoOperation(); } private void consolidate() { @@ -647,18 +646,6 @@ public class KeyListFragment extends LoaderFragment } } - @Override - public void onResume() { - super.onResume(); - KeyserverSyncAdapterService.preventAndCancelUpdates(); - } - - @Override - public void onPause() { - super.onPause(); - KeyserverSyncAdapterService.allowUpdates(); - } - @Override public void fabMoveUp(int height) { ObjectAnimator anim = ObjectAnimator.ofFloat(mFab, "translationY", 0, -height); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java index 0fa6566f8..6f5d98afd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/MainActivity.java @@ -19,9 +19,6 @@ package org.sufficientlysecure.keychain.ui; -import android.accounts.Account; -import android.accounts.AccountManager; -import android.content.ContentResolver; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -40,14 +37,11 @@ import com.mikepenz.materialdrawer.DrawerBuilder; import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; import com.mikepenz.materialdrawer.model.interfaces.IDrawerItem; -import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.remote.ui.AppsListFragment; -import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService; import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity; import org.sufficientlysecure.keychain.util.FabContainer; -import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Preferences; public class MainActivity extends BaseNfcActivity implements FabContainer, OnBackStackChangedListener { @@ -170,19 +164,6 @@ public class MainActivity extends BaseNfcActivity implements FabContainer, OnBac } } - enablePeriodicKeyserverSync(); - - } - - private void enablePeriodicKeyserverSync() { - // TODO: Increase periodic update time after testing - Log.e("PHILIP", "enabled periodic keyserversybc"); - ContentResolver.addPeriodicSync( - new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE), - Constants.PROVIDER_AUTHORITY, - new Bundle(), - 2*60 - ); } private void setFragment(Fragment fragment, boolean addToBackStack) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/OrbotRequiredDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/OrbotRequiredDialogActivity.java index d1c247a76..d12b3fc22 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/OrbotRequiredDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/OrbotRequiredDialogActivity.java @@ -18,12 +18,20 @@ package org.sufficientlysecure.keychain.ui; +import android.app.ProgressDialog; import android.content.Intent; import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; import android.support.v4.app.FragmentActivity; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; @@ -34,8 +42,14 @@ import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; public class OrbotRequiredDialogActivity extends FragmentActivity implements OrbotHelper.DialogActions { + public static final int MESSAGE_ORBOT_STARTED = 1; + public static final int MESSAGE_ORBOT_IGNORE = 2; + public static final int MESSAGE_DIALOG_CANCEL = 3; + // if suppplied and true will start Orbot directly without showing dialog public static final String EXTRA_START_ORBOT = "start_orbot"; + // used for communicating results when triggered from a service + public static final String EXTRA_MESSENGER = "messenger"; // to provide any previous crypto input into which proxy preference is merged public static final String EXTRA_CRYPTO_INPUT = "extra_crypto_input"; @@ -43,6 +57,9 @@ public class OrbotRequiredDialogActivity extends FragmentActivity public static final String RESULT_CRYPTO_INPUT = "result_crypto_input"; private CryptoInputParcel mCryptoInputParcel; + private Messenger mMessenger; + + private ProgressDialog mShowOrbotProgressDialog; @Override protected void onCreate(Bundle savedInstanceState) { @@ -54,9 +71,15 @@ public class OrbotRequiredDialogActivity extends FragmentActivity mCryptoInputParcel = new CryptoInputParcel(); } + mMessenger = getIntent().getParcelableExtra(EXTRA_MESSENGER); + boolean startOrbotDirect = getIntent().getBooleanExtra(EXTRA_START_ORBOT, false); if (startOrbotDirect) { - OrbotHelper.bestPossibleOrbotStart(this, this); + mShowOrbotProgressDialog = new ProgressDialog(this); + mShowOrbotProgressDialog.setTitle(R.string.progress_starting_orbot); + mShowOrbotProgressDialog.setCancelable(false); + mShowOrbotProgressDialog.show(); + OrbotHelper.bestPossibleOrbotStart(this, this, false); } else { showDialog(); } @@ -84,13 +107,32 @@ public class OrbotRequiredDialogActivity extends FragmentActivity super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case OrbotHelper.START_TOR_RESULT: { - onOrbotStarted(); // assumption that orbot was started, no way to tell for sure + dismissOrbotProgressDialog(); + // unfortunately, this result is returned immediately and not when Orbot is started + // 10s is approximately the longest time Orbot has taken to start + new Handler().postDelayed(new Runnable() { + @Override + public void run() { + onOrbotStarted(); // assumption that orbot was started + } + }, 10000); } } } + /** + * for when Orbot is started without showing the dialog by the EXTRA_START_ORBOT intent extra + */ + private void dismissOrbotProgressDialog() { + if (mShowOrbotProgressDialog != null) { + mShowOrbotProgressDialog.dismiss(); + } + } + @Override public void onOrbotStarted() { + dismissOrbotProgressDialog(); + sendMessage(MESSAGE_ORBOT_STARTED); Intent intent = new Intent(); // send back unmodified CryptoInputParcel for a retry intent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel); @@ -100,6 +142,7 @@ public class OrbotRequiredDialogActivity extends FragmentActivity @Override public void onNeutralButton() { + sendMessage(MESSAGE_ORBOT_IGNORE); Intent intent = new Intent(); mCryptoInputParcel.addParcelableProxy(ParcelableProxy.getForNoProxy()); intent.putExtra(RESULT_CRYPTO_INPUT, mCryptoInputParcel); @@ -109,6 +152,19 @@ public class OrbotRequiredDialogActivity extends FragmentActivity @Override public void onCancel() { + sendMessage(MESSAGE_DIALOG_CANCEL); finish(); } + + private void sendMessage(int what) { + if (mMessenger != null) { + Message msg = Message.obtain(); + msg.what = what; + try { + mMessenger.send(msg); + } catch (RemoteException e) { + Log.e(Constants.TAG, "Could not deliver message", e); + } + } + } } \ No newline at end of file diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java index fcf5dc11e..aa4e7d840 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/BaseActivity.java @@ -30,6 +30,7 @@ import android.view.ViewGroup; import android.widget.TextView; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService; import org.sufficientlysecure.keychain.ui.util.ThemeChanger; /** @@ -51,6 +52,7 @@ public abstract class BaseActivity extends AppCompatActivity { @Override protected void onResume() { super.onResume(); + KeyserverSyncAdapterService.cancelUpdates(this); if (mThemeChanger.changeTheme()) { Intent intent = getIntent(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java index d5ee75f9b..6305d2033 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java @@ -361,7 +361,7 @@ public class OrbotHelper { .show(fragmentActivity.getSupportFragmentManager(), "OrbotHelperOrbotStartDialog"); } - }.startOrbotAndListen(fragmentActivity); + }.startOrbotAndListen(fragmentActivity, true); return false; } else { @@ -385,7 +385,8 @@ public class OrbotHelper { * activities wishing to respond to a change in Orbot state. */ public static void bestPossibleOrbotStart(final DialogActions dialogActions, - final Activity activity) { + final Activity activity, + boolean showProgress) { new SilentStartManager() { @Override @@ -397,23 +398,23 @@ public class OrbotHelper { protected void onSilentStartDisabled() { requestShowOrbotStart(activity); } - }.startOrbotAndListen(activity); + }.startOrbotAndListen(activity, showProgress); } /** * base class for listening to silent orbot starts. Also handles display of progress dialog. */ - private static abstract class SilentStartManager { + public static abstract class SilentStartManager { private ProgressDialog mProgressDialog; - public void startOrbotAndListen(Context context) { - mProgressDialog = new ProgressDialog(ThemeChanger.getDialogThemeWrapper(context)); - mProgressDialog.setMessage(context.getString(R.string.progress_starting_orbot)); - mProgressDialog.setCancelable(false); - mProgressDialog.show(); + public void startOrbotAndListen(final Context context, final boolean showProgress) { + Log.e("PHILIP", "starting orbot listener"); + if (showProgress) { + showProgressDialog(context); + } - BroadcastReceiver receiver = new BroadcastReceiver() { + final BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { switch (intent.getStringExtra(OrbotHelper.EXTRA_STATUS)) { @@ -423,14 +424,18 @@ public class OrbotHelper { new Handler().postDelayed(new Runnable() { @Override public void run() { - mProgressDialog.dismiss(); + if (showProgress) { + mProgressDialog.dismiss(); + } onOrbotStarted(); } }, 1000); break; case OrbotHelper.STATUS_STARTS_DISABLED: context.unregisterReceiver(this); - mProgressDialog.dismiss(); + if (showProgress) { + mProgressDialog.dismiss(); + } onSilentStartDisabled(); break; @@ -444,6 +449,13 @@ public class OrbotHelper { requestStartTor(context); } + private void showProgressDialog(Context context) { + mProgressDialog = new ProgressDialog(ThemeChanger.getDialogThemeWrapper(context)); + mProgressDialog.setMessage(context.getString(R.string.progress_starting_orbot)); + mProgressDialog.setCancelable(false); + mProgressDialog.show(); + } + protected abstract void onOrbotStarted(); protected abstract void onSilentStartDisabled(); -- cgit v1.2.3 From 93d66d39f7b985b33538a9f5531e47571693b901 Mon Sep 17 00:00:00 2001 From: Adithya Abraham Philip Date: Sun, 16 Aug 2015 19:47:38 +0530 Subject: changed orbot notification icons --- .../keychain/service/KeyserverSyncAdapterService.java | 14 ++++++++------ .../keychain/ui/OrbotRequiredDialogActivity.java | 5 ++++- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java index 11d543f49..c50f6a6e9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java @@ -63,8 +63,6 @@ public class KeyserverSyncAdapterService extends Service { public int onStartCommand(final Intent intent, int flags, final int startId) { Log.e("PHILIP", "Sync adapter service starting" + intent.getAction()); - NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); - manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); switch (intent.getAction()) { case ACTION_CANCEL: { mCancelled.set(true); @@ -88,10 +86,14 @@ public class KeyserverSyncAdapterService extends Service { break; } case ACTION_IGNORE_TOR: { + NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); asyncKeyUpdate(this, new CryptoInputParcel(ParcelableProxy.getForNoProxy())); break; } case ACTION_START_ORBOT: { + NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); Intent startOrbot = new Intent(this, OrbotRequiredDialogActivity.class); startOrbot.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startOrbot.putExtra(OrbotRequiredDialogActivity.EXTRA_START_ORBOT, true); @@ -126,7 +128,8 @@ public class KeyserverSyncAdapterService extends Service { break; } case ACTION_DISMISS_NOTIFICATION: { - // notification is dismissed at the beginning + NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + manager.cancel(Constants.Notification.KEYSERVER_SYNC_FAIL_ORBOT); stopSelf(startId); break; } @@ -417,7 +420,6 @@ public class KeyserverSyncAdapterService extends Service { } private Notification getOrbotNoification(Context context) { - // TODO: PHILIP work in progress NotificationCompat.Builder builder = new NotificationCompat.Builder(context); builder.setSmallIcon(R.drawable.ic_stat_notify_24dp) .setLargeIcon(getBitmap(R.drawable.ic_launcher, context)) @@ -435,7 +437,7 @@ public class KeyserverSyncAdapterService extends Service { PendingIntent.FLAG_CANCEL_CURRENT ); - builder.addAction(R.drawable.abc_ic_clear_mtrl_alpha, + builder.addAction(R.drawable.ic_stat_tor_off, context.getString(R.string.keyserver_sync_orbot_notif_ignore), ignoreTorPi); @@ -448,7 +450,7 @@ public class KeyserverSyncAdapterService extends Service { PendingIntent.FLAG_CANCEL_CURRENT ); - builder.addAction(R.drawable.abc_ic_commit_search_api_mtrl_alpha, + builder.addAction(R.drawable.ic_stat_tor, context.getString(R.string.keyserver_sync_orbot_notif_start), startOrbotPi ); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/OrbotRequiredDialogActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/OrbotRequiredDialogActivity.java index d12b3fc22..0e70cda14 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/OrbotRequiredDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/OrbotRequiredDialogActivity.java @@ -26,11 +26,13 @@ import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import android.support.v4.app.FragmentActivity; +import android.view.ContextThemeWrapper; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.ui.util.ThemeChanger; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.ParcelableProxy; import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; @@ -75,7 +77,8 @@ public class OrbotRequiredDialogActivity extends FragmentActivity boolean startOrbotDirect = getIntent().getBooleanExtra(EXTRA_START_ORBOT, false); if (startOrbotDirect) { - mShowOrbotProgressDialog = new ProgressDialog(this); + ContextThemeWrapper theme = ThemeChanger.getDialogThemeWrapper(this); + mShowOrbotProgressDialog = new ProgressDialog(theme); mShowOrbotProgressDialog.setTitle(R.string.progress_starting_orbot); mShowOrbotProgressDialog.setCancelable(false); mShowOrbotProgressDialog.show(); -- cgit v1.2.3 From 3f8f70b0a99554aae52fd80d31c29f32a34df5d2 Mon Sep 17 00:00:00 2001 From: Adithya Abraham Philip Date: Mon, 17 Aug 2015 03:08:42 +0530 Subject: added sync preferences --- .../org/sufficientlysecure/keychain/Constants.java | 3 + .../service/KeyserverSyncAdapterService.java | 10 +++- .../keychain/ui/SettingsActivity.java | 64 +++++++++++++++++++++- 3 files changed, 74 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index 03d39f6be..be38c6dba 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -104,6 +104,9 @@ public final class Constants { public static final String PROXY_PORT = "proxyPort"; public static final String PROXY_TYPE = "proxyType"; public static final String THEME = "theme"; + // keyserver sync settings + public static final String SYNC_CONTACTS = "syncContacts"; + public static final String SYNC_KEYSERVER = "syncKeyserver"; public static final class Theme { public static final String LIGHT = "light"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java index c50f6a6e9..baf649701 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java @@ -162,6 +162,12 @@ public class KeyserverSyncAdapterService extends Service { postponeSync(); } } + + @Override + public void onSyncCanceled() { + super.onSyncCanceled(); + cancelUpdates(KeyserverSyncAdapterService.this); + } } @Override @@ -402,7 +408,9 @@ public class KeyserverSyncAdapterService extends Service { } /** - * will cancel an update already in progress + * will cancel an update already in progress. We send an Intent to cancel it instead of simply + * modifying a static variable sync the service is running in a process that is different from + * the default application process where the UI code runs. * * @param context used to send an Intent to the service requesting cancellation. */ diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index c18156428..0ae0c62a5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -18,11 +18,12 @@ package org.sufficientlysecure.keychain.ui; -import android.annotation.TargetApi; +import android.accounts.Account; +import android.accounts.AccountManager; import android.app.Activity; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.os.Build; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.EditTextPreference; @@ -31,6 +32,7 @@ import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; +import android.provider.ContactsContract; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.View; @@ -467,11 +469,69 @@ public class SettingsActivity extends AppCompatPreferenceActivity { } } + /** + * This fragment shows the keyserver/contacts sync preferences + */ + public static class SyncSettingsFragment extends PreferenceFragment { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Load the preferences from an XML resource + addPreferencesFromResource(R.xml.sync_preferences); + } + + @Override + public void onResume() { + super.onResume(); + // this needs to be done in onResume since the user can change sync values from Android + // settings and we need to reflect that change when the user navigates back + AccountManager manager = AccountManager.get(getActivity()); + final Account account = manager.getAccountsByType(Constants.ACCOUNT_TYPE)[0]; + // for keyserver sync + initializeSyncCheckBox( + (CheckBoxPreference) findPreference(Constants.Pref.SYNC_KEYSERVER), + account, + Constants.PROVIDER_AUTHORITY + ); + // for contacts sync + initializeSyncCheckBox( + (CheckBoxPreference) findPreference(Constants.Pref.SYNC_CONTACTS), + account, + ContactsContract.AUTHORITY + ); + } + + private void initializeSyncCheckBox(CheckBoxPreference syncCheckBox, final Account account, + final String authority) { + boolean syncEnabled = ContentResolver.getSyncAutomatically(account, authority); + syncCheckBox.setChecked(syncEnabled); + + syncCheckBox.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + boolean syncEnabled = (Boolean) newValue; + if (syncEnabled) { + ContentResolver.setSyncAutomatically(account, authority, true); + } else { + // disable syncs + ContentResolver.setSyncAutomatically(account, authority, false); + // cancel any ongoing/pending syncs + ContentResolver.cancelSync(account, authority); + } + return true; + } + }); + } + } + protected boolean isValidFragment(String fragmentName) { return AdvancedPrefsFragment.class.getName().equals(fragmentName) || CloudSearchPrefsFragment.class.getName().equals(fragmentName) || ProxyPrefsFragment.class.getName().equals(fragmentName) || GuiPrefsFragment.class.getName().equals(fragmentName) + || SyncSettingsFragment.class.getName().equals(fragmentName) || super.isValidFragment(fragmentName); } -- cgit v1.2.3 From f625a26bbd9e4e1049480028b7738ff4e37fd712 Mon Sep 17 00:00:00 2001 From: Adithya Abraham Philip Date: Mon, 17 Aug 2015 03:39:24 +0530 Subject: added on/off summaries --- .../keychain/ui/SettingsActivity.java | 27 +++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index 0ae0c62a5..842059da5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -503,10 +503,12 @@ public class SettingsActivity extends AppCompatPreferenceActivity { ); } - private void initializeSyncCheckBox(CheckBoxPreference syncCheckBox, final Account account, + private void initializeSyncCheckBox(final CheckBoxPreference syncCheckBox, + final Account account, final String authority) { boolean syncEnabled = ContentResolver.getSyncAutomatically(account, authority); syncCheckBox.setChecked(syncEnabled); + setSummary(syncCheckBox, authority, syncEnabled); syncCheckBox.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @Override @@ -520,10 +522,33 @@ public class SettingsActivity extends AppCompatPreferenceActivity { // cancel any ongoing/pending syncs ContentResolver.cancelSync(account, authority); } + setSummary(syncCheckBox, authority, syncEnabled); return true; } }); } + + private void setSummary(CheckBoxPreference syncCheckBox, String authority, + boolean checked) { + switch (authority) { + case Constants.PROVIDER_AUTHORITY: { + if (checked) { + syncCheckBox.setSummary(R.string.label_sync_settings_keyserver_summary_on); + } else { + syncCheckBox.setSummary(R.string.label_sync_settings_keyserver_summary_off); + } + break; + } + case ContactsContract.AUTHORITY: { + if (checked) { + syncCheckBox.setSummary(R.string.label_sync_settings_contacts_summary_on); + } else { + syncCheckBox.setSummary(R.string.label_sync_settings_contacts_summary_off); + } + break; + } + } + } } protected boolean isValidFragment(String fragmentName) { -- cgit v1.2.3 From 0251f0e41695d3961dde553ba9ab76fa46583abb Mon Sep 17 00:00:00 2001 From: Adithya Abraham Philip Date: Wed, 19 Aug 2015 01:13:12 +0530 Subject: introduced constants for keyserver sync, fixed sync issue --- .../org/sufficientlysecure/keychain/Constants.java | 3 +- .../keychain/KeychainApplication.java | 20 +----- .../keychain/keyimport/HkpKeyserver.java | 2 +- .../keychain/operations/ImportOperation.java | 2 +- .../keychain/operations/RevokeOperation.java | 4 +- .../operations/results/OperationResult.java | 5 +- .../keychain/provider/ProviderHelper.java | 13 ++-- .../service/ContactSyncAdapterService.java | 2 +- .../service/KeyserverSyncAdapterService.java | 82 ++++++++++++++-------- .../keychain/util/Preferences.java | 9 ++- .../keychain/util/orbot/OrbotHelper.java | 2 +- 11 files changed, 77 insertions(+), 67 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index be38c6dba..a244972fb 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -29,6 +29,7 @@ public final class Constants { public static final boolean DEBUG = BuildConfig.DEBUG; public static final boolean DEBUG_LOG_DB_QUERIES = false; public static final boolean DEBUG_SYNC_REMOVE_CONTACTS = false; + public static final boolean DEBUG_KEYSERVER_SYNC = false; public static final String TAG = DEBUG ? "Keychain D" : "Keychain"; @@ -126,7 +127,7 @@ public final class Constants { public static final class Defaults { public static final String KEY_SERVERS = "hkps://hkps.pool.sks-keyservers.net, hkps://pgp.mit.edu"; - public static final int PREF_VERSION = 5; + public static final int PREF_VERSION = 6; } public static final class key { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java index 1d6de260d..311ef2d3b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/KeychainApplication.java @@ -23,12 +23,10 @@ import android.app.Application; 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; -import android.os.Bundle; import android.os.Environment; import android.provider.ContactsContract; import android.widget.Toast; @@ -36,7 +34,7 @@ import android.widget.Toast; import org.spongycastle.jce.provider.BouncyCastleProvider; import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider; -import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService; import org.sufficientlysecure.keychain.ui.ConsolidateDialogActivity; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.util.Log; @@ -45,7 +43,6 @@ import org.sufficientlysecure.keychain.util.Preferences; import org.sufficientlysecure.keychain.util.TlsHelper; import java.security.Security; -import java.util.Arrays; import java.util.HashMap; @@ -99,7 +96,7 @@ public class KeychainApplication extends Application { setupAccountAsNeeded(this); // Update keyserver list as needed - Preferences.getPreferences(this).upgradePreferences(); + Preferences.getPreferences(this).upgradePreferences(this); TlsHelper.addStaticCA("pool.sks-keyservers.net", getAssets(), "sks-keyservers.netCA.cer"); @@ -151,18 +148,7 @@ public class KeychainApplication extends Application { // for contact sync ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1); ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true); - // for keyserver sync - ContentResolver.setIsSyncable(account, Constants.PROVIDER_AUTHORITY, 1); - ContentResolver.setSyncAutomatically(account, Constants.PROVIDER_AUTHORITY, - true); - // TODO: Increase periodic update time after testing - Log.e("PHILIP", "enabled periodic keyserversync"); - ContentResolver.addPeriodicSync( - new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE), - Constants.PROVIDER_AUTHORITY, - new Bundle(), - 60 - ); + KeyserverSyncAdapterService.enableKeyserverSync(context); } else { Log.e(Constants.TAG, "Adding account failed!"); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java index 5aac05712..558b8ce7d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/keyimport/HkpKeyserver.java @@ -360,7 +360,7 @@ public class HkpKeyserver extends Keyserver { try { data = query(request, proxy); } catch (HttpError httpError) { - Log.e(Constants.TAG, "Failed to get key at HkpKeyserver", httpError); + Log.d(Constants.TAG, "Failed to get key at HkpKeyserver", httpError); throw new QueryFailedException("not found"); } Matcher matcher = PgpHelper.PGP_PUBLIC_KEY.matcher(data); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java index bde161db7..4b991ddda 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java @@ -232,7 +232,7 @@ public class ImportOperation extends BaseOperation { log.add(LogType.MSG_IMPORT_FETCH_ERROR_DECODE, 3); } } catch (Keyserver.QueryFailedException e) { - Log.e(Constants.TAG, "query failed", e); + Log.d(Constants.TAG, "query failed", e); log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER_ERROR, 3, e.getMessage()); } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java index 9aceb946c..975cf541a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/RevokeOperation.java @@ -93,13 +93,13 @@ public class RevokeOperation extends BaseOperation { log.add(OperationResult.LogType.MSG_REVOKE_OK, 1); return new RevokeResult(RevokeResult.RESULT_OK, log, masterKeyId); } else { - log.add(OperationResult.LogType.MSG_REVOKE_KEY_FAIL, 1); + log.add(OperationResult.LogType.MSG_REVOKE_ERROR_KEY_FAIL, 1); return new RevokeResult(RevokeResult.RESULT_ERROR, log, masterKeyId); } } catch (PgpKeyNotFoundException | ProviderHelper.NotFoundException e) { Log.e(Constants.TAG, "could not find key to revoke", e); - log.add(OperationResult.LogType.MSG_REVOKE_KEY_FAIL, 1); + log.add(OperationResult.LogType.MSG_REVOKE_ERROR_KEY_FAIL, 1); return new RevokeResult(RevokeResult.RESULT_ERROR, log, masterKeyId); } } 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 d498bd9a1..5ac1e89bb 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 @@ -763,10 +763,9 @@ public abstract class OperationResult implements Parcelable { MSG_DEL_FAIL (LogLevel.WARN, R.plurals.msg_del_fail), MSG_REVOKE_ERROR_EMPTY (LogLevel.ERROR, R.string.msg_revoke_error_empty), - MSG_REVOKE_ERROR_MULTI_SECRET (LogLevel.DEBUG, R.string.msg_revoke_error_multi_secret), - MSG_REVOKE_ERROR_NOT_FOUND (LogLevel.DEBUG, R.string.msg_revoke_error_multi_secret), + MSG_REVOKE_ERROR_NOT_FOUND (LogLevel.ERROR, R.string.msg_revoke_error_not_found), MSG_REVOKE (LogLevel.DEBUG, R.string.msg_revoke_key), - MSG_REVOKE_KEY_FAIL (LogLevel.ERROR, R.string.msg_revoke_key_fail), + MSG_REVOKE_ERROR_KEY_FAIL (LogLevel.ERROR, R.string.msg_revoke_key_fail), MSG_REVOKE_OK (LogLevel.OK, R.string.msg_revoke_ok), // keybase verification 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 e8eea6831..cbb71276c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -700,20 +700,14 @@ public class ProviderHelper { new String[]{"" + masterKeyId}, null ); - ContentValues lastUpdatedEntry = null; if (lastUpdatedCursor.moveToNext()) { - lastUpdatedEntry = new ContentValues(2); + // there was an entry to re-insert + // this operation must happen after the new key is inserted + ContentValues lastUpdatedEntry = new ContentValues(2); lastUpdatedEntry.put(UpdatedKeys.MASTER_KEY_ID, lastUpdatedCursor.getLong(INDEX_MASTER_KEY_ID)); lastUpdatedEntry.put(UpdatedKeys.LAST_UPDATED, lastUpdatedCursor.getLong(INDEX_LAST_UPDATED)); - Log.e("PHILIP", "cv: " + lastUpdatedEntry + " actual: " + masterKeyId); - } - lastUpdatedCursor.close(); - - if (lastUpdatedEntry != null) { - // there was an entry to re-insert - // this operation must happen after the new key is inserted operations.add( ContentProviderOperation .newInsert(UpdatedKeys.CONTENT_URI) @@ -721,6 +715,7 @@ public class ProviderHelper { .build() ); } + lastUpdatedCursor.close(); try { // delete old version of this keyRing, which also deletes all keys and userIds on cascade diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java index 7688b9252..b36d23775 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/ContactSyncAdapterService.java @@ -45,7 +45,7 @@ public class ContactSyncAdapterService extends Service { @Override public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, final SyncResult syncResult) { - Log.d(Constants.TAG, "Performing a sync!"); + Log.d(Constants.TAG, "Performing a contact sync!"); // TODO: Import is currently disabled for 2.8, until we implement proper origin management // importDone.set(false); // KeychainApplication.setupAccountAsNeeded(ContactSyncAdapterService.this); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java index baf649701..3243df1a8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeyserverSyncAdapterService.java @@ -1,6 +1,7 @@ package org.sufficientlysecure.keychain.service; import android.accounts.Account; +import android.accounts.AccountManager; import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationManager; @@ -25,6 +26,7 @@ import android.os.Messenger; import android.os.PowerManager; import android.os.SystemClock; import android.support.v4.app.NotificationCompat; +import android.widget.Toast; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -50,6 +52,20 @@ import java.util.concurrent.atomic.AtomicBoolean; public class KeyserverSyncAdapterService extends Service { + // how often a sync should be initiated, in s + public static final long SYNC_INTERVAL = + Constants.DEBUG_KEYSERVER_SYNC + ? TimeUnit.MINUTES.toSeconds(2) : TimeUnit.DAYS.toSeconds(3); + // time since last update after which a key should be updated again, in s + public static final long KEY_UPDATE_LIMIT = + Constants.DEBUG_KEYSERVER_SYNC ? 1 : TimeUnit.DAYS.toSeconds(7); + // time by which a sync is postponed in case of a + public static final long SYNC_POSTPONE_TIME = + Constants.DEBUG_KEYSERVER_SYNC ? 30 * 1000 : TimeUnit.MINUTES.toMillis(5); + // Time taken by Orbot before a new circuit is created + public static final int ORBOT_CIRCUIT_TIMEOUT = (int) TimeUnit.MINUTES.toMillis(10); + + private static final String ACTION_IGNORE_TOR = "ignore_tor"; private static final String ACTION_UPDATE_ALL = "update_all"; private static final String ACTION_SYNC_NOW = "sync_now"; @@ -61,8 +77,6 @@ public class KeyserverSyncAdapterService extends Service { @Override public int onStartCommand(final Intent intent, int flags, final int startId) { - Log.e("PHILIP", "Sync adapter service starting" + intent.getAction()); - switch (intent.getAction()) { case ACTION_CANCEL: { mCancelled.set(true); @@ -103,7 +117,6 @@ public class KeyserverSyncAdapterService extends Service { public void handleMessage(Message msg) { switch (msg.what) { case OrbotRequiredDialogActivity.MESSAGE_ORBOT_STARTED: { - Log.e("PHILIP", "orbot activity returned"); asyncKeyUpdate(KeyserverSyncAdapterService.this, new CryptoInputParcel()); break; @@ -177,10 +190,8 @@ public class KeyserverSyncAdapterService extends Service { private void handleUpdateResult(ImportKeyResult result) { if (result.isPending()) { - Log.e(Constants.TAG, "Keyserver sync pending result: " - + result.getRequiredInputParcel().mType); // result is pending due to Orbot not being started - // try to start it silently, if disabled show notificationaa + // try to start it silently, if disabled show notifications new OrbotHelper.SilentStartManager() { @Override protected void onOrbotStarted() { @@ -199,7 +210,8 @@ public class KeyserverSyncAdapterService extends Service { } }.startOrbotAndListen(this, false); } else if (isUpdateCancelled()) { - Log.d(Constants.TAG, "Keyserver sync cancelled"); + Log.d(Constants.TAG, "Keyserver sync cancelled, postponing by" + SYNC_POSTPONE_TIME + + "ms"); postponeSync(); } else { Log.d(Constants.TAG, "Keyserver sync completed: Updated: " + result.mUpdatedKeys @@ -216,14 +228,13 @@ public class KeyserverSyncAdapterService extends Service { PendingIntent.FLAG_UPDATE_CURRENT); alarmManager.set( AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + 30 * 1000, + SystemClock.elapsedRealtime() + SYNC_POSTPONE_TIME, pi ); } private void asyncKeyUpdate(final Context context, final CryptoInputParcel cryptoInputParcel) { - Log.e("PHILIP", "async key update starting"); new Thread(new Runnable() { @Override public void run() { @@ -234,7 +245,7 @@ public class KeyserverSyncAdapterService extends Service { } private synchronized ImportKeyResult updateKeysFromKeyserver(final Context context, - final CryptoInputParcel cryptoInputParcel) { + final CryptoInputParcel cryptoInputParcel) { mCancelled.set(false); ArrayList keyList = getKeysToUpdate(context); @@ -278,10 +289,6 @@ public class KeyserverSyncAdapterService extends Service { private ImportKeyResult staggeredUpdate(Context context, ArrayList keyList, CryptoInputParcel cryptoInputParcel) { Log.d(Constants.TAG, "Starting staggered update"); - // assuming maxCircuitDirtiness is 10min in Tor - // final int MAX_CIRCUIT_DIRTINESS = (int) TimeUnit.MINUTES.toSeconds(10); - // TODO: PHILIP remove after testing - final int MAX_CIRCUIT_DIRTINESS = (int) TimeUnit.MINUTES.toSeconds(1); // final int WEEK_IN_SECONDS = (int) TimeUnit.DAYS.toSeconds(7); final int WEEK_IN_SECONDS = 0; ImportOperation.KeyImportAccumulator accumulator @@ -289,10 +296,10 @@ public class KeyserverSyncAdapterService extends Service { for (ParcelableKeyRing keyRing : keyList) { int waitTime; int staggeredTime = new Random().nextInt(1 + 2 * (WEEK_IN_SECONDS / keyList.size())); - if (staggeredTime >= MAX_CIRCUIT_DIRTINESS) { + if (staggeredTime >= ORBOT_CIRCUIT_TIMEOUT) { waitTime = staggeredTime; } else { - waitTime = MAX_CIRCUIT_DIRTINESS + new Random().nextInt(MAX_CIRCUIT_DIRTINESS); + waitTime = ORBOT_CIRCUIT_TIMEOUT + new Random().nextInt(ORBOT_CIRCUIT_TIMEOUT); } Log.d(Constants.TAG, "Updating key with fingerprint " + keyRing.mExpectedFingerprint + " with a wait time of " + waitTime + "s"); @@ -342,18 +349,14 @@ public class KeyserverSyncAdapterService extends Service { final int INDEX_LAST_UPDATED = 1; // all time in seconds not milliseconds - // TODO: PHILIP correct TIME_MAX after testing - // final long TIME_MAX = TimeUnit.DAYS.toSeconds(7); - final long TIME_MAX = 1; final long CURRENT_TIME = GregorianCalendar.getInstance().getTimeInMillis() / 1000; - Log.e("PHILIP", "week: " + TIME_MAX + " current: " + CURRENT_TIME); Cursor updatedKeysCursor = context.getContentResolver().query( KeychainContract.UpdatedKeys.CONTENT_URI, new String[]{ KeychainContract.UpdatedKeys.MASTER_KEY_ID, KeychainContract.UpdatedKeys.LAST_UPDATED }, - "? - " + KeychainContract.UpdatedKeys.LAST_UPDATED + " < " + TIME_MAX, + "? - " + KeychainContract.UpdatedKeys.LAST_UPDATED + " < " + KEY_UPDATE_LIMIT, new String[]{"" + CURRENT_TIME}, null ); @@ -361,7 +364,7 @@ public class KeyserverSyncAdapterService extends Service { ArrayList ignoreMasterKeyIds = new ArrayList<>(); while (updatedKeysCursor.moveToNext()) { long masterKeyId = updatedKeysCursor.getLong(INDEX_UPDATED_KEYS_MASTER_KEY_ID); - Log.d(Constants.TAG, "Keyserver sync: {" + masterKeyId + "} last updated at {" + Log.d(Constants.TAG, "Keyserver sync: Ignoring {" + masterKeyId + "} last updated at {" + updatedKeysCursor.getLong(INDEX_LAST_UPDATED) + "}s"); ignoreMasterKeyIds.add(masterKeyId); } @@ -391,6 +394,7 @@ public class KeyserverSyncAdapterService extends Service { if (ignoreMasterKeyIds.contains(keyId)) { continue; } + Log.d(Constants.TAG, "Keyserver sync: Updating {" + keyId + "}"); String fingerprint = KeyFormattingUtils .convertFingerprintToHex(keyCursor.getBlob(INDEX_FINGERPRINT)); String hexKeyId = KeyFormattingUtils @@ -420,13 +424,6 @@ public class KeyserverSyncAdapterService extends Service { context.startService(intent); } - // TODO: PHILIP remove! - @Override - public void onDestroy() { - Log.e("PHILIP", "onDestroy"); - super.onDestroy(); - } - private Notification getOrbotNoification(Context context) { NotificationCompat.Builder builder = new NotificationCompat.Builder(context); builder.setSmallIcon(R.drawable.ic_stat_notify_24dp) @@ -467,6 +464,33 @@ public class KeyserverSyncAdapterService extends Service { return builder.build(); } + public static void enableKeyserverSync(Context context) { + try { + AccountManager manager = AccountManager.get(context); + Account[] accounts = manager.getAccountsByType(Constants.ACCOUNT_TYPE); + + Account account = new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE); + if (accounts.length == 0) { + if (!manager.addAccountExplicitly(account, null, null)) { + Log.e(Constants.TAG, "Adding account failed!"); + } + } + // for keyserver sync + ContentResolver.setIsSyncable(account, Constants.PROVIDER_AUTHORITY, 1); + ContentResolver.setSyncAutomatically(account, Constants.PROVIDER_AUTHORITY, + true); + ContentResolver.addPeriodicSync( + account, + Constants.PROVIDER_AUTHORITY, + new Bundle(), + SYNC_INTERVAL + ); + } catch (SecurityException e) { + Log.e(Constants.TAG, "SecurityException when adding the account", e); + Toast.makeText(context, R.string.reinstall_openkeychain, Toast.LENGTH_LONG).show(); + } + } + // from de.azapps.mirakel.helper.Helpers from https://github.com/MirakelX/mirakel-android private Bitmap getBitmap(int resId, Context context) { int mLargeIconWidth = (int) context.getResources().getDimension( diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java index 0596b0079..370511c4f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java @@ -26,6 +26,7 @@ import android.preference.PreferenceManager; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants.Pref; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService; import java.net.Proxy; import java.util.ArrayList; @@ -306,7 +307,7 @@ public class Preferences { return new ProxyPrefs(true, false, Constants.Orbot.PROXY_HOST, Constants.Orbot.PROXY_PORT, Constants.Orbot.PROXY_TYPE); } else if (useNormalProxy) { - return new ProxyPrefs(useTor, useNormalProxy, getProxyHost(), getProxyPort(), getProxyType()); + return new ProxyPrefs(false, true, getProxyHost(), getProxyPort(), getProxyType()); } else { return new ProxyPrefs(false, false, null, -1, null); } @@ -356,7 +357,7 @@ public class Preferences { } } - public void upgradePreferences() { + public void upgradePreferences(Context context) { if (mSharedPreferences.getInt(Constants.Pref.PREF_DEFAULT_VERSION, 0) != Constants.Defaults.PREF_VERSION) { switch (mSharedPreferences.getInt(Constants.Pref.PREF_DEFAULT_VERSION, 0)) { @@ -394,6 +395,10 @@ public class Preferences { } // fall through case 5: { + KeyserverSyncAdapterService.enableKeyserverSync(context); + } + // fall through + case 6: { } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java index 6305d2033..d85ad9128 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/orbot/OrbotHelper.java @@ -409,7 +409,7 @@ public class OrbotHelper { private ProgressDialog mProgressDialog; public void startOrbotAndListen(final Context context, final boolean showProgress) { - Log.e("PHILIP", "starting orbot listener"); + Log.d(Constants.TAG, "starting orbot listener"); if (showProgress) { showProgressDialog(context); } -- cgit v1.2.3 From 16903357e127aacbc7e82da5dc3c762184089a12 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 22 Aug 2015 12:22:43 +0200 Subject: mention filename to export to in log --- .../org/sufficientlysecure/keychain/operations/ExportOperation.java | 2 ++ .../sufficientlysecure/keychain/operations/results/OperationResult.java | 1 + 2 files changed, 3 insertions(+) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java index a5b70a41f..531ac01f2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ExportOperation.java @@ -145,6 +145,8 @@ public class ExportOperation extends BaseOperation { return new ExportResult(ExportResult.RESULT_ERROR, log); } + log.add(LogType.MSG_EXPORT_FILE_NAME, 1, outputFile); + // check if storage is ready if (!FileHelper.isStorageMounted(outputFile)) { log.add(LogType.MSG_EXPORT_ERROR_STORAGE, 1); 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 5ac1e89bb..a2164009a 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 @@ -719,6 +719,7 @@ public abstract class OperationResult implements Parcelable { MSG_IMPORT_SUCCESS (LogLevel.OK, R.string.msg_import_success), MSG_EXPORT (LogLevel.START, R.plurals.msg_export), + MSG_EXPORT_FILE_NAME (LogLevel.INFO, R.string.msg_export_file_name), MSG_EXPORT_UPLOAD_PUBLIC (LogLevel.START, R.string.msg_export_upload_public), MSG_EXPORT_PUBLIC (LogLevel.DEBUG, R.string.msg_export_public), MSG_EXPORT_SECRET (LogLevel.DEBUG, R.string.msg_export_secret), -- cgit v1.2.3 From e3d1f68e797b7d6cf27c64bef0c5d183031d4f38 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 22 Aug 2015 12:54:19 +0200 Subject: trigger indexing of file after operation (#1498) --- .../java/org/sufficientlysecure/keychain/util/ExportHelper.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java index 5f2329170..72620e712 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java @@ -20,6 +20,8 @@ package org.sufficientlysecure.keychain.util; import java.io.File; +import android.content.Intent; +import android.net.Uri; import android.support.v4.app.FragmentActivity; import org.sufficientlysecure.keychain.Constants; @@ -98,7 +100,10 @@ public class ExportHelper } @Override - public void onCryptoOperationSuccess(ExportResult result) { + final public void onCryptoOperationSuccess(ExportResult result) { + // trigger scan of the created 'media' file so it shows up on MTP + // http://stackoverflow.com/questions/13737261/nexus-4-not-showing-files-via-mtp + mActivity.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(mExportFile))); result.createNotify(mActivity).show(); } -- cgit v1.2.3 From 7ba0b93ceb2919aa549c14560ab434c39a364445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 24 Aug 2015 00:37:38 +0200 Subject: Move proxy pref values out of strings --- .../java/org/sufficientlysecure/keychain/Constants.java | 5 +++++ .../sufficientlysecure/keychain/util/Preferences.java | 17 ++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index a244972fb..b6832a9c7 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -114,6 +114,11 @@ public final class Constants { public static final String DARK = "dark"; public static final String DEFAULT = Constants.Pref.Theme.LIGHT; } + + public static final class ProxyType { + public static final String TYPE_HTTP = "proxyHttp"; + public static final String TYPE_SOCKS = "proxySocks"; + } } /** diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java index 370511c4f..60d0e6ac1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java @@ -286,16 +286,19 @@ public class Preferences { } public Proxy.Type getProxyType() { - final String typeHttp = mResources.getString(R.string.pref_proxy_type_value_http); - final String typeSocks = mResources.getString(R.string.pref_proxy_type_value_socks); + final String typeHttp = Pref.ProxyType.TYPE_HTTP; + final String typeSocks = Pref.ProxyType.TYPE_SOCKS; String type = mSharedPreferences.getString(Pref.PROXY_TYPE, typeHttp); - if (type.equals(typeHttp)) return Proxy.Type.HTTP; - else if (type.equals(typeSocks)) return Proxy.Type.SOCKS; - else { // shouldn't happen - Log.e(Constants.TAG, "Invalid Proxy Type in preferences"); - return null; + switch (type) { + case typeHttp: + return Proxy.Type.HTTP; + case typeSocks: + return Proxy.Type.SOCKS; + default: // shouldn't happen + Log.e(Constants.TAG, "Invalid Proxy Type in preferences"); + return null; } } -- cgit v1.2.3 From d6f3fb4e18d689de34867a302db309bd56f3e32d Mon Sep 17 00:00:00 2001 From: Adithya Abraham Philip Date: Mon, 24 Aug 2015 05:58:05 +0530 Subject: fixed update all crash --- .../keychain/operations/ImportOperation.java | 86 ++++++++-------------- 1 file changed, 31 insertions(+), 55 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java index 4b991ddda..7b224fe8e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java @@ -23,9 +23,7 @@ import java.io.IOException; import java.net.Proxy; import java.util.ArrayList; import java.util.GregorianCalendar; -import java.util.HashSet; import java.util.Iterator; -import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; @@ -100,29 +98,9 @@ public class ImportOperation extends BaseOperation { return serialKeyRingImport(entries, num, keyServerUri, mProgressable, proxy); } - public ImportKeyResult serialKeyRingImport(List entries, - String keyServerUri, Proxy proxy) { - - Iterator it = entries.iterator(); - int numEntries = entries.size(); - - return serialKeyRingImport(it, numEntries, keyServerUri, mProgressable, proxy); - - } - - public ImportKeyResult serialKeyRingImport(List entries, String keyServerUri, - Progressable progressable, Proxy proxy) { - - Iterator it = entries.iterator(); - int numEntries = entries.size(); - - return serialKeyRingImport(it, numEntries, keyServerUri, progressable, proxy); - - } - @NonNull - public ImportKeyResult serialKeyRingImport(ParcelableFileCache cache, - String keyServerUri, Proxy proxy) { + private ImportKeyResult serialKeyRingImport(ParcelableFileCache cache, + String keyServerUri, Proxy proxy) { // get entries from cached file try { @@ -144,7 +122,7 @@ public class ImportOperation extends BaseOperation { /** * Since the introduction of multithreaded import, we expect calling functions to handle the - * key sync i,eContactSyncAdapterService.requestSync() + * contact-to-key sync i.e ContactSyncAdapterService.requestSync() * * @param entries keys to import * @param num number of keys to import @@ -153,9 +131,9 @@ public class ImportOperation extends BaseOperation { * progress of a single key being imported */ @NonNull - public ImportKeyResult serialKeyRingImport(Iterator entries, int num, - String keyServerUri, Progressable progressable, - Proxy proxy) { + private ImportKeyResult serialKeyRingImport(Iterator entries, int num, + String keyServerUri, Progressable progressable, + Proxy proxy) { if (progressable != null) { progressable.setProgress(R.string.progress_importing, 0, 100); } @@ -294,15 +272,19 @@ public class ImportOperation extends BaseOperation { } SaveKeyringResult result; - mProviderHelper.clearLog(); - if (key.isSecret()) { - result = mProviderHelper.saveSecretKeyRing(key, - new ProgressScaler(progressable, (int) (position * progSteps), - (int) ((position + 1) * progSteps), 100)); - } else { - result = mProviderHelper.savePublicKeyRing(key, - new ProgressScaler(progressable, (int) (position * progSteps), - (int) ((position + 1) * progSteps), 100)); + // synchronizing prevents https://github.com/open-keychain/open-keychain/issues/1221 + // and https://github.com/open-keychain/open-keychain/issues/1480 + synchronized (mProviderHelper) { + mProviderHelper.clearLog(); + if (key.isSecret()) { + result = mProviderHelper.saveSecretKeyRing(key, + new ProgressScaler(progressable, (int) (position * progSteps), + (int) ((position + 1) * progSteps), 100)); + } else { + result = mProviderHelper.savePublicKeyRing(key, + new ProgressScaler(progressable, (int) (position * progSteps), + (int) ((position + 1) * progSteps), 100)); + } } if (!result.success()) { badKeys += 1; @@ -328,7 +310,6 @@ public class ImportOperation extends BaseOperation { } log.add(result, 2); - } catch (IOException | PgpGeneralException e) { Log.e(Constants.TAG, "Encountered bad key on import!", e); ++badKeys; @@ -338,9 +319,15 @@ public class ImportOperation extends BaseOperation { } // Special: consolidate on secret key import (cannot be cancelled!) + // synchronized on mProviderHelper to prevent + // https://github.com/open-keychain/open-keychain/issues/1221 since a consolidate deletes + // and re-inserts keys, which could conflict with a parallel db key update if (secret > 0) { setPreventCancel(); - ConsolidateResult result = mProviderHelper.consolidateDatabaseStep1(progressable); + ConsolidateResult result; + synchronized (mProviderHelper) { + result = mProviderHelper.consolidateDatabaseStep1(progressable); + } log.add(result, 1); } @@ -422,20 +409,8 @@ public class ImportOperation extends BaseOperation { } else { proxy = cryptoInput.getParcelableProxy().getProxy(); } - // if there is more than one key with the same fingerprint, we do a serial import to - // prevent - // https://github.com/open-keychain/open-keychain/issues/1221 - HashSet keyFingerprintSet = new HashSet<>(); - for (int i = 0; i < keyList.size(); i++) { - keyFingerprintSet.add(keyList.get(i).mExpectedFingerprint); - } - if (keyFingerprintSet.size() == keyList.size()) { - // all keys have unique fingerprints - result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer, - proxy); - } else { - result = serialKeyRingImport(keyList, keyServer, proxy); - } + + result = multiThreadedKeyImport(keyList.iterator(), keyList.size(), keyServer, proxy); } ContactSyncAdapterService.requestSync(); @@ -473,7 +448,8 @@ public class ImportOperation extends BaseOperation { ArrayList list = new ArrayList<>(); list.add(pkRing); - return serialKeyRingImport(list, keyServer, ignoreProgressable, proxy); + return serialKeyRingImport(list.iterator(), 1, keyServer, + ignoreProgressable, proxy); } }; @@ -497,7 +473,7 @@ public class ImportOperation extends BaseOperation { } return accumulator.getConsolidatedResult(); } - return null; // TODO: Decide if we should just crash instead of returning null + return new ImportKeyResult(ImportKeyResult.RESULT_FAIL_NOTHING, new OperationLog()); } /** -- cgit v1.2.3 From 1144ebb94cad7de9bee336206b3685641f70af16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Mon, 24 Aug 2015 02:37:26 +0200 Subject: API: Fix get sign key, related to OpenPgpKeyPreference bugs --- .../keychain/remote/OpenPgpService.java | 42 ++++++++++++++-------- .../remote/ui/SelectSignKeyIdActivity.java | 25 ++++++------- 2 files changed, 37 insertions(+), 30 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index ea016c657..9e6681341 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -715,28 +715,40 @@ public class OpenPgpService extends RemoteService { } private Intent getSignKeyIdImpl(Intent data) { - String preferredUserId = data.getStringExtra(OpenPgpApi.EXTRA_USER_ID); + // if data already contains EXTRA_SIGN_KEY_ID, it has been executed again + // after user interaction. Then, we just need to return the long again! + if (data.hasExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID)) { + long signKeyId = data.getLongExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, + Constants.key.none); - Intent intent = new Intent(getBaseContext(), SelectSignKeyIdActivity.class); - String currentPkg = getCurrentCallingPackage(); - intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(currentPkg)); - intent.putExtra(SelectSignKeyIdActivity.EXTRA_USER_ID, preferredUserId); - intent.putExtra(SelectSignKeyIdActivity.EXTRA_DATA, data); + Intent result = new Intent(); + result.putExtra(OpenPgpApi.EXTRA_SIGN_KEY_ID, signKeyId); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_SUCCESS); + return result; + } else { + String preferredUserId = data.getStringExtra(OpenPgpApi.EXTRA_USER_ID); - PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0, - intent, - PendingIntent.FLAG_CANCEL_CURRENT); + Intent intent = new Intent(getBaseContext(), SelectSignKeyIdActivity.class); + String currentPkg = getCurrentCallingPackage(); + intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(currentPkg)); + intent.putExtra(SelectSignKeyIdActivity.EXTRA_USER_ID, preferredUserId); + intent.putExtra(SelectSignKeyIdActivity.EXTRA_DATA, data); - // return PendingIntent to be executed by client - Intent result = new Intent(); - result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED); - result.putExtra(OpenPgpApi.RESULT_INTENT, pi); + PendingIntent pi = PendingIntent.getActivity(getBaseContext(), 0, + intent, + PendingIntent.FLAG_CANCEL_CURRENT); + + // return PendingIntent to be executed by client + Intent result = new Intent(); + result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_USER_INTERACTION_REQUIRED); + result.putExtra(OpenPgpApi.RESULT_INTENT, pi); - return result; + return result; + } } private Intent getKeyIdsImpl(Intent data) { - // if data already contains key ids extra GET_KEY_IDS has been executed again + // if data already contains EXTRA_KEY_IDS, it has been executed again // after user interaction. Then, we just need to return the array again! if (data.hasExtra(OpenPgpApi.EXTRA_KEY_IDS)) { long[] keyIdsArray = data.getLongArrayExtra(OpenPgpApi.EXTRA_KEY_IDS); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java index cb9f46f7f..bed49a6f6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/SelectSignKeyIdActivity.java @@ -40,14 +40,9 @@ public class SelectSignKeyIdActivity extends BaseActivity { private static final int REQUEST_CODE_CREATE_KEY = 0x00008884; - private Uri mAppUri; private String mPreferredUserId; private Intent mData; - private SelectSignKeyIdListFragment mListFragment; - private TextView mActionCreateKey; - private TextView mNone; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -62,15 +57,15 @@ public class SelectSignKeyIdActivity extends BaseActivity { } }); - mActionCreateKey = (TextView) findViewById(R.id.api_select_sign_key_create_key); - mActionCreateKey.setOnClickListener(new View.OnClickListener() { + TextView createKeyButton = (TextView) findViewById(R.id.api_select_sign_key_create_key); + createKeyButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { createKey(mPreferredUserId); } }); - mNone = (TextView) findViewById(R.id.api_select_sign_key_none); - mNone.setOnClickListener(new View.OnClickListener() { + TextView noneButton = (TextView) findViewById(R.id.api_select_sign_key_none); + noneButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 0 is "none" @@ -82,16 +77,16 @@ public class SelectSignKeyIdActivity extends BaseActivity { }); Intent intent = getIntent(); - mAppUri = intent.getData(); + Uri appUri = intent.getData(); mPreferredUserId = intent.getStringExtra(EXTRA_USER_ID); mData = intent.getParcelableExtra(EXTRA_DATA); - if (mAppUri == null) { + if (appUri == null) { Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!"); finish(); return; } else { - Log.d(Constants.TAG, "uri: " + mAppUri); - startListFragments(savedInstanceState, mAppUri, mData); + Log.d(Constants.TAG, "uri: " + appUri); + startListFragments(savedInstanceState, appUri, mData); } } @@ -113,11 +108,11 @@ public class SelectSignKeyIdActivity extends BaseActivity { } // Create an instance of the fragments - mListFragment = SelectSignKeyIdListFragment.newInstance(dataUri, data); + SelectSignKeyIdListFragment listFragment = SelectSignKeyIdListFragment.newInstance(dataUri, data); // Add the fragment to the 'fragment_container' FrameLayout // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! getSupportFragmentManager().beginTransaction() - .replace(R.id.api_select_sign_key_list_fragment, mListFragment) + .replace(R.id.api_select_sign_key_list_fragment, listFragment) .commitAllowingStateLoss(); // do it immediately! getSupportFragmentManager().executePendingTransactions(); -- cgit v1.2.3 From d3bda969539061e11aaa2dc4ea8eec84942b136b Mon Sep 17 00:00:00 2001 From: mgeier63 Date: Thu, 27 Aug 2015 09:12:21 +0200 Subject: service side support for https://github.com/open-keychain/openpgp-api/pull/3 --- .../keychain/remote/OpenPgpService.java | 129 +++++++++++---------- .../keychain/remote/OpenPgpService2.java | 120 +++++++++++++++++++ 2 files changed, 188 insertions(+), 61 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService2.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index 9e6681341..e67f43c4c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -25,6 +25,7 @@ import android.net.Uri; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.Parcelable; +import android.support.annotation.Nullable; import android.text.TextUtils; import org.openintents.openpgp.IOpenPgpService; @@ -842,67 +843,8 @@ public class OpenPgpService extends RemoteService { private final IOpenPgpService.Stub mBinder = new IOpenPgpService.Stub() { @Override public Intent execute(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output) { - try { - Intent errorResult = checkRequirements(data); - if (errorResult != null) { - return errorResult; - } - - String action = data.getAction(); - switch (action) { - case OpenPgpApi.ACTION_CLEARTEXT_SIGN: { - return signImpl(data, input, output, true); - } - case OpenPgpApi.ACTION_SIGN: { - // DEPRECATED: same as ACTION_CLEARTEXT_SIGN - Log.w(Constants.TAG, "You are using a deprecated API call, please use ACTION_CLEARTEXT_SIGN instead of ACTION_SIGN!"); - return signImpl(data, input, output, true); - } - case OpenPgpApi.ACTION_DETACHED_SIGN: { - return signImpl(data, input, output, false); - } - case OpenPgpApi.ACTION_ENCRYPT: { - return encryptAndSignImpl(data, input, output, false); - } - case OpenPgpApi.ACTION_SIGN_AND_ENCRYPT: { - return encryptAndSignImpl(data, input, output, true); - } - case OpenPgpApi.ACTION_DECRYPT_VERIFY: { - return decryptAndVerifyImpl(data, input, output, false); - } - case OpenPgpApi.ACTION_DECRYPT_METADATA: { - return decryptAndVerifyImpl(data, input, output, true); - } - case OpenPgpApi.ACTION_GET_SIGN_KEY_ID: { - return getSignKeyIdImpl(data); - } - case OpenPgpApi.ACTION_GET_KEY_IDS: { - return getKeyIdsImpl(data); - } - case OpenPgpApi.ACTION_GET_KEY: { - return getKeyImpl(data); - } - default: { - return null; - } - } - } finally { - // always close input and output file descriptors even in error cases - if (input != null) { - try { - input.close(); - } catch (IOException e) { - Log.e(Constants.TAG, "IOException when closing input ParcelFileDescriptor", e); - } - } - if (output != null) { - try { - output.close(); - } catch (IOException e) { - Log.e(Constants.TAG, "IOException when closing output ParcelFileDescriptor", e); - } - } - } + Log.w(Constants.TAG, "You are using a deprecated service which may lead to truncated data on return, please use IOpenPgpService2!"); + return executeInternal(data, input, output); } }; @@ -912,4 +854,69 @@ public class OpenPgpService extends RemoteService { return mBinder; } + + + protected Intent executeInternal(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output) { + try { + Intent errorResult = checkRequirements(data); + if (errorResult != null) { + return errorResult; + } + + String action = data.getAction(); + switch (action) { + case OpenPgpApi.ACTION_CLEARTEXT_SIGN: { + return signImpl(data, input, output, true); + } + case OpenPgpApi.ACTION_SIGN: { + // DEPRECATED: same as ACTION_CLEARTEXT_SIGN + Log.w(Constants.TAG, "You are using a deprecated API call, please use ACTION_CLEARTEXT_SIGN instead of ACTION_SIGN!"); + return signImpl(data, input, output, true); + } + case OpenPgpApi.ACTION_DETACHED_SIGN: { + return signImpl(data, input, output, false); + } + case OpenPgpApi.ACTION_ENCRYPT: { + return encryptAndSignImpl(data, input, output, false); + } + case OpenPgpApi.ACTION_SIGN_AND_ENCRYPT: { + return encryptAndSignImpl(data, input, output, true); + } + case OpenPgpApi.ACTION_DECRYPT_VERIFY: { + return decryptAndVerifyImpl(data, input, output, false); + } + case OpenPgpApi.ACTION_DECRYPT_METADATA: { + return decryptAndVerifyImpl(data, input, output, true); + } + case OpenPgpApi.ACTION_GET_SIGN_KEY_ID: { + return getSignKeyIdImpl(data); + } + case OpenPgpApi.ACTION_GET_KEY_IDS: { + return getKeyIdsImpl(data); + } + case OpenPgpApi.ACTION_GET_KEY: { + return getKeyImpl(data); + } + default: { + return null; + } + } + } finally { + // always close input and output file descriptors even in error cases + if (input != null) { + try { + input.close(); + } catch (IOException e) { + Log.e(Constants.TAG, "IOException when closing input ParcelFileDescriptor", e); + } + } + if (output != null) { + try { + output.close(); + } catch (IOException e) { + Log.e(Constants.TAG, "IOException when closing output ParcelFileDescriptor", e); + } + } + } + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService2.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService2.java new file mode 100644 index 000000000..317d4dd73 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService2.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2013-2015 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.remote; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Binder; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.Parcelable; +import android.text.TextUtils; + +import org.openintents.openpgp.IOpenPgpService2; +import org.openintents.openpgp.OpenPgpDecryptionResult; +import org.openintents.openpgp.OpenPgpError; +import org.openintents.openpgp.OpenPgpMetadata; +import org.openintents.openpgp.OpenPgpSignatureResult; +import org.openintents.openpgp.util.OpenPgpApi; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel; +import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; +import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants; +import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel; +import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation; +import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity; +import org.sufficientlysecure.keychain.remote.ui.SelectAllowedKeysActivity; +import org.sufficientlysecure.keychain.remote.ui.SelectSignKeyIdActivity; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; +import org.sufficientlysecure.keychain.ui.ImportKeysActivity; +import org.sufficientlysecure.keychain.ui.NfcOperationActivity; +import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; +import org.sufficientlysecure.keychain.ui.ViewKeyActivity; +import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +public class OpenPgpService2 extends OpenPgpService { + + + + private Map mOutputPipeMap = new HashMap(); + + private long createKey(int id) { + int callingPid = Binder.getCallingPid(); + return ((long) callingPid << 32) | ((long) id & 0xFFFFFFFL); + } + + private final IOpenPgpService2.Stub mBinder = new IOpenPgpService2.Stub() { + + @Override + public ParcelFileDescriptor createOutputPipe(int outputPipeId) { + try { + ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); + mOutputPipeMap.put(createKey(outputPipeId), pipe[1]); + return pipe[0]; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + + } + + @Override + public Intent execute(Intent data, ParcelFileDescriptor input,int outputPipeId) { + long key = createKey(outputPipeId); + ParcelFileDescriptor output = mOutputPipeMap.get(key); + mOutputPipeMap.remove(key); + return executeInternal(data, input, output); + } + + + + + }; + + @Override + public IBinder onBind(Intent intent) { + return mBinder; + } + + + +} -- cgit v1.2.3 From ec7a3a704701537eda24afe16081b18b34497928 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 27 Aug 2015 12:24:37 +0200 Subject: Experimental feature setting --- .../org/sufficientlysecure/keychain/Constants.java | 2 ++ .../keychain/ui/SettingsActivity.java | 42 ++++++++++++++++++---- .../keychain/util/Preferences.java | 14 +++++++- 3 files changed, 51 insertions(+), 7 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index b6832a9c7..17d550ce5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -108,6 +108,8 @@ public final class Constants { // keyserver sync settings public static final String SYNC_CONTACTS = "syncContacts"; public static final String SYNC_KEYSERVER = "syncKeyserver"; + // other settings + public static final String ENABLE_EXPERIMENTAL_FEATURES = "enableExperimentalFeatures"; public static final class Theme { public static final String LIGHT = "light"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index 842059da5..63be1e20f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -32,6 +32,7 @@ import android.preference.Preference; import android.preference.PreferenceActivity; import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; +import android.preference.SwitchPreference; import android.provider.ContactsContract; import android.support.v7.widget.Toolbar; import android.text.TextUtils; @@ -100,7 +101,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { ); } else if (action != null && action.equals(ACTION_PREFS_ADV)) { - addPreferencesFromResource(R.xml.adv_preferences); + addPreferencesFromResource(R.xml.passphrase_preferences); initializePassphraseCacheSubs( (CheckBoxPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_SUBS)); @@ -221,14 +222,14 @@ public class SettingsActivity extends AppCompatPreferenceActivity { /** * This fragment shows the PIN/password preferences */ - public static class AdvancedPrefsFragment extends PreferenceFragment { + public static class PassphrasePrefsFragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Load the preferences from an XML resource - addPreferencesFromResource(R.xml.adv_preferences); + addPreferencesFromResource(R.xml.passphrase_preferences); initializePassphraseCacheSubs( (CheckBoxPreference) findPreference(Constants.Pref.PASSPHRASE_CACHE_SUBS)); @@ -472,7 +473,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { /** * This fragment shows the keyserver/contacts sync preferences */ - public static class SyncSettingsFragment extends PreferenceFragment { + public static class SyncPrefsFragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { @@ -551,12 +552,30 @@ public class SettingsActivity extends AppCompatPreferenceActivity { } } + /** + * This fragment shows other preferences + */ + public static class OtherPrefsFragment extends PreferenceFragment { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Load the preferences from an XML resource + addPreferencesFromResource(R.xml.other_preferences); + + initializeEnableExperimentalFeatures( + (SwitchPreference) findPreference(Constants.Pref.ENABLE_EXPERIMENTAL_FEATURES)); + } + } + protected boolean isValidFragment(String fragmentName) { - return AdvancedPrefsFragment.class.getName().equals(fragmentName) + return PassphrasePrefsFragment.class.getName().equals(fragmentName) || CloudSearchPrefsFragment.class.getName().equals(fragmentName) || ProxyPrefsFragment.class.getName().equals(fragmentName) || GuiPrefsFragment.class.getName().equals(fragmentName) - || SyncSettingsFragment.class.getName().equals(fragmentName) + || SyncPrefsFragment.class.getName().equals(fragmentName) + || OtherPrefsFragment.class.getName().equals(fragmentName) || super.isValidFragment(fragmentName); } @@ -656,4 +675,15 @@ public class SettingsActivity extends AppCompatPreferenceActivity { } }); } + + private static void initializeEnableExperimentalFeatures(final SwitchPreference mEnableExperimentalFeatures) { + mEnableExperimentalFeatures.setChecked(sPreferences.getEnableExperimentalFeatures()); + mEnableExperimentalFeatures.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + public boolean onPreferenceChange(Preference preference, Object newValue) { + mEnableExperimentalFeatures.setChecked((Boolean) newValue); + sPreferences.setEnableExperimentalFeatures((Boolean) newValue); + return false; + } + }); + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java index 60d0e6ac1..75f83d1f6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java @@ -335,7 +335,7 @@ public class Preferences { } } - // proxy preference functions ends here + // cloud prefs public CloudSearchPrefs getCloudSearchPrefs() { return new CloudSearchPrefs(mSharedPreferences.getBoolean(Pref.SEARCH_KEYSERVER, true), @@ -360,6 +360,18 @@ public class Preferences { } } + // other prefs + + public void setEnableExperimentalFeatures(boolean enableExperimentalFeatures) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putBoolean(Pref.ENABLE_EXPERIMENTAL_FEATURES, enableExperimentalFeatures); + editor.commit(); + } + + public boolean getEnableExperimentalFeatures() { + return mSharedPreferences.getBoolean(Pref.ENABLE_EXPERIMENTAL_FEATURES, false); + } + public void upgradePreferences(Context context) { if (mSharedPreferences.getInt(Constants.Pref.PREF_DEFAULT_VERSION, 0) != Constants.Defaults.PREF_VERSION) { -- cgit v1.2.3 From 7a98ffc90a34a381a68c5b485183373ed307320a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 27 Aug 2015 12:34:54 +0200 Subject: Remove dead code --- .../operations/KeybaseVerificationOperation.java | 4 +- .../keychain/pgp/PgpDecryptVerify.java | 1239 -------------------- .../keychain/pgp/PgpDecryptVerifyOperation.java | 1239 ++++++++++++++++++++ .../keychain/remote/OpenPgpService.java | 4 +- .../keychain/service/KeychainService.java | 4 +- .../keychain/ui/CertifyFingerprintFragment.java | 11 +- .../keychain/ui/PassphraseDialogActivity.java | 69 +- .../keychain/ui/PassphraseWizardActivity.java | 577 --------- 8 files changed, 1279 insertions(+), 1868 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java index 30f37dd4f..8f1abde83 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/KeybaseVerificationOperation.java @@ -43,7 +43,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.operations.results.KeybaseVerificationResult; import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.provider.ProviderHelper; @@ -141,7 +141,7 @@ public class KeybaseVerificationOperation extends BaseOperation - * Copyright (C) 2010-2014 Thialfihar - * - * 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 android.content.Context; -import android.support.annotation.NonNull; -import android.webkit.MimeTypeMap; - -import org.openintents.openpgp.OpenPgpDecryptionResult; -import org.openintents.openpgp.OpenPgpMetadata; -import org.openintents.openpgp.OpenPgpSignatureResult; -import org.spongycastle.bcpg.ArmoredInputStream; -import org.spongycastle.openpgp.PGPCompressedData; -import org.spongycastle.openpgp.PGPDataValidationException; -import org.spongycastle.openpgp.PGPEncryptedData; -import org.spongycastle.openpgp.PGPEncryptedDataList; -import org.spongycastle.openpgp.PGPException; -import org.spongycastle.openpgp.PGPKeyValidationException; -import org.spongycastle.openpgp.PGPLiteralData; -import org.spongycastle.openpgp.PGPOnePassSignature; -import org.spongycastle.openpgp.PGPOnePassSignatureList; -import org.spongycastle.openpgp.PGPPBEEncryptedData; -import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; -import org.spongycastle.openpgp.PGPSignature; -import org.spongycastle.openpgp.PGPSignatureList; -import org.spongycastle.openpgp.PGPUtil; -import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory; -import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory; -import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; -import org.spongycastle.openpgp.operator.jcajce.CachingDataDecryptorFactory; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; -import org.spongycastle.util.encoders.DecoderException; -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.Constants.key; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.BaseOperation; -import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; -import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; -import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; -import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; -import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.FileHelper; -import org.sufficientlysecure.keychain.util.InputData; -import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.Passphrase; -import org.sufficientlysecure.keychain.util.ProgressScaler; - -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.security.SignatureException; -import java.util.Date; -import java.util.Iterator; - -public class PgpDecryptVerify extends BaseOperation { - - public PgpDecryptVerify(Context context, ProviderHelper providerHelper, Progressable progressable) { - super(context, providerHelper, progressable); - } - - /** Decrypts and/or verifies data based on parameters of PgpDecryptVerifyInputParcel. */ - @NonNull - public DecryptVerifyResult execute(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput) { - InputData inputData; - OutputStream outputStream; - - if (input.getInputBytes() != null) { - byte[] inputBytes = input.getInputBytes(); - inputData = new InputData(new ByteArrayInputStream(inputBytes), inputBytes.length); - } else { - try { - InputStream inputStream = mContext.getContentResolver().openInputStream(input.getInputUri()); - long inputSize = FileHelper.getFileSize(mContext, input.getInputUri(), 0); - inputData = new InputData(inputStream, inputSize); - } catch (FileNotFoundException e) { - Log.e(Constants.TAG, "Input URI could not be opened: " + input.getInputUri(), e); - OperationLog log = new OperationLog(); - log.add(LogType.MSG_DC_ERROR_INPUT, 1); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - } - - if (input.getOutputUri() == null) { - outputStream = new ByteArrayOutputStream(); - } else { - try { - outputStream = mContext.getContentResolver().openOutputStream(input.getOutputUri()); - } catch (FileNotFoundException e) { - Log.e(Constants.TAG, "Output URI could not be opened: " + input.getOutputUri(), e); - OperationLog log = new OperationLog(); - log.add(LogType.MSG_DC_ERROR_IO, 1); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - } - - DecryptVerifyResult result = executeInternal(input, cryptoInput, inputData, outputStream); - if (outputStream instanceof ByteArrayOutputStream) { - byte[] outputData = ((ByteArrayOutputStream) outputStream).toByteArray(); - result.setOutputBytes(outputData); - } - - return result; - - } - - @NonNull - public DecryptVerifyResult execute(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput, - InputData inputData, OutputStream outputStream) { - return executeInternal(input, cryptoInput, inputData, outputStream); - } - - @NonNull - private DecryptVerifyResult executeInternal(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput, - InputData inputData, OutputStream outputStream) { - try { - if (input.getDetachedSignature() != null) { - Log.d(Constants.TAG, "Detached signature present, verifying with this signature only"); - - return verifyDetachedSignature(input, inputData, outputStream, 0); - } else { - // automatically works with PGP ascii armor and PGP binary - InputStream in = PGPUtil.getDecoderStream(inputData.getInputStream()); - - if (in instanceof ArmoredInputStream) { - ArmoredInputStream aIn = (ArmoredInputStream) in; - // it is ascii armored - Log.d(Constants.TAG, "ASCII Armor Header Line: " + aIn.getArmorHeaderLine()); - - if (input.isSignedLiteralData()) { - return verifySignedLiteralData(input, aIn, outputStream, 0); - } else if (aIn.isClearText()) { - // a cleartext signature, verify it with the other method - return verifyCleartextSignature(aIn, outputStream, 0); - } else { - // else: ascii armored encryption! go on... - return decryptVerify(input, cryptoInput, in, outputStream, 0); - } - } else { - return decryptVerify(input, cryptoInput, in, outputStream, 0); - } - } - } catch (PGPException e) { - Log.d(Constants.TAG, "PGPException", e); - OperationLog log = new OperationLog(); - log.add(LogType.MSG_DC_ERROR_PGP_EXCEPTION, 1); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } catch (DecoderException | ArrayIndexOutOfBoundsException e) { - // these can happen if assumptions in JcaPGPObjectFactory.nextObject() aren't - // fulfilled, so we need to catch them here to handle this gracefully - Log.d(Constants.TAG, "data error", e); - OperationLog log = new OperationLog(); - log.add(LogType.MSG_DC_ERROR_IO, 1); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } catch (IOException e) { - Log.d(Constants.TAG, "IOException", e); - OperationLog log = new OperationLog(); - log.add(LogType.MSG_DC_ERROR_IO, 1); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - } - - /**Verify signed plaintext data (PGP/INLINE). */ - @NonNull - private DecryptVerifyResult verifySignedLiteralData( - PgpDecryptVerifyInputParcel input, InputStream in, OutputStream out, int indent) - throws IOException, PGPException { - OperationLog log = new OperationLog(); - log.add(LogType.MSG_VL, indent); - - // thinking that the proof-fetching operation is going to take most of the time - updateProgress(R.string.progress_reading_data, 75, 100); - - JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in); - Object o = pgpF.nextObject(); - if (o instanceof PGPCompressedData) { - log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent + 1); - - pgpF = new JcaPGPObjectFactory(((PGPCompressedData) o).getDataStream()); - o = pgpF.nextObject(); - updateProgress(R.string.progress_decompressing_data, 80, 100); - } - - // all we want to see is a OnePassSignatureList followed by LiteralData - if (!(o instanceof PGPOnePassSignatureList)) { - log.add(LogType.MSG_VL_ERROR_MISSING_SIGLIST, indent); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) o; - - // go through all signatures (should be just one), make sure we have - // the key and it matches the one we’re looking for - CanonicalizedPublicKeyRing signingRing = null; - CanonicalizedPublicKey signingKey = null; - int signatureIndex = -1; - for (int i = 0; i < sigList.size(); ++i) { - try { - long sigKeyId = sigList.get(i).getKeyID(); - signingRing = mProviderHelper.getCanonicalizedPublicKeyRing( - KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId) - ); - signingKey = signingRing.getPublicKey(sigKeyId); - signatureIndex = i; - } catch (ProviderHelper.NotFoundException e) { - Log.d(Constants.TAG, "key not found, trying next signature..."); - } - } - - // there has to be a key, and it has to be the right one - if (signingKey == null) { - log.add(LogType.MSG_VL_ERROR_MISSING_KEY, indent); - Log.d(Constants.TAG, "Failed to find key in signed-literal message"); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - - String fingerprint = KeyFormattingUtils.convertFingerprintToHex(signingRing.getFingerprint()); - if (!(input.getRequiredSignerFingerprint().equals(fingerprint))) { - log.add(LogType.MSG_VL_ERROR_MISSING_KEY, indent); - Log.d(Constants.TAG, "Fingerprint mismatch; wanted " + input.getRequiredSignerFingerprint() + - " got " + fingerprint + "!"); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - - OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder(); - - PGPOnePassSignature signature = sigList.get(signatureIndex); - signatureResultBuilder.initValid(signingRing, signingKey); - - JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = - new JcaPGPContentVerifierBuilderProvider() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey()); - - o = pgpF.nextObject(); - - if (!(o instanceof PGPLiteralData)) { - log.add(LogType.MSG_VL_ERROR_MISSING_LITERAL, indent); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - - PGPLiteralData literalData = (PGPLiteralData) o; - - log.add(LogType.MSG_DC_CLEAR_DATA, indent + 1); - updateProgress(R.string.progress_decrypting, 85, 100); - - InputStream dataIn = literalData.getInputStream(); - - int length; - byte[] buffer = new byte[1 << 16]; - while ((length = dataIn.read(buffer)) > 0) { - out.write(buffer, 0, length); - signature.update(buffer, 0, length); - } - - updateProgress(R.string.progress_verifying_signature, 95, 100); - log.add(LogType.MSG_VL_CLEAR_SIGNATURE_CHECK, indent + 1); - - PGPSignatureList signatureList = (PGPSignatureList) pgpF.nextObject(); - PGPSignature messageSignature = signatureList.get(signatureIndex); - - // Verify signature and check binding signatures - boolean validSignature = signature.verify(messageSignature); - if (validSignature) { - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1); - } else { - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1); - } - signatureResultBuilder.setValidSignature(validSignature); - - OpenPgpSignatureResult signatureResult = signatureResultBuilder.build(); - - if (signatureResult.getResult() != OpenPgpSignatureResult.RESULT_VALID_CONFIRMED - && signatureResult.getResult() != OpenPgpSignatureResult.RESULT_VALID_UNCONFIRMED) { - log.add(LogType.MSG_VL_ERROR_INTEGRITY_CHECK, indent); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - - updateProgress(R.string.progress_done, 100, 100); - - log.add(LogType.MSG_VL_OK, indent); - - // Return a positive result, with metadata and verification info - DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); - result.setSignatureResult(signatureResult); - result.setDecryptionResult( - new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED)); - return result; - } - - - /** Decrypt and/or verify binary or ascii armored pgp data. */ - @NonNull - private DecryptVerifyResult decryptVerify( - PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput, - InputStream in, OutputStream out, int indent) throws IOException, PGPException { - - OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder(); - OpenPgpDecryptionResultBuilder decryptionResultBuilder = new OpenPgpDecryptionResultBuilder(); - OperationLog log = new OperationLog(); - - log.add(LogType.MSG_DC, indent); - indent += 1; - - JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in); - PGPEncryptedDataList enc; - Object o = pgpF.nextObject(); - - int currentProgress = 0; - updateProgress(R.string.progress_reading_data, currentProgress, 100); - - if (o instanceof PGPEncryptedDataList) { - enc = (PGPEncryptedDataList) o; - } else { - enc = (PGPEncryptedDataList) pgpF.nextObject(); - } - - if (enc == null) { - log.add(LogType.MSG_DC_ERROR_INVALID_DATA, indent); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - - InputStream clear; - PGPEncryptedData encryptedData; - - PGPPublicKeyEncryptedData encryptedDataAsymmetric = null; - PGPPBEEncryptedData encryptedDataSymmetric = null; - CanonicalizedSecretKey secretEncryptionKey = null; - Iterator it = enc.getEncryptedDataObjects(); - boolean asymmetricPacketFound = false; - boolean symmetricPacketFound = false; - boolean anyPacketFound = false; - - // If the input stream is armored, and there is a charset specified, take a note for later - // https://tools.ietf.org/html/rfc4880#page56 - String charset = null; - if (in instanceof ArmoredInputStream) { - ArmoredInputStream aIn = (ArmoredInputStream) in; - if (aIn.getArmorHeaders() != null) { - for (String header : aIn.getArmorHeaders()) { - String[] pieces = header.split(":", 2); - if (pieces.length == 2 && "charset".equalsIgnoreCase(pieces[0])) { - charset = pieces[1].trim(); - break; - } - } - if (charset != null) { - log.add(LogType.MSG_DC_CHARSET, indent, charset); - } - } - } - - Passphrase passphrase = null; - boolean skippedDisallowedKey = false; - - // go through all objects and find one we can decrypt - while (it.hasNext()) { - Object obj = it.next(); - if (obj instanceof PGPPublicKeyEncryptedData) { - anyPacketFound = true; - - currentProgress += 2; - updateProgress(R.string.progress_finding_key, currentProgress, 100); - - PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; - long subKeyId = encData.getKeyID(); - - log.add(LogType.MSG_DC_ASYM, indent, - KeyFormattingUtils.convertKeyIdToHex(subKeyId)); - - CanonicalizedSecretKeyRing secretKeyRing; - try { - // get actual keyring object based on master key id - secretKeyRing = mProviderHelper.getCanonicalizedSecretKeyRing( - KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId) - ); - } catch (ProviderHelper.NotFoundException e) { - // continue with the next packet in the while loop - log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); - continue; - } - if (secretKeyRing == null) { - // continue with the next packet in the while loop - log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); - continue; - } - - // allow only specific keys for decryption? - if (input.getAllowedKeyIds() != null) { - long masterKeyId = secretKeyRing.getMasterKeyId(); - Log.d(Constants.TAG, "encData.getKeyID(): " + subKeyId); - Log.d(Constants.TAG, "mAllowedKeyIds: " + input.getAllowedKeyIds()); - Log.d(Constants.TAG, "masterKeyId: " + masterKeyId); - - if (!input.getAllowedKeyIds().contains(masterKeyId)) { - // this key is in our db, but NOT allowed! - // continue with the next packet in the while loop - skippedDisallowedKey = true; - log.add(LogType.MSG_DC_ASKIP_NOT_ALLOWED, indent + 1); - continue; - } - } - - // get subkey which has been used for this encryption packet - secretEncryptionKey = secretKeyRing.getSecretKey(subKeyId); - if (secretEncryptionKey == null) { - // should actually never happen, so no need to be more specific. - log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); - continue; - } - - /* secret key exists in database and is allowed! */ - asymmetricPacketFound = true; - - encryptedDataAsymmetric = encData; - - if (secretEncryptionKey.getSecretKeyType() == SecretKeyType.DIVERT_TO_CARD) { - passphrase = null; - } else if (cryptoInput.hasPassphrase()) { - passphrase = cryptoInput.getPassphrase(); - } else { - // if no passphrase was explicitly set try to get it from the cache service - try { - // returns "" if key has no passphrase - passphrase = getCachedPassphrase(subKeyId); - log.add(LogType.MSG_DC_PASS_CACHED, indent + 1); - } catch (PassphraseCacheInterface.NoSecretKeyException e) { - log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - - // if passphrase was not cached, return here indicating that a passphrase is missing! - if (passphrase == null) { - log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1); - return new DecryptVerifyResult(log, - RequiredInputParcel.createRequiredDecryptPassphrase( - secretKeyRing.getMasterKeyId(), secretEncryptionKey.getKeyId()), - cryptoInput); - } - } - - // check for insecure encryption key - if ( ! PgpSecurityConstants.isSecureKey(secretEncryptionKey)) { - log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1); - decryptionResultBuilder.setInsecure(true); - } - - // break out of while, only decrypt the first packet where we have a key - break; - - } else if (obj instanceof PGPPBEEncryptedData) { - anyPacketFound = true; - - log.add(LogType.MSG_DC_SYM, indent); - - if (!input.isAllowSymmetricDecryption()) { - log.add(LogType.MSG_DC_SYM_SKIP, indent + 1); - continue; - } - - /* - * When mAllowSymmetricDecryption == true and we find a data packet here, - * we do not search for other available asymmetric packets! - */ - symmetricPacketFound = true; - - encryptedDataSymmetric = (PGPPBEEncryptedData) obj; - - // if no passphrase is given, return here - // indicating that a passphrase is missing! - if (!cryptoInput.hasPassphrase()) { - - try { - passphrase = getCachedPassphrase(key.symmetric); - log.add(LogType.MSG_DC_PASS_CACHED, indent + 1); - } catch (PassphraseCacheInterface.NoSecretKeyException e) { - // nvm - } - - if (passphrase == null) { - log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1); - return new DecryptVerifyResult(log, - RequiredInputParcel.createRequiredSymmetricPassphrase(), - cryptoInput); - } - - } else { - passphrase = cryptoInput.getPassphrase(); - } - - // break out of while, only decrypt the first packet - break; - } - } - - // More data, just acknowledge and ignore. - while (it.hasNext()) { - Object obj = it.next(); - if (obj instanceof PGPPublicKeyEncryptedData) { - PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; - long subKeyId = encData.getKeyID(); - log.add(LogType.MSG_DC_TRAIL_ASYM, indent, - KeyFormattingUtils.convertKeyIdToHex(subKeyId)); - } else if (obj instanceof PGPPBEEncryptedData) { - log.add(LogType.MSG_DC_TRAIL_SYM, indent); - } else { - log.add(LogType.MSG_DC_TRAIL_UNKNOWN, indent); - } - } - - log.add(LogType.MSG_DC_PREP_STREAMS, indent); - - // we made sure above one of these two would be true - int symmetricEncryptionAlgo; - if (symmetricPacketFound) { - currentProgress += 2; - updateProgress(R.string.progress_preparing_streams, currentProgress, 100); - - PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(); - PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder( - digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - passphrase.getCharArray()); - - try { - clear = encryptedDataSymmetric.getDataStream(decryptorFactory); - } catch (PGPDataValidationException e) { - log.add(LogType.MSG_DC_ERROR_SYM_PASSPHRASE, indent +1); - return new DecryptVerifyResult(log, - RequiredInputParcel.createRequiredSymmetricPassphrase(), cryptoInput); - } - - encryptedData = encryptedDataSymmetric; - - symmetricEncryptionAlgo = encryptedDataSymmetric.getSymmetricAlgorithm(decryptorFactory); - } else if (asymmetricPacketFound) { - currentProgress += 2; - updateProgress(R.string.progress_extracting_key, currentProgress, 100); - - try { - log.add(LogType.MSG_DC_UNLOCKING, indent + 1); - if (!secretEncryptionKey.unlock(passphrase)) { - log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent + 1); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - } catch (PgpGeneralException e) { - log.add(LogType.MSG_DC_ERROR_EXTRACT_KEY, indent + 1); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - - currentProgress += 2; - updateProgress(R.string.progress_preparing_streams, currentProgress, 100); - - CachingDataDecryptorFactory decryptorFactory - = secretEncryptionKey.getCachingDecryptorFactory(cryptoInput); - - // special case: if the decryptor does not have a session key cached for this encrypted - // data, and can't actually decrypt on its own, return a pending intent - if (!decryptorFactory.canDecrypt() - && !decryptorFactory.hasCachedSessionData(encryptedDataAsymmetric)) { - - log.add(LogType.MSG_DC_PENDING_NFC, indent + 1); - return new DecryptVerifyResult(log, RequiredInputParcel.createNfcDecryptOperation( - secretEncryptionKey.getRing().getMasterKeyId(), - secretEncryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0] - ), - cryptoInput); - - } - - try { - clear = encryptedDataAsymmetric.getDataStream(decryptorFactory); - } catch (PGPKeyValidationException | ArrayIndexOutOfBoundsException e) { - log.add(LogType.MSG_DC_ERROR_CORRUPT_DATA, indent + 1); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - - symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory); - - cryptoInput.addCryptoData(decryptorFactory.getCachedSessionKeys()); - - encryptedData = encryptedDataAsymmetric; - } else { - // there wasn't even any useful data - if (!anyPacketFound) { - log.add(LogType.MSG_DC_ERROR_NO_DATA, indent + 1); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_NO_DATA, log); - } - // there was data but key wasn't allowed - if (skippedDisallowedKey) { - log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_KEY_DISALLOWED, log); - } - // no packet has been found where we have the corresponding secret key in our db - log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - decryptionResultBuilder.setEncrypted(true); - - // Check for insecure encryption algorithms! - if (!PgpSecurityConstants.isSecureSymmetricAlgorithm(symmetricEncryptionAlgo)) { - log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1); - decryptionResultBuilder.setInsecure(true); - } - - JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear); - Object dataChunk = plainFact.nextObject(); - int signatureIndex = -1; - CanonicalizedPublicKeyRing signingRing = null; - CanonicalizedPublicKey signingKey = null; - - log.add(LogType.MSG_DC_CLEAR, indent); - indent += 1; - - if (dataChunk instanceof PGPCompressedData) { - log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent + 1); - currentProgress += 2; - updateProgress(R.string.progress_decompressing_data, currentProgress, 100); - - PGPCompressedData compressedData = (PGPCompressedData) dataChunk; - - JcaPGPObjectFactory fact = new JcaPGPObjectFactory(compressedData.getDataStream()); - dataChunk = fact.nextObject(); - plainFact = fact; - } - - PGPOnePassSignature signature = null; - if (dataChunk instanceof PGPOnePassSignatureList) { - log.add(LogType.MSG_DC_CLEAR_SIGNATURE, indent + 1); - currentProgress += 2; - updateProgress(R.string.progress_processing_signature, currentProgress, 100); - - PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk; - - // NOTE: following code is similar to processSignature, but for PGPOnePassSignature - - // go through all signatures - // and find out for which signature we have a key in our database - for (int i = 0; i < sigList.size(); ++i) { - try { - long sigKeyId = sigList.get(i).getKeyID(); - signingRing = mProviderHelper.getCanonicalizedPublicKeyRing( - KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId) - ); - signingKey = signingRing.getPublicKey(sigKeyId); - signatureIndex = i; - } catch (ProviderHelper.NotFoundException e) { - Log.d(Constants.TAG, "key not found, trying next signature..."); - } - } - - if (signingKey != null) { - // key found in our database! - signature = sigList.get(signatureIndex); - - signatureResultBuilder.initValid(signingRing, signingKey); - - JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = - new JcaPGPContentVerifierBuilderProvider() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey()); - } else { - // no key in our database -> return "unknown pub key" status including the first key id - if (!sigList.isEmpty()) { - signatureResultBuilder.setSignatureAvailable(true); - signatureResultBuilder.setKnownKey(false); - signatureResultBuilder.setKeyId(sigList.get(0).getKeyID()); - } - } - - // check for insecure signing key - // TODO: checks on signingRing ? - if (signingKey != null && ! PgpSecurityConstants.isSecureKey(signingKey)) { - log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1); - signatureResultBuilder.setInsecure(true); - } - - dataChunk = plainFact.nextObject(); - } - - if (dataChunk instanceof PGPSignatureList) { - // skip - dataChunk = plainFact.nextObject(); - } - - OpenPgpMetadata metadata; - - if (dataChunk instanceof PGPLiteralData) { - log.add(LogType.MSG_DC_CLEAR_DATA, indent + 1); - indent += 2; - currentProgress += 4; - updateProgress(R.string.progress_decrypting, currentProgress, 100); - - PGPLiteralData literalData = (PGPLiteralData) dataChunk; - - String originalFilename = literalData.getFileName(); - String mimeType = null; - if (literalData.getFormat() == PGPLiteralData.TEXT - || literalData.getFormat() == PGPLiteralData.UTF8) { - mimeType = "text/plain"; - } else { - // try to guess from file ending - String extension = MimeTypeMap.getFileExtensionFromUrl(originalFilename); - if (extension != null) { - MimeTypeMap mime = MimeTypeMap.getSingleton(); - mimeType = mime.getMimeTypeFromExtension(extension); - } - if (mimeType == null) { - mimeType = "application/octet-stream"; - } - } - - if (!"".equals(originalFilename)) { - log.add(LogType.MSG_DC_CLEAR_META_FILE, indent + 1, originalFilename); - } - log.add(LogType.MSG_DC_CLEAR_META_MIME, indent + 1, - mimeType); - log.add(LogType.MSG_DC_CLEAR_META_TIME, indent + 1, - new Date(literalData.getModificationTime().getTime()).toString()); - - // return here if we want to decrypt the metadata only - if (input.isDecryptMetadataOnly()) { - - // this operation skips the entire stream to find the data length! - Long originalSize = literalData.findDataLength(); - - if (originalSize != null) { - log.add(LogType.MSG_DC_CLEAR_META_SIZE, indent + 1, - Long.toString(originalSize)); - } else { - log.add(LogType.MSG_DC_CLEAR_META_SIZE_UNKNOWN, indent + 1); - } - - metadata = new OpenPgpMetadata( - originalFilename, - mimeType, - literalData.getModificationTime().getTime(), - originalSize == null ? 0 : originalSize); - - log.add(LogType.MSG_DC_OK_META_ONLY, indent); - DecryptVerifyResult result = - new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); - result.setCharset(charset); - result.setDecryptionMetadata(metadata); - return result; - } - - int endProgress; - if (signature != null) { - endProgress = 90; - } else if (encryptedData.isIntegrityProtected()) { - endProgress = 95; - } else { - endProgress = 100; - } - ProgressScaler progressScaler = - new ProgressScaler(mProgressable, currentProgress, endProgress, 100); - - InputStream dataIn = literalData.getInputStream(); - - long alreadyWritten = 0; - long wholeSize = 0; // TODO inputData.getSize() - inputData.getStreamPosition(); - int length; - byte[] buffer = new byte[1 << 16]; - while ((length = dataIn.read(buffer)) > 0) { - // Log.d(Constants.TAG, "read bytes: " + length); - if (out != null) { - out.write(buffer, 0, length); - } - - // update signature buffer if signature is also present - if (signature != null) { - signature.update(buffer, 0, length); - } - - alreadyWritten += length; - if (wholeSize > 0) { - long progress = 100 * alreadyWritten / wholeSize; - // stop at 100% for wrong file sizes... - if (progress > 100) { - progress = 100; - } - progressScaler.setProgress((int) progress, 100); - } - // TODO: slow annealing to fake a progress? - } - - metadata = new OpenPgpMetadata( - originalFilename, - mimeType, - literalData.getModificationTime().getTime(), - alreadyWritten); - - if (signature != null) { - updateProgress(R.string.progress_verifying_signature, 90, 100); - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent); - - PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject(); - PGPSignature messageSignature = signatureList.get(signatureIndex); - - // TODO: what about binary signatures? - - // Verify signature - boolean validSignature = signature.verify(messageSignature); - if (validSignature) { - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1); - } else { - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1); - } - - // check for insecure hash algorithms - if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) { - log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1); - signatureResultBuilder.setInsecure(true); - } - - signatureResultBuilder.setValidSignature(validSignature); - } - - indent -= 1; - } else { - // If there is no literalData, we don't have any metadata - metadata = null; - } - - if (encryptedData.isIntegrityProtected()) { - updateProgress(R.string.progress_verifying_integrity, 95, 100); - - if (encryptedData.verify()) { - log.add(LogType.MSG_DC_INTEGRITY_CHECK_OK, indent); - } else { - log.add(LogType.MSG_DC_ERROR_INTEGRITY_CHECK, indent); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - } else { - // If no valid signature is present: - // Handle missing integrity protection like failed integrity protection! - // The MDC packet can be stripped by an attacker! - Log.d(Constants.TAG, "MDC fail"); - if (!signatureResultBuilder.isValidSignature()) { - log.add(LogType.MSG_DC_INSECURE_MDC_MISSING, indent); - decryptionResultBuilder.setInsecure(true); - } - } - - updateProgress(R.string.progress_done, 100, 100); - - log.add(LogType.MSG_DC_OK, indent); - - // Return a positive result, with metadata and verification info - DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); - result.setCachedCryptoInputParcel(cryptoInput); - result.setSignatureResult(signatureResultBuilder.build()); - result.setCharset(charset); - result.setDecryptionResult(decryptionResultBuilder.build()); - result.setDecryptionMetadata(metadata); - return result; - - } - - /** - * This method verifies cleartext signatures - * as defined in http://tools.ietf.org/html/rfc4880#section-7 - *

- * The method is heavily based on - * pg/src/main/java/org/spongycastle/openpgp/examples/ClearSignedFileProcessor.java - */ - @NonNull - private DecryptVerifyResult verifyCleartextSignature( - ArmoredInputStream aIn, OutputStream outputStream, int indent) throws IOException, PGPException { - - OperationLog log = new OperationLog(); - - OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder(); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - - updateProgress(R.string.progress_reading_data, 0, 100); - - ByteArrayOutputStream lineOut = new ByteArrayOutputStream(); - int lookAhead = readInputLine(lineOut, aIn); - byte[] lineSep = getLineSeparator(); - - byte[] line = lineOut.toByteArray(); - out.write(line, 0, getLengthWithoutSeparator(line)); - out.write(lineSep); - - while (lookAhead != -1 && aIn.isClearText()) { - lookAhead = readInputLine(lineOut, lookAhead, aIn); - line = lineOut.toByteArray(); - out.write(line, 0, getLengthWithoutSeparator(line)); - out.write(lineSep); - } - - out.close(); - - byte[] clearText = out.toByteArray(); - if (outputStream != null) { - outputStream.write(clearText); - outputStream.close(); - } - - updateProgress(R.string.progress_processing_signature, 60, 100); - JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(aIn); - - PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject(); - if (sigList == null) { - log.add(LogType.MSG_DC_ERROR_INVALID_DATA, 0); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - - PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder, log, indent); - - if (signature != null) { - try { - updateProgress(R.string.progress_verifying_signature, 90, 100); - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent); - - InputStream sigIn = new BufferedInputStream(new ByteArrayInputStream(clearText)); - - lookAhead = readInputLine(lineOut, sigIn); - - processLine(signature, lineOut.toByteArray()); - - if (lookAhead != -1) { - do { - lookAhead = readInputLine(lineOut, lookAhead, sigIn); - - signature.update((byte) '\r'); - signature.update((byte) '\n'); - - processLine(signature, lineOut.toByteArray()); - } while (lookAhead != -1); - } - - // Verify signature and check binding signatures - boolean validSignature = signature.verify(); - if (validSignature) { - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1); - } else { - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1); - } - - // check for insecure hash algorithms - if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) { - log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1); - signatureResultBuilder.setInsecure(true); - } - - signatureResultBuilder.setValidSignature(validSignature); - - } catch (SignatureException e) { - Log.d(Constants.TAG, "SignatureException", e); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - } - - updateProgress(R.string.progress_done, 100, 100); - - log.add(LogType.MSG_DC_OK, indent); - - OpenPgpMetadata metadata = new OpenPgpMetadata( - "", - "text/plain", - -1, - clearText.length); - - DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); - result.setSignatureResult(signatureResultBuilder.build()); - result.setDecryptionResult( - new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED)); - result.setDecryptionMetadata(metadata); - return result; - } - - @NonNull - private DecryptVerifyResult verifyDetachedSignature( - PgpDecryptVerifyInputParcel input, InputData inputData, OutputStream out, int indent) - throws IOException, PGPException { - - OperationLog log = new OperationLog(); - - OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder(); - - updateProgress(R.string.progress_processing_signature, 0, 100); - InputStream detachedSigIn = new ByteArrayInputStream(input.getDetachedSignature()); - detachedSigIn = PGPUtil.getDecoderStream(detachedSigIn); - - JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(detachedSigIn); - - PGPSignatureList sigList; - Object o = pgpFact.nextObject(); - if (o instanceof PGPCompressedData) { - PGPCompressedData c1 = (PGPCompressedData) o; - pgpFact = new JcaPGPObjectFactory(c1.getDataStream()); - sigList = (PGPSignatureList) pgpFact.nextObject(); - } else if (o instanceof PGPSignatureList) { - sigList = (PGPSignatureList) o; - } else { - log.add(LogType.MSG_DC_ERROR_INVALID_DATA, 0); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - - PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder, log, indent); - - if (signature != null) { - updateProgress(R.string.progress_reading_data, 60, 100); - - ProgressScaler progressScaler = new ProgressScaler(mProgressable, 60, 90, 100); - long alreadyWritten = 0; - long wholeSize = inputData.getSize() - inputData.getStreamPosition(); - int length; - byte[] buffer = new byte[1 << 16]; - InputStream in = inputData.getInputStream(); - while ((length = in.read(buffer)) > 0) { - if (out != null) { - out.write(buffer, 0, length); - } - - // update signature buffer if signature is also present - signature.update(buffer, 0, length); - - alreadyWritten += length; - if (wholeSize > 0) { - long progress = 100 * alreadyWritten / wholeSize; - // stop at 100% for wrong file sizes... - if (progress > 100) { - progress = 100; - } - progressScaler.setProgress((int) progress, 100); - } - // TODO: slow annealing to fake a progress? - } - - updateProgress(R.string.progress_verifying_signature, 90, 100); - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent); - - // Verify signature and check binding signatures - boolean validSignature = signature.verify(); - if (validSignature) { - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1); - } else { - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1); - } - - // check for insecure hash algorithms - if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) { - log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1); - signatureResultBuilder.setInsecure(true); - } - - signatureResultBuilder.setValidSignature(validSignature); - } - - updateProgress(R.string.progress_done, 100, 100); - - log.add(LogType.MSG_DC_OK, indent); - - DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); - result.setSignatureResult(signatureResultBuilder.build()); - result.setDecryptionResult( - new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED)); - return result; - } - - private PGPSignature processPGPSignatureList( - PGPSignatureList sigList, OpenPgpSignatureResultBuilder signatureResultBuilder, - OperationLog log, int indent) - throws PGPException { - CanonicalizedPublicKeyRing signingRing = null; - CanonicalizedPublicKey signingKey = null; - int signatureIndex = -1; - - // go through all signatures - // and find out for which signature we have a key in our database - for (int i = 0; i < sigList.size(); ++i) { - try { - long sigKeyId = sigList.get(i).getKeyID(); - signingRing = mProviderHelper.getCanonicalizedPublicKeyRing( - KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId) - ); - signingKey = signingRing.getPublicKey(sigKeyId); - signatureIndex = i; - } catch (ProviderHelper.NotFoundException e) { - Log.d(Constants.TAG, "key not found, trying next signature..."); - } - } - - PGPSignature signature = null; - - if (signingKey != null) { - // key found in our database! - signature = sigList.get(signatureIndex); - - signatureResultBuilder.initValid(signingRing, signingKey); - - JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = - new JcaPGPContentVerifierBuilderProvider() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey()); - } else { - // no key in our database -> return "unknown pub key" status including the first key id - if (!sigList.isEmpty()) { - signatureResultBuilder.setSignatureAvailable(true); - signatureResultBuilder.setKnownKey(false); - signatureResultBuilder.setKeyId(sigList.get(0).getKeyID()); - } - } - - // check for insecure signing key - // TODO: checks on signingRing ? - if (signingKey != null && ! PgpSecurityConstants.isSecureKey(signingKey)) { - log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1); - signatureResultBuilder.setInsecure(true); - } - - return signature; - } - - /** - * Mostly taken from ClearSignedFileProcessor in Bouncy Castle - */ - private static void processLine(PGPSignature sig, byte[] line) - throws SignatureException { - int length = getLengthWithoutWhiteSpace(line); - if (length > 0) { - sig.update(line, 0, length); - } - } - - private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn) - throws IOException { - bOut.reset(); - - int lookAhead = -1; - int ch; - - while ((ch = fIn.read()) >= 0) { - bOut.write(ch); - if (ch == '\r' || ch == '\n') { - lookAhead = readPastEOL(bOut, ch, fIn); - break; - } - } - - return lookAhead; - } - - private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn) - throws IOException { - bOut.reset(); - - int ch = lookAhead; - - do { - bOut.write(ch); - if (ch == '\r' || ch == '\n') { - lookAhead = readPastEOL(bOut, ch, fIn); - break; - } - } while ((ch = fIn.read()) >= 0); - - if (ch < 0) { - lookAhead = -1; - } - - return lookAhead; - } - - private static int readPastEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn) - throws IOException { - int lookAhead = fIn.read(); - - if (lastCh == '\r' && lookAhead == '\n') { - bOut.write(lookAhead); - lookAhead = fIn.read(); - } - - return lookAhead; - } - - private static int getLengthWithoutSeparator(byte[] line) { - int end = line.length - 1; - - while (end >= 0 && isLineEnding(line[end])) { - end--; - } - - return end + 1; - } - - private static boolean isLineEnding(byte b) { - return b == '\r' || b == '\n'; - } - - private static int getLengthWithoutWhiteSpace(byte[] line) { - int end = line.length - 1; - - while (end >= 0 && isWhiteSpace(line[end])) { - end--; - } - - return end + 1; - } - - private static boolean isWhiteSpace(byte b) { - return b == '\r' || b == '\n' || b == '\t' || b == ' '; - } - - private static byte[] getLineSeparator() { - String nl = System.getProperty("line.separator"); - return nl.getBytes(); - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java new file mode 100644 index 000000000..dd30156f9 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java @@ -0,0 +1,1239 @@ +/* + * Copyright (C) 2012-2014 Dominik Schürmann + * Copyright (C) 2010-2014 Thialfihar + * + * 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 android.content.Context; +import android.support.annotation.NonNull; +import android.webkit.MimeTypeMap; + +import org.openintents.openpgp.OpenPgpDecryptionResult; +import org.openintents.openpgp.OpenPgpMetadata; +import org.openintents.openpgp.OpenPgpSignatureResult; +import org.spongycastle.bcpg.ArmoredInputStream; +import org.spongycastle.openpgp.PGPCompressedData; +import org.spongycastle.openpgp.PGPDataValidationException; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.spongycastle.openpgp.PGPEncryptedDataList; +import org.spongycastle.openpgp.PGPException; +import org.spongycastle.openpgp.PGPKeyValidationException; +import org.spongycastle.openpgp.PGPLiteralData; +import org.spongycastle.openpgp.PGPOnePassSignature; +import org.spongycastle.openpgp.PGPOnePassSignatureList; +import org.spongycastle.openpgp.PGPPBEEncryptedData; +import org.spongycastle.openpgp.PGPPublicKeyEncryptedData; +import org.spongycastle.openpgp.PGPSignature; +import org.spongycastle.openpgp.PGPSignatureList; +import org.spongycastle.openpgp.PGPUtil; +import org.spongycastle.openpgp.jcajce.JcaPGPObjectFactory; +import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory; +import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider; +import org.spongycastle.openpgp.operator.jcajce.CachingDataDecryptorFactory; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; +import org.spongycastle.util.encoders.DecoderException; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Constants.key; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.BaseOperation; +import org.sufficientlysecure.keychain.pgp.CanonicalizedSecretKey.SecretKeyType; +import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; +import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; +import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; +import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; +import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.FileHelper; +import org.sufficientlysecure.keychain.util.InputData; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Passphrase; +import org.sufficientlysecure.keychain.util.ProgressScaler; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.SignatureException; +import java.util.Date; +import java.util.Iterator; + +public class PgpDecryptVerifyOperation extends BaseOperation { + + public PgpDecryptVerifyOperation(Context context, ProviderHelper providerHelper, Progressable progressable) { + super(context, providerHelper, progressable); + } + + /** Decrypts and/or verifies data based on parameters of PgpDecryptVerifyInputParcel. */ + @NonNull + public DecryptVerifyResult execute(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput) { + InputData inputData; + OutputStream outputStream; + + if (input.getInputBytes() != null) { + byte[] inputBytes = input.getInputBytes(); + inputData = new InputData(new ByteArrayInputStream(inputBytes), inputBytes.length); + } else { + try { + InputStream inputStream = mContext.getContentResolver().openInputStream(input.getInputUri()); + long inputSize = FileHelper.getFileSize(mContext, input.getInputUri(), 0); + inputData = new InputData(inputStream, inputSize); + } catch (FileNotFoundException e) { + Log.e(Constants.TAG, "Input URI could not be opened: " + input.getInputUri(), e); + OperationLog log = new OperationLog(); + log.add(LogType.MSG_DC_ERROR_INPUT, 1); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + } + + if (input.getOutputUri() == null) { + outputStream = new ByteArrayOutputStream(); + } else { + try { + outputStream = mContext.getContentResolver().openOutputStream(input.getOutputUri()); + } catch (FileNotFoundException e) { + Log.e(Constants.TAG, "Output URI could not be opened: " + input.getOutputUri(), e); + OperationLog log = new OperationLog(); + log.add(LogType.MSG_DC_ERROR_IO, 1); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + } + + DecryptVerifyResult result = executeInternal(input, cryptoInput, inputData, outputStream); + if (outputStream instanceof ByteArrayOutputStream) { + byte[] outputData = ((ByteArrayOutputStream) outputStream).toByteArray(); + result.setOutputBytes(outputData); + } + + return result; + + } + + @NonNull + public DecryptVerifyResult execute(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput, + InputData inputData, OutputStream outputStream) { + return executeInternal(input, cryptoInput, inputData, outputStream); + } + + @NonNull + private DecryptVerifyResult executeInternal(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput, + InputData inputData, OutputStream outputStream) { + try { + if (input.getDetachedSignature() != null) { + Log.d(Constants.TAG, "Detached signature present, verifying with this signature only"); + + return verifyDetachedSignature(input, inputData, outputStream, 0); + } else { + // automatically works with PGP ascii armor and PGP binary + InputStream in = PGPUtil.getDecoderStream(inputData.getInputStream()); + + if (in instanceof ArmoredInputStream) { + ArmoredInputStream aIn = (ArmoredInputStream) in; + // it is ascii armored + Log.d(Constants.TAG, "ASCII Armor Header Line: " + aIn.getArmorHeaderLine()); + + if (input.isSignedLiteralData()) { + return verifySignedLiteralData(input, aIn, outputStream, 0); + } else if (aIn.isClearText()) { + // a cleartext signature, verify it with the other method + return verifyCleartextSignature(aIn, outputStream, 0); + } else { + // else: ascii armored encryption! go on... + return decryptVerify(input, cryptoInput, in, outputStream, 0); + } + } else { + return decryptVerify(input, cryptoInput, in, outputStream, 0); + } + } + } catch (PGPException e) { + Log.d(Constants.TAG, "PGPException", e); + OperationLog log = new OperationLog(); + log.add(LogType.MSG_DC_ERROR_PGP_EXCEPTION, 1); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } catch (DecoderException | ArrayIndexOutOfBoundsException e) { + // these can happen if assumptions in JcaPGPObjectFactory.nextObject() aren't + // fulfilled, so we need to catch them here to handle this gracefully + Log.d(Constants.TAG, "data error", e); + OperationLog log = new OperationLog(); + log.add(LogType.MSG_DC_ERROR_IO, 1); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } catch (IOException e) { + Log.d(Constants.TAG, "IOException", e); + OperationLog log = new OperationLog(); + log.add(LogType.MSG_DC_ERROR_IO, 1); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + } + + /**Verify signed plaintext data (PGP/INLINE). */ + @NonNull + private DecryptVerifyResult verifySignedLiteralData( + PgpDecryptVerifyInputParcel input, InputStream in, OutputStream out, int indent) + throws IOException, PGPException { + OperationLog log = new OperationLog(); + log.add(LogType.MSG_VL, indent); + + // thinking that the proof-fetching operation is going to take most of the time + updateProgress(R.string.progress_reading_data, 75, 100); + + JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in); + Object o = pgpF.nextObject(); + if (o instanceof PGPCompressedData) { + log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent + 1); + + pgpF = new JcaPGPObjectFactory(((PGPCompressedData) o).getDataStream()); + o = pgpF.nextObject(); + updateProgress(R.string.progress_decompressing_data, 80, 100); + } + + // all we want to see is a OnePassSignatureList followed by LiteralData + if (!(o instanceof PGPOnePassSignatureList)) { + log.add(LogType.MSG_VL_ERROR_MISSING_SIGLIST, indent); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) o; + + // go through all signatures (should be just one), make sure we have + // the key and it matches the one we’re looking for + CanonicalizedPublicKeyRing signingRing = null; + CanonicalizedPublicKey signingKey = null; + int signatureIndex = -1; + for (int i = 0; i < sigList.size(); ++i) { + try { + long sigKeyId = sigList.get(i).getKeyID(); + signingRing = mProviderHelper.getCanonicalizedPublicKeyRing( + KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId) + ); + signingKey = signingRing.getPublicKey(sigKeyId); + signatureIndex = i; + } catch (ProviderHelper.NotFoundException e) { + Log.d(Constants.TAG, "key not found, trying next signature..."); + } + } + + // there has to be a key, and it has to be the right one + if (signingKey == null) { + log.add(LogType.MSG_VL_ERROR_MISSING_KEY, indent); + Log.d(Constants.TAG, "Failed to find key in signed-literal message"); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + + String fingerprint = KeyFormattingUtils.convertFingerprintToHex(signingRing.getFingerprint()); + if (!(input.getRequiredSignerFingerprint().equals(fingerprint))) { + log.add(LogType.MSG_VL_ERROR_MISSING_KEY, indent); + Log.d(Constants.TAG, "Fingerprint mismatch; wanted " + input.getRequiredSignerFingerprint() + + " got " + fingerprint + "!"); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + + OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder(); + + PGPOnePassSignature signature = sigList.get(signatureIndex); + signatureResultBuilder.initValid(signingRing, signingKey); + + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = + new JcaPGPContentVerifierBuilderProvider() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey()); + + o = pgpF.nextObject(); + + if (!(o instanceof PGPLiteralData)) { + log.add(LogType.MSG_VL_ERROR_MISSING_LITERAL, indent); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + + PGPLiteralData literalData = (PGPLiteralData) o; + + log.add(LogType.MSG_DC_CLEAR_DATA, indent + 1); + updateProgress(R.string.progress_decrypting, 85, 100); + + InputStream dataIn = literalData.getInputStream(); + + int length; + byte[] buffer = new byte[1 << 16]; + while ((length = dataIn.read(buffer)) > 0) { + out.write(buffer, 0, length); + signature.update(buffer, 0, length); + } + + updateProgress(R.string.progress_verifying_signature, 95, 100); + log.add(LogType.MSG_VL_CLEAR_SIGNATURE_CHECK, indent + 1); + + PGPSignatureList signatureList = (PGPSignatureList) pgpF.nextObject(); + PGPSignature messageSignature = signatureList.get(signatureIndex); + + // Verify signature and check binding signatures + boolean validSignature = signature.verify(messageSignature); + if (validSignature) { + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1); + } else { + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1); + } + signatureResultBuilder.setValidSignature(validSignature); + + OpenPgpSignatureResult signatureResult = signatureResultBuilder.build(); + + if (signatureResult.getResult() != OpenPgpSignatureResult.RESULT_VALID_CONFIRMED + && signatureResult.getResult() != OpenPgpSignatureResult.RESULT_VALID_UNCONFIRMED) { + log.add(LogType.MSG_VL_ERROR_INTEGRITY_CHECK, indent); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + + updateProgress(R.string.progress_done, 100, 100); + + log.add(LogType.MSG_VL_OK, indent); + + // Return a positive result, with metadata and verification info + DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); + result.setSignatureResult(signatureResult); + result.setDecryptionResult( + new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED)); + return result; + } + + + /** Decrypt and/or verify binary or ascii armored pgp data. */ + @NonNull + private DecryptVerifyResult decryptVerify( + PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput, + InputStream in, OutputStream out, int indent) throws IOException, PGPException { + + OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder(); + OpenPgpDecryptionResultBuilder decryptionResultBuilder = new OpenPgpDecryptionResultBuilder(); + OperationLog log = new OperationLog(); + + log.add(LogType.MSG_DC, indent); + indent += 1; + + JcaPGPObjectFactory pgpF = new JcaPGPObjectFactory(in); + PGPEncryptedDataList enc; + Object o = pgpF.nextObject(); + + int currentProgress = 0; + updateProgress(R.string.progress_reading_data, currentProgress, 100); + + if (o instanceof PGPEncryptedDataList) { + enc = (PGPEncryptedDataList) o; + } else { + enc = (PGPEncryptedDataList) pgpF.nextObject(); + } + + if (enc == null) { + log.add(LogType.MSG_DC_ERROR_INVALID_DATA, indent); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + + InputStream clear; + PGPEncryptedData encryptedData; + + PGPPublicKeyEncryptedData encryptedDataAsymmetric = null; + PGPPBEEncryptedData encryptedDataSymmetric = null; + CanonicalizedSecretKey secretEncryptionKey = null; + Iterator it = enc.getEncryptedDataObjects(); + boolean asymmetricPacketFound = false; + boolean symmetricPacketFound = false; + boolean anyPacketFound = false; + + // If the input stream is armored, and there is a charset specified, take a note for later + // https://tools.ietf.org/html/rfc4880#page56 + String charset = null; + if (in instanceof ArmoredInputStream) { + ArmoredInputStream aIn = (ArmoredInputStream) in; + if (aIn.getArmorHeaders() != null) { + for (String header : aIn.getArmorHeaders()) { + String[] pieces = header.split(":", 2); + if (pieces.length == 2 && "charset".equalsIgnoreCase(pieces[0])) { + charset = pieces[1].trim(); + break; + } + } + if (charset != null) { + log.add(LogType.MSG_DC_CHARSET, indent, charset); + } + } + } + + Passphrase passphrase = null; + boolean skippedDisallowedKey = false; + + // go through all objects and find one we can decrypt + while (it.hasNext()) { + Object obj = it.next(); + if (obj instanceof PGPPublicKeyEncryptedData) { + anyPacketFound = true; + + currentProgress += 2; + updateProgress(R.string.progress_finding_key, currentProgress, 100); + + PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; + long subKeyId = encData.getKeyID(); + + log.add(LogType.MSG_DC_ASYM, indent, + KeyFormattingUtils.convertKeyIdToHex(subKeyId)); + + CanonicalizedSecretKeyRing secretKeyRing; + try { + // get actual keyring object based on master key id + secretKeyRing = mProviderHelper.getCanonicalizedSecretKeyRing( + KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(subKeyId) + ); + } catch (ProviderHelper.NotFoundException e) { + // continue with the next packet in the while loop + log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); + continue; + } + if (secretKeyRing == null) { + // continue with the next packet in the while loop + log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); + continue; + } + + // allow only specific keys for decryption? + if (input.getAllowedKeyIds() != null) { + long masterKeyId = secretKeyRing.getMasterKeyId(); + Log.d(Constants.TAG, "encData.getKeyID(): " + subKeyId); + Log.d(Constants.TAG, "mAllowedKeyIds: " + input.getAllowedKeyIds()); + Log.d(Constants.TAG, "masterKeyId: " + masterKeyId); + + if (!input.getAllowedKeyIds().contains(masterKeyId)) { + // this key is in our db, but NOT allowed! + // continue with the next packet in the while loop + skippedDisallowedKey = true; + log.add(LogType.MSG_DC_ASKIP_NOT_ALLOWED, indent + 1); + continue; + } + } + + // get subkey which has been used for this encryption packet + secretEncryptionKey = secretKeyRing.getSecretKey(subKeyId); + if (secretEncryptionKey == null) { + // should actually never happen, so no need to be more specific. + log.add(LogType.MSG_DC_ASKIP_NO_KEY, indent + 1); + continue; + } + + /* secret key exists in database and is allowed! */ + asymmetricPacketFound = true; + + encryptedDataAsymmetric = encData; + + if (secretEncryptionKey.getSecretKeyType() == SecretKeyType.DIVERT_TO_CARD) { + passphrase = null; + } else if (cryptoInput.hasPassphrase()) { + passphrase = cryptoInput.getPassphrase(); + } else { + // if no passphrase was explicitly set try to get it from the cache service + try { + // returns "" if key has no passphrase + passphrase = getCachedPassphrase(subKeyId); + log.add(LogType.MSG_DC_PASS_CACHED, indent + 1); + } catch (PassphraseCacheInterface.NoSecretKeyException e) { + log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + + // if passphrase was not cached, return here indicating that a passphrase is missing! + if (passphrase == null) { + log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1); + return new DecryptVerifyResult(log, + RequiredInputParcel.createRequiredDecryptPassphrase( + secretKeyRing.getMasterKeyId(), secretEncryptionKey.getKeyId()), + cryptoInput); + } + } + + // check for insecure encryption key + if ( ! PgpSecurityConstants.isSecureKey(secretEncryptionKey)) { + log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1); + decryptionResultBuilder.setInsecure(true); + } + + // break out of while, only decrypt the first packet where we have a key + break; + + } else if (obj instanceof PGPPBEEncryptedData) { + anyPacketFound = true; + + log.add(LogType.MSG_DC_SYM, indent); + + if (!input.isAllowSymmetricDecryption()) { + log.add(LogType.MSG_DC_SYM_SKIP, indent + 1); + continue; + } + + /* + * When mAllowSymmetricDecryption == true and we find a data packet here, + * we do not search for other available asymmetric packets! + */ + symmetricPacketFound = true; + + encryptedDataSymmetric = (PGPPBEEncryptedData) obj; + + // if no passphrase is given, return here + // indicating that a passphrase is missing! + if (!cryptoInput.hasPassphrase()) { + + try { + passphrase = getCachedPassphrase(key.symmetric); + log.add(LogType.MSG_DC_PASS_CACHED, indent + 1); + } catch (PassphraseCacheInterface.NoSecretKeyException e) { + // nvm + } + + if (passphrase == null) { + log.add(LogType.MSG_DC_PENDING_PASSPHRASE, indent + 1); + return new DecryptVerifyResult(log, + RequiredInputParcel.createRequiredSymmetricPassphrase(), + cryptoInput); + } + + } else { + passphrase = cryptoInput.getPassphrase(); + } + + // break out of while, only decrypt the first packet + break; + } + } + + // More data, just acknowledge and ignore. + while (it.hasNext()) { + Object obj = it.next(); + if (obj instanceof PGPPublicKeyEncryptedData) { + PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj; + long subKeyId = encData.getKeyID(); + log.add(LogType.MSG_DC_TRAIL_ASYM, indent, + KeyFormattingUtils.convertKeyIdToHex(subKeyId)); + } else if (obj instanceof PGPPBEEncryptedData) { + log.add(LogType.MSG_DC_TRAIL_SYM, indent); + } else { + log.add(LogType.MSG_DC_TRAIL_UNKNOWN, indent); + } + } + + log.add(LogType.MSG_DC_PREP_STREAMS, indent); + + // we made sure above one of these two would be true + int symmetricEncryptionAlgo; + if (symmetricPacketFound) { + currentProgress += 2; + updateProgress(R.string.progress_preparing_streams, currentProgress, 100); + + PGPDigestCalculatorProvider digestCalcProvider = new JcaPGPDigestCalculatorProviderBuilder() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(); + PBEDataDecryptorFactory decryptorFactory = new JcePBEDataDecryptorFactoryBuilder( + digestCalcProvider).setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( + passphrase.getCharArray()); + + try { + clear = encryptedDataSymmetric.getDataStream(decryptorFactory); + } catch (PGPDataValidationException e) { + log.add(LogType.MSG_DC_ERROR_SYM_PASSPHRASE, indent +1); + return new DecryptVerifyResult(log, + RequiredInputParcel.createRequiredSymmetricPassphrase(), cryptoInput); + } + + encryptedData = encryptedDataSymmetric; + + symmetricEncryptionAlgo = encryptedDataSymmetric.getSymmetricAlgorithm(decryptorFactory); + } else if (asymmetricPacketFound) { + currentProgress += 2; + updateProgress(R.string.progress_extracting_key, currentProgress, 100); + + try { + log.add(LogType.MSG_DC_UNLOCKING, indent + 1); + if (!secretEncryptionKey.unlock(passphrase)) { + log.add(LogType.MSG_DC_ERROR_BAD_PASSPHRASE, indent + 1); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + } catch (PgpGeneralException e) { + log.add(LogType.MSG_DC_ERROR_EXTRACT_KEY, indent + 1); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + + currentProgress += 2; + updateProgress(R.string.progress_preparing_streams, currentProgress, 100); + + CachingDataDecryptorFactory decryptorFactory + = secretEncryptionKey.getCachingDecryptorFactory(cryptoInput); + + // special case: if the decryptor does not have a session key cached for this encrypted + // data, and can't actually decrypt on its own, return a pending intent + if (!decryptorFactory.canDecrypt() + && !decryptorFactory.hasCachedSessionData(encryptedDataAsymmetric)) { + + log.add(LogType.MSG_DC_PENDING_NFC, indent + 1); + return new DecryptVerifyResult(log, RequiredInputParcel.createNfcDecryptOperation( + secretEncryptionKey.getRing().getMasterKeyId(), + secretEncryptionKey.getKeyId(), encryptedDataAsymmetric.getSessionKey()[0] + ), + cryptoInput); + + } + + try { + clear = encryptedDataAsymmetric.getDataStream(decryptorFactory); + } catch (PGPKeyValidationException | ArrayIndexOutOfBoundsException e) { + log.add(LogType.MSG_DC_ERROR_CORRUPT_DATA, indent + 1); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + + symmetricEncryptionAlgo = encryptedDataAsymmetric.getSymmetricAlgorithm(decryptorFactory); + + cryptoInput.addCryptoData(decryptorFactory.getCachedSessionKeys()); + + encryptedData = encryptedDataAsymmetric; + } else { + // there wasn't even any useful data + if (!anyPacketFound) { + log.add(LogType.MSG_DC_ERROR_NO_DATA, indent + 1); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_NO_DATA, log); + } + // there was data but key wasn't allowed + if (skippedDisallowedKey) { + log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_KEY_DISALLOWED, log); + } + // no packet has been found where we have the corresponding secret key in our db + log.add(LogType.MSG_DC_ERROR_NO_KEY, indent + 1); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + decryptionResultBuilder.setEncrypted(true); + + // Check for insecure encryption algorithms! + if (!PgpSecurityConstants.isSecureSymmetricAlgorithm(symmetricEncryptionAlgo)) { + log.add(LogType.MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO, indent + 1); + decryptionResultBuilder.setInsecure(true); + } + + JcaPGPObjectFactory plainFact = new JcaPGPObjectFactory(clear); + Object dataChunk = plainFact.nextObject(); + int signatureIndex = -1; + CanonicalizedPublicKeyRing signingRing = null; + CanonicalizedPublicKey signingKey = null; + + log.add(LogType.MSG_DC_CLEAR, indent); + indent += 1; + + if (dataChunk instanceof PGPCompressedData) { + log.add(LogType.MSG_DC_CLEAR_DECOMPRESS, indent + 1); + currentProgress += 2; + updateProgress(R.string.progress_decompressing_data, currentProgress, 100); + + PGPCompressedData compressedData = (PGPCompressedData) dataChunk; + + JcaPGPObjectFactory fact = new JcaPGPObjectFactory(compressedData.getDataStream()); + dataChunk = fact.nextObject(); + plainFact = fact; + } + + PGPOnePassSignature signature = null; + if (dataChunk instanceof PGPOnePassSignatureList) { + log.add(LogType.MSG_DC_CLEAR_SIGNATURE, indent + 1); + currentProgress += 2; + updateProgress(R.string.progress_processing_signature, currentProgress, 100); + + PGPOnePassSignatureList sigList = (PGPOnePassSignatureList) dataChunk; + + // NOTE: following code is similar to processSignature, but for PGPOnePassSignature + + // go through all signatures + // and find out for which signature we have a key in our database + for (int i = 0; i < sigList.size(); ++i) { + try { + long sigKeyId = sigList.get(i).getKeyID(); + signingRing = mProviderHelper.getCanonicalizedPublicKeyRing( + KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId) + ); + signingKey = signingRing.getPublicKey(sigKeyId); + signatureIndex = i; + } catch (ProviderHelper.NotFoundException e) { + Log.d(Constants.TAG, "key not found, trying next signature..."); + } + } + + if (signingKey != null) { + // key found in our database! + signature = sigList.get(signatureIndex); + + signatureResultBuilder.initValid(signingRing, signingKey); + + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = + new JcaPGPContentVerifierBuilderProvider() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey()); + } else { + // no key in our database -> return "unknown pub key" status including the first key id + if (!sigList.isEmpty()) { + signatureResultBuilder.setSignatureAvailable(true); + signatureResultBuilder.setKnownKey(false); + signatureResultBuilder.setKeyId(sigList.get(0).getKeyID()); + } + } + + // check for insecure signing key + // TODO: checks on signingRing ? + if (signingKey != null && ! PgpSecurityConstants.isSecureKey(signingKey)) { + log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1); + signatureResultBuilder.setInsecure(true); + } + + dataChunk = plainFact.nextObject(); + } + + if (dataChunk instanceof PGPSignatureList) { + // skip + dataChunk = plainFact.nextObject(); + } + + OpenPgpMetadata metadata; + + if (dataChunk instanceof PGPLiteralData) { + log.add(LogType.MSG_DC_CLEAR_DATA, indent + 1); + indent += 2; + currentProgress += 4; + updateProgress(R.string.progress_decrypting, currentProgress, 100); + + PGPLiteralData literalData = (PGPLiteralData) dataChunk; + + String originalFilename = literalData.getFileName(); + String mimeType = null; + if (literalData.getFormat() == PGPLiteralData.TEXT + || literalData.getFormat() == PGPLiteralData.UTF8) { + mimeType = "text/plain"; + } else { + // try to guess from file ending + String extension = MimeTypeMap.getFileExtensionFromUrl(originalFilename); + if (extension != null) { + MimeTypeMap mime = MimeTypeMap.getSingleton(); + mimeType = mime.getMimeTypeFromExtension(extension); + } + if (mimeType == null) { + mimeType = "application/octet-stream"; + } + } + + if (!"".equals(originalFilename)) { + log.add(LogType.MSG_DC_CLEAR_META_FILE, indent + 1, originalFilename); + } + log.add(LogType.MSG_DC_CLEAR_META_MIME, indent + 1, + mimeType); + log.add(LogType.MSG_DC_CLEAR_META_TIME, indent + 1, + new Date(literalData.getModificationTime().getTime()).toString()); + + // return here if we want to decrypt the metadata only + if (input.isDecryptMetadataOnly()) { + + // this operation skips the entire stream to find the data length! + Long originalSize = literalData.findDataLength(); + + if (originalSize != null) { + log.add(LogType.MSG_DC_CLEAR_META_SIZE, indent + 1, + Long.toString(originalSize)); + } else { + log.add(LogType.MSG_DC_CLEAR_META_SIZE_UNKNOWN, indent + 1); + } + + metadata = new OpenPgpMetadata( + originalFilename, + mimeType, + literalData.getModificationTime().getTime(), + originalSize == null ? 0 : originalSize); + + log.add(LogType.MSG_DC_OK_META_ONLY, indent); + DecryptVerifyResult result = + new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); + result.setCharset(charset); + result.setDecryptionMetadata(metadata); + return result; + } + + int endProgress; + if (signature != null) { + endProgress = 90; + } else if (encryptedData.isIntegrityProtected()) { + endProgress = 95; + } else { + endProgress = 100; + } + ProgressScaler progressScaler = + new ProgressScaler(mProgressable, currentProgress, endProgress, 100); + + InputStream dataIn = literalData.getInputStream(); + + long alreadyWritten = 0; + long wholeSize = 0; // TODO inputData.getSize() - inputData.getStreamPosition(); + int length; + byte[] buffer = new byte[1 << 16]; + while ((length = dataIn.read(buffer)) > 0) { + // Log.d(Constants.TAG, "read bytes: " + length); + if (out != null) { + out.write(buffer, 0, length); + } + + // update signature buffer if signature is also present + if (signature != null) { + signature.update(buffer, 0, length); + } + + alreadyWritten += length; + if (wholeSize > 0) { + long progress = 100 * alreadyWritten / wholeSize; + // stop at 100% for wrong file sizes... + if (progress > 100) { + progress = 100; + } + progressScaler.setProgress((int) progress, 100); + } + // TODO: slow annealing to fake a progress? + } + + metadata = new OpenPgpMetadata( + originalFilename, + mimeType, + literalData.getModificationTime().getTime(), + alreadyWritten); + + if (signature != null) { + updateProgress(R.string.progress_verifying_signature, 90, 100); + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent); + + PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject(); + PGPSignature messageSignature = signatureList.get(signatureIndex); + + // TODO: what about binary signatures? + + // Verify signature + boolean validSignature = signature.verify(messageSignature); + if (validSignature) { + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1); + } else { + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1); + } + + // check for insecure hash algorithms + if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) { + log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1); + signatureResultBuilder.setInsecure(true); + } + + signatureResultBuilder.setValidSignature(validSignature); + } + + indent -= 1; + } else { + // If there is no literalData, we don't have any metadata + metadata = null; + } + + if (encryptedData.isIntegrityProtected()) { + updateProgress(R.string.progress_verifying_integrity, 95, 100); + + if (encryptedData.verify()) { + log.add(LogType.MSG_DC_INTEGRITY_CHECK_OK, indent); + } else { + log.add(LogType.MSG_DC_ERROR_INTEGRITY_CHECK, indent); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + } else { + // If no valid signature is present: + // Handle missing integrity protection like failed integrity protection! + // The MDC packet can be stripped by an attacker! + Log.d(Constants.TAG, "MDC fail"); + if (!signatureResultBuilder.isValidSignature()) { + log.add(LogType.MSG_DC_INSECURE_MDC_MISSING, indent); + decryptionResultBuilder.setInsecure(true); + } + } + + updateProgress(R.string.progress_done, 100, 100); + + log.add(LogType.MSG_DC_OK, indent); + + // Return a positive result, with metadata and verification info + DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); + result.setCachedCryptoInputParcel(cryptoInput); + result.setSignatureResult(signatureResultBuilder.build()); + result.setCharset(charset); + result.setDecryptionResult(decryptionResultBuilder.build()); + result.setDecryptionMetadata(metadata); + return result; + + } + + /** + * This method verifies cleartext signatures + * as defined in http://tools.ietf.org/html/rfc4880#section-7 + *

+ * The method is heavily based on + * pg/src/main/java/org/spongycastle/openpgp/examples/ClearSignedFileProcessor.java + */ + @NonNull + private DecryptVerifyResult verifyCleartextSignature( + ArmoredInputStream aIn, OutputStream outputStream, int indent) throws IOException, PGPException { + + OperationLog log = new OperationLog(); + + OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder(); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + updateProgress(R.string.progress_reading_data, 0, 100); + + ByteArrayOutputStream lineOut = new ByteArrayOutputStream(); + int lookAhead = readInputLine(lineOut, aIn); + byte[] lineSep = getLineSeparator(); + + byte[] line = lineOut.toByteArray(); + out.write(line, 0, getLengthWithoutSeparator(line)); + out.write(lineSep); + + while (lookAhead != -1 && aIn.isClearText()) { + lookAhead = readInputLine(lineOut, lookAhead, aIn); + line = lineOut.toByteArray(); + out.write(line, 0, getLengthWithoutSeparator(line)); + out.write(lineSep); + } + + out.close(); + + byte[] clearText = out.toByteArray(); + if (outputStream != null) { + outputStream.write(clearText); + outputStream.close(); + } + + updateProgress(R.string.progress_processing_signature, 60, 100); + JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(aIn); + + PGPSignatureList sigList = (PGPSignatureList) pgpFact.nextObject(); + if (sigList == null) { + log.add(LogType.MSG_DC_ERROR_INVALID_DATA, 0); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + + PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder, log, indent); + + if (signature != null) { + try { + updateProgress(R.string.progress_verifying_signature, 90, 100); + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent); + + InputStream sigIn = new BufferedInputStream(new ByteArrayInputStream(clearText)); + + lookAhead = readInputLine(lineOut, sigIn); + + processLine(signature, lineOut.toByteArray()); + + if (lookAhead != -1) { + do { + lookAhead = readInputLine(lineOut, lookAhead, sigIn); + + signature.update((byte) '\r'); + signature.update((byte) '\n'); + + processLine(signature, lineOut.toByteArray()); + } while (lookAhead != -1); + } + + // Verify signature and check binding signatures + boolean validSignature = signature.verify(); + if (validSignature) { + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1); + } else { + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1); + } + + // check for insecure hash algorithms + if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) { + log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1); + signatureResultBuilder.setInsecure(true); + } + + signatureResultBuilder.setValidSignature(validSignature); + + } catch (SignatureException e) { + Log.d(Constants.TAG, "SignatureException", e); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + } + + updateProgress(R.string.progress_done, 100, 100); + + log.add(LogType.MSG_DC_OK, indent); + + OpenPgpMetadata metadata = new OpenPgpMetadata( + "", + "text/plain", + -1, + clearText.length); + + DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); + result.setSignatureResult(signatureResultBuilder.build()); + result.setDecryptionResult( + new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED)); + result.setDecryptionMetadata(metadata); + return result; + } + + @NonNull + private DecryptVerifyResult verifyDetachedSignature( + PgpDecryptVerifyInputParcel input, InputData inputData, OutputStream out, int indent) + throws IOException, PGPException { + + OperationLog log = new OperationLog(); + + OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder(); + + updateProgress(R.string.progress_processing_signature, 0, 100); + InputStream detachedSigIn = new ByteArrayInputStream(input.getDetachedSignature()); + detachedSigIn = PGPUtil.getDecoderStream(detachedSigIn); + + JcaPGPObjectFactory pgpFact = new JcaPGPObjectFactory(detachedSigIn); + + PGPSignatureList sigList; + Object o = pgpFact.nextObject(); + if (o instanceof PGPCompressedData) { + PGPCompressedData c1 = (PGPCompressedData) o; + pgpFact = new JcaPGPObjectFactory(c1.getDataStream()); + sigList = (PGPSignatureList) pgpFact.nextObject(); + } else if (o instanceof PGPSignatureList) { + sigList = (PGPSignatureList) o; + } else { + log.add(LogType.MSG_DC_ERROR_INVALID_DATA, 0); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + + PGPSignature signature = processPGPSignatureList(sigList, signatureResultBuilder, log, indent); + + if (signature != null) { + updateProgress(R.string.progress_reading_data, 60, 100); + + ProgressScaler progressScaler = new ProgressScaler(mProgressable, 60, 90, 100); + long alreadyWritten = 0; + long wholeSize = inputData.getSize() - inputData.getStreamPosition(); + int length; + byte[] buffer = new byte[1 << 16]; + InputStream in = inputData.getInputStream(); + while ((length = in.read(buffer)) > 0) { + if (out != null) { + out.write(buffer, 0, length); + } + + // update signature buffer if signature is also present + signature.update(buffer, 0, length); + + alreadyWritten += length; + if (wholeSize > 0) { + long progress = 100 * alreadyWritten / wholeSize; + // stop at 100% for wrong file sizes... + if (progress > 100) { + progress = 100; + } + progressScaler.setProgress((int) progress, 100); + } + // TODO: slow annealing to fake a progress? + } + + updateProgress(R.string.progress_verifying_signature, 90, 100); + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent); + + // Verify signature and check binding signatures + boolean validSignature = signature.verify(); + if (validSignature) { + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1); + } else { + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1); + } + + // check for insecure hash algorithms + if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) { + log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1); + signatureResultBuilder.setInsecure(true); + } + + signatureResultBuilder.setValidSignature(validSignature); + } + + updateProgress(R.string.progress_done, 100, 100); + + log.add(LogType.MSG_DC_OK, indent); + + DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); + result.setSignatureResult(signatureResultBuilder.build()); + result.setDecryptionResult( + new OpenPgpDecryptionResult(OpenPgpDecryptionResult.RESULT_NOT_ENCRYPTED)); + return result; + } + + private PGPSignature processPGPSignatureList( + PGPSignatureList sigList, OpenPgpSignatureResultBuilder signatureResultBuilder, + OperationLog log, int indent) + throws PGPException { + CanonicalizedPublicKeyRing signingRing = null; + CanonicalizedPublicKey signingKey = null; + int signatureIndex = -1; + + // go through all signatures + // and find out for which signature we have a key in our database + for (int i = 0; i < sigList.size(); ++i) { + try { + long sigKeyId = sigList.get(i).getKeyID(); + signingRing = mProviderHelper.getCanonicalizedPublicKeyRing( + KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(sigKeyId) + ); + signingKey = signingRing.getPublicKey(sigKeyId); + signatureIndex = i; + } catch (ProviderHelper.NotFoundException e) { + Log.d(Constants.TAG, "key not found, trying next signature..."); + } + } + + PGPSignature signature = null; + + if (signingKey != null) { + // key found in our database! + signature = sigList.get(signatureIndex); + + signatureResultBuilder.initValid(signingRing, signingKey); + + JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider = + new JcaPGPContentVerifierBuilderProvider() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + signature.init(contentVerifierBuilderProvider, signingKey.getPublicKey()); + } else { + // no key in our database -> return "unknown pub key" status including the first key id + if (!sigList.isEmpty()) { + signatureResultBuilder.setSignatureAvailable(true); + signatureResultBuilder.setKnownKey(false); + signatureResultBuilder.setKeyId(sigList.get(0).getKeyID()); + } + } + + // check for insecure signing key + // TODO: checks on signingRing ? + if (signingKey != null && ! PgpSecurityConstants.isSecureKey(signingKey)) { + log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1); + signatureResultBuilder.setInsecure(true); + } + + return signature; + } + + /** + * Mostly taken from ClearSignedFileProcessor in Bouncy Castle + */ + private static void processLine(PGPSignature sig, byte[] line) + throws SignatureException { + int length = getLengthWithoutWhiteSpace(line); + if (length > 0) { + sig.update(line, 0, length); + } + } + + private static int readInputLine(ByteArrayOutputStream bOut, InputStream fIn) + throws IOException { + bOut.reset(); + + int lookAhead = -1; + int ch; + + while ((ch = fIn.read()) >= 0) { + bOut.write(ch); + if (ch == '\r' || ch == '\n') { + lookAhead = readPastEOL(bOut, ch, fIn); + break; + } + } + + return lookAhead; + } + + private static int readInputLine(ByteArrayOutputStream bOut, int lookAhead, InputStream fIn) + throws IOException { + bOut.reset(); + + int ch = lookAhead; + + do { + bOut.write(ch); + if (ch == '\r' || ch == '\n') { + lookAhead = readPastEOL(bOut, ch, fIn); + break; + } + } while ((ch = fIn.read()) >= 0); + + if (ch < 0) { + lookAhead = -1; + } + + return lookAhead; + } + + private static int readPastEOL(ByteArrayOutputStream bOut, int lastCh, InputStream fIn) + throws IOException { + int lookAhead = fIn.read(); + + if (lastCh == '\r' && lookAhead == '\n') { + bOut.write(lookAhead); + lookAhead = fIn.read(); + } + + return lookAhead; + } + + private static int getLengthWithoutSeparator(byte[] line) { + int end = line.length - 1; + + while (end >= 0 && isLineEnding(line[end])) { + end--; + } + + return end + 1; + } + + private static boolean isLineEnding(byte b) { + return b == '\r' || b == '\n'; + } + + private static int getLengthWithoutWhiteSpace(byte[] line) { + int end = line.length - 1; + + while (end >= 0 && isWhiteSpace(line[end])) { + end--; + } + + return end + 1; + } + + private static boolean isWhiteSpace(byte b) { + return b == '\r' || b == '\n' || b == '\t' || b == ' '; + } + + private static byte[] getLineSeparator() { + String nl = System.getProperty("line.separator"); + return nl.getBytes(); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index 9e6681341..49079f585 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -38,7 +38,7 @@ import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel; import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult; import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants; -import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel; import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation; @@ -533,7 +533,7 @@ public class OpenPgpService extends RemoteService { byte[] detachedSignature = data.getByteArrayExtra(OpenPgpApi.EXTRA_DETACHED_SIGNATURE); - PgpDecryptVerify op = new PgpDecryptVerify(this, mProviderHelper, null); + PgpDecryptVerifyOperation op = new PgpDecryptVerifyOperation(this, mProviderHelper, null); long inputLength = inputStream.available(); InputData inputData = new InputData(inputStream, inputLength); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java index dca2a08c2..eff27f112 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainService.java @@ -40,7 +40,7 @@ import org.sufficientlysecure.keychain.operations.PromoteKeyOperation; import org.sufficientlysecure.keychain.operations.RevokeOperation; import org.sufficientlysecure.keychain.operations.SignEncryptOperation; import org.sufficientlysecure.keychain.operations.results.OperationResult; -import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; +import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyOperation; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.SignEncryptParcel; @@ -111,7 +111,7 @@ public class KeychainService extends Service implements Progressable { op = new SignEncryptOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled); } else if (inputParcel instanceof PgpDecryptVerifyInputParcel) { - op = new PgpDecryptVerify(outerThis, new ProviderHelper(outerThis), outerThis); + op = new PgpDecryptVerifyOperation(outerThis, new ProviderHelper(outerThis), outerThis); } else if (inputParcel instanceof SaveKeyringParcel) { op = new EditKeyOperation(outerThis, new ProviderHelper(outerThis), outerThis, mActionCanceled); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java index a6b8a0e85..7a9df1576 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java @@ -51,9 +51,6 @@ public class CertifyFingerprintFragment extends LoaderFragment implements private Uri mDataUri; - private View mActionNo; - private View mActionYes; - /** * Creates new instance of this fragment */ @@ -72,18 +69,18 @@ public class CertifyFingerprintFragment extends LoaderFragment implements View root = super.onCreateView(inflater, superContainer, savedInstanceState); View view = inflater.inflate(R.layout.certify_fingerprint_fragment, getContainer()); - mActionNo = view.findViewById(R.id.certify_fingerprint_button_no); - mActionYes = view.findViewById(R.id.certify_fingerprint_button_yes); + View actionNo = view.findViewById(R.id.certify_fingerprint_button_no); + View actionYes = view.findViewById(R.id.certify_fingerprint_button_yes); mFingerprint = (TextView) view.findViewById(R.id.certify_fingerprint_fingerprint); - mActionNo.setOnClickListener(new View.OnClickListener() { + actionNo.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { getActivity().finish(); } }); - mActionYes.setOnClickListener(new View.OnClickListener() { + actionYes.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { certify(mDataUri); 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 d7224bd04..881fb05aa 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseDialogActivity.java @@ -280,51 +280,42 @@ public class PassphraseDialogActivity extends FragmentActivity { mPassphraseText.setText(message); - if (keyType == CanonicalizedSecretKey.SecretKeyType.PATTERN) { - // start pattern dialog and show progress circle here... -// Intent patternActivity = new Intent(getActivity(), LockPatternActivity.class); -// patternActivity.putExtra(LockPatternActivity.EXTRA_PATTERN, "123"); -// startActivityForResult(patternActivity, REQUEST_CODE_ENTER_PATTERN); - mInput.setVisibility(View.INVISIBLE); - mProgress.setVisibility(View.VISIBLE); - } else { - // Hack to open keyboard. - // This is the only method that I found to work across all Android versions - // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/ - // Notes: * onCreateView can't be used because we want to add buttons to the dialog - // * opening in onActivityCreated does not work on Android 4.4 - mPassphraseEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - mPassphraseEditText.post(new Runnable() { - @Override - public void run() { - if (getActivity() == null || mPassphraseEditText == null) { - return; - } - InputMethodManager imm = (InputMethodManager) getActivity() - .getSystemService(Context.INPUT_METHOD_SERVICE); - imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT); + // Hack to open keyboard. + // This is the only method that I found to work across all Android versions + // http://turbomanage.wordpress.com/2012/05/02/show-soft-keyboard-automatically-when-edittext-receives-focus/ + // Notes: * onCreateView can't be used because we want to add buttons to the dialog + // * opening in onActivityCreated does not work on Android 4.4 + mPassphraseEditText.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + mPassphraseEditText.post(new Runnable() { + @Override + public void run() { + if (getActivity() == null || mPassphraseEditText == null) { + return; } - }); - } - }); - mPassphraseEditText.requestFocus(); - - mPassphraseEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE); - mPassphraseEditText.setOnEditorActionListener(this); - - if ((keyType == CanonicalizedSecretKey.SecretKeyType.DIVERT_TO_CARD && Preferences.getPreferences(activity).useNumKeypadForYubiKeyPin()) - || keyType == CanonicalizedSecretKey.SecretKeyType.PIN) { - mPassphraseEditText.setInputType(InputType.TYPE_CLASS_NUMBER); - mPassphraseEditText.setTransformationMethod(PasswordTransformationMethod.getInstance()); - } else { - mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); + InputMethodManager imm = (InputMethodManager) getActivity() + .getSystemService(Context.INPUT_METHOD_SERVICE); + imm.showSoftInput(mPassphraseEditText, InputMethodManager.SHOW_IMPLICIT); + } + }); } + }); + mPassphraseEditText.requestFocus(); + mPassphraseEditText.setImeActionLabel(getString(android.R.string.ok), EditorInfo.IME_ACTION_DONE); + mPassphraseEditText.setOnEditorActionListener(this); + + if ((keyType == CanonicalizedSecretKey.SecretKeyType.DIVERT_TO_CARD && Preferences.getPreferences(activity).useNumKeypadForYubiKeyPin()) + || keyType == CanonicalizedSecretKey.SecretKeyType.PIN) { + mPassphraseEditText.setInputType(InputType.TYPE_CLASS_NUMBER); mPassphraseEditText.setTransformationMethod(PasswordTransformationMethod.getInstance()); + } else { + mPassphraseEditText.setRawInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD); } + mPassphraseEditText.setTransformationMethod(PasswordTransformationMethod.getInstance()); + AlertDialog dialog = alert.create(); dialog.setButton(DialogInterface.BUTTON_POSITIVE, activity.getString(R.string.btn_unlock), (DialogInterface.OnClickListener) null); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java deleted file mode 100644 index e55494145..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/PassphraseWizardActivity.java +++ /dev/null @@ -1,577 +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; - -import android.annotation.TargetApi; -import android.support.v7.app.AlertDialog; -import android.app.PendingIntent; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.IntentFilter; -import android.nfc.FormatException; -import android.nfc.NdefMessage; -import android.nfc.NdefRecord; -import android.nfc.NfcAdapter; -import android.nfc.Tag; -import android.nfc.tech.Ndef; -import android.os.Build; -import android.os.Bundle; -import android.provider.Settings; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentTransaction; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.Toast; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.util.Log; - -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.security.SecureRandom; -import java.util.Arrays; -import java.util.List; - -@TargetApi(Build.VERSION_CODES.HONEYCOMB) -public class PassphraseWizardActivity extends FragmentActivity { -//public class PassphraseWizardActivity extends FragmentActivity implements LockPatternView.OnPatternListener { - //create or authenticate - public String selectedAction; - //for lockpattern - public static char[] pattern; - private static String passphrase = ""; - //nfc string - private static byte[] output = new byte[8]; - - public static final String CREATE_METHOD = "create"; - public static final String AUTHENTICATION = "authenticate"; - - NfcAdapter adapter; - PendingIntent pendingIntent; - IntentFilter writeTagFilters[]; - boolean writeMode; - Tag myTag; - boolean writeNFC = false; - boolean readNFC = false; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getActionBar() != null) { - getActionBar().setTitle(R.string.unlock_method); - } - - selectedAction = getIntent().getAction(); - if (savedInstanceState == null) { - SelectMethods selectMethods = new SelectMethods(); - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.add(R.id.fragmentContainer, selectMethods).commit(); - } - setContentView(R.layout.passphrase_wizard); - - adapter = NfcAdapter.getDefaultAdapter(this); - if (adapter != null) { - pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, PassphraseWizardActivity.class).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); - IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED); - tagDetected.addCategory(Intent.CATEGORY_DEFAULT); - writeTagFilters = new IntentFilter[]{tagDetected}; - } - } - - public void noPassphrase(View view) { - passphrase = ""; - Toast.makeText(this, R.string.no_passphrase_set, Toast.LENGTH_SHORT).show(); - this.finish(); - } - - public void passphrase(View view) { - Passphrase passphrase = new Passphrase(); - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.fragmentContainer, passphrase).addToBackStack(null).commit(); - } - - public void startLockpattern(View view) { - if (getActionBar() != null) { - getActionBar().setTitle(R.string.draw_lockpattern); - } -// LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction); -// LockPatternFragment lpf = LockPatternFragment.newInstance("asd"); - -// FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); -// transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit(); - } - - public void cancel(View view) { - this.finish(); - } - - public void savePassphrase(View view) { - EditText passphrase = (EditText) findViewById(R.id.passphrase); - passphrase.setError(null); - String pw = passphrase.getText().toString(); - //check and save passphrase - if (selectedAction.equals(CREATE_METHOD)) { - EditText passphraseAgain = (EditText) findViewById(R.id.passphraseAgain); - passphraseAgain.setError(null); - String pwAgain = passphraseAgain.getText().toString(); - - if (!TextUtils.isEmpty(pw)) { - if (!TextUtils.isEmpty(pwAgain)) { - if (pw.equals(pwAgain)) { - PassphraseWizardActivity.passphrase = pw; - Toast.makeText(this, getString(R.string.passphrase_saved), Toast.LENGTH_SHORT).show(); - this.finish(); - } else { - passphrase.setError(getString(R.string.passphrase_invalid)); - passphrase.requestFocus(); - } - } else { - passphraseAgain.setError(getString(R.string.missing_passphrase)); - passphraseAgain.requestFocus(); - } - } else { - passphrase.setError(getString(R.string.missing_passphrase)); - passphrase.requestFocus(); - } - } - //check for right passphrase - if (selectedAction.equals(AUTHENTICATION)) { - if (pw.equals(PassphraseWizardActivity.passphrase)) { - Toast.makeText(this, getString(R.string.unlocked), Toast.LENGTH_SHORT).show(); - this.finish(); - } else { - passphrase.setError(getString(R.string.passphrase_invalid)); - passphrase.requestFocus(); - } - } - } - - public void NFC(View view) { - if (adapter != null) { - if (getActionBar() != null) { - getActionBar().setTitle(R.string.nfc_title); - } - NFCFragment nfc = new NFCFragment(); - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.replace(R.id.fragmentContainer, nfc).addToBackStack(null).commit(); - - //if you want to create a new method or just authenticate - if (CREATE_METHOD.equals(selectedAction)) { - writeNFC = true; - } else if (AUTHENTICATION.equals(selectedAction)) { - readNFC = true; - } - - if (!adapter.isEnabled()) { - showAlertDialog(getString(R.string.enable_nfc), true); - } - } else { - showAlertDialog(getString(R.string.no_nfc_support), false); - } - } - - @Override - protected void onNewIntent(Intent intent) { - if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) { - myTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); - - if (writeNFC && CREATE_METHOD.equals(selectedAction)) { - //write new password on NFC tag - try { - if (myTag != null) { - write(myTag); - writeNFC = false; //just write once - Toast.makeText(this, R.string.nfc_write_succesful, Toast.LENGTH_SHORT).show(); - //advance to lockpattern -// LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction); -// FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); -// transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit(); - } - } catch (IOException | FormatException e) { - Log.e(Constants.TAG, "Failed to write on NFC tag", e); - } - - } else if (readNFC && AUTHENTICATION.equals(selectedAction)) { - //read pw from NFC tag - try { - if (myTag != null) { - //if tag detected, read tag - String pwtag = read(myTag); - if (output != null && pwtag.equals(output.toString())) { - - //passwort matches, go to next view - Toast.makeText(this, R.string.passphrases_match + "!", Toast.LENGTH_SHORT).show(); - -// LockPatternFragmentOld lpf = LockPatternFragmentOld.newInstance(selectedAction); -// FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); -// transaction.replace(R.id.fragmentContainer, lpf).addToBackStack(null).commit(); - readNFC = false; //just once - } else { - //passwort doesnt match - TextView nfc = (TextView) findViewById(R.id.nfcText); - nfc.setText(R.string.nfc_wrong_tag); - } - } - } catch (IOException | FormatException e) { - Log.e(Constants.TAG, "Failed to read NFC tag", e); - } - } - } - } - - private void write(Tag tag) throws IOException, FormatException { - //generate new random key and write them on the tag - SecureRandom sr = new SecureRandom(); - sr.nextBytes(output); - NdefRecord[] records = {createRecord(output.toString())}; - NdefMessage message = new NdefMessage(records); - Ndef ndef = Ndef.get(tag); - ndef.connect(); - ndef.writeNdefMessage(message); - ndef.close(); - } - - private String read(Tag tag) throws IOException, FormatException { - //read string from tag - String password = null; - Ndef ndef = Ndef.get(tag); - ndef.connect(); - NdefMessage ndefMessage = ndef.getCachedNdefMessage(); - - NdefRecord[] records = ndefMessage.getRecords(); - for (NdefRecord ndefRecord : records) { - if (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) { - try { - password = readText(ndefRecord); - } catch (UnsupportedEncodingException e) { - Log.e(Constants.TAG, "Failed to read password from tag", e); - } - } - } - ndef.close(); - return password; - } - - private String readText(NdefRecord record) throws UnsupportedEncodingException { - //low-level method for reading nfc - byte[] payload = record.getPayload(); - String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16"; - int languageCodeLength = payload[0] & 0063; - return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding); - } - - private NdefRecord createRecord(String text) throws UnsupportedEncodingException { - //low-level method for writing nfc - String lang = "en"; - byte[] textBytes = text.getBytes(); - byte[] langBytes = lang.getBytes("US-ASCII"); - int langLength = langBytes.length; - int textLength = textBytes.length; - byte[] payload = new byte[1 + langLength + textLength]; - - // set status byte (see NDEF spec for actual bits) - payload[0] = (byte) langLength; - // copy langbytes and textbytes into payload - System.arraycopy(langBytes, 0, payload, 1, langLength); - System.arraycopy(textBytes, 0, payload, 1 + langLength, textLength); - return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], payload); - } - - public void showAlertDialog(String message, boolean nfc) { - //This method shows an AlertDialog - AlertDialog.Builder alert = new AlertDialog.Builder(this); - alert.setTitle("Information").setMessage(message).setPositiveButton("Ok", - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - } - } - ); - if (nfc) { - - alert.setNeutralButton(R.string.nfc_settings, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialogInterface, int i) { - startActivity(new Intent(Settings.ACTION_NFC_SETTINGS)); - } - } - ); - } - alert.show(); - } - - @Override - public void onPause() { - //pause this app and free nfc intent - super.onPause(); - if (adapter != null) { - WriteModeOff(); - } - } - - @Override - public void onResume() { - //resume this app and get nfc intent - super.onResume(); - if (adapter != null) { - WriteModeOn(); - } - } - - private void WriteModeOn() { - //enable nfc for this view - writeMode = true; - adapter.enableForegroundDispatch(this, pendingIntent, writeTagFilters, null); - } - - private void WriteModeOff() { - //disable nfc for this view - writeMode = false; - adapter.disableForegroundDispatch(this); - } - - public static class SelectMethods extends Fragment { -// private OnFragmentInteractionListener mListener; - - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - */ - public SelectMethods() { - - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - @Override - public void onResume() { - super.onResume(); - if (getActivity().getActionBar() != null) { - getActivity().getActionBar().setTitle(R.string.unlock_method); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.passphrase_wizard_fragment_select_methods, container, false); - } - -// @Override -// public void onAttach(Activity activity) { -// super.onAttach(activity); -// try { -// mListener = (OnFragmentInteractionListener) activity; -// } catch (ClassCastException e) { -// throw new ClassCastException(activity.toString() -// + " must implement OnFragmentInteractionListener"); -// } -// } -// -// @Override -// public void onDetach() { -// super.onDetach(); -// mListener = null; -// } - - /** - * This interface must be implemented by activities that contain this - * fragment to allow an interaction in this fragment to be communicated - * to the activity and potentially other fragments contained in that - * activity. - *

- * See the Android Training lesson Communicating with Other Fragments for more information. - */ -// public static interface OnFragmentInteractionListener { -// public void onFragmentInteraction(Uri uri); -// } - - } - - - // /** -// * A simple {@link android.support.v4.app.Fragment} subclass. -// * Activities that contain this fragment must implement the -// * {@link com.haibison.android.lockpattern.Passphrase.OnFragmentInteractionListener} interface -// * to handle interaction events. -// */ - public static class Passphrase extends Fragment { - -// private OnFragmentInteractionListener mListener; - - public Passphrase() { - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - // Inflate the layout for this fragment - View view = inflater.inflate(R.layout.passphrase_wizard_fragment_passphrase, container, false); - EditText passphraseAgain = (EditText) view.findViewById(R.id.passphraseAgain); - TextView passphraseText = (TextView) view.findViewById(R.id.passphraseText); - TextView passphraseTextAgain = (TextView) view.findViewById(R.id.passphraseTextAgain); - String selectedAction = getActivity().getIntent().getAction(); - if (selectedAction.equals(AUTHENTICATION)) { - passphraseAgain.setVisibility(View.GONE); - passphraseTextAgain.setVisibility(View.GONE); - passphraseText.setText(R.string.enter_passphrase); -// getActivity().getActionBar().setTitle(R.string.enter_passphrase); - } else if (selectedAction.equals(CREATE_METHOD)) { - passphraseAgain.setVisibility(View.VISIBLE); - passphraseTextAgain.setVisibility(View.VISIBLE); - passphraseText.setText(R.string.passphrase); -// getActivity().getActionBar().setTitle(R.string.set_passphrase); - } - return view; - } - -// @Override -// public void onAttach(Activity activity) { -// super.onAttach(activity); -// try { -// mListener = (OnFragmentInteractionListener) activity; -// } catch (ClassCastException e) { -// throw new ClassCastException(activity.toString() -// + " must implement OnFragmentInteractionListener"); -// } -// } -// -// @Override -// public void onDetach() { -// super.onDetach(); -// mListener = null; -// } - -// /** -// * This interface must be implemented by activities that contain this -// * fragment to allow an interaction in this fragment to be communicated -// * to the activity and potentially other fragments contained in that -// * activity. -// *

-// * See the Android Training lesson Communicating with Other Fragments for more information. -// */ -// public interface OnFragmentInteractionListener { -// public void onFragmentInteraction(Uri uri); -// } - } - - - /** - * A simple {@link android.support.v4.app.Fragment} subclass. - * Activities that contain this fragment must implement the - * interface - * to handle interaction events. - * Use the method to - * create an instance of this fragment. - */ - public static class NFCFragment extends Fragment { - // TODO: Rename parameter arguments, choose names that match - // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER - private static final String ARG_PARAM1 = "param1"; - private static final String ARG_PARAM2 = "param2"; - - // TODO: Rename and change types of parameters - private String mParam1; - private String mParam2; - -// private OnFragmentInteractionListener mListener; - - /** - * Use this factory method to create a new instance of - * this fragment using the provided parameters. - * - * @param param1 Parameter 1. - * @param param2 Parameter 2. - * @return A new instance of fragment SelectMethods. - */ - // TODO: Rename and change types and number of parameters - public static NFCFragment newInstance(String param1, String param2) { - NFCFragment fragment = new NFCFragment(); - Bundle args = new Bundle(); - args.putString(ARG_PARAM1, param1); - args.putString(ARG_PARAM2, param2); - fragment.setArguments(args); - return fragment; - } - - public NFCFragment() { - // Required empty public constructor - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getArguments() != null) { - mParam1 = getArguments().getString(ARG_PARAM1); - mParam2 = getArguments().getString(ARG_PARAM2); - } - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - // Inflate the layout for this fragment - return inflater.inflate(R.layout.passphrase_wizard_fragment_nfc, container, false); - } - -// // TODO: Rename method, update argument and hook method into UI event -// public void onButtonPressed(Uri uri) { -// if (mListener != null) { -// mListener.onFragmentInteraction(uri); -// } -// } - -// @Override -// public void onAttach(Activity activity) { -// super.onAttach(activity); -// try { -// mListener = (OnFragmentInteractionListener) activity; -// } catch (ClassCastException e) { -// throw new ClassCastException(activity.toString() -// + " must implement OnFragmentInteractionListener"); -// } -// } - - -// @Override -// public void onDetach() { -// super.onDetach(); -// mListener = null; -// } - } - -} -- cgit v1.2.3 From c292b8d9c3497b1482f3e4856033e10d06dd9388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 27 Aug 2015 13:59:26 +0200 Subject: Experimental features settings reworked --- .../org/sufficientlysecure/keychain/Constants.java | 3 +- .../keychain/ui/SettingsActivity.java | 36 +++++++++++++++------- .../keychain/util/Preferences.java | 21 +++++++++---- 3 files changed, 42 insertions(+), 18 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index 17d550ce5..60bcf187d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -109,7 +109,8 @@ public final class Constants { public static final String SYNC_CONTACTS = "syncContacts"; public static final String SYNC_KEYSERVER = "syncKeyserver"; // other settings - public static final String ENABLE_EXPERIMENTAL_FEATURES = "enableExperimentalFeatures"; + public static final String EXPERIMENTAL_ENABLE_WORD_CONFIRM = "experimentalEnableWordConfirm"; + public static final String EXPERIMENTAL_ENABLE_LINKED_IDENTITIES = "experimentalEnableLinkedIdentities"; public static final class Theme { public static final String LIGHT = "light"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index 63be1e20f..aec45824e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -553,19 +553,22 @@ public class SettingsActivity extends AppCompatPreferenceActivity { } /** - * This fragment shows other preferences + * This fragment shows experimental features */ - public static class OtherPrefsFragment extends PreferenceFragment { + public static class ExperimentalPrefsFragment extends PreferenceFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Load the preferences from an XML resource - addPreferencesFromResource(R.xml.other_preferences); + addPreferencesFromResource(R.xml.experimental_preferences); - initializeEnableExperimentalFeatures( - (SwitchPreference) findPreference(Constants.Pref.ENABLE_EXPERIMENTAL_FEATURES)); + initializeExperimentalEnableWordConfirm( + (SwitchPreference) findPreference(Constants.Pref.EXPERIMENTAL_ENABLE_WORD_CONFIRM)); + + initializeExperimentalEnableLinkedIdentities( + (SwitchPreference) findPreference(Constants.Pref.EXPERIMENTAL_ENABLE_LINKED_IDENTITIES)); } } @@ -575,7 +578,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { || ProxyPrefsFragment.class.getName().equals(fragmentName) || GuiPrefsFragment.class.getName().equals(fragmentName) || SyncPrefsFragment.class.getName().equals(fragmentName) - || OtherPrefsFragment.class.getName().equals(fragmentName) + || ExperimentalPrefsFragment.class.getName().equals(fragmentName) || super.isValidFragment(fragmentName); } @@ -676,12 +679,23 @@ public class SettingsActivity extends AppCompatPreferenceActivity { }); } - private static void initializeEnableExperimentalFeatures(final SwitchPreference mEnableExperimentalFeatures) { - mEnableExperimentalFeatures.setChecked(sPreferences.getEnableExperimentalFeatures()); - mEnableExperimentalFeatures.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + private static void initializeExperimentalEnableWordConfirm(final SwitchPreference mExperimentalEnableWordConfirm) { + mExperimentalEnableWordConfirm.setChecked(sPreferences.getExperimentalEnableWordConfirm()); + mExperimentalEnableWordConfirm.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + public boolean onPreferenceChange(Preference preference, Object newValue) { + mExperimentalEnableWordConfirm.setChecked((Boolean) newValue); + sPreferences.setExperimentalEnableWordConfirm((Boolean) newValue); + return false; + } + }); + } + + private static void initializeExperimentalEnableLinkedIdentities(final SwitchPreference mExperimentalEnableLinkedIdentities) { + mExperimentalEnableLinkedIdentities.setChecked(sPreferences.getExperimentalEnableLinkedIdentities()); + mExperimentalEnableLinkedIdentities.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { public boolean onPreferenceChange(Preference preference, Object newValue) { - mEnableExperimentalFeatures.setChecked((Boolean) newValue); - sPreferences.setEnableExperimentalFeatures((Boolean) newValue); + mExperimentalEnableLinkedIdentities.setChecked((Boolean) newValue); + sPreferences.setExperimentalEnableLinkedIdentities((Boolean) newValue); return false; } }); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java index 75f83d1f6..3225c3765 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java @@ -25,7 +25,6 @@ import android.content.res.Resources; import android.preference.PreferenceManager; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants.Pref; -import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.service.KeyserverSyncAdapterService; import java.net.Proxy; @@ -360,16 +359,26 @@ public class Preferences { } } - // other prefs + // experimental prefs - public void setEnableExperimentalFeatures(boolean enableExperimentalFeatures) { + public void setExperimentalEnableWordConfirm(boolean enableWordConfirm) { SharedPreferences.Editor editor = mSharedPreferences.edit(); - editor.putBoolean(Pref.ENABLE_EXPERIMENTAL_FEATURES, enableExperimentalFeatures); + editor.putBoolean(Pref.EXPERIMENTAL_ENABLE_WORD_CONFIRM, enableWordConfirm); editor.commit(); } - public boolean getEnableExperimentalFeatures() { - return mSharedPreferences.getBoolean(Pref.ENABLE_EXPERIMENTAL_FEATURES, false); + public boolean getExperimentalEnableWordConfirm() { + return mSharedPreferences.getBoolean(Pref.EXPERIMENTAL_ENABLE_WORD_CONFIRM, false); + } + + public void setExperimentalEnableLinkedIdentities(boolean enableLinkedIdentities) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putBoolean(Pref.EXPERIMENTAL_ENABLE_LINKED_IDENTITIES, enableLinkedIdentities); + editor.commit(); + } + + public boolean getExperimentalEnableLinkedIdentities() { + return mSharedPreferences.getBoolean(Pref.EXPERIMENTAL_ENABLE_LINKED_IDENTITIES, false); } public void upgradePreferences(Context context) { -- cgit v1.2.3 From 905e738621e43908e5dd19894544acf97f9076e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 27 Aug 2015 14:05:58 +0200 Subject: Use SwitchPreference more often --- .../keychain/ui/SettingsActivity.java | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index aec45824e..83351c68c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -94,10 +94,10 @@ public class SettingsActivity extends AppCompatPreferenceActivity { } }); initializeSearchKeyserver( - (CheckBoxPreference) findPreference(Constants.Pref.SEARCH_KEYSERVER) + (SwitchPreference) findPreference(Constants.Pref.SEARCH_KEYSERVER) ); initializeSearchKeybase( - (CheckBoxPreference) findPreference(Constants.Pref.SEARCH_KEYBASE) + (SwitchPreference) findPreference(Constants.Pref.SEARCH_KEYBASE) ); } else if (action != null && action.equals(ACTION_PREFS_ADV)) { @@ -195,10 +195,10 @@ public class SettingsActivity extends AppCompatPreferenceActivity { } }); initializeSearchKeyserver( - (CheckBoxPreference) findPreference(Constants.Pref.SEARCH_KEYSERVER) + (SwitchPreference) findPreference(Constants.Pref.SEARCH_KEYSERVER) ); initializeSearchKeybase( - (CheckBoxPreference) findPreference(Constants.Pref.SEARCH_KEYBASE) + (SwitchPreference) findPreference(Constants.Pref.SEARCH_KEYBASE) ); } @@ -256,8 +256,8 @@ public class SettingsActivity extends AppCompatPreferenceActivity { } public static class Initializer { - private CheckBoxPreference mUseTor; - private CheckBoxPreference mUseNormalProxy; + private SwitchPreference mUseTor; + private SwitchPreference mUseNormalProxy; private EditTextPreference mProxyHost; private EditTextPreference mProxyPort; private ListPreference mProxyType; @@ -293,8 +293,8 @@ public class SettingsActivity extends AppCompatPreferenceActivity { mActivity.addPreferencesFromResource(R.xml.proxy_prefs); } - mUseTor = (CheckBoxPreference) automaticallyFindPreference(Constants.Pref.USE_TOR_PROXY); - mUseNormalProxy = (CheckBoxPreference) automaticallyFindPreference(Constants.Pref.USE_NORMAL_PROXY); + mUseTor = (SwitchPreference) automaticallyFindPreference(Constants.Pref.USE_TOR_PROXY); + mUseNormalProxy = (SwitchPreference) automaticallyFindPreference(Constants.Pref.USE_NORMAL_PROXY); mProxyHost = (EditTextPreference) automaticallyFindPreference(Constants.Pref.PROXY_HOST); mProxyPort = (EditTextPreference) automaticallyFindPreference(Constants.Pref.PROXY_PORT); mProxyType = (ListPreference) automaticallyFindPreference(Constants.Pref.PROXY_TYPE); @@ -492,19 +492,19 @@ public class SettingsActivity extends AppCompatPreferenceActivity { final Account account = manager.getAccountsByType(Constants.ACCOUNT_TYPE)[0]; // for keyserver sync initializeSyncCheckBox( - (CheckBoxPreference) findPreference(Constants.Pref.SYNC_KEYSERVER), + (SwitchPreference) findPreference(Constants.Pref.SYNC_KEYSERVER), account, Constants.PROVIDER_AUTHORITY ); // for contacts sync initializeSyncCheckBox( - (CheckBoxPreference) findPreference(Constants.Pref.SYNC_CONTACTS), + (SwitchPreference) findPreference(Constants.Pref.SYNC_CONTACTS), account, ContactsContract.AUTHORITY ); } - private void initializeSyncCheckBox(final CheckBoxPreference syncCheckBox, + private void initializeSyncCheckBox(final SwitchPreference syncCheckBox, final Account account, final String authority) { boolean syncEnabled = ContentResolver.getSyncAutomatically(account, authority); @@ -529,7 +529,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { }); } - private void setSummary(CheckBoxPreference syncCheckBox, String authority, + private void setSummary(SwitchPreference syncCheckBox, String authority, boolean checked) { switch (authority) { case Constants.PROVIDER_AUTHORITY: { @@ -623,7 +623,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { }); } - private static void initializeSearchKeyserver(final CheckBoxPreference mSearchKeyserver) { + private static void initializeSearchKeyserver(final SwitchPreference mSearchKeyserver) { Preferences.CloudSearchPrefs prefs = sPreferences.getCloudSearchPrefs(); mSearchKeyserver.setChecked(prefs.searchKeyserver); mSearchKeyserver.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { @@ -636,7 +636,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { }); } - private static void initializeSearchKeybase(final CheckBoxPreference mSearchKeybase) { + private static void initializeSearchKeybase(final SwitchPreference mSearchKeybase) { Preferences.CloudSearchPrefs prefs = sPreferences.getCloudSearchPrefs(); mSearchKeybase.setChecked(prefs.searchKeybase); mSearchKeybase.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { -- cgit v1.2.3 From 3f4150e4ef8bc0de7c9f971c4bea15115d506ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 27 Aug 2015 15:58:33 +0200 Subject: Fix theme setting --- .../main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java | 2 ++ 1 file changed, 2 insertions(+) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index 83351c68c..43edc8220 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -569,6 +569,8 @@ public class SettingsActivity extends AppCompatPreferenceActivity { initializeExperimentalEnableLinkedIdentities( (SwitchPreference) findPreference(Constants.Pref.EXPERIMENTAL_ENABLE_LINKED_IDENTITIES)); + + initializeTheme((ListPreference) findPreference(Constants.Pref.THEME)); } } -- cgit v1.2.3 From 9e3c0415497eb72b66bf7300df72966732f036c6 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 27 Aug 2015 16:13:32 +0200 Subject: some cleanup in FileHelper, and allow input of filename during export --- .../keychain/ui/BackupFragment.java | 22 ++- .../keychain/ui/DecryptListFragment.java | 16 +- .../ui/EncryptDecryptOverviewFragment.java | 6 +- .../keychain/ui/EncryptFilesFragment.java | 26 +-- .../keychain/ui/ImportKeysFileFragment.java | 4 +- .../keychain/ui/LogDisplayFragment.java | 2 +- .../keychain/ui/dialog/FileDialogFragment.java | 22 ++- .../keychain/util/ExportHelper.java | 2 +- .../keychain/util/FileHelper.java | 197 ++++++++++++--------- 9 files changed, 158 insertions(+), 139 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupFragment.java index 2d9ac6ee3..2f935008a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupFragment.java @@ -47,7 +47,20 @@ public class BackupFragment extends Fragment { private int mIndex; static final int REQUEST_REPEAT_PASSPHRASE = 1; + private ExportHelper mExportHelper; + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + // we won't get attached to a non-fragment activity, so the cast should be safe + mExportHelper = new ExportHelper((FragmentActivity) activity); + } + + @Override + public void onDetach() { + super.onDetach(); + mExportHelper = null; + } @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { @@ -80,8 +93,7 @@ public class BackupFragment extends Fragment { } if (!includeSecretKeys) { - ExportHelper exportHelper = new ExportHelper(activity); - exportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, false); + mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, false); return; } @@ -136,8 +148,7 @@ public class BackupFragment extends Fragment { return; } - ExportHelper exportHelper = new ExportHelper(activity); - exportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true); + mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true); } }.execute(activity.getContentResolver()); @@ -167,8 +178,7 @@ public class BackupFragment extends Fragment { return; } - ExportHelper exportHelper = new ExportHelper(getActivity()); - exportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true); + mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true); } } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java index f57d2d056..26e56280a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java @@ -36,7 +36,6 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; -import android.os.Build; import android.os.Bundle; import android.os.Parcelable; import android.support.v7.widget.DefaultItemAnimator; @@ -222,18 +221,6 @@ public class DecryptListFragment } } - private void askForOutputFilename(Uri inputUri, String originalFilename, String mimeType) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - File file = new File(inputUri.getPath()); - File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR; - File targetFile = new File(parentDir, originalFilename); - 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, mimeType, originalFilename, REQUEST_CODE_OUTPUT); - } - } - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { @@ -534,7 +521,8 @@ public class DecryptListFragment return true; } mCurrentInputUri = model.mInputUri; - askForOutputFilename(model.mInputUri, metadata.getFilename(), metadata.getMimeType()); + FileHelper.saveDocument(this, metadata.getFilename(), model.mInputUri, metadata.getMimeType(), + R.string.title_decrypt_to_file, R.string.specify_file_to_decrypt_to, REQUEST_CODE_OUTPUT); return true; case R.id.decrypt_delete: deleteFile(activity, model.mInputUri); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java index fc72a6c9f..84660ca7a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptDecryptOverviewFragment.java @@ -83,11 +83,7 @@ public class EncryptDecryptOverviewFragment extends Fragment { mDecryptFile.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - FileHelper.openDocument(EncryptDecryptOverviewFragment.this, "*/*", REQUEST_CODE_INPUT); - } else { - FileHelper.openFile(EncryptDecryptOverviewFragment.this, null, "*/*", REQUEST_CODE_INPUT); - } + FileHelper.openDocument(EncryptDecryptOverviewFragment.this, null, "*/*", false, REQUEST_CODE_INPUT); } }); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java index 63d37f296..8572a5712 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptFilesFragment.java @@ -18,7 +18,6 @@ package org.sufficientlysecure.keychain.ui; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Date; @@ -196,13 +195,9 @@ public class EncryptFilesFragment } private void addInputUri() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - FileHelper.openDocument(EncryptFilesFragment.this, "*/*", true, REQUEST_CODE_INPUT); - } else { - FileHelper.openFile(EncryptFilesFragment.this, mFilesAdapter.getModelCount() == 0 ? - null : mFilesAdapter.getModelItem(mFilesAdapter.getModelCount() - 1).inputUri, - "*/*", REQUEST_CODE_INPUT); - } + FileHelper.openDocument(EncryptFilesFragment.this, mFilesAdapter.getModelCount() == 0 ? + null : mFilesAdapter.getModelItem(mFilesAdapter.getModelCount() - 1).inputUri, + "*/*", true, REQUEST_CODE_INPUT); } private void addInputUri(Uri inputUri) { @@ -230,19 +225,8 @@ public class EncryptFilesFragment (mEncryptFilenames ? "1" : FileHelper.getFilename(getActivity(), model.inputUri)) + (mUseArmor ? Constants.FILE_EXTENSION_ASC : Constants.FILE_EXTENSION_PGP_MAIN); Uri inputUri = model.inputUri; - saveDocumentIntent(targetName, inputUri); - } - - private void saveDocumentIntent(String targetName, Uri inputUri) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { - File file = new File(inputUri.getPath()); - File parentDir = file.exists() ? file.getParentFile() : Constants.Path.APP_DIR; - File targetFile = new File(parentDir, targetName); - FileHelper.saveFile(this, getString(R.string.title_encrypt_to_file), - getString(R.string.specify_file_to_encrypt_to), targetFile, REQUEST_CODE_OUTPUT); - } else { - FileHelper.saveDocument(this, "*/*", targetName, REQUEST_CODE_OUTPUT); - } + FileHelper.saveDocument(this, targetName, inputUri, + R.string.title_encrypt_to_file, R.string.specify_file_to_encrypt_to, REQUEST_CODE_OUTPUT); } public void addFile(Intent data) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java index 538fa16c7..746c75600 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysFileFragment.java @@ -64,8 +64,8 @@ public class ImportKeysFileFragment extends Fragment { // open .asc or .gpg files // setting it to text/plain prevents Cyanogenmod's file manager from selecting asc // or gpg types! - FileHelper.openFile(ImportKeysFileFragment.this, Uri.fromFile(Constants.Path.APP_DIR), - "*/*", REQUEST_CODE_FILE); + FileHelper.openDocument(ImportKeysFileFragment.this, + Uri.fromFile(Constants.Path.APP_DIR), "*/*", false, REQUEST_CODE_FILE); } }); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index 1cd1a3099..4de83337e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -252,7 +252,7 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe String message = this.getString(R.string.specify_file_to_export_log_to); - FileHelper.saveFile(new FileHelper.FileDialogCallback() { + FileHelper.saveDocumentDialog(new FileHelper.FileDialogCallback() { @Override public void onFileSelected(File file, boolean checked) { writeToLogFile(mResult.getLog(), file); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java index 5ef8618ce..84774ae5e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/FileDialogFragment.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Message; import android.os.Messenger; @@ -119,14 +120,19 @@ public class FileDialogFragment extends DialogFragment { mFilename = (EditText) view.findViewById(R.id.input); mFilename.setText(mFile.getName()); mBrowse = (ImageButton) view.findViewById(R.id.btn_browse); - mBrowse.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - // only .asc or .gpg files - // setting it to text/plain prevents Cynaogenmod's file manager from selecting asc - // or gpg types! - FileHelper.openFile(FileDialogFragment.this, Uri.fromFile(mFile), "*/*", REQUEST_CODE); - } - }); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + mBrowse.setVisibility(View.GONE); + } else { + mBrowse.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + // only .asc or .gpg files + // setting it to text/plain prevents Cynaogenmod's file manager from selecting asc + // or gpg types! + FileHelper.saveDocumentKitKat( + FileDialogFragment.this, "*/*", mFile.getName(), REQUEST_CODE); + } + }); + } mCheckBox = (CheckBox) view.findViewById(R.id.checkbox); if (checkboxText == null) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java index 72620e712..45dc33906 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/ExportHelper.java @@ -69,7 +69,7 @@ public class ExportHelper : R.string.specify_backup_dest_single); } - FileHelper.saveFile(new FileHelper.FileDialogCallback() { + FileHelper.saveDocumentDialog(new FileHelper.FileDialogCallback() { @Override public void onFileSelected(File file, boolean checked) { mExportFile = file; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java index 4a00f46cb..9fb362412 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java @@ -28,16 +28,19 @@ import android.graphics.Bitmap; import android.graphics.Point; import android.net.Uri; import android.os.Build; +import android.os.Build.VERSION_CODES; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.provider.DocumentsContract; import android.provider.OpenableColumns; +import android.support.annotation.StringRes; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.widget.Toast; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment; @@ -46,42 +49,95 @@ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.text.DecimalFormat; + +/** This class offers a number of helper functions for saving documents. + * + * There are three entry points here: openDocument, saveDocument and + * saveDocumentDialog. Each behaves a little differently depending on whether + * the Android version used is pre or post KitKat. + * + * - openDocument queries for a document for reading. Used in "open encrypted + * file" ui flow. On pre-kitkat, this relies on an external file manager, + * and will fail with a toast message if none is installed. + * + * - saveDocument queries for a document name for saving. on pre-kitkat, this + * shows a dialog where a filename can be input. on kitkat and up, it + * directly triggers a "save document" intent. Used in "save encrypted file" + * ui flow. + * + * - saveDocumentDialog queries for a document. this shows a dialog on all + * versions of android. the browse button opens an external browser on + * pre-kitkat or the "save document" intent on post-kitkat devices. Used in + * "backup key" ui flow. + * + * It is noteworthy that the "saveDocument" call is essentially substituted + * by the "saveDocumentDialog" on pre-kitkat devices. + * + */ public class FileHelper { - /** - * Checks if external storage is mounted if file is located on external storage - * - * @return true if storage is mounted - */ - public static boolean isStorageMounted(String file) { - if (file.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) { - if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - return false; - } + public static void openDocument(Fragment fragment, Uri last, String mimeType, boolean multiple, int requestCode) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + openDocumentPreKitKat(fragment, last, mimeType, multiple, requestCode); + } else { + openDocumentKitKat(fragment, mimeType, multiple, requestCode); } + } - return true; + public static void saveDocument(Fragment fragment, String targetName, Uri inputUri, + @StringRes int title, @StringRes int message, int requestCode) { + saveDocument(fragment, targetName, inputUri, "*/*", title, message, requestCode); } - /** - * Opens the preferred installed file manager on Android and shows a toast if no manager is - * installed. - * - * @param last default selected Uri, not supported by all file managers - * @param mimeType can be text/plain for example - * @param requestCode requestCode used to identify the result coming back from file manager to - * onActivityResult() in your activity - */ - public static void openFile(Fragment fragment, Uri last, String mimeType, int requestCode) { + public static void saveDocument(Fragment fragment, String targetName, Uri inputUri, String mimeType, + @StringRes int title, @StringRes int message, int requestCode) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + saveDocumentDialog(fragment, targetName, inputUri, title, message, requestCode); + } else { + saveDocumentKitKat(fragment, mimeType, targetName, requestCode); + } + } + + public static void saveDocumentDialog(final Fragment fragment, String targetName, Uri inputUri, + @StringRes int title, @StringRes int message, final int requestCode) { + + saveDocumentDialog(fragment, targetName, inputUri, title, message, new FileDialogCallback() { + // is this a good idea? seems hacky... + @Override + public void onFileSelected(File file, boolean checked) { + Intent intent = new Intent(); + intent.setData(Uri.fromFile(file)); + fragment.onActivityResult(requestCode, Activity.RESULT_OK, intent); + } + }); + } + + public static void saveDocumentDialog(final Fragment fragment, String targetName, Uri inputUri, + @StringRes int title, @StringRes int message, FileDialogCallback callback) { + + File file = inputUri == null ? null : new File(inputUri.getPath()); + File parentDir = file != null && file.exists() ? file.getParentFile() : Constants.Path.APP_DIR; + File targetFile = new File(parentDir, targetName); + saveDocumentDialog(callback, fragment.getActivity().getSupportFragmentManager(), + fragment.getString(title), fragment.getString(message), targetFile, null); + + } + + /** Opens the preferred installed file manager on Android and shows a toast + * if no manager is installed. */ + private static void openDocumentPreKitKat( + Fragment fragment, Uri last, String mimeType, boolean multiple, int requestCode) { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); - + if (Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) { + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple); + } intent.setData(last); intent.setType(mimeType); @@ -92,11 +148,34 @@ public class FileHelper { Toast.makeText(fragment.getActivity(), R.string.no_filemanager_installed, Toast.LENGTH_SHORT).show(); } + + } + + /** Opens the storage browser on Android 4.4 or later for opening a file */ + @TargetApi(Build.VERSION_CODES.KITKAT) + private static void openDocumentKitKat(Fragment fragment, String mimeType, boolean multiple, int requestCode) { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType(mimeType); + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple); + fragment.startActivityForResult(intent, requestCode); + } + + /** Opens the storage browser on Android 4.4 or later for saving a file. */ + @TargetApi(Build.VERSION_CODES.KITKAT) + public static void saveDocumentKitKat(Fragment fragment, String mimeType, String suggestedName, int requestCode) { + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType(mimeType); + intent.putExtra("android.content.extra.SHOW_ADVANCED", true); // Note: This is not documented, but works + intent.putExtra(Intent.EXTRA_TITLE, suggestedName); + fragment.startActivityForResult(intent, requestCode); } - public static void saveFile(final FileDialogCallback callback, final FragmentManager fragmentManager, - final String title, final String message, final File defaultFile, - final String checkMsg) { + public static void saveDocumentDialog( + final FileDialogCallback callback, final FragmentManager fragmentManager, + final String title, final String message, final File defaultFile, + final String checkMsg) { // Message is received after file is selected Handler returnHandler = new Handler() { @Override @@ -123,61 +202,6 @@ public class FileHelper { }); } - public static void saveFile(Fragment fragment, String title, String message, File defaultFile, int requestCode) { - saveFile(fragment, title, message, defaultFile, requestCode, null); - } - - public static void saveFile(final Fragment fragment, String title, String message, File defaultFile, - final int requestCode, String checkMsg) { - saveFile(new FileDialogCallback() { - @Override - public void onFileSelected(File file, boolean checked) { - Intent intent = new Intent(); - intent.setData(Uri.fromFile(file)); - fragment.onActivityResult(requestCode, Activity.RESULT_OK, intent); - } - }, fragment.getActivity().getSupportFragmentManager(), title, message, defaultFile, checkMsg); - } - - @TargetApi(Build.VERSION_CODES.KITKAT) - public static void openDocument(Fragment fragment, String mimeType, int requestCode) { - openDocument(fragment, mimeType, false, requestCode); - } - - /** - * Opens the storage browser on Android 4.4 or later for opening a file - * - * @param mimeType can be text/plain for example - * @param multiple allow file chooser to return multiple files - * @param requestCode used to identify the result coming back from storage browser onActivityResult() in your - */ - @TargetApi(Build.VERSION_CODES.KITKAT) - public static void openDocument(Fragment fragment, String mimeType, boolean multiple, int requestCode) { - Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType(mimeType); - intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, multiple); - - fragment.startActivityForResult(intent, requestCode); - } - - /** - * Opens the storage browser on Android 4.4 or later for saving a file - * - * @param mimeType can be text/plain for example - * @param suggestedName a filename desirable for the file to be saved - * @param requestCode used to identify the result coming back from storage browser onActivityResult() in your - */ - @TargetApi(Build.VERSION_CODES.KITKAT) - public static void saveDocument(Fragment fragment, String mimeType, String suggestedName, int requestCode) { - Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); - intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType(mimeType); - intent.putExtra("android.content.extra.SHOW_ADVANCED", true); // Note: This is not documented, but works - intent.putExtra(Intent.EXTRA_TITLE, suggestedName); - fragment.startActivityForResult(intent, requestCode); - } - public static String getFilename(Context context, Uri uri) { String filename = null; try { @@ -298,6 +322,17 @@ public class FileHelper { } } + /** Checks if external storage is mounted if file is located on external storage. */ + public static boolean isStorageMounted(String file) { + if (file.startsWith(Environment.getExternalStorageDirectory().getAbsolutePath())) { + if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { + return false; + } + } + + return true; + } + public interface FileDialogCallback { void onFileSelected(File file, boolean checked); } -- cgit v1.2.3 From e7991e8dc883087ffba802bb1ed97e93664636e2 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 27 Aug 2015 16:34:08 +0200 Subject: use nicer filenames during export --- .../keychain/ui/BackupFragment.java | 22 +++++++++++++++++++--- .../keychain/ui/ViewKeyActivity.java | 4 +++- 2 files changed, 22 insertions(+), 4 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupFragment.java index 2f935008a..a3ea8ad9a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/BackupFragment.java @@ -18,7 +18,11 @@ package org.sufficientlysecure.keychain.ui; +import java.io.File; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; +import java.util.Locale; import android.app.Activity; import android.content.ContentResolver; @@ -93,7 +97,7 @@ public class BackupFragment extends Fragment { } if (!includeSecretKeys) { - mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, false); + startBackup(false); return; } @@ -148,7 +152,7 @@ public class BackupFragment extends Fragment { return; } - mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true); + startBackup(true); } }.execute(activity.getContentResolver()); @@ -178,7 +182,19 @@ public class BackupFragment extends Fragment { return; } - mExportHelper.showExportKeysDialog(null, Constants.Path.APP_DIR_FILE, true); + startBackup(true); } } + + private void startBackup(boolean exportSecret) { + File filename; + String date = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date()); + if (exportSecret) { + filename = new File(Constants.Path.APP_DIR, "keys_" + date + ".asc"); + } else { + filename = new File(Constants.Path.APP_DIR, "keys_" + date + ".pub.asc"); + } + mExportHelper.showExportKeysDialog(null, filename, exportSecret); + } + } 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 1d0e085da..8ca4f021c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -80,6 +80,7 @@ import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.NfcHelper; import org.sufficientlysecure.keychain.util.Preferences; +import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -413,7 +414,8 @@ public class ViewKeyActivity extends BaseNfcActivity implements private void backupToFile() { new ExportHelper(this).showExportKeysDialog( - mMasterKeyId, Constants.Path.APP_DIR_FILE, true); + mMasterKeyId, new File(Constants.Path.APP_DIR, + KeyFormattingUtils.convertKeyIdToHex(mMasterKeyId) + ".sec.asc"), true); } private void deleteKey() { -- cgit v1.2.3 From 1b5afc32471c5c056acdb1ccdc749eda45f135cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 27 Aug 2015 21:53:33 +0200 Subject: Experimental word comparison --- .../keychain/ui/CertifyFingerprintActivity.java | 9 +- .../keychain/ui/CertifyFingerprintFragment.java | 36 +++++- .../keychain/ui/ViewKeyActivity.java | 12 +- .../keychain/ui/util/ExperimentalWordConfirm.java | 126 +++++++++++++++++++++ 4 files changed, 174 insertions(+), 9 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ExperimentalWordConfirm.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java index 016ab5f3c..c5528e40b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintActivity.java @@ -30,6 +30,8 @@ public class CertifyFingerprintActivity extends BaseActivity { protected Uri mDataUri; + public static final String EXTRA_ENABLE_WORD_CONFIRM = "enable_word_confirm"; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -40,6 +42,7 @@ public class CertifyFingerprintActivity extends BaseActivity { finish(); return; } + boolean enableWordConfirm = getIntent().getBooleanExtra(EXTRA_ENABLE_WORD_CONFIRM, false); setFullScreenDialogClose(new View.OnClickListener() { @Override @@ -50,7 +53,7 @@ public class CertifyFingerprintActivity extends BaseActivity { Log.i(Constants.TAG, "mDataUri: " + mDataUri.toString()); - startFragment(savedInstanceState, mDataUri); + startFragment(savedInstanceState, mDataUri, enableWordConfirm); } @Override @@ -58,7 +61,7 @@ public class CertifyFingerprintActivity extends BaseActivity { setContentView(R.layout.certify_fingerprint_activity); } - private void startFragment(Bundle savedInstanceState, Uri dataUri) { + private void startFragment(Bundle savedInstanceState, Uri dataUri, boolean enableWordConfirm) { // 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. @@ -67,7 +70,7 @@ public class CertifyFingerprintActivity extends BaseActivity { } // Create an instance of the fragment - CertifyFingerprintFragment frag = CertifyFingerprintFragment.newInstance(dataUri); + CertifyFingerprintFragment frag = CertifyFingerprintFragment.newInstance(dataUri, enableWordConfirm); // Add the fragment to the 'fragment_container' FrameLayout // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java index 7a9df1576..552fa34c0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyFingerprintFragment.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; import android.database.Cursor; +import android.graphics.Typeface; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.LoaderManager; @@ -34,6 +35,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.ui.util.ExperimentalWordConfirm; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; @@ -44,20 +46,24 @@ public class CertifyFingerprintFragment extends LoaderFragment implements static final int REQUEST_CERTIFY = 1; public static final String ARG_DATA_URI = "uri"; + public static final String ARG_ENABLE_WORD_CONFIRM = "enable_word_confirm"; private TextView mFingerprint; + private TextView mIntro; private static final int LOADER_ID_UNIFIED = 0; private Uri mDataUri; + private boolean mEnableWordConfirm; /** * Creates new instance of this fragment */ - public static CertifyFingerprintFragment newInstance(Uri dataUri) { + public static CertifyFingerprintFragment newInstance(Uri dataUri, boolean enableWordConfirm) { CertifyFingerprintFragment frag = new CertifyFingerprintFragment(); Bundle args = new Bundle(); args.putParcelable(ARG_DATA_URI, dataUri); + args.putBoolean(ARG_ENABLE_WORD_CONFIRM, enableWordConfirm); frag.setArguments(args); @@ -73,6 +79,7 @@ public class CertifyFingerprintFragment extends LoaderFragment implements View actionYes = view.findViewById(R.id.certify_fingerprint_button_yes); mFingerprint = (TextView) view.findViewById(R.id.certify_fingerprint_fingerprint); + mIntro = (TextView) view.findViewById(R.id.certify_fingerprint_intro); actionNo.setOnClickListener(new View.OnClickListener() { @Override @@ -100,6 +107,11 @@ public class CertifyFingerprintFragment extends LoaderFragment implements getActivity().finish(); return; } + mEnableWordConfirm = getArguments().getBoolean(ARG_ENABLE_WORD_CONFIRM); + + if (mEnableWordConfirm) { + mIntro.setText(R.string.certify_fingerprint_text_words); + } loadData(dataUri); } @@ -146,10 +158,13 @@ public class CertifyFingerprintFragment extends LoaderFragment implements switch (loader.getId()) { case LOADER_ID_UNIFIED: { if (data.moveToFirst()) { - byte[] fingerprintBlob = data.getBlob(INDEX_UNIFIED_FINGERPRINT); - String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fingerprintBlob); - mFingerprint.setText(KeyFormattingUtils.colorizeFingerprint(fingerprint)); + + if (mEnableWordConfirm) { + displayWordConfirm(fingerprintBlob); + } else { + displayHexConfirm(fingerprintBlob); + } break; } @@ -159,6 +174,19 @@ public class CertifyFingerprintFragment extends LoaderFragment implements setContentShown(true); } + private void displayHexConfirm(byte[] fingerprintBlob) { + String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fingerprintBlob); + mFingerprint.setText(KeyFormattingUtils.colorizeFingerprint(fingerprint)); + } + + private void displayWordConfirm(byte[] fingerprintBlob) { + String fingerprint = ExperimentalWordConfirm.getWords(getActivity(), fingerprintBlob); + + mFingerprint.setTextSize(24); + mFingerprint.setTypeface(Typeface.DEFAULT, Typeface.BOLD); + mFingerprint.setText(fingerprint); + } + /** * This is called when the last Cursor provided to onLoadFinished() above is about to be closed. * We need to make sure we are no longer using it. 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 1d0e085da..1c4a096b2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -349,7 +349,11 @@ public class ViewKeyActivity extends BaseNfcActivity implements return true; } case R.id.menu_key_view_certify_fingerprint: { - certifyFingeprint(mDataUri); + certifyFingeprint(mDataUri, false); + return true; + } + case R.id.menu_key_view_certify_fingerprint_word: { + certifyFingeprint(mDataUri, true); return true; } } @@ -364,6 +368,9 @@ public class ViewKeyActivity extends BaseNfcActivity implements exportKey.setVisible(mIsSecret); MenuItem certifyFingerprint = menu.findItem(R.id.menu_key_view_certify_fingerprint); certifyFingerprint.setVisible(!mIsSecret && !mIsVerified && !mIsExpired && !mIsRevoked); + MenuItem certifyFingerprintWord = menu.findItem(R.id.menu_key_view_certify_fingerprint_word); + certifyFingerprintWord.setVisible(!mIsSecret && !mIsVerified && !mIsExpired && !mIsRevoked + && Preferences.getPreferences(this).getExperimentalEnableWordConfirm()); return true; } @@ -375,9 +382,10 @@ public class ViewKeyActivity extends BaseNfcActivity implements startActivityForResult(scanQrCode, REQUEST_QR_FINGERPRINT); } - private void certifyFingeprint(Uri dataUri) { + private void certifyFingeprint(Uri dataUri, boolean enableWordConfirm) { Intent intent = new Intent(this, CertifyFingerprintActivity.class); intent.setData(dataUri); + intent.putExtra(CertifyFingerprintActivity.EXTRA_ENABLE_WORD_CONFIRM, enableWordConfirm); startActivityForResult(intent, REQUEST_CERTIFY); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ExperimentalWordConfirm.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ExperimentalWordConfirm.java new file mode 100644 index 000000000..43ccac24f --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/util/ExperimentalWordConfirm.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2015 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.util; + +import android.content.Context; + +import org.spongycastle.util.Arrays; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.BitSet; + +public class ExperimentalWordConfirm { + + public static String getWords(Context context, byte[] fingerprintBlob) { + ArrayList words = new ArrayList<>(); + + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader( + context.getAssets().open("word_confirm_list.txt"), + "UTF-8" + )); + + String line = reader.readLine(); + while (line != null) { + words.add(line); + + line = reader.readLine(); + } + } catch (IOException e) { + throw new RuntimeException("IOException", e); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException ignored) { + } + } + } + + String fingerprint = ""; + + // NOTE: 160 bit SHA-1 truncated to 156 bit + byte[] fingerprintBlobTruncated = Arrays.copyOfRange(fingerprintBlob, 0, 156 / 8); + + // TODO: implement key stretching to minimize fp length? + + // BitSet bits = BitSet.valueOf(fingerprintBlob); // min API 19 and little endian! + BitSet bits = bitSetToByteArray(fingerprintBlobTruncated); + Log.d(Constants.TAG, "bits: " + bits.toString()); + + final int CHUNK_SIZE = 13; + final int LAST_CHUNK_INDEX = fingerprintBlobTruncated.length * 8 / CHUNK_SIZE; // 12 + Log.d(Constants.TAG, "LAST_CHUNK_INDEX: " + LAST_CHUNK_INDEX); + + int from = 0; + int to = CHUNK_SIZE; + for (int i = 0; i < (LAST_CHUNK_INDEX + 1); i++) { + Log.d(Constants.TAG, "from: " + from + " to: " + to); + + BitSet setIndex = bits.get(from, to); + int wordIndex = (int) bitSetToLong(setIndex); + // int wordIndex = (int) setIndex.toLongArray()[0]; // min API 19 + + fingerprint += words.get(wordIndex); + + if (i != LAST_CHUNK_INDEX) { + // line break every 3 words + if (to % (CHUNK_SIZE * 3) == 0) { + fingerprint += "\n"; + } else { + fingerprint += " "; + } + } + + from = to; + to += CHUNK_SIZE; + } + + return fingerprint; + } + + /** + * Returns a BitSet containing the values in bytes. + * BIG ENDIAN! + */ + private static BitSet bitSetToByteArray(byte[] bytes) { + int arrayLength = bytes.length * 8; + BitSet bits = new BitSet(); + + for (int i = 0; i < arrayLength; i++) { + if ((bytes[bytes.length - i / 8 - 1] & (1 << (i % 8))) > 0) { + bits.set(i); + } + } + return bits; + } + + private static long bitSetToLong(BitSet bits) { + long value = 0L; + for (int i = 0; i < bits.length(); ++i) { + value += bits.get(i) ? (1L << i) : 0L; + } + return value; + } +} -- cgit v1.2.3 From 2ad39c27d96d66941bc9a81b69e5905098d39da8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 28 Aug 2015 04:20:43 +0200 Subject: Collapsing Toolbar --- .../keychain/ui/ViewKeyActivity.java | 67 +++++++++++----------- 1 file changed, 32 insertions(+), 35 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 1c4a096b2..ab97ac463 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -32,6 +32,9 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.provider.ContactsContract; +import android.support.design.widget.AppBarLayout; +import android.support.design.widget.CollapsingToolbarLayout; +import android.support.design.widget.FloatingActionButton; import android.support.v4.app.ActivityCompat; import android.support.v4.app.FragmentManager; import android.support.v4.app.LoaderManager; @@ -51,7 +54,6 @@ import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; -import com.getbase.floatingactionbutton.FloatingActionButton; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -107,10 +109,10 @@ public class ViewKeyActivity extends BaseNfcActivity implements private ArrayList mKeyList; private CryptoOperationHelper mOperationHelper; - private TextView mName; private TextView mStatusText; private ImageView mStatusImage; - private RelativeLayout mBigToolbar; + private AppBarLayout mAppBarLayout; + private CollapsingToolbarLayout mCollapsingToolbarLayout; private ImageButton mActionEncryptFile; private ImageButton mActionEncryptText; @@ -156,10 +158,10 @@ public class ViewKeyActivity extends BaseNfcActivity implements setTitle(null); - mName = (TextView) findViewById(R.id.view_key_name); mStatusText = (TextView) findViewById(R.id.view_key_status); mStatusImage = (ImageView) findViewById(R.id.view_key_status_image); - mBigToolbar = (RelativeLayout) findViewById(R.id.toolbar_big); + mAppBarLayout = (AppBarLayout) findViewById(R.id.app_bar_layout); + mCollapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar); mActionEncryptFile = (ImageButton) findViewById(R.id.view_key_action_encrypt_files); mActionEncryptText = (ImageButton) findViewById(R.id.view_key_action_encrypt_text); @@ -736,9 +738,9 @@ public class ViewKeyActivity extends BaseNfcActivity implements // get name, email, and comment from USER_ID KeyRing.UserId mainUserId = KeyRing.splitUserId(data.getString(INDEX_USER_ID)); if (mainUserId.name != null) { - mName.setText(mainUserId.name); + mCollapsingToolbarLayout.setTitle(mainUserId.name); } else { - mName.setText(R.string.user_id_no_name); + mCollapsingToolbarLayout.setTitle(getString(R.string.user_id_no_name)); } mMasterKeyId = data.getLong(INDEX_MASTER_KEY_ID); @@ -789,9 +791,9 @@ public class ViewKeyActivity extends BaseNfcActivity implements State.REVOKED, R.color.icons, true); color = getResources().getColor(R.color.key_flag_red); - mActionEncryptFile.setVisibility(View.GONE); - mActionEncryptText.setVisibility(View.GONE); - mActionNfc.setVisibility(View.GONE); + mActionEncryptFile.setVisibility(View.INVISIBLE); + mActionEncryptText.setVisibility(View.INVISIBLE); + mActionNfc.setVisibility(View.INVISIBLE); mFab.setVisibility(View.GONE); mQrCodeLayout.setVisibility(View.GONE); } else if (mIsExpired) { @@ -805,9 +807,9 @@ public class ViewKeyActivity extends BaseNfcActivity implements State.EXPIRED, R.color.icons, true); color = getResources().getColor(R.color.key_flag_red); - mActionEncryptFile.setVisibility(View.GONE); - mActionEncryptText.setVisibility(View.GONE); - mActionNfc.setVisibility(View.GONE); + mActionEncryptFile.setVisibility(View.INVISIBLE); + mActionEncryptText.setVisibility(View.INVISIBLE); + mActionNfc.setVisibility(View.INVISIBLE); mFab.setVisibility(View.GONE); mQrCodeLayout.setVisibility(View.GONE); } else if (mIsSecret) { @@ -822,15 +824,15 @@ public class ViewKeyActivity extends BaseNfcActivity implements 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 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(); @@ -852,7 +854,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements } mFab.setVisibility(View.VISIBLE); // noinspection deprecation (no getDrawable with theme at current minApi level 15!) - mFab.setIconDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp)); + mFab.setImageDrawable(getResources().getDrawable(R.drawable.ic_repeat_white_24dp)); } else { mActionEncryptFile.setVisibility(View.VISIBLE); mActionEncryptText.setVisibility(View.VISIBLE); @@ -880,22 +882,17 @@ public class ViewKeyActivity extends BaseNfcActivity implements } if (mPreviousColor == 0 || mPreviousColor == color) { - mStatusBar.setBackgroundColor(getStatusBarBackgroundColor(color)); - mBigToolbar.setBackgroundColor(color); + mAppBarLayout.setBackgroundColor(color); + mCollapsingToolbarLayout.setContentScrimColor(color); + mCollapsingToolbarLayout.setStatusBarScrimColor(getStatusBarBackgroundColor(color)); mPreviousColor = color; } else { - ObjectAnimator colorFade1 = - ObjectAnimator.ofObject(mStatusBar, "backgroundColor", - new ArgbEvaluator(), mPreviousColor, - getStatusBarBackgroundColor(color)); - ObjectAnimator colorFade2 = - ObjectAnimator.ofObject(mBigToolbar, "backgroundColor", + ObjectAnimator colorFade = + ObjectAnimator.ofObject(mAppBarLayout, "backgroundColor", new ArgbEvaluator(), mPreviousColor, color); - colorFade1.setDuration(1200); - colorFade2.setDuration(1200); - colorFade1.start(); - colorFade2.start(); + colorFade.setDuration(1200); + colorFade.start(); mPreviousColor = color; } -- cgit v1.2.3 From 6fa5f0b129eb30cfdb24da69530a2f107e8ae591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 28 Aug 2015 14:22:52 +0200 Subject: introduce text protection and fix color change after confirm --- .../org/sufficientlysecure/keychain/ui/ViewKeyActivity.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 ff213de74..4ae568d28 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -48,6 +48,7 @@ import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Animation.AnimationListener; import android.view.animation.AnimationUtils; +import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.RelativeLayout; @@ -120,6 +121,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements private ImageButton mActionNfc; private FloatingActionButton mFab; private ImageView mPhoto; + private FrameLayout mPhotoLayout; private ImageView mQrCode; private CardView mQrCodeLayout; @@ -169,6 +171,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements mActionNfc = (ImageButton) findViewById(R.id.view_key_action_nfc); mFab = (FloatingActionButton) findViewById(R.id.fab); mPhoto = (ImageView) findViewById(R.id.view_key_photo); + mPhotoLayout = (FrameLayout) findViewById(R.id.view_key_photo_layout); mQrCode = (ImageView) findViewById(R.id.view_key_qr_code); mQrCodeLayout = (CardView) findViewById(R.id.view_key_qr_code_layout); @@ -779,8 +782,12 @@ public class ViewKeyActivity extends BaseNfcActivity implements } protected void onPostExecute(Bitmap photo) { + if (photo == null) { + return; + } + mPhoto.setImageBitmap(photo); - mPhoto.setVisibility(View.VISIBLE); + mPhotoLayout.setVisibility(View.VISIBLE); } }; @@ -892,6 +899,8 @@ public class ViewKeyActivity extends BaseNfcActivity implements ObjectAnimator colorFade = ObjectAnimator.ofObject(mAppBarLayout, "backgroundColor", new ArgbEvaluator(), mPreviousColor, color); + mCollapsingToolbarLayout.setContentScrimColor(color); + mCollapsingToolbarLayout.setStatusBarScrimColor(getStatusBarBackgroundColor(color)); colorFade.setDuration(1200); colorFade.start(); -- cgit v1.2.3 From 765ec094c9415fcaddd65b7b743179b2ea7dc098 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 28 Aug 2015 15:18:11 +0200 Subject: Make keybase an experimental feature --- .../org/sufficientlysecure/keychain/Constants.java | 1 + .../keychain/ui/SettingsActivity.java | 15 + .../keychain/ui/ViewKeyActivity.java | 7 + .../keychain/ui/ViewKeyAdvActivity.java | 6 - .../keychain/ui/ViewKeyKeybaseFragment.java | 520 ++++++++++++++++++++ .../keychain/ui/ViewKeyTrustFragment.java | 531 --------------------- .../keychain/util/Preferences.java | 10 + 7 files changed, 553 insertions(+), 537 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index 60bcf187d..6a9656b28 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -111,6 +111,7 @@ public final class Constants { // other settings public static final String EXPERIMENTAL_ENABLE_WORD_CONFIRM = "experimentalEnableWordConfirm"; public static final String EXPERIMENTAL_ENABLE_LINKED_IDENTITIES = "experimentalEnableLinkedIdentities"; + public static final String EXPERIMENTAL_ENABLE_KEYBASE = "experimentalEnableKeybase"; public static final class Theme { public static final String LIGHT = "light"; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index 43edc8220..4077f1c84 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -570,7 +570,11 @@ public class SettingsActivity extends AppCompatPreferenceActivity { initializeExperimentalEnableLinkedIdentities( (SwitchPreference) findPreference(Constants.Pref.EXPERIMENTAL_ENABLE_LINKED_IDENTITIES)); + initializeExperimentalEnableKeybase( + (SwitchPreference) findPreference(Constants.Pref.EXPERIMENTAL_ENABLE_KEYBASE)); + initializeTheme((ListPreference) findPreference(Constants.Pref.THEME)); + } } @@ -702,4 +706,15 @@ public class SettingsActivity extends AppCompatPreferenceActivity { } }); } + + private static void initializeExperimentalEnableKeybase(final SwitchPreference mExperimentalKeybase) { + mExperimentalKeybase.setChecked(sPreferences.getExperimentalEnableKeybase()); + mExperimentalKeybase.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + public boolean onPreferenceChange(Preference preference, Object newValue) { + mExperimentalKeybase.setChecked((Boolean) newValue); + sPreferences.setExperimentalEnableKeybase((Boolean) newValue); + return false; + } + }); + } } 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 4ae568d28..06126ebc4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -300,6 +300,13 @@ public class ViewKeyActivity extends BaseNfcActivity implements .replace(R.id.view_key_fragment, frag) .commit(); + if (Preferences.getPreferences(this).getExperimentalEnableKeybase()) { + final ViewKeyKeybaseFragment keybaseFrag = ViewKeyKeybaseFragment.newInstance(mDataUri); + manager.beginTransaction() + .replace(R.id.view_key_keybase_fragment, keybaseFrag) + .commit(); + } + // need to postpone loading of the yubikey fragment until after mMasterKeyId // is available, but we mark here that this should be done mShowYubikeyAfterCreation = true; diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java index 673092e61..edd9feec9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyAdvActivity.java @@ -57,7 +57,6 @@ public class ViewKeyAdvActivity extends BaseActivity implements public static final int TAB_IDENTITIES = 1; public static final int TAB_SUBKEYS = 2; public static final int TAB_CERTS = 3; - public static final int TAB_KEYBASE = 4; // view private ViewPager mViewPager; @@ -140,11 +139,6 @@ public class ViewKeyAdvActivity extends BaseActivity implements adapter.addTab(ViewKeyAdvCertsFragment.class, certsBundle, getString(R.string.key_view_tab_certs)); - Bundle trustBundle = new Bundle(); - trustBundle.putParcelable(ViewKeyTrustFragment.ARG_DATA_URI, dataUri); - adapter.addTab(ViewKeyTrustFragment.class, - trustBundle, getString(R.string.key_view_tab_keybase)); - // update layout after operations mSlidingTabLayout.setViewPager(mViewPager); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java new file mode 100644 index 000000000..266633061 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyKeybaseFragment.java @@ -0,0 +1,520 @@ +/* + * Copyright (C) 2014 Tim Bray + * + * 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; + +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Typeface; +import android.net.Uri; +import android.os.AsyncTask; +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.text.Spanned; +import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; +import android.text.style.StyleSpan; +import android.text.style.URLSpan; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TableLayout; +import android.widget.TableRow; +import android.widget.TextView; + +import com.textuality.keybase.lib.KeybaseException; +import com.textuality.keybase.lib.Proof; +import com.textuality.keybase.lib.User; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.operations.results.KeybaseVerificationResult; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; +import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.ParcelableProxy; +import org.sufficientlysecure.keychain.util.Preferences; +import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; + +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.List; + +public class ViewKeyKeybaseFragment extends LoaderFragment implements + LoaderManager.LoaderCallbacks, + CryptoOperationHelper.Callback { + + public static final String ARG_DATA_URI = "uri"; + + private TextView mReportHeader; + private TableLayout mProofListing; + private LayoutInflater mInflater; + private View mProofVerifyHeader; + private TextView mProofVerifyDetail; + + private static final int LOADER_ID_DATABASE = 1; + + // for retrieving the key we’re working on + private Uri mDataUri; + + private Proof mProof; + + // for CryptoOperationHelper,Callback + private String mKeybaseProof; + private String mKeybaseFingerprint; + private CryptoOperationHelper + mKeybaseOpHelper; + + /** + * Creates new instance of this fragment + */ + public static ViewKeyKeybaseFragment newInstance(Uri dataUri) { + ViewKeyKeybaseFragment frag = new ViewKeyKeybaseFragment(); + Bundle args = new Bundle(); + args.putParcelable(ARG_DATA_URI, dataUri); + + frag.setArguments(args); + + return frag; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { + View root = super.onCreateView(inflater, superContainer, savedInstanceState); + View view = inflater.inflate(R.layout.view_key_adv_keybase_fragment, getContainer()); + mInflater = inflater; + + mReportHeader = (TextView) view.findViewById(R.id.view_key_trust_cloud_narrative); + mProofListing = (TableLayout) view.findViewById(R.id.view_key_proof_list); + mProofVerifyHeader = view.findViewById(R.id.view_key_proof_verify_header); + mProofVerifyDetail = (TextView) view.findViewById(R.id.view_key_proof_verify_detail); + mReportHeader.setVisibility(View.GONE); + mProofListing.setVisibility(View.GONE); + mProofVerifyHeader.setVisibility(View.GONE); + mProofVerifyDetail.setVisibility(View.GONE); + + return root; + } + + @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; + } + mDataUri = dataUri; + + // retrieve the key from the database + getLoaderManager().initLoader(LOADER_ID_DATABASE, null, this); + } + + static final String[] TRUST_PROJECTION = new String[]{ + KeyRings._ID, KeyRings.FINGERPRINT, KeyRings.IS_REVOKED, KeyRings.IS_EXPIRED, + KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED + }; + static final int INDEX_TRUST_FINGERPRINT = 1; + static final int INDEX_TRUST_IS_REVOKED = 2; + static final int INDEX_TRUST_IS_EXPIRED = 3; + static final int INDEX_UNIFIED_HAS_ANY_SECRET = 4; + static final int INDEX_VERIFIED = 5; + + public Loader onCreateLoader(int id, Bundle args) { + setContentShown(false); + + switch (id) { + case LOADER_ID_DATABASE: { + Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri); + return new CursorLoader(getActivity(), baseUri, TRUST_PROJECTION, null, null, null); + } + // decided to just use an AsyncTask for keybase, but maybe later + 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; + } + + boolean nothingSpecial = true; + + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + if (data.moveToFirst()) { + + final byte[] fp = data.getBlob(INDEX_TRUST_FINGERPRINT); + final String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fp); + + startSearch(fingerprint); + } + + setContentShown(true); + } + + private void startSearch(final String fingerprint) { + final Preferences.ProxyPrefs proxyPrefs = + Preferences.getPreferences(getActivity()).getProxyPrefs(); + + OrbotHelper.DialogActions dialogActions = new OrbotHelper.DialogActions() { + @Override + public void onOrbotStarted() { + new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint); + } + + @Override + public void onNeutralButton() { + new DescribeKey(ParcelableProxy.getForNoProxy()) + .execute(fingerprint); + } + + @Override + public void onCancel() { + + } + }; + + if (OrbotHelper.putOrbotInRequiredState(dialogActions, getActivity())) { + new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint); + } + } + + /** + * This is called when the last Cursor provided to onLoadFinished() above is about to be closed. + * We need to make sure we are no longer using it. + */ + public void onLoaderReset(Loader loader) { + // no-op in this case I think + } + + class ResultPage { + String mHeader; + final List mProofs; + + public ResultPage(String header, List proofs) { + mHeader = header; + mProofs = proofs; + } + } + + // look for evidence from keybase in the background, make tabular version of result + // + private class DescribeKey extends AsyncTask { + ParcelableProxy mParcelableProxy; + + public DescribeKey(ParcelableProxy parcelableProxy) { + mParcelableProxy = parcelableProxy; + } + + @Override + protected ResultPage doInBackground(String... args) { + String fingerprint = args[0]; + + final ArrayList proofList = new ArrayList(); + final Hashtable> proofs = new Hashtable>(); + try { + User keybaseUser = User.findByFingerprint(fingerprint, mParcelableProxy.getProxy()); + for (Proof proof : keybaseUser.getProofs()) { + Integer proofType = proof.getType(); + appendIfOK(proofs, proofType, proof); + } + + // a one-liner in a modern programming language + for (Integer proofType : proofs.keySet()) { + Proof[] x = {}; + Proof[] proofsFor = proofs.get(proofType).toArray(x); + if (proofsFor.length > 0) { + SpannableStringBuilder ssb = new SpannableStringBuilder(); + int i = 0; + while (i < proofsFor.length - 1) { + appendProofLinks(ssb, fingerprint, proofsFor[i]); + ssb.append(", "); + i++; + } + appendProofLinks(ssb, fingerprint, proofsFor[i]); + proofList.add(formatSpannableString(ssb, getProofNarrative(proofType))); + } + } + + } catch (KeybaseException ignored) { + } + + return new ResultPage(getString(R.string.key_trust_results_prefix), proofList); + } + + private SpannableStringBuilder formatSpannableString(SpannableStringBuilder proofLinks, String proofType) { + //Formatting SpannableStringBuilder with String.format() causes the links to stop working. + //This method is to insert the links while reserving the links + + SpannableStringBuilder ssb = new SpannableStringBuilder(); + ssb.append(proofType); + if (proofType.contains("%s")) { + int i = proofType.indexOf("%s"); + ssb.replace(i, i + 2, proofLinks); + } else ssb.append(proofLinks); + + return ssb; + } + + private SpannableStringBuilder appendProofLinks(SpannableStringBuilder ssb, final String fingerprint, final Proof proof) throws KeybaseException { + int startAt = ssb.length(); + String handle = proof.getHandle(); + ssb.append(handle); + ssb.setSpan(new URLSpan(proof.getServiceUrl()), startAt, startAt + handle.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + if (haveProofFor(proof.getType())) { + ssb.append("\u00a0["); + startAt = ssb.length(); + String verify = getString(R.string.keybase_verify); + ssb.append(verify); + ClickableSpan clicker = new ClickableSpan() { + @Override + public void onClick(View view) { + verify(proof, fingerprint); + } + }; + ssb.setSpan(clicker, startAt, startAt + verify.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.append("]"); + } + return ssb; + } + + @Override + protected void onPostExecute(ResultPage result) { + super.onPostExecute(result); + if (result.mProofs.isEmpty()) { + result.mHeader = getActivity().getString(R.string.key_trust_no_cloud_evidence); + } + + mReportHeader.setVisibility(View.VISIBLE); + mProofListing.setVisibility(View.VISIBLE); + mReportHeader.setText(result.mHeader); + + int rowNumber = 1; + for (CharSequence s : result.mProofs) { + TableRow row = (TableRow) mInflater.inflate(R.layout.view_key_adv_keybase_proof, null); + TextView number = (TextView) row.findViewById(R.id.proof_number); + TextView text = (TextView) row.findViewById(R.id.proof_text); + number.setText(Integer.toString(rowNumber++) + ". "); + text.setText(s); + text.setMovementMethod(LinkMovementMethod.getInstance()); + mProofListing.addView(row); + } + } + } + + private String getProofNarrative(int proofType) { + int stringIndex; + switch (proofType) { + case Proof.PROOF_TYPE_TWITTER: + stringIndex = R.string.keybase_narrative_twitter; + break; + case Proof.PROOF_TYPE_GITHUB: + stringIndex = R.string.keybase_narrative_github; + break; + case Proof.PROOF_TYPE_DNS: + stringIndex = R.string.keybase_narrative_dns; + break; + case Proof.PROOF_TYPE_WEB_SITE: + stringIndex = R.string.keybase_narrative_web_site; + break; + case Proof.PROOF_TYPE_HACKERNEWS: + stringIndex = R.string.keybase_narrative_hackernews; + break; + case Proof.PROOF_TYPE_COINBASE: + stringIndex = R.string.keybase_narrative_coinbase; + break; + case Proof.PROOF_TYPE_REDDIT: + stringIndex = R.string.keybase_narrative_reddit; + break; + default: + stringIndex = R.string.keybase_narrative_unknown; + } + return getActivity().getString(stringIndex); + } + + private void appendIfOK(Hashtable> table, Integer proofType, Proof proof) throws KeybaseException { + ArrayList list = table.get(proofType); + if (list == null) { + list = new ArrayList(); + table.put(proofType, list); + } + list.add(proof); + } + + // which proofs do we have working verifiers for? + private boolean haveProofFor(int proofType) { + switch (proofType) { + case Proof.PROOF_TYPE_TWITTER: + return true; + case Proof.PROOF_TYPE_GITHUB: + return true; + case Proof.PROOF_TYPE_DNS: + return true; + case Proof.PROOF_TYPE_WEB_SITE: + return true; + case Proof.PROOF_TYPE_HACKERNEWS: + return true; + case Proof.PROOF_TYPE_COINBASE: + return true; + case Proof.PROOF_TYPE_REDDIT: + return true; + default: + return false; + } + } + + private void verify(final Proof proof, final String fingerprint) { + + mProof = proof; + mKeybaseProof = proof.toString(); + mKeybaseFingerprint = fingerprint; + + mProofVerifyDetail.setVisibility(View.GONE); + + mKeybaseOpHelper = new CryptoOperationHelper<>(1, this, this, + R.string.progress_verifying_signature); + mKeybaseOpHelper.cryptoOperation(); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (mKeybaseOpHelper != null) { + mKeybaseOpHelper.handleActivityResult(requestCode, resultCode, data); + } + } + + // CryptoOperationHelper.Callback methods + @Override + public KeybaseVerificationParcel createOperationInput() { + return new KeybaseVerificationParcel(mKeybaseProof, mKeybaseFingerprint); + } + + @Override + public void onCryptoOperationSuccess(KeybaseVerificationResult result) { + + result.createNotify(getActivity()).show(); + + String proofUrl = result.mProofUrl; + String presenceUrl = result.mPresenceUrl; + String presenceLabel = result.mPresenceLabel; + + Proof proof = mProof; // TODO: should ideally be contained in result + + String proofLabel; + switch (proof.getType()) { + case Proof.PROOF_TYPE_TWITTER: + proofLabel = getString(R.string.keybase_twitter_proof); + break; + case Proof.PROOF_TYPE_DNS: + proofLabel = getString(R.string.keybase_dns_proof); + break; + case Proof.PROOF_TYPE_WEB_SITE: + proofLabel = getString(R.string.keybase_web_site_proof); + break; + case Proof.PROOF_TYPE_GITHUB: + proofLabel = getString(R.string.keybase_github_proof); + break; + case Proof.PROOF_TYPE_REDDIT: + proofLabel = getString(R.string.keybase_reddit_proof); + break; + default: + proofLabel = getString(R.string.keybase_a_post); + break; + } + + SpannableStringBuilder ssb = new SpannableStringBuilder(); + + ssb.append(getString(R.string.keybase_proof_succeeded)); + StyleSpan bold = new StyleSpan(Typeface.BOLD); + ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.append("\n\n"); + int length = ssb.length(); + ssb.append(proofLabel); + if (proofUrl != null) { + URLSpan postLink = new URLSpan(proofUrl); + ssb.setSpan(postLink, length, length + proofLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + if (Proof.PROOF_TYPE_DNS == proof.getType()) { + ssb.append(" ").append(getString(R.string.keybase_for_the_domain)).append(" "); + } else { + ssb.append(" ").append(getString(R.string.keybase_fetched_from)).append(" "); + } + length = ssb.length(); + URLSpan presenceLink = new URLSpan(presenceUrl); + ssb.append(presenceLabel); + ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned + .SPAN_EXCLUSIVE_EXCLUSIVE); + if (Proof.PROOF_TYPE_REDDIT == proof.getType()) { + ssb.append(", "). + append(getString(R.string.keybase_reddit_attribution)). + append(" “").append(proof.getHandle()).append("”, "); + } + ssb.append(" ").append(getString(R.string.keybase_contained_signature)); + + displaySpannableResult(ssb); + } + + @Override + public void onCryptoOperationCancelled() { + + } + + @Override + public void onCryptoOperationError(KeybaseVerificationResult result) { + + result.createNotify(getActivity()).show(); + + SpannableStringBuilder ssb = new SpannableStringBuilder(); + + ssb.append(getString(R.string.keybase_proof_failure)); + String msg = getString(result.getLog().getLast().mType.mMsgId); + if (msg == null) { + msg = getString(R.string.keybase_unknown_proof_failure); + } + StyleSpan bold = new StyleSpan(Typeface.BOLD); + ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + ssb.append("\n\n").append(msg); + + displaySpannableResult(ssb); + } + + @Override + public boolean onCryptoSetProgress(String msg, int progress, int max) { + return false; + } + + private void displaySpannableResult(SpannableStringBuilder ssb) { + mProofVerifyHeader.setVisibility(View.VISIBLE); + mProofVerifyDetail.setVisibility(View.VISIBLE); + mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance()); + mProofVerifyDetail.setText(ssb); + } +} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java deleted file mode 100644 index 150acdc90..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyTrustFragment.java +++ /dev/null @@ -1,531 +0,0 @@ -/* - * Copyright (C) 2014 Tim Bray - * - * 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; - -import android.content.Intent; -import android.database.Cursor; -import android.graphics.Typeface; -import android.net.Uri; -import android.os.AsyncTask; -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.text.Spanned; -import android.text.method.LinkMovementMethod; -import android.text.style.ClickableSpan; -import android.text.style.StyleSpan; -import android.text.style.URLSpan; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TableLayout; -import android.widget.TableRow; -import android.widget.TextView; - -import com.textuality.keybase.lib.KeybaseException; -import com.textuality.keybase.lib.Proof; -import com.textuality.keybase.lib.User; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.operations.results.KeybaseVerificationResult; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.service.KeybaseVerificationParcel; -import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; -import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; -import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.ParcelableProxy; -import org.sufficientlysecure.keychain.util.Preferences; -import org.sufficientlysecure.keychain.util.orbot.OrbotHelper; - -import java.util.ArrayList; -import java.util.Hashtable; -import java.util.List; - -public class ViewKeyTrustFragment extends LoaderFragment implements - LoaderManager.LoaderCallbacks, - CryptoOperationHelper.Callback { - - public static final String ARG_DATA_URI = "uri"; - - private View mStartSearch; - private TextView mTrustReadout; - private TextView mReportHeader; - private TableLayout mProofListing; - private LayoutInflater mInflater; - private View mProofVerifyHeader; - private TextView mProofVerifyDetail; - - private static final int LOADER_ID_DATABASE = 1; - - // for retrieving the key we’re working on - private Uri mDataUri; - - private Proof mProof; - - // for CryptoOperationHelper,Callback - private String mKeybaseProof; - private String mKeybaseFingerprint; - private CryptoOperationHelper - mKeybaseOpHelper; - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup superContainer, Bundle savedInstanceState) { - View root = super.onCreateView(inflater, superContainer, savedInstanceState); - View view = inflater.inflate(R.layout.view_key_adv_keybase_fragment, getContainer()); - mInflater = inflater; - - mTrustReadout = (TextView) view.findViewById(R.id.view_key_trust_readout); - mStartSearch = view.findViewById(R.id.view_key_trust_search_cloud); - mStartSearch.setEnabled(false); - mReportHeader = (TextView) view.findViewById(R.id.view_key_trust_cloud_narrative); - mProofListing = (TableLayout) view.findViewById(R.id.view_key_proof_list); - mProofVerifyHeader = view.findViewById(R.id.view_key_proof_verify_header); - mProofVerifyDetail = (TextView) view.findViewById(R.id.view_key_proof_verify_detail); - mReportHeader.setVisibility(View.GONE); - mProofListing.setVisibility(View.GONE); - mProofVerifyHeader.setVisibility(View.GONE); - mProofVerifyDetail.setVisibility(View.GONE); - - return root; - } - - @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; - } - mDataUri = dataUri; - - // retrieve the key from the database - getLoaderManager().initLoader(LOADER_ID_DATABASE, null, this); - } - - static final String[] TRUST_PROJECTION = new String[]{ - KeyRings._ID, KeyRings.FINGERPRINT, KeyRings.IS_REVOKED, KeyRings.IS_EXPIRED, - KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED - }; - static final int INDEX_TRUST_FINGERPRINT = 1; - static final int INDEX_TRUST_IS_REVOKED = 2; - static final int INDEX_TRUST_IS_EXPIRED = 3; - static final int INDEX_UNIFIED_HAS_ANY_SECRET = 4; - static final int INDEX_VERIFIED = 5; - - public Loader onCreateLoader(int id, Bundle args) { - setContentShown(false); - - switch (id) { - case LOADER_ID_DATABASE: { - Uri baseUri = KeyRings.buildUnifiedKeyRingUri(mDataUri); - return new CursorLoader(getActivity(), baseUri, TRUST_PROJECTION, null, null, null); - } - // decided to just use an AsyncTask for keybase, but maybe later - 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; - } - - boolean nothingSpecial = true; - StringBuilder message = new StringBuilder(); - - // Swap the new cursor in. (The framework will take care of closing the - // old cursor once we return.) - if (data.moveToFirst()) { - - if (data.getInt(INDEX_UNIFIED_HAS_ANY_SECRET) != 0) { - message.append(getString(R.string.key_trust_it_is_yours)).append("\n"); - nothingSpecial = false; - } else if (data.getInt(INDEX_VERIFIED) != 0) { - message.append(getString(R.string.key_trust_already_verified)).append("\n"); - nothingSpecial = false; - } - - // If this key is revoked, don’t trust it! - if (data.getInt(INDEX_TRUST_IS_REVOKED) != 0) { - message.append(getString(R.string.key_trust_revoked)). - append(getString(R.string.key_trust_old_keys)); - - nothingSpecial = false; - } else { - if (data.getInt(INDEX_TRUST_IS_EXPIRED) != 0) { - - // if expired, don’t trust it! - message.append(getString(R.string.key_trust_expired)). - append(getString(R.string.key_trust_old_keys)); - - nothingSpecial = false; - } - } - - if (nothingSpecial) { - message.append(getString(R.string.key_trust_maybe)); - } - - final byte[] fp = data.getBlob(INDEX_TRUST_FINGERPRINT); - final String fingerprint = KeyFormattingUtils.convertFingerprintToHex(fp); - if (fingerprint != null) { - - mStartSearch.setEnabled(true); - mStartSearch.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - final Preferences.ProxyPrefs proxyPrefs = - Preferences.getPreferences(getActivity()).getProxyPrefs(); - - OrbotHelper.DialogActions dialogActions = new OrbotHelper.DialogActions() { - @Override - public void onOrbotStarted() { - mStartSearch.setEnabled(false); - new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint); - } - - @Override - public void onNeutralButton() { - mStartSearch.setEnabled(false); - new DescribeKey(ParcelableProxy.getForNoProxy()) - .execute(fingerprint); - } - - @Override - public void onCancel() { - - } - }; - - if (OrbotHelper.putOrbotInRequiredState(dialogActions, getActivity())) { - mStartSearch.setEnabled(false); - new DescribeKey(proxyPrefs.parcelableProxy).execute(fingerprint); - } - } - }); - } - } - - mTrustReadout.setText(message); - setContentShown(true); - } - - /** - * This is called when the last Cursor provided to onLoadFinished() above is about to be closed. - * We need to make sure we are no longer using it. - */ - public void onLoaderReset(Loader loader) { - // no-op in this case I think - } - - class ResultPage { - String mHeader; - final List mProofs; - - public ResultPage(String header, List proofs) { - mHeader = header; - mProofs = proofs; - } - } - - // look for evidence from keybase in the background, make tabular version of result - // - private class DescribeKey extends AsyncTask { - ParcelableProxy mParcelableProxy; - - public DescribeKey(ParcelableProxy parcelableProxy) { - mParcelableProxy = parcelableProxy; - } - - @Override - protected ResultPage doInBackground(String... args) { - String fingerprint = args[0]; - - final ArrayList proofList = new ArrayList(); - final Hashtable> proofs = new Hashtable>(); - try { - User keybaseUser = User.findByFingerprint(fingerprint, mParcelableProxy.getProxy()); - for (Proof proof : keybaseUser.getProofs()) { - Integer proofType = proof.getType(); - appendIfOK(proofs, proofType, proof); - } - - // a one-liner in a modern programming language - for (Integer proofType : proofs.keySet()) { - Proof[] x = {}; - Proof[] proofsFor = proofs.get(proofType).toArray(x); - if (proofsFor.length > 0) { - SpannableStringBuilder ssb = new SpannableStringBuilder(); - int i = 0; - while (i < proofsFor.length - 1) { - appendProofLinks(ssb, fingerprint, proofsFor[i]); - ssb.append(", "); - i++; - } - appendProofLinks(ssb, fingerprint, proofsFor[i]); - proofList.add(formatSpannableString(ssb, getProofNarrative(proofType))); - } - } - - } catch (KeybaseException ignored) { - } - - return new ResultPage(getString(R.string.key_trust_results_prefix), proofList); - } - - private SpannableStringBuilder formatSpannableString(SpannableStringBuilder proofLinks,String proofType){ - //Formatting SpannableStringBuilder with String.format() causes the links to stop working. - //This method is to insert the links while reserving the links - - SpannableStringBuilder ssb = new SpannableStringBuilder(); - ssb.append(proofType); - if(proofType.contains("%s")){ - int i = proofType.indexOf("%s"); - ssb.replace(i,i+2,proofLinks); - } - else ssb.append(proofLinks); - - return ssb; - } - - private SpannableStringBuilder appendProofLinks(SpannableStringBuilder ssb, final String fingerprint, final Proof proof) throws KeybaseException { - int startAt = ssb.length(); - String handle = proof.getHandle(); - ssb.append(handle); - ssb.setSpan(new URLSpan(proof.getServiceUrl()), startAt, startAt + handle.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - if (haveProofFor(proof.getType())) { - ssb.append("\u00a0["); - startAt = ssb.length(); - String verify = getString(R.string.keybase_verify); - ssb.append(verify); - ClickableSpan clicker = new ClickableSpan() { - @Override - public void onClick(View view) { - verify(proof, fingerprint); - } - }; - ssb.setSpan(clicker, startAt, startAt + verify.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - ssb.append("]"); - } - return ssb; - } - - @Override - protected void onPostExecute(ResultPage result) { - super.onPostExecute(result); - if (result.mProofs.isEmpty()) { - result.mHeader = getActivity().getString(R.string.key_trust_no_cloud_evidence); - } - - mStartSearch.setVisibility(View.GONE); - mReportHeader.setVisibility(View.VISIBLE); - mProofListing.setVisibility(View.VISIBLE); - mReportHeader.setText(result.mHeader); - - int rowNumber = 1; - for (CharSequence s : result.mProofs) { - TableRow row = (TableRow) mInflater.inflate(R.layout.view_key_adv_keybase_proof, null); - TextView number = (TextView) row.findViewById(R.id.proof_number); - TextView text = (TextView) row.findViewById(R.id.proof_text); - number.setText(Integer.toString(rowNumber++) + ". "); - text.setText(s); - text.setMovementMethod(LinkMovementMethod.getInstance()); - mProofListing.addView(row); - } - - // mSearchReport.loadDataWithBaseURL("file:///android_res/drawable/", s, "text/html", "UTF-8", null); - } - } - - private String getProofNarrative(int proofType) { - int stringIndex; - switch (proofType) { - case Proof.PROOF_TYPE_TWITTER: stringIndex = R.string.keybase_narrative_twitter; break; - case Proof.PROOF_TYPE_GITHUB: stringIndex = R.string.keybase_narrative_github; break; - case Proof.PROOF_TYPE_DNS: stringIndex = R.string.keybase_narrative_dns; break; - case Proof.PROOF_TYPE_WEB_SITE: stringIndex = R.string.keybase_narrative_web_site; break; - case Proof.PROOF_TYPE_HACKERNEWS: stringIndex = R.string.keybase_narrative_hackernews; break; - case Proof.PROOF_TYPE_COINBASE: stringIndex = R.string.keybase_narrative_coinbase; break; - case Proof.PROOF_TYPE_REDDIT: stringIndex = R.string.keybase_narrative_reddit; break; - default: stringIndex = R.string.keybase_narrative_unknown; - } - return getActivity().getString(stringIndex); - } - - private void appendIfOK(Hashtable> table, Integer proofType, Proof proof) throws KeybaseException { - ArrayList list = table.get(proofType); - if (list == null) { - list = new ArrayList(); - table.put(proofType, list); - } - list.add(proof); - } - - // which proofs do we have working verifiers for? - private boolean haveProofFor(int proofType) { - switch (proofType) { - case Proof.PROOF_TYPE_TWITTER: return true; - case Proof.PROOF_TYPE_GITHUB: return true; - case Proof.PROOF_TYPE_DNS: return true; - case Proof.PROOF_TYPE_WEB_SITE: return true; - case Proof.PROOF_TYPE_HACKERNEWS: return true; - case Proof.PROOF_TYPE_COINBASE: return true; - case Proof.PROOF_TYPE_REDDIT: return true; - default: return false; - } - } - - private void verify(final Proof proof, final String fingerprint) { - - mProof = proof; - mKeybaseProof = proof.toString(); - mKeybaseFingerprint = fingerprint; - - mProofVerifyDetail.setVisibility(View.GONE); - - mKeybaseOpHelper = new CryptoOperationHelper<>(1, this, this, - R.string.progress_verifying_signature); - mKeybaseOpHelper.cryptoOperation(); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (mKeybaseOpHelper != null) { - mKeybaseOpHelper.handleActivityResult(requestCode, resultCode, data); - } - } - - // CryptoOperationHelper.Callback methods - @Override - public KeybaseVerificationParcel createOperationInput() { - return new KeybaseVerificationParcel(mKeybaseProof, mKeybaseFingerprint); - } - - @Override - public void onCryptoOperationSuccess(KeybaseVerificationResult result) { - - result.createNotify(getActivity()).show(); - - String proofUrl = result.mProofUrl; - String presenceUrl = result.mPresenceUrl; - String presenceLabel = result.mPresenceLabel; - - Proof proof = mProof; // TODO: should ideally be contained in result - - String proofLabel; - switch (proof.getType()) { - case Proof.PROOF_TYPE_TWITTER: - proofLabel = getString(R.string.keybase_twitter_proof); - break; - case Proof.PROOF_TYPE_DNS: - proofLabel = getString(R.string.keybase_dns_proof); - break; - case Proof.PROOF_TYPE_WEB_SITE: - proofLabel = getString(R.string.keybase_web_site_proof); - break; - case Proof.PROOF_TYPE_GITHUB: - proofLabel = getString(R.string.keybase_github_proof); - break; - case Proof.PROOF_TYPE_REDDIT: - proofLabel = getString(R.string.keybase_reddit_proof); - break; - default: - proofLabel = getString(R.string.keybase_a_post); - break; - } - - SpannableStringBuilder ssb = new SpannableStringBuilder(); - - ssb.append(getString(R.string.keybase_proof_succeeded)); - StyleSpan bold = new StyleSpan(Typeface.BOLD); - ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - ssb.append("\n\n"); - int length = ssb.length(); - ssb.append(proofLabel); - if (proofUrl != null) { - URLSpan postLink = new URLSpan(proofUrl); - ssb.setSpan(postLink, length, length + proofLabel.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - } - if (Proof.PROOF_TYPE_DNS == proof.getType()) { - ssb.append(" ").append(getString(R.string.keybase_for_the_domain)).append(" "); - } else { - ssb.append(" ").append(getString(R.string.keybase_fetched_from)).append(" "); - } - length = ssb.length(); - URLSpan presenceLink = new URLSpan(presenceUrl); - ssb.append(presenceLabel); - ssb.setSpan(presenceLink, length, length + presenceLabel.length(), Spanned - .SPAN_EXCLUSIVE_EXCLUSIVE); - if (Proof.PROOF_TYPE_REDDIT == proof.getType()) { - ssb.append(", "). - append(getString(R.string.keybase_reddit_attribution)). - append(" “").append(proof.getHandle()).append("”, "); - } - ssb.append(" ").append(getString(R.string.keybase_contained_signature)); - - displaySpannableResult(ssb); - } - - @Override - public void onCryptoOperationCancelled() { - - } - - @Override - public void onCryptoOperationError(KeybaseVerificationResult result) { - - result.createNotify(getActivity()).show(); - - SpannableStringBuilder ssb = new SpannableStringBuilder(); - - ssb.append(getString(R.string.keybase_proof_failure)); - String msg = getString(result.getLog().getLast().mType.mMsgId); - if (msg == null) { - msg = getString(R.string.keybase_unknown_proof_failure); - } - StyleSpan bold = new StyleSpan(Typeface.BOLD); - ssb.setSpan(bold, 0, ssb.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); - ssb.append("\n\n").append(msg); - - displaySpannableResult(ssb); - } - - @Override - public boolean onCryptoSetProgress(String msg, int progress, int max) { - return false; - } - - private void displaySpannableResult(SpannableStringBuilder ssb) { - mProofVerifyHeader.setVisibility(View.VISIBLE); - mProofVerifyDetail.setVisibility(View.VISIBLE); - mProofVerifyDetail.setMovementMethod(LinkMovementMethod.getInstance()); - mProofVerifyDetail.setText(ssb); - } -} diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java index 3225c3765..4ef215036 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Preferences.java @@ -381,6 +381,16 @@ public class Preferences { return mSharedPreferences.getBoolean(Pref.EXPERIMENTAL_ENABLE_LINKED_IDENTITIES, false); } + public void setExperimentalEnableKeybase(boolean enableKeybase) { + SharedPreferences.Editor editor = mSharedPreferences.edit(); + editor.putBoolean(Pref.EXPERIMENTAL_ENABLE_KEYBASE, enableKeybase); + editor.commit(); + } + + public boolean getExperimentalEnableKeybase() { + return mSharedPreferences.getBoolean(Pref.EXPERIMENTAL_ENABLE_KEYBASE, false); + } + public void upgradePreferences(Context context) { if (mSharedPreferences.getInt(Constants.Pref.PREF_DEFAULT_VERSION, 0) != Constants.Defaults.PREF_VERSION) { -- cgit v1.2.3 From 76522d90faa5a8727493a34786b4490919defc67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 28 Aug 2015 17:14:40 +0200 Subject: Cleanup OpenPgpService2 code, update openpgp-api lib --- .../keychain/remote/OpenPgpService2.java | 54 ++-------------------- 1 file changed, 3 insertions(+), 51 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService2.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService2.java index 317d4dd73..110302e55 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService2.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService2.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2015 Dominik Schürmann + * Copyright (C) 2015 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 @@ -17,64 +17,21 @@ package org.sufficientlysecure.keychain.remote; -import android.app.PendingIntent; -import android.content.Context; import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.os.ParcelFileDescriptor; -import android.os.Parcelable; -import android.text.TextUtils; import org.openintents.openpgp.IOpenPgpService2; -import org.openintents.openpgp.OpenPgpDecryptionResult; -import org.openintents.openpgp.OpenPgpError; -import org.openintents.openpgp.OpenPgpMetadata; -import org.openintents.openpgp.OpenPgpSignatureResult; -import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; -import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel; -import org.sufficientlysecure.keychain.operations.results.PgpSignEncryptResult; -import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; -import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; -import org.sufficientlysecure.keychain.pgp.PgpSecurityConstants; -import org.sufficientlysecure.keychain.pgp.PgpSignEncryptInputParcel; -import org.sufficientlysecure.keychain.pgp.PgpSignEncryptOperation; -import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; -import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts; -import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; -import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; -import org.sufficientlysecure.keychain.provider.ProviderHelper; -import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity; -import org.sufficientlysecure.keychain.remote.ui.SelectAllowedKeysActivity; -import org.sufficientlysecure.keychain.remote.ui.SelectSignKeyIdActivity; -import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; -import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; -import org.sufficientlysecure.keychain.ui.ImportKeysActivity; -import org.sufficientlysecure.keychain.ui.NfcOperationActivity; -import org.sufficientlysecure.keychain.ui.PassphraseDialogActivity; -import org.sufficientlysecure.keychain.ui.ViewKeyActivity; -import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.Passphrase; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Date; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; public class OpenPgpService2 extends OpenPgpService { - - private Map mOutputPipeMap = new HashMap(); private long createKey(int id) { @@ -91,23 +48,20 @@ public class OpenPgpService2 extends OpenPgpService { mOutputPipeMap.put(createKey(outputPipeId), pipe[1]); return pipe[0]; } catch (IOException e) { - e.printStackTrace(); + Log.e(Constants.TAG, "IOException in OpenPgpService2", e); return null; } } @Override - public Intent execute(Intent data, ParcelFileDescriptor input,int outputPipeId) { + public Intent execute(Intent data, ParcelFileDescriptor input, int outputPipeId) { long key = createKey(outputPipeId); ParcelFileDescriptor output = mOutputPipeMap.get(key); mOutputPipeMap.remove(key); return executeInternal(data, input, output); } - - - }; @Override @@ -115,6 +69,4 @@ public class OpenPgpService2 extends OpenPgpService { return mBinder; } - - } -- cgit v1.2.3 From 8a780c9c492711af915a1e37d6e90a3c1df50552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 28 Aug 2015 17:46:57 +0200 Subject: Support OpenPGP API v9 --- .../keychain/remote/OpenPgpService.java | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index cc4a08fb4..57dd068ef 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -66,8 +66,10 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashSet; +import java.util.List; public class OpenPgpService extends RemoteService { @@ -813,19 +815,14 @@ public class OpenPgpService extends RemoteService { } // version code is required and needs to correspond to version code of service! - // History of versions in org.openintents.openpgp.util.OpenPgpApi - // we support 3, 4, 5, 6 - if (data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 3 - && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 4 - && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 5 - && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 6 - && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 7 - && data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) != 8) { + // History of versions in openpgp-api's CHANGELOG.md + List supportedVersions = Arrays.asList(3, 4, 5, 6, 7, 8, 9); + if (!supportedVersions.contains(data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1))) { Intent result = new Intent(); OpenPgpError error = new OpenPgpError (OpenPgpError.INCOMPATIBLE_API_VERSIONS, "Incompatible API versions!\n" + "used API version: " + data.getIntExtra(OpenPgpApi.EXTRA_API_VERSION, -1) + "\n" - + "supported API versions: 3-8"); + + "supported API versions: " + supportedVersions.toString()); result.putExtra(OpenPgpApi.RESULT_ERROR, error); result.putExtra(OpenPgpApi.RESULT_CODE, OpenPgpApi.RESULT_CODE_ERROR); return result; @@ -855,7 +852,6 @@ public class OpenPgpService extends RemoteService { } - protected Intent executeInternal(Intent data, ParcelFileDescriptor input, ParcelFileDescriptor output) { try { Intent errorResult = checkRequirements(data); -- cgit v1.2.3 From feef4a93abd2ebcd7a4dd6868c9a924628863a06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sat, 29 Aug 2015 17:19:58 +0200 Subject: Reword experimental settings --- .../java/org/sufficientlysecure/keychain/ui/SettingsActivity.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index 4077f1c84..21d0f1dbd 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -615,11 +615,13 @@ public class SettingsActivity extends AppCompatPreferenceActivity { private static void initializeTheme(final ListPreference mTheme) { mTheme.setValue(sPreferences.getTheme()); - mTheme.setSummary(mTheme.getEntry()); + mTheme.setSummary(mTheme.getEntry() + "\n" + + mTheme.getContext().getString(R.string.label_experimental_settings_theme_summary)); mTheme.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { public boolean onPreferenceChange(Preference preference, Object newValue) { mTheme.setValue((String) newValue); - mTheme.setSummary(mTheme.getEntry()); + mTheme.setSummary(mTheme.getEntry() + "\n" + + mTheme.getContext().getString(R.string.label_experimental_settings_theme_summary)); sPreferences.setTheme((String) newValue); ((SettingsActivity) mTheme.getContext()).recreate(); -- cgit v1.2.3 From bff302f80865a89a46e2c111bb2f3c99986bc285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sat, 29 Aug 2015 17:27:04 +0200 Subject: Show linked identity menu item and linked identities only when feature is enabled --- .../org/sufficientlysecure/keychain/ui/ViewKeyActivity.java | 3 ++- .../org/sufficientlysecure/keychain/ui/ViewKeyFragment.java | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 fd50ed5ef..2ffb7227e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -389,7 +389,8 @@ public class ViewKeyActivity extends BaseNfcActivity implements exportKey.setVisible(mIsSecret); MenuItem addLinked = menu.findItem(R.id.menu_key_view_add_linked_identity); - addLinked.setVisible(mIsSecret); + addLinked.setVisible(mIsSecret + && Preferences.getPreferences(this).getExperimentalEnableLinkedIdentities()); MenuItem certifyFingerprint = menu.findItem(R.id.menu_key_view_certify_fingerprint); certifyFingerprint.setVisible(!mIsSecret && !mIsVerified && !mIsExpired && !mIsRevoked); 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 89e5d741f..32e2d2def 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -56,6 +56,7 @@ import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment; import org.sufficientlysecure.keychain.ui.linked.LinkedIdViewFragment.OnIdentityLoadedListener; import org.sufficientlysecure.keychain.util.ContactHelper; import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.util.Preferences; import java.util.List; @@ -395,10 +396,12 @@ public class ViewKeyFragment extends LoaderFragment implements mUserIds.setAdapter(mUserIdsAdapter); getLoaderManager().initLoader(LOADER_ID_USER_IDS, null, this); - mLinkedIdsAdapter = - new LinkedIdsAdapter(getActivity(), null, 0, mIsSecret, mLinkedIdsExpander); - mLinkedIds.setAdapter(mLinkedIdsAdapter); - getLoaderManager().initLoader(LOADER_ID_LINKED_IDS, null, this); + if (Preferences.getPreferences(getActivity()).getExperimentalEnableLinkedIdentities()) { + mLinkedIdsAdapter = + new LinkedIdsAdapter(getActivity(), null, 0, mIsSecret, mLinkedIdsExpander); + mLinkedIds.setAdapter(mLinkedIdsAdapter); + getLoaderManager().initLoader(LOADER_ID_LINKED_IDS, null, this); + } long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID); // we need to load linked contact here to prevent lag introduced by loader -- cgit v1.2.3 From ee8101816349c53b119d5942bfc3076a10a1c744 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Mon, 31 Aug 2015 13:40:22 +0200 Subject: linked: disable DNS resource (for now) --- .../sufficientlysecure/keychain/linked/LinkedTokenResource.java | 8 ++++---- .../keychain/ui/linked/LinkedIdSelectFragment.java | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 61e275166..998ec3ad4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java @@ -117,10 +117,10 @@ public abstract class LinkedTokenResource extends LinkedResource { if (res != null) { return res; } - res = DnsResource.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; 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 8249f0bb6..947ebb52f 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 @@ -56,6 +56,7 @@ public class LinkedIdSelectFragment extends Fragment { } }); + /* view.findViewById(R.id.linked_create_dns_button) .setOnClickListener(new View.OnClickListener() { @Override @@ -66,6 +67,7 @@ public class LinkedIdSelectFragment extends Fragment { mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); } }); + */ view.findViewById(R.id.linked_create_twitter_button) .setOnClickListener(new View.OnClickListener() { -- cgit v1.2.3 From 9668abfe9edf09f36dc361b008106708f5c1dd9c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 1 Sep 2015 06:40:28 +0200 Subject: add simple StatusIndicator widget --- .../keychain/ui/widget/StatusIndicator.java | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java new file mode 100644 index 000000000..f5b1cf3b6 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java @@ -0,0 +1,25 @@ +package org.sufficientlysecure.keychain.ui.widget; + + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; + +import org.sufficientlysecure.keychain.R; + + +public class StatusIndicator extends ToolableViewAnimator { + + public StatusIndicator(Context context) { + super(context); + + LayoutInflater.from(context).inflate(R.layout.status_indicator, this, true); + } + + public StatusIndicator(Context context, AttributeSet attrs) { + super(context, attrs); + + LayoutInflater.from(context).inflate(R.layout.status_indicator, this, true); + } + +} -- cgit v1.2.3 From b52a0303ca3a6b10661f36a0ff2102eb21dffaf7 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 1 Sep 2015 06:41:47 +0200 Subject: linked: redesign github resource creation, implement ouath flow (WIP) --- .../ui/linked/LinkedIdCreateFinalFragment.java | 18 +- .../ui/linked/LinkedIdCreateGithubFragment.java | 246 +++++++++++++++++++++ .../keychain/ui/linked/LinkedIdSelectFragment.java | 4 +- .../keychain/ui/linked/LinkedIdWizard.java | 57 +++++ 4 files changed, 316 insertions(+), 9 deletions(-) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 24499a467..e09b1e755 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 @@ -5,6 +5,7 @@ import android.graphics.PorterDuff; import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcelable; +import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; @@ -48,16 +49,19 @@ public abstract class LinkedIdCreateFinalFragment extends CryptoOperationFragmen protected abstract View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); - @Override + @Override @NonNull 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) { - cryptoOperation(); - } - }); + View nextButton = view.findViewById(R.id.next_button); + if (nextButton != null) { + nextButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + cryptoOperation(); + } + }); + } view.findViewById(R.id.back_button).setOnClickListener(new View.OnClickListener() { @Override diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java new file mode 100644 index 000000000..71a831741 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -0,0 +1,246 @@ +/* + * 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.BufferedInputStream; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.net.URL; + +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; +import android.widget.ViewAnimator; + +import javax.net.ssl.HttpsURLConnection; +import org.json.JSONException; +import org.json.JSONObject; +import org.sufficientlysecure.keychain.BuildConfig; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.ui.widget.StatusIndicator; +import org.sufficientlysecure.keychain.util.Log; + + +public class LinkedIdCreateGithubFragment extends Fragment { + + ViewAnimator mProceedContainer; + EditText mGithubUsername, mGithubPassword; + + StatusIndicator mStatus1, mStatus2, mStatus3; + + public static LinkedIdCreateGithubFragment newInstance() { + return new LinkedIdCreateGithubFragment(); + } + + @Override @NonNull + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.linked_create_github_fragment, container, false); + + mProceedContainer = (ViewAnimator) view.findViewById(R.id.proceed_container); + + mGithubUsername = (EditText) view.findViewById(R.id.username); + mGithubPassword = (EditText) view.findViewById(R.id.password); + + mStatus1 = (StatusIndicator) view.findViewById(R.id.linked_status_step1); + mStatus2 = (StatusIndicator) view.findViewById(R.id.linked_status_step2); + mStatus3 = (StatusIndicator) view.findViewById(R.id.linked_status_step3); + + view.findViewById(R.id.button_send).setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + step1GetOAuthToken(); + } + }); + + return view; + } + + @Override + public void onResume() { + super.onResume(); + + LinkedIdWizard wizard = (LinkedIdWizard) getActivity(); + final String oAuthCode = wizard.oAuthGetCode(); + final String oAuthState = wizard.oAuthGetState(); + if (oAuthCode == null) { + Log.d(Constants.TAG, "no code"); + return; + } + + Log.d(Constants.TAG, "got code: " + oAuthCode); + + new AsyncTask() { + @Override + protected JSONObject doInBackground(Void... dummy) { + try { + + JSONObject params = new JSONObject(); + params.put("client_id", "7a011b66275f244d3f21"); + params.put("client_secret", "eaced8a6655719d8c6848396de97b3f5d7a89fec"); + params.put("code", oAuthCode); + params.put("state", oAuthState); + + return jsonHttpRequest("https://github.com/login/oauth/access_token", params, null); + + } catch (IOException e) { + Log.e(Constants.TAG, "error in request", e); + } catch (JSONException e) { + throw new AssertionError("json error, this is a bug!"); + } + return null; + } + + @Override + protected void onPostExecute(JSONObject result) { + super.onPostExecute(result); + + Log.d(Constants.TAG, "response: " + result); + + if (result == null || !result.has("access_token")) { + mStatus1.setDisplayedChild(3); + return; + } + + mStatus1.setDisplayedChild(2); + step2PostGist(result.optString("access_token")); + + } + }.execute(); + + } + + private void step1GetOAuthToken() { + + mStatus1.setDisplayedChild(1); + mStatus2.setDisplayedChild(0); + mStatus3.setDisplayedChild(0); + + mProceedContainer.setDisplayedChild(1); + + LinkedIdWizard wizard = (LinkedIdWizard) getActivity(); + wizard.oAuthRequest("github.com/login/oauth/authorize", "7a011b66275f244d3f21", "gist"); + + } + + private void step2PostGist(final String accessToken) { + + mStatus2.setDisplayedChild(1); + + new AsyncTask() { + @Override + protected JSONObject doInBackground(Void... dummy) { + try { + + JSONObject file = new JSONObject(); + file.put("content", "hello!"); + + JSONObject files = new JSONObject(); + files.put("file1.txt", file); + + JSONObject params = new JSONObject(); + params.put("public", true); + params.put("description", "OpenKeychain API Tests"); + params.put("files", files); + + return jsonHttpRequest("https://api.github.com/gists", params, accessToken); + + } catch (IOException e) { + Log.e(Constants.TAG, "error in request", e); + } catch (JSONException e) { + throw new AssertionError("json error, this is a bug!"); + } + return null; + } + + @Override + protected void onPostExecute(JSONObject result) { + super.onPostExecute(result); + + Log.d(Constants.TAG, "response: " + result); + + if (result == null) { + mStatus2.setDisplayedChild(3); + return; + } + + mStatus2.setDisplayedChild(2); + } + }.execute(); + + } + + private static JSONObject jsonHttpRequest(String url, JSONObject params, String accessToken) + throws IOException { + + HttpsURLConnection nection = (HttpsURLConnection) new URL(url).openConnection(); + nection.setDoInput(true); + nection.setDoOutput(true); + nection.setRequestProperty("Content-Type", "application/json"); + nection.setRequestProperty("Accept", "application/json"); + nection.setRequestProperty("User-Agent", "OpenKeychain " + BuildConfig.VERSION_NAME); + if (accessToken != null) { + nection.setRequestProperty("Authorization", "token " + accessToken); + } + + OutputStream os = nection.getOutputStream(); + BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os, "UTF-8")); + writer.write(params.toString()); + writer.flush(); + writer.close(); + os.close(); + + try { + + nection.connect(); + InputStream in = new BufferedInputStream(nection.getInputStream()); + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + StringBuilder response = new StringBuilder(); + while (true) { + String line = reader.readLine(); + if (line == null) { + break; + } + response.append(line); + } + + try { + return new JSONObject(response.toString()); + } catch (JSONException e) { + throw new IOException(e); + } + + } finally { + nection.disconnect(); + } + + } + +} 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 947ebb52f..a17a97013 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 @@ -84,8 +84,8 @@ public class LinkedIdSelectFragment extends Fragment { .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - LinkedIdCreateGithubStep1Fragment frag = - LinkedIdCreateGithubStep1Fragment.newInstance(); + LinkedIdCreateGithubFragment frag = + LinkedIdCreateGithubFragment.newInstance(); mLinkedIdWizard.loadFragment(null, frag, LinkedIdWizard.FRAG_ACTION_TO_RIGHT); } 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 a29f175c0..2fb6384b2 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,7 +17,10 @@ package org.sufficientlysecure.keychain.ui.linked; +import java.util.Random; + import android.content.Context; +import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -25,6 +28,7 @@ import android.support.v4.app.FragmentTransaction; import android.view.View; import android.view.inputmethod.InputMethodManager; +import org.spongycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; @@ -32,6 +36,8 @@ 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.ui.util.Notify; +import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.util.Log; public class LinkedIdWizard extends BaseActivity { @@ -125,4 +131,55 @@ public class LinkedIdWizard extends BaseActivity { inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0); } + private String mOAuthCode, mOAuthState; + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + + Uri uri = intent.getData(); + if (uri != null) { + Log.d(Constants.TAG, "received oauth uri: " + uri); + String state = uri.getQueryParameter("state"); + if (!mOAuthState.equalsIgnoreCase(state)) { + Notify.create(this, "Authentication Error!", Style.ERROR).show(); + return; + } + mOAuthCode = uri.getQueryParameter("code"); + } else { + Log.d(Constants.TAG, "received oauth uri: null"); + } + + } + + public String oAuthGetCode() { + try { + return mOAuthCode; + } finally { + mOAuthCode = null; + } + } + + public String oAuthGetState() { + return mOAuthState; + } + + public void oAuthRequest(String hostAndPath, String clientId, String scope) { + + byte[] buf = new byte[16]; + new Random().nextBytes(buf); + mOAuthState = new String(Hex.encode(buf)); + + Intent intent = new Intent( + Intent.ACTION_VIEW, + Uri.parse("https://" + hostAndPath + + "?client_id=" + clientId + + "&scope=" + scope + + "&redirect_uri=oauth-openkeychain://linked/" + + "&state=" + mOAuthState)); + + startActivity(intent); + + } + } -- cgit v1.2.3 From 3e55fca0e5dcad792d821c3a20fc4c404083a124 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 1 Sep 2015 07:09:53 +0200 Subject: linked: work on github design --- .../ui/linked/LinkedIdCreateGithubFragment.java | 63 +++++++++++++++++----- .../keychain/ui/widget/StatusIndicator.java | 5 ++ 2 files changed, 56 insertions(+), 12 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index 71a831741..b30fc091e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -36,7 +36,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.ViewAnimator; import javax.net.ssl.HttpsURLConnection; @@ -51,8 +50,7 @@ import org.sufficientlysecure.keychain.util.Log; public class LinkedIdCreateGithubFragment extends Fragment { - ViewAnimator mProceedContainer; - EditText mGithubUsername, mGithubPassword; + ViewAnimator mButtonContainer; StatusIndicator mStatus1, mStatus2, mStatus3; @@ -64,10 +62,7 @@ public class LinkedIdCreateGithubFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.linked_create_github_fragment, container, false); - mProceedContainer = (ViewAnimator) view.findViewById(R.id.proceed_container); - - mGithubUsername = (EditText) view.findViewById(R.id.username); - mGithubPassword = (EditText) view.findViewById(R.id.password); + mButtonContainer = (ViewAnimator) view.findViewById(R.id.button_container); mStatus1 = (StatusIndicator) view.findViewById(R.id.linked_status_step1); mStatus2 = (StatusIndicator) view.findViewById(R.id.linked_status_step2); @@ -102,13 +97,25 @@ public class LinkedIdCreateGithubFragment extends Fragment { protected JSONObject doInBackground(Void... dummy) { try { + long timer = System.currentTimeMillis(); + JSONObject params = new JSONObject(); params.put("client_id", "7a011b66275f244d3f21"); params.put("client_secret", "eaced8a6655719d8c6848396de97b3f5d7a89fec"); params.put("code", oAuthCode); params.put("state", oAuthState); - return jsonHttpRequest("https://github.com/login/oauth/access_token", params, null); + JSONObject result = jsonHttpRequest("https://github.com/login/oauth/access_token", params, null); + + // ux flow: this operation should take at last a second + timer = System.currentTimeMillis() -timer; + if (timer < 1000) try { + Thread.sleep(1000 -timer); + } catch (InterruptedException e) { + // never mind + } + + return result; } catch (IOException e) { Log.e(Constants.TAG, "error in request", e); @@ -143,10 +150,30 @@ public class LinkedIdCreateGithubFragment extends Fragment { mStatus2.setDisplayedChild(0); mStatus3.setDisplayedChild(0); - mProceedContainer.setDisplayedChild(1); + mButtonContainer.setDisplayedChild(1); - LinkedIdWizard wizard = (LinkedIdWizard) getActivity(); - wizard.oAuthRequest("github.com/login/oauth/authorize", "7a011b66275f244d3f21", "gist"); + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + try { + Thread.sleep(250); + } catch (InterruptedException e) { + // never mind + } + + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + LinkedIdWizard wizard = (LinkedIdWizard) getActivity(); + if (wizard == null) { + return; + } + wizard.oAuthRequest("github.com/login/oauth/authorize", "7a011b66275f244d3f21", "gist"); + } + }.execute(); } @@ -159,6 +186,8 @@ public class LinkedIdCreateGithubFragment extends Fragment { protected JSONObject doInBackground(Void... dummy) { try { + long timer = System.currentTimeMillis(); + JSONObject file = new JSONObject(); file.put("content", "hello!"); @@ -170,7 +199,17 @@ public class LinkedIdCreateGithubFragment extends Fragment { params.put("description", "OpenKeychain API Tests"); params.put("files", files); - return jsonHttpRequest("https://api.github.com/gists", params, accessToken); + JSONObject result = jsonHttpRequest("https://api.github.com/gists", params, accessToken); + + // ux flow: this operation should take at last a second + timer = System.currentTimeMillis() -timer; + if (timer < 1000) try { + Thread.sleep(1000 -timer); + } catch (InterruptedException e) { + // never mind + } + + return result; } catch (IOException e) { Log.e(Constants.TAG, "error in request", e); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java index f5b1cf3b6..bef40d57c 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java @@ -4,6 +4,7 @@ package org.sufficientlysecure.keychain.ui.widget; import android.content.Context; import android.util.AttributeSet; import android.view.LayoutInflater; +import android.view.animation.AnimationUtils; import org.sufficientlysecure.keychain.R; @@ -14,12 +15,16 @@ public class StatusIndicator extends ToolableViewAnimator { super(context); LayoutInflater.from(context).inflate(R.layout.status_indicator, this, true); + setInAnimation(AnimationUtils.loadAnimation(context, R.anim.fade_in)); + setOutAnimation(AnimationUtils.loadAnimation(context, R.anim.fade_out)); } public StatusIndicator(Context context, AttributeSet attrs) { super(context, attrs); LayoutInflater.from(context).inflate(R.layout.status_indicator, this, true); + setInAnimation(AnimationUtils.loadAnimation(context, R.anim.fade_in)); + setOutAnimation(AnimationUtils.loadAnimation(context, R.anim.fade_out)); } } -- cgit v1.2.3 From 47ef8cbe558a442fd7206208d941c0dcaa907f6a Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 1 Sep 2015 13:48:49 +0200 Subject: fix missing date in CryptoInputParcel in some cases (could be a bigger problem!) --- .../sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java index 6d7bf4cd0..52c6797d5 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/base/CryptoOperationHelper.java @@ -19,6 +19,8 @@ package org.sufficientlysecure.keychain.ui.base; +import java.util.Date; + import android.app.Activity; import android.app.ProgressDialog; import android.content.Intent; @@ -315,7 +317,7 @@ public class CryptoOperationHelper Date: Tue, 1 Sep 2015 13:58:04 +0200 Subject: linked: redesign github resource creation, implement ouath flow --- .../ui/linked/LinkedIdCreateGithubFragment.java | 77 ++++++++++++++++++++-- .../keychain/ui/linked/LinkedIdWizard.java | 8 +-- 2 files changed, 74 insertions(+), 11 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index b30fc091e..de3493e0a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -26,12 +26,13 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.net.URI; import java.net.URL; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.v4.app.Fragment; +import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -44,20 +45,34 @@ import org.json.JSONObject; import org.sufficientlysecure.keychain.BuildConfig; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.linked.LinkedAttribute; +import org.sufficientlysecure.keychain.linked.resources.GithubResource; +import org.sufficientlysecure.keychain.operations.results.EditKeyResult; +import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.widget.StatusIndicator; import org.sufficientlysecure.keychain.util.Log; -public class LinkedIdCreateGithubFragment extends Fragment { +public class LinkedIdCreateGithubFragment extends CryptoOperationFragment { ViewAnimator mButtonContainer; StatusIndicator mStatus1, mStatus2, mStatus3; + byte[] mFingerprint; + long mMasterKeyId; + private SaveKeyringParcel mSaveKeyringParcel; + public static LinkedIdCreateGithubFragment newInstance() { return new LinkedIdCreateGithubFragment(); } + public LinkedIdCreateGithubFragment() { + super(null); + } + @Override @NonNull public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.linked_create_github_fragment, container, false); @@ -83,6 +98,9 @@ public class LinkedIdCreateGithubFragment extends Fragment { super.onResume(); LinkedIdWizard wizard = (LinkedIdWizard) getActivity(); + mFingerprint = wizard.mFingerprint; + mMasterKeyId = wizard.mMasterKeyId; + final String oAuthCode = wizard.oAuthGetCode(); final String oAuthState = wizard.oAuthGetState(); if (oAuthCode == null) { @@ -90,6 +108,8 @@ public class LinkedIdCreateGithubFragment extends Fragment { return; } + final String gistText = GithubResource.generate(wizard, mFingerprint); + Log.d(Constants.TAG, "got code: " + oAuthCode); new AsyncTask() { @@ -137,7 +157,7 @@ public class LinkedIdCreateGithubFragment extends Fragment { } mStatus1.setDisplayedChild(2); - step2PostGist(result.optString("access_token")); + step2PostGist(result.optString("access_token"), gistText); } }.execute(); @@ -177,7 +197,7 @@ public class LinkedIdCreateGithubFragment extends Fragment { } - private void step2PostGist(final String accessToken) { + private void step2PostGist(final String accessToken, final String gistText) { mStatus2.setDisplayedChild(1); @@ -189,7 +209,7 @@ public class LinkedIdCreateGithubFragment extends Fragment { long timer = System.currentTimeMillis(); JSONObject file = new JSONObject(); - file.put("content", "hello!"); + file.put("content", gistText); JSONObject files = new JSONObject(); files.put("file1.txt", file); @@ -230,12 +250,57 @@ public class LinkedIdCreateGithubFragment extends Fragment { return; } - mStatus2.setDisplayedChild(2); + try { + String gistId = result.getString("id"); + JSONObject owner = result.getJSONObject("owner"); + String gistLogin = owner.getString("login"); + + URI uri = URI.create("https://gist.github.com/" + gistLogin + "/" + gistId); + GithubResource resource = GithubResource.create(uri); + + mStatus2.setDisplayedChild(2); + step3EditKey(resource); + + } catch (JSONException e) { + mStatus2.setDisplayedChild(3); + e.printStackTrace(); + } + } }.execute(); } + private void step3EditKey(GithubResource resource) { + + mStatus3.setDisplayedChild(1); + + WrappedUserAttribute ua = LinkedAttribute.fromResource(resource).toUserAttribute(); + mSaveKeyringParcel = new SaveKeyringParcel(mMasterKeyId, mFingerprint); + mSaveKeyringParcel.mAddUserAttribute.add(ua); + + cryptoOperation(); + + } + + @Nullable + @Override + public SaveKeyringParcel createOperationInput() { + // if this is null, the cryptoOperation silently aborts - which is what we want in that case + return mSaveKeyringParcel; + } + + @Override + public void onCryptoOperationSuccess(EditKeyResult result) { + mStatus3.setDisplayedChild(2); + } + + @Override + public void onCryptoOperationError(EditKeyResult result) { + result.createNotify(getActivity()).show(this); + mStatus3.setDisplayedChild(3); + } + private static JSONObject jsonHttpRequest(String url, JSONObject params, String accessToken) throws IOException { 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 2fb6384b2..3441bb399 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 @@ -138,16 +138,14 @@ public class LinkedIdWizard extends BaseActivity { super.onNewIntent(intent); Uri uri = intent.getData(); - if (uri != null) { - Log.d(Constants.TAG, "received oauth uri: " + uri); + Log.d(Constants.TAG, "received oauth uri: " + uri); + if (mOAuthState != null && uri != null) { String state = uri.getQueryParameter("state"); if (!mOAuthState.equalsIgnoreCase(state)) { - Notify.create(this, "Authentication Error!", Style.ERROR).show(); + Notify.create(this, "OAuth State Error!", Style.ERROR).show(); return; } mOAuthCode = uri.getQueryParameter("code"); - } else { - Log.d(Constants.TAG, "received oauth uri: null"); } } -- cgit v1.2.3 From 1b3f70073ae81a7aee5e6fca3f021bfcfe2065fe Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 1 Sep 2015 13:58:52 +0200 Subject: linked: smooth over progress with tactical pauses --- .../ui/linked/LinkedIdCreateGithubFragment.java | 42 ++++++++++++---------- 1 file changed, 23 insertions(+), 19 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index de3493e0a..b59166721 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -31,6 +31,7 @@ import java.net.URL; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Handler; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.LayoutInflater; @@ -172,28 +173,18 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment() { + new Handler().postDelayed(new Runnable() { @Override - protected Void doInBackground(Void... params) { - try { - Thread.sleep(250); - } catch (InterruptedException e) { - // never mind - } - - return null; - } + public void run() { - @Override - protected void onPostExecute(Void aVoid) { - super.onPostExecute(aVoid); LinkedIdWizard wizard = (LinkedIdWizard) getActivity(); if (wizard == null) { return; } wizard.oAuthRequest("github.com/login/oauth/authorize", "7a011b66275f244d3f21", "gist"); + } - }.execute(); + }, 250); } @@ -271,15 +262,22 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment Date: Tue, 1 Sep 2015 13:59:17 +0200 Subject: statusindicator: don't redisplay child with animation if already shown --- .../org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java index bef40d57c..11fdb5df4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java @@ -27,4 +27,10 @@ public class StatusIndicator extends ToolableViewAnimator { setOutAnimation(AnimationUtils.loadAnimation(context, R.anim.fade_out)); } + @Override + public void setDisplayedChild(int whichChild) { + if (whichChild != getDisplayedChild()) { + super.setDisplayedChild(whichChild); + } + } } -- cgit v1.2.3 From 2cda48642722069c7e26d0a25d196e8a41128290 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 2 Sep 2015 04:13:32 +0200 Subject: linked: use webview instead of app, and some minor layoutings --- .../ui/linked/LinkedIdCreateGithubFragment.java | 216 ++++++++++++++++----- .../keychain/ui/linked/LinkedIdWizard.java | 54 ------ 2 files changed, 171 insertions(+), 99 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index b59166721..f04e10529 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -28,8 +28,18 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.net.URI; import java.net.URL; - +import java.util.Random; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnDismissListener; +import android.content.Intent; +import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; @@ -38,11 +48,16 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.ImageView; +import android.widget.TextView; import android.widget.ViewAnimator; import javax.net.ssl.HttpsURLConnection; import org.json.JSONException; import org.json.JSONObject; +import org.spongycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.BuildConfig; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -50,7 +65,9 @@ import org.sufficientlysecure.keychain.linked.LinkedAttribute; import org.sufficientlysecure.keychain.linked.resources.GithubResource; import org.sufficientlysecure.keychain.operations.results.EditKeyResult; import org.sufficientlysecure.keychain.pgp.WrappedUserAttribute; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; +import org.sufficientlysecure.keychain.ui.ViewKeyActivity; import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.widget.StatusIndicator; import org.sufficientlysecure.keychain.util.Log; @@ -65,6 +82,7 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment() { @Override protected JSONObject doInBackground(Void... dummy) { try { - long timer = System.currentTimeMillis(); - JSONObject params = new JSONObject(); params.put("client_id", "7a011b66275f244d3f21"); params.put("client_secret", "eaced8a6655719d8c6848396de97b3f5d7a89fec"); params.put("code", oAuthCode); params.put("state", oAuthState); - JSONObject result = jsonHttpRequest("https://github.com/login/oauth/access_token", params, null); - - // ux flow: this operation should take at last a second - timer = System.currentTimeMillis() -timer; - if (timer < 1000) try { - Thread.sleep(1000 -timer); - } catch (InterruptedException e) { - // never mind - } - - return result; + return jsonHttpRequest("https://github.com/login/oauth/access_token", params, null); } catch (IOException e) { Log.e(Constants.TAG, "error in request", e); @@ -165,29 +202,6 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment() { + @Override + protected Void doInBackground(Void... dummy) { + try { + HttpsURLConnection nection = (HttpsURLConnection) new URL( + "https://api.github.com/applications/7a011b66275f244d3f21/tokens/" + token) + .openConnection(); + nection.setRequestMethod("DELETE"); + nection.connect(); + } catch (IOException e) { + // nvm + } + return null; + } + }.execute(); + + } + private void step3EditKey(final GithubResource resource) { + // set item data while we're there + { + Context context = getActivity(); + mLinkedIdTitle.setText(resource.getDisplayTitle(context)); + mLinkedIdComment.setText(resource.getDisplayComment(context)); + } + mStatus3.setDisplayedChild(1); new Handler().postDelayed(new Runnable() { @@ -291,6 +334,23 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment= Build.VERSION_CODES.LOLLIPOP) { + mButtonContainer.getCurrentView().setTransitionName("linked_item"); + activity.finishAfterTransition(); + } else { + activity.finish(); + } + } + }, 1000); } @Override @@ -305,6 +365,72 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment sCurveWhitelist = new HashSet<>(Arrays.asList( + NISTNamedCurves.getOID("P-256").getId(), + NISTNamedCurves.getOID("P-384").getId(), + NISTNamedCurves.getOID("P-521").getId() + )); public static boolean isSecureKey(CanonicalizedPublicKey key) { switch (key.getAlgorithm()) { -- cgit v1.2.3 From 60e9141b8612ee8d0344e3c66109dfb9877e108a Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 6 Sep 2015 00:49:19 +0200 Subject: linked: basic shared element transition --- .../keychain/ui/ViewKeyActivity.java | 21 +++- .../keychain/ui/ViewKeyFragment.java | 58 ++++++---- .../keychain/ui/adapter/LinkedIdsAdapter.java | 5 + .../ui/linked/LinkedIdCreateGithubFragment.java | 48 +++++++- .../linked/LinkedIdCreateGithubStep1Fragment.java | 125 --------------------- .../linked/LinkedIdCreateGithubStep2Fragment.java | 116 ------------------- .../keychain/ui/linked/LinkedIdViewFragment.java | 2 + 7 files changed, 105 insertions(+), 270 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep1Fragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep2Fragment.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 2ffb7227e..a15dc74ca 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -18,6 +18,11 @@ package org.sufficientlysecure.keychain.ui; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; import android.annotation.SuppressLint; @@ -55,7 +60,6 @@ import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; - import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.keyimport.ParcelableKeyRing; @@ -68,9 +72,10 @@ import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.ImportKeyringParcel; -import org.sufficientlysecure.keychain.ui.linked.LinkedIdWizard; +import org.sufficientlysecure.keychain.ui.ViewKeyFragment.PostponeType; import org.sufficientlysecure.keychain.ui.base.BaseNfcActivity; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; +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.KeyFormattingUtils.State; @@ -84,9 +89,6 @@ import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.NfcHelper; import org.sufficientlysecure.keychain.util.Preferences; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; public class ViewKeyActivity extends BaseNfcActivity implements LoaderManager.LoaderCallbacks, @@ -102,6 +104,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements static final int REQUEST_DELETE = 4; public static final String EXTRA_DISPLAY_RESULT = "display_result"; + public static final String EXTRA_LINKED_TRANSITION = "linked_transition"; ProviderHelper mProviderHelper; @@ -294,9 +297,15 @@ public class ViewKeyActivity extends BaseNfcActivity implements return; } + boolean linkedTransition = getIntent().getBooleanExtra(EXTRA_LINKED_TRANSITION, false); + if (linkedTransition && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + postponeEnterTransition(); + } + FragmentManager manager = getSupportFragmentManager(); // Create an instance of the fragment - final ViewKeyFragment frag = ViewKeyFragment.newInstance(mDataUri); + final ViewKeyFragment frag = ViewKeyFragment.newInstance(mDataUri, + linkedTransition ? PostponeType.LINKED : PostponeType.NONE); manager.beginTransaction() .replace(R.id.view_key_fragment, frag) .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 32e2d2def..7be695de0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyFragment.java @@ -18,7 +18,11 @@ package org.sufficientlysecure.keychain.ui; + import java.io.IOException; +import java.util.List; + +import android.annotation.TargetApi; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -26,6 +30,7 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; +import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Handler; import android.provider.ContactsContract; @@ -39,17 +44,20 @@ import android.transition.TransitionInflater; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.view.ViewTreeObserver.OnPreDrawListener; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; +import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; -import android.widget.*; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; -import org.sufficientlysecure.keychain.ui.adapter.LinkedIdsAdapter; 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; @@ -58,22 +66,20 @@ import org.sufficientlysecure.keychain.util.ContactHelper; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Preferences; -import java.util.List; - public class ViewKeyFragment extends LoaderFragment implements LoaderManager.LoaderCallbacks { public static final String ARG_DATA_URI = "uri"; + public static final String ARG_POSTPONE_TYPE = "postpone_type"; private ListView mUserIds; //private ListView mLinkedSystemContact; - boolean mIsSecret = false; + enum PostponeType { + NONE, LINKED; + } - CardView mSystemContactCard; - LinearLayout mSystemContactLayout; - ImageView mSystemContactPicture; - TextView mSystemContactName; + boolean mIsSecret = false; private static final int LOADER_ID_UNIFIED = 0; private static final int LOADER_ID_USER_IDS = 1; @@ -89,6 +95,13 @@ public class ViewKeyFragment extends LoaderFragment implements private LinkedIdsAdapter mLinkedIdsAdapter; private Uri mDataUri; + private PostponeType mPostponeType; + + private CardView mSystemContactCard; + private LinearLayout mSystemContactLayout; + private ImageView mSystemContactPicture; + private TextView mSystemContactName; + private ListView mLinkedIds; private CardView mLinkedIdsCard; private byte[] mFingerprint; @@ -97,10 +110,11 @@ public class ViewKeyFragment extends LoaderFragment implements /** * Creates new instance of this fragment */ - public static ViewKeyFragment newInstance(Uri dataUri) { + public static ViewKeyFragment newInstance(Uri dataUri, PostponeType postponeType) { ViewKeyFragment frag = new ViewKeyFragment(); Bundle args = new Bundle(); args.putParcelable(ARG_DATA_URI, dataUri); + args.putInt(ARG_POSTPONE_TYPE, postponeType.ordinal()); frag.setArguments(args); @@ -271,6 +285,7 @@ public class ViewKeyFragment extends LoaderFragment implements super.onActivityCreated(savedInstanceState); Uri dataUri = getArguments().getParcelable(ARG_DATA_URI); + mPostponeType = PostponeType.values()[getArguments().getInt(ARG_POSTPONE_TYPE, 0)]; if (dataUri == null) { Log.e(Constants.TAG, "Data missing. Should be Uri of key!"); getActivity().finish(); @@ -325,10 +340,10 @@ public class ViewKeyFragment extends LoaderFragment implements @Override public Loader onCreateLoader(int id, Bundle args) { - setContentShown(false); switch (id) { case LOADER_ID_UNIFIED: { + setContentShown(false, false); Uri baseUri = KeychainContract.KeyRings.buildUnifiedKeyRingUri(mDataUri); return new CursorLoader(getActivity(), baseUri, UNIFIED_PROJECTION, null, null, null); } @@ -390,6 +405,7 @@ public class ViewKeyFragment extends LoaderFragment implements mIsSecret = data.getInt(INDEX_HAS_ANY_SECRET) != 0; mFingerprint = data.getBlob(INDEX_FINGERPRINT); + long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID); // load user ids after we know if it's a secret key mUserIdsAdapter = new UserIdsAdapter(getActivity(), null, 0, !mIsSecret, null); @@ -403,13 +419,6 @@ public class ViewKeyFragment extends LoaderFragment implements getLoaderManager().initLoader(LOADER_ID_LINKED_IDS, null, this); } - long masterKeyId = data.getLong(INDEX_MASTER_KEY_ID); - // we need to load linked contact here to prevent lag introduced by loader - // for the linked contact - long contactId = ContactHelper.findContactId( - getActivity().getContentResolver(), - masterKeyId); - loadLinkedSystemContact(contactId); Bundle linkedContactData = new Bundle(); linkedContactData.putLong(LOADER_EXTRA_LINKED_CONTACT_MASTER_KEY_ID, masterKeyId); @@ -423,6 +432,7 @@ public class ViewKeyFragment extends LoaderFragment implements } case LOADER_ID_USER_IDS: { + setContentShown(true, false); mUserIdsAdapter.swapCursor(data); break; } @@ -430,6 +440,17 @@ public class ViewKeyFragment extends LoaderFragment implements case LOADER_ID_LINKED_IDS: { mLinkedIdsAdapter.swapCursor(data); mLinkedIdsCard.setVisibility(mLinkedIdsAdapter.getCount() > 0 ? View.VISIBLE : View.GONE); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mPostponeType == PostponeType.LINKED) { + mLinkedIdsCard.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() { + @TargetApi(VERSION_CODES.LOLLIPOP) + @Override + public boolean onPreDraw() { + mLinkedIdsCard.getViewTreeObserver().removeOnPreDrawListener(this); + getActivity().startPostponedEnterTransition(); + return true; + } + }); + } break; } @@ -442,7 +463,6 @@ public class ViewKeyFragment extends LoaderFragment implements } } - setContentShown(true); } /** 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 805666bb2..5cf0e6e08 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 @@ -23,6 +23,7 @@ import android.app.Activity; import android.content.Context; import android.database.Cursor; import android.net.Uri; +import android.os.Build; import android.support.v4.content.CursorLoader; import android.util.Log; import android.view.LayoutInflater; @@ -135,6 +136,10 @@ public class LinkedIdsAdapter extends UserAttributesAdapter { UriAttribute id = getItemAtPosition(cursor); holder.setData(mContext, id); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + view.setTransitionName(id.mUri.toString()); + } + } public UriAttribute getItemAtPosition(Cursor cursor) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index f04e10529..0cc21f448 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -44,10 +44,13 @@ import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.app.ActivityOptionsCompat; +import android.support.v4.app.FragmentActivity; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.webkit.CookieManager; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.ImageView; @@ -83,6 +86,7 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment= Build.VERSION_CODES.LOLLIPOP) { + linkedItem.setTransitionName(resource.toUri().toString()); + } + revokeToken(accessToken); mStatus2.setDisplayedChild(2); @@ -334,25 +356,44 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment= Build.VERSION_CODES.LOLLIPOP) { - mButtonContainer.getCurrentView().setTransitionName("linked_item"); - activity.finishAfterTransition(); + intent.putExtra(ViewKeyActivity.EXTRA_LINKED_TRANSITION, true); + View linkedItem = mButtonContainer.getChildAt(2); + + Bundle options = ActivityOptionsCompat.makeSceneTransitionAnimation( + activity, linkedItem, linkedItem.getTransitionName()).toBundle(); + activity.startActivity(intent, options); + mFinishOnStop = true; } else { + activity.startActivity(intent); activity.finish(); } } }, 1000); } + @Override + public void onStop() { + super.onStop(); + if (mFinishOnStop) { + Activity activity = getActivity(); + activity.setResult(Activity.RESULT_OK); + activity.finish(); + } + } + @Override public void onCryptoOperationError(EditKeyResult result) { result.createNotify(getActivity()).show(this); @@ -365,7 +406,6 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment - * - * 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.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.create(getActivity(), - "Connection error while checking username!", Notify.Style.ERROR).show(); - return; - } - - if (!result) { - Notify.create(getActivity(), - "This handle does not exist on Github!", Notify.Style.ERROR).show(); - 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); - - return view; - } - - /* not used at this point, too much hassle - 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 deleted file mode 100644 index a3a8c779f..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubStep2Fragment.java +++ /dev/null @@ -1,116 +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.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.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.linked.LinkedTokenResource; -import org.sufficientlysecure.keychain.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); - - 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; - } - - @Override - LinkedTokenResource getResource(OperationLog log) { - return GithubResource.searchInGithubStream(getActivity(), mResourceHandle, mResourceString, log); - } - - @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/LinkedIdViewFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdViewFragment.java index 7007fa50c..5630932b4 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 @@ -211,6 +211,8 @@ public class LinkedIdViewFragment extends CryptoOperationFragment implements null, State.INVALID, KeyFormattingUtils.DEFAULT_COLOR); break; } + } else { + mViewHolder.mLinkedIdHolder.vVerified.setImageResource(R.drawable.octo_link_24dp); } mViewHolder.mLinkedIdHolder.setData(mContext, mLinkedId); -- cgit v1.2.3 From 31cfbdc5d25f79ecda7c9c9290b8d80a4df5d89a Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 6 Sep 2015 00:58:10 +0200 Subject: linked: fix "navigate up" in LinkedIdWizard (imperfect) --- .../keychain/ui/ViewKeyActivity.java | 3 +- .../keychain/ui/linked/LinkedIdWizard.java | 35 ++++++++++++++++++++++ .../keychain/ui/widget/KeySpinner.java | 3 +- 3 files changed, 38 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 a15dc74ca..930c1fc26 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -371,6 +371,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements Intent intent = new Intent(this, LinkedIdWizard.class); intent.setData(mDataUri); startActivity(intent); + finish(); return true; } case R.id.menu_key_view_edit: { @@ -427,7 +428,7 @@ public class ViewKeyActivity extends BaseNfcActivity implements private void certifyImmediate() { Intent intent = new Intent(this, CertifyKeyActivity.class); - intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[]{mMasterKeyId}); + intent.putExtra(CertifyKeyActivity.EXTRA_KEY_IDS, new long[] { mMasterKeyId }); startActivityForResult(intent, REQUEST_CERTIFY); } 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 2c29d1d77..8c677199d 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 @@ -19,10 +19,14 @@ package org.sufficientlysecure.keychain.ui.linked; import android.content.Context; +import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; +import android.support.v4.app.NavUtils; +import android.support.v4.app.TaskStackBuilder; +import android.view.MenuItem; import android.view.View; import android.view.inputmethod.InputMethodManager; @@ -31,6 +35,7 @@ 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.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.base.BaseActivity; import org.sufficientlysecure.keychain.util.Log; @@ -126,4 +131,34 @@ public class LinkedIdWizard extends BaseActivity { inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0); } + @Override + public void onBackPressed() { + if (!getFragmentManager().popBackStackImmediate()) { + navigateBack(); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + // Respond to the action bar's Up/Home button + case android.R.id.home: + navigateBack(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private void navigateBack() { + Intent upIntent = NavUtils.getParentActivityIntent(this); + upIntent.setData(KeyRings.buildGenericKeyRingUri(mMasterKeyId)); + // This activity is NOT part of this app's task, so create a new task + // when navigating up, with a synthesized back stack. + TaskStackBuilder.create(this) + // Add all of this activity's parents to the back stack + .addNextIntentWithParentStack(upIntent) + // Navigate up to the closest parent + .startActivities(); + } + } 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 e3ec3d34b..04ed35deb 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 @@ -119,8 +119,7 @@ public abstract class KeySpinner extends AppCompatSpinner implements if (getContext() instanceof FragmentActivity) { ((FragmentActivity) getContext()).getSupportLoaderManager().restartLoader(LOADER_ID, null, this); } else { - throw new AssertionError("KeySpinner must be attached to FragmentActivity, this is " - + getContext().getClass()); + // ignore, this happens during preview! we use fragmentactivities everywhere either way } } -- cgit v1.2.3 From 8e60bf70a1ef6bd7be3220afc45c9a59c231d2d4 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sun, 6 Sep 2015 00:59:06 +0200 Subject: linked: clear cookies and don't save form data in oauth webview --- .../keychain/ui/linked/LinkedIdCreateGithubFragment.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index 0cc21f448..eeb2ad116 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -423,7 +423,7 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment Date: Tue, 8 Sep 2015 14:46:18 +0200 Subject: linked: move client id and secret into static vars (build vars later) --- .../keychain/ui/linked/LinkedIdCreateGithubFragment.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index eeb2ad116..5e8d60b8d 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -78,6 +78,9 @@ import org.sufficientlysecure.keychain.util.Log; public class LinkedIdCreateGithubFragment extends CryptoOperationFragment { + public static final String GITHUB_CLIENT_ID = "7a011b66275f244d3f21"; + public static final String GITHUB_CLIENT_SECRET = "eaced8a6655719d8c6848396de97b3f5d7a89fec"; + ViewAnimator mButtonContainer; StatusIndicator mStatus1, mStatus2, mStatus3; @@ -154,7 +157,7 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment Date: Tue, 8 Sep 2015 15:51:27 +0200 Subject: statusindicator: method to set states by name --- .../sufficientlysecure/keychain/ui/widget/StatusIndicator.java | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java index 11fdb5df4..2784ac5f0 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/StatusIndicator.java @@ -11,6 +11,10 @@ import org.sufficientlysecure.keychain.R; public class StatusIndicator extends ToolableViewAnimator { + public enum Status { + IDLE, PROGRESS, OK, ERROR + } + public StatusIndicator(Context context) { super(context); @@ -33,4 +37,9 @@ public class StatusIndicator extends ToolableViewAnimator { super.setDisplayedChild(whichChild); } } + + public void setDisplayedChild(Status status) { + setDisplayedChild(status.ordinal()); + } + } -- cgit v1.2.3 From 0ef929fb7ff58e5fdb75cd0892c12cf57a43b321 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Tue, 8 Sep 2015 15:53:05 +0200 Subject: linked: move between fixed states --- .../ui/linked/LinkedIdCreateGithubFragment.java | 66 ++++++++++++++++++---- 1 file changed, 56 insertions(+), 10 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index 5e8d60b8d..e42623c6e 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -73,11 +73,16 @@ import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.ui.ViewKeyActivity; import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; import org.sufficientlysecure.keychain.ui.widget.StatusIndicator; +import org.sufficientlysecure.keychain.ui.widget.StatusIndicator.Status; import org.sufficientlysecure.keychain.util.Log; public class LinkedIdCreateGithubFragment extends CryptoOperationFragment { + enum State { + IDLE, AUTH_PROCESS, AUTH_ERROR, POST_PROCESS, POST_ERROR, LID_PROCESS, LID_ERROR, DONE + } + public static final String GITHUB_CLIENT_ID = "7a011b66275f244d3f21"; public static final String GITHUB_CLIENT_SECRET = "eaced8a6655719d8c6848396de97b3f5d7a89fec"; @@ -148,9 +153,7 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment() { @Override @@ -291,11 +293,10 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment Date: Tue, 8 Sep 2015 15:53:24 +0200 Subject: linked: add "retry" --- .../ui/linked/LinkedIdCreateGithubFragment.java | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index e42623c6e..6df31a596 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -53,6 +53,7 @@ import android.view.ViewGroup; import android.webkit.CookieManager; import android.webkit.WebView; import android.webkit.WebViewClient; +import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import android.widget.ViewAnimator; @@ -79,6 +80,8 @@ import org.sufficientlysecure.keychain.util.Log; public class LinkedIdCreateGithubFragment extends CryptoOperationFragment { + private Button mRetryButton; + enum State { IDLE, AUTH_PROCESS, AUTH_ERROR, POST_PROCESS, POST_ERROR, LID_PROCESS, LID_ERROR, DONE } @@ -114,6 +117,8 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment Date: Tue, 8 Sep 2015 15:53:49 +0200 Subject: linked: fix auth token revocation (maybe?) --- .../keychain/ui/linked/LinkedIdCreateGithubFragment.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index 6df31a596..d1a39d7c3 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -46,6 +46,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.app.FragmentActivity; +import android.util.Base64; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -317,9 +318,12 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment Date: Thu, 10 Sep 2015 01:45:59 +0200 Subject: linked: work on github lid creation error handling --- .../ui/linked/LinkedIdCreateGithubFragment.java | 163 ++++++++++++++++----- 1 file changed, 125 insertions(+), 38 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index d1a39d7c3..f8f4a7020 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -26,6 +26,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.net.SocketTimeoutException; import java.net.URI; import java.net.URL; import java.util.Random; @@ -74,6 +75,8 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.ui.ViewKeyActivity; import org.sufficientlysecure.keychain.ui.base.CryptoOperationFragment; +import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.ui.widget.StatusIndicator; import org.sufficientlysecure.keychain.ui.widget.StatusIndicator.Status; import org.sufficientlysecure.keychain.util.Log; @@ -172,10 +175,24 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment() { + + Exception mException; + @Override protected JSONObject doInBackground(Void... dummy) { try { @@ -199,13 +214,13 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment() { + + Exception mException; + @Override protected JSONObject doInBackground(Void... dummy) { try { @@ -264,8 +305,8 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment= Build.VERSION_CODES.LOLLIPOP) { - linkedItem.setTransitionName(resource.toUri().toString()); - } - - // we only need authorization for this one operation, drop it afterwards - revokeToken(accessToken); - - step3EditKey(resource); - + resource = GithubResource.create(uri); } catch (JSONException e) { setState(State.POST_ERROR); - e.printStackTrace(); + return; + } + + View linkedItem = mButtonContainer.getChildAt(2); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + linkedItem.setTransitionName(resource.toUri().toString()); } + // we only need authorization for this one operation, drop it afterwards + revokeToken(accessToken); + + step3EditKey(resource); } + }.execute(); } @@ -402,6 +462,16 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment Date: Thu, 10 Sep 2015 02:11:43 +0200 Subject: linked: fix screwups from last commit, sigh --- .../ui/linked/LinkedIdCreateGithubFragment.java | 26 ++++++++++++++-------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index f8f4a7020..b5dd2d758 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -244,18 +244,19 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment Date: Thu, 10 Sep 2015 02:23:49 +0200 Subject: linked: don't need javascript, so we don't enable it --- .../keychain/ui/linked/LinkedIdCreateGithubFragment.java | 1 - 1 file changed, 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index b5dd2d758..56a405cc2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -504,7 +504,6 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment Date: Thu, 10 Sep 2015 11:46:31 +0200 Subject: linked: localization for github creation --- .../ui/linked/LinkedIdCreateGithubFragment.java | 35 ++++++++++------------ 1 file changed, 16 insertions(+), 19 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index 56a405cc2..6cf8b4fcf 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -31,7 +31,6 @@ import java.net.URI; import java.net.URL; import java.util.Random; -import android.annotation.SuppressLint; import android.app.Activity; import android.app.Dialog; import android.content.Context; @@ -41,6 +40,8 @@ import android.content.Intent; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; @@ -244,19 +245,18 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment= Build.VERSION_CODES.LOLLIPOP) { + if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) { linkedItem.setTransitionName(resource.toUri().toString()); } @@ -533,7 +532,7 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment Date: Thu, 10 Sep 2015 12:10:24 +0200 Subject: linked: move github client id and secret into build vars --- .../keychain/ui/linked/LinkedIdCreateGithubFragment.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index 6cf8b4fcf..c2898bf4a 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -91,9 +91,6 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment Date: Thu, 10 Sep 2015 12:12:35 +0200 Subject: linked: deprecate unused method, for now --- .../sufficientlysecure/keychain/linked/resources/GithubResource.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 30ce075b4..7a97ffd96 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 @@ -79,7 +79,8 @@ public class GithubResource extends LinkedTokenResource { } - @SuppressWarnings("deprecation") + @Deprecated // not used for now, but could be used to pick up earlier posted gist if already present? + @SuppressWarnings({ "deprecation", "unused" }) public static GithubResource searchInGithubStream( Context context, String screenName, String needle, OperationLog log) { -- cgit v1.2.3 From c6f1f505bbed01ac91f614fc8fbd6d0a68ba267e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 10 Sep 2015 12:17:25 +0200 Subject: linked: ditch create dns fragment (for now) --- .../ui/linked/LinkedIdCreateDnsStep1Fragment.java | 129 ---------------- .../ui/linked/LinkedIdCreateDnsStep2Fragment.java | 172 --------------------- 2 files changed, 301 deletions(-) delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java delete mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 deleted file mode 100644 index c54d0c948..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep1Fragment.java +++ /dev/null @@ -1,129 +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.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.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 proofText = DnsResource.generateText( - mLinkedIdWizard.mFingerprint); - - LinkedIdCreateDnsStep2Fragment frag = - LinkedIdCreateDnsStep2Fragment.newInstance(uri, 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.ic_stat_retyped_ok, 0); - } else { - mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0, - R.drawable.ic_stat_retyped_bad, 0); - } - } else { - // remove drawable if email is empty - mEditDns.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); - } - } - }); - - 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 deleted file mode 100644 index c9eca8882..000000000 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateDnsStep2Fragment.java +++ /dev/null @@ -1,172 +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 java.io.FileNotFoundException; -import java.io.PrintWriter; - -import android.app.Activity; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -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 android.widget.TextView; - -import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.linked.LinkedTokenResource; -import org.sufficientlysecure.keychain.linked.resources.DnsResource; -import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; -import org.sufficientlysecure.keychain.ui.util.Notify; -import org.sufficientlysecure.keychain.ui.util.Notify.Style; - -public class LinkedIdCreateDnsStep2Fragment extends LinkedIdCreateFinalFragment { - - private static final int REQUEST_CODE_OUTPUT = 0x00007007; - - public static final String DOMAIN = "domain", TEXT = "text"; - - TextView mTextView; - - String mResourceDomain; - String mResourceString; - - public static LinkedIdCreateDnsStep2Fragment newInstance - (String uri, String proofText) { - - LinkedIdCreateDnsStep2Fragment frag = new LinkedIdCreateDnsStep2Fragment(); - - Bundle args = new Bundle(); - args.putString(DOMAIN, uri); - args.putString(TEXT, proofText); - frag.setArguments(args); - - return frag; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - mResourceDomain = getArguments().getString(DOMAIN); - mResourceString = getArguments().getString(TEXT); - - } - - @Override - protected View newView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.linked_create_dns_fragment_step2, container, false); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = super.onCreateView(inflater, container, savedInstanceState); - - if (view != null) { - - 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) { - proofToClipboard(); - } - }); - - mTextView = (TextView) view.findViewById(R.id.linked_create_dns_text); - mTextView.setText(mResourceString); - - } - - return view; - } - - @Override - LinkedTokenResource getResource(OperationLog log) { - return DnsResource.createNew(mResourceDomain); - } - - 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 proofToClipboard() { - Activity activity = getActivity(); - if (activity == null) { - return; - } - - ClipboardManager clipMan = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE); - if (clipMan == null) { - Notify.create(activity, R.string.error_clipboard_copy, Style.ERROR).show(); - return; - } - - ClipData clip = ClipData.newPlainText(Constants.CLIPBOARD_LABEL, mResourceString); - clipMan.setPrimaryClip(clip); - - Notify.create(getActivity(), R.string.linked_text_clipboard, Notify.Style.OK).show(); - } - - private void saveFile(Uri uri) { - try { - PrintWriter out = - new PrintWriter(getActivity().getContentResolver().openOutputStream(uri)); - out.print(mResourceString); - if (out.checkError()) { - Notify.create(getActivity(), "Error writing file!", Style.ERROR).show(); - } - } catch (FileNotFoundException e) { - Notify.create(getActivity(), "File could not be opened for writing!", Style.ERROR).show(); - e.printStackTrace(); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - switch (requestCode) { - // For saving a file - case REQUEST_CODE_OUTPUT: - if (data == null) { - return; - } - Uri uri = data.getData(); - saveFile(uri); - break; - default: - super.onActivityResult(requestCode, resultCode, data); - } - } - -} -- cgit v1.2.3 From cb9a66e2463768342415e36648a57f27ff797f99 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 10 Sep 2015 16:57:07 +0200 Subject: linked: improve fragment robustness, save cookies through destroyed fragment --- .../ui/linked/LinkedIdCreateGithubFragment.java | 54 ++++++++++++++++------ 1 file changed, 40 insertions(+), 14 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java index c2898bf4a..ccb20a764 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/linked/LinkedIdCreateGithubFragment.java @@ -85,6 +85,7 @@ import org.sufficientlysecure.keychain.util.Log; public class LinkedIdCreateGithubFragment extends CryptoOperationFragment { + public static final String ARG_GITHUB_COOKIE = "github_cookie"; private Button mRetryButton; enum State { @@ -229,18 +230,18 @@ public class LinkedIdCreateGithubFragment extends CryptoOperationFragment Date: Thu, 10 Sep 2015 21:44:15 +0200 Subject: add support for signed-only data in the backend (#1507) --- .../operations/results/OperationResult.java | 1 + .../keychain/pgp/PgpDecryptVerifyOperation.java | 685 +++++++++++---------- 2 files changed, 369 insertions(+), 317 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 f213b1aad..46852d783 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 @@ -631,6 +631,7 @@ public abstract class OperationResult implements Parcelable { MSG_DC_TRAIL_SYM (LogLevel.DEBUG, R.string.msg_dc_trail_sym), MSG_DC_TRAIL_UNKNOWN (LogLevel.DEBUG, R.string.msg_dc_trail_unknown), MSG_DC_UNLOCKING (LogLevel.INFO, R.string.msg_dc_unlocking), + MSG_DC_INSECURE_ENCRYPTION_KEY (LogLevel.WARN, R.string.msg_dc_insecure_encryption_key), MSG_DC_INSECURE_SYMMETRIC_ENCRYPTION_ALGO(LogLevel.WARN, R.string.msg_dc_insecure_symmetric_encryption_algo), MSG_DC_INSECURE_HASH_ALGO(LogLevel.ERROR, R.string.msg_dc_insecure_hash_algo), MSG_DC_INSECURE_MDC_MISSING(LogLevel.ERROR, R.string.msg_dc_insecure_mdc_missing), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java index dd30156f9..a538c9bd1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java @@ -313,6 +313,27 @@ public class PgpDecryptVerifyOperation extends BaseOperation it = enc.getEncryptedDataObjects(); - boolean asymmetricPacketFound = false; - boolean symmetricPacketFound = false; - boolean anyPacketFound = false; - // If the input stream is armored, and there is a charset specified, take a note for later // https://tools.ietf.org/html/rfc4880#page56 String charset = null; @@ -375,8 +368,328 @@ public class PgpDecryptVerifyOperation extends BaseOperation return "unknown pub key" status including the first key id + if (!sigList.isEmpty()) { + signatureResultBuilder.setSignatureAvailable(true); + signatureResultBuilder.setKnownKey(false); + signatureResultBuilder.setKeyId(sigList.get(0).getKeyID()); + } + } + + // check for insecure signing key + // TODO: checks on signingRing ? + if (signingKey != null && ! PgpSecurityConstants.isSecureKey(signingKey)) { + log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1); + signatureResultBuilder.setInsecure(true); + } + + dataChunk = plainFact.nextObject(); + } + + if (dataChunk instanceof PGPSignatureList) { + // skip + dataChunk = plainFact.nextObject(); + } + + OpenPgpMetadata metadata; + + if ( ! (dataChunk instanceof PGPLiteralData)) { + + log.add(LogType.MSG_DC_ERROR_INVALID_DATA, indent); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + + } + + log.add(LogType.MSG_DC_CLEAR_DATA, indent + 1); + indent += 2; + currentProgress += 4; + updateProgress(R.string.progress_decrypting, currentProgress, 100); + + PGPLiteralData literalData = (PGPLiteralData) dataChunk; + + String originalFilename = literalData.getFileName(); + String mimeType = null; + if (literalData.getFormat() == PGPLiteralData.TEXT + || literalData.getFormat() == PGPLiteralData.UTF8) { + mimeType = "text/plain"; + } else { + // try to guess from file ending + String extension = MimeTypeMap.getFileExtensionFromUrl(originalFilename); + if (extension != null) { + MimeTypeMap mime = MimeTypeMap.getSingleton(); + mimeType = mime.getMimeTypeFromExtension(extension); + } + if (mimeType == null) { + mimeType = "application/octet-stream"; + } + } + + if (!"".equals(originalFilename)) { + log.add(LogType.MSG_DC_CLEAR_META_FILE, indent + 1, originalFilename); + } + log.add(LogType.MSG_DC_CLEAR_META_MIME, indent + 1, + mimeType); + log.add(LogType.MSG_DC_CLEAR_META_TIME, indent + 1, + new Date(literalData.getModificationTime().getTime()).toString()); + + // return here if we want to decrypt the metadata only + if (input.isDecryptMetadataOnly()) { + + // this operation skips the entire stream to find the data length! + Long originalSize = literalData.findDataLength(); + + if (originalSize != null) { + log.add(LogType.MSG_DC_CLEAR_META_SIZE, indent + 1, + Long.toString(originalSize)); + } else { + log.add(LogType.MSG_DC_CLEAR_META_SIZE_UNKNOWN, indent + 1); + } + + metadata = new OpenPgpMetadata( + originalFilename, + mimeType, + literalData.getModificationTime().getTime(), + originalSize == null ? 0 : originalSize); + + log.add(LogType.MSG_DC_OK_META_ONLY, indent); + DecryptVerifyResult result = + new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); + result.setCharset(charset); + result.setDecryptionMetadata(metadata); + return result; + } + + int endProgress; + if (signature != null) { + endProgress = 90; + } else if (esResult != null && esResult.encryptedData.isIntegrityProtected()) { + endProgress = 95; + } else { + endProgress = 100; + } + ProgressScaler progressScaler = + new ProgressScaler(mProgressable, currentProgress, endProgress, 100); + + InputStream dataIn = literalData.getInputStream(); + + long alreadyWritten = 0; + long wholeSize = 0; // TODO inputData.getSize() - inputData.getStreamPosition(); + int length; + byte[] buffer = new byte[1 << 16]; + while ((length = dataIn.read(buffer)) > 0) { + // Log.d(Constants.TAG, "read bytes: " + length); + if (out != null) { + out.write(buffer, 0, length); + } + + // update signature buffer if signature is also present + if (signature != null) { + signature.update(buffer, 0, length); + } + + alreadyWritten += length; + if (wholeSize > 0) { + long progress = 100 * alreadyWritten / wholeSize; + // stop at 100% for wrong file sizes... + if (progress > 100) { + progress = 100; + } + progressScaler.setProgress((int) progress, 100); + } + // TODO: slow annealing to fake a progress? + } + + metadata = new OpenPgpMetadata( + originalFilename, mimeType, literalData.getModificationTime().getTime(), alreadyWritten); + + if (signature != null) { + updateProgress(R.string.progress_verifying_signature, 90, 100); + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent); + + PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject(); + PGPSignature messageSignature = signatureList.get(signatureIndex); + + // Verify signature + boolean validSignature = signature.verify(messageSignature); + if (validSignature) { + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1); + } else { + log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1); + } + + // check for insecure hash algorithms + if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) { + log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1); + signatureResultBuilder.setInsecure(true); + } + + signatureResultBuilder.setValidSignature(validSignature); + } + + indent -= 1; + + if (esResult != null && esResult.encryptedData.isIntegrityProtected()) { + updateProgress(R.string.progress_verifying_integrity, 95, 100); + + if (esResult.encryptedData.verify()) { + log.add(LogType.MSG_DC_INTEGRITY_CHECK_OK, indent); + } else { + log.add(LogType.MSG_DC_ERROR_INTEGRITY_CHECK, indent); + return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); + } + } else { + // If no valid signature is present: + // Handle missing integrity protection like failed integrity protection! + // The MDC packet can be stripped by an attacker! + Log.d(Constants.TAG, "MDC fail"); + if (!signatureResultBuilder.isValidSignature()) { + log.add(LogType.MSG_DC_INSECURE_MDC_MISSING, indent); + decryptionResultBuilder.setInsecure(true); + } + } + + updateProgress(R.string.progress_done, 100, 100); + + log.add(LogType.MSG_DC_OK, indent); + + // Return a positive result, with metadata and verification info + DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); + + result.setCachedCryptoInputParcel(cryptoInput); + result.setSignatureResult(signatureResultBuilder.build()); + result.setCharset(charset); + result.setDecryptionResult(decryptionResultBuilder.build()); + result.setDecryptionMetadata(metadata); + + return result; + + } + + private EncryptStreamResult handleEncryptedPacket(PgpDecryptVerifyInputParcel input, CryptoInputParcel cryptoInput, + PGPEncryptedDataList enc, OperationLog log, int indent, int currentProgress) throws PGPException { + + // TODO is this necessary? + /* + else if (obj instanceof PGPEncryptedDataList) { + enc = (PGPEncryptedDataList) pgpF.nextObject(); + } + */ + + EncryptStreamResult result = new EncryptStreamResult(); + + boolean asymmetricPacketFound = false; + boolean symmetricPacketFound = false; + boolean anyPacketFound = false; + + PGPPublicKeyEncryptedData encryptedDataAsymmetric = null; + PGPPBEEncryptedData encryptedDataSymmetric = null; + CanonicalizedSecretKey secretEncryptionKey = null; + Passphrase passphrase = null; - boolean skippedDisallowedKey = false; + + Iterator it = enc.getEncryptedDataObjects(); // go through all objects and find one we can decrypt while (it.hasNext()) { @@ -420,7 +733,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation return "unknown pub key" status including the first key id - if (!sigList.isEmpty()) { - signatureResultBuilder.setSignatureAvailable(true); - signatureResultBuilder.setKnownKey(false); - signatureResultBuilder.setKeyId(sigList.get(0).getKeyID()); - } - } - - // check for insecure signing key - // TODO: checks on signingRing ? - if (signingKey != null && ! PgpSecurityConstants.isSecureKey(signingKey)) { - log.add(LogType.MSG_DC_INSECURE_KEY, indent + 1); - signatureResultBuilder.setInsecure(true); - } - - dataChunk = plainFact.nextObject(); - } - - if (dataChunk instanceof PGPSignatureList) { - // skip - dataChunk = plainFact.nextObject(); - } - - OpenPgpMetadata metadata; - - if (dataChunk instanceof PGPLiteralData) { - log.add(LogType.MSG_DC_CLEAR_DATA, indent + 1); - indent += 2; - currentProgress += 4; - updateProgress(R.string.progress_decrypting, currentProgress, 100); - - PGPLiteralData literalData = (PGPLiteralData) dataChunk; - - String originalFilename = literalData.getFileName(); - String mimeType = null; - if (literalData.getFormat() == PGPLiteralData.TEXT - || literalData.getFormat() == PGPLiteralData.UTF8) { - mimeType = "text/plain"; - } else { - // try to guess from file ending - String extension = MimeTypeMap.getFileExtensionFromUrl(originalFilename); - if (extension != null) { - MimeTypeMap mime = MimeTypeMap.getSingleton(); - mimeType = mime.getMimeTypeFromExtension(extension); - } - if (mimeType == null) { - mimeType = "application/octet-stream"; - } - } - - if (!"".equals(originalFilename)) { - log.add(LogType.MSG_DC_CLEAR_META_FILE, indent + 1, originalFilename); - } - log.add(LogType.MSG_DC_CLEAR_META_MIME, indent + 1, - mimeType); - log.add(LogType.MSG_DC_CLEAR_META_TIME, indent + 1, - new Date(literalData.getModificationTime().getTime()).toString()); - - // return here if we want to decrypt the metadata only - if (input.isDecryptMetadataOnly()) { - - // this operation skips the entire stream to find the data length! - Long originalSize = literalData.findDataLength(); - - if (originalSize != null) { - log.add(LogType.MSG_DC_CLEAR_META_SIZE, indent + 1, - Long.toString(originalSize)); - } else { - log.add(LogType.MSG_DC_CLEAR_META_SIZE_UNKNOWN, indent + 1); - } - - metadata = new OpenPgpMetadata( - originalFilename, - mimeType, - literalData.getModificationTime().getTime(), - originalSize == null ? 0 : originalSize); - - log.add(LogType.MSG_DC_OK_META_ONLY, indent); - DecryptVerifyResult result = - new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); - result.setCharset(charset); - result.setDecryptionMetadata(metadata); - return result; - } - - int endProgress; - if (signature != null) { - endProgress = 90; - } else if (encryptedData.isIntegrityProtected()) { - endProgress = 95; - } else { - endProgress = 100; - } - ProgressScaler progressScaler = - new ProgressScaler(mProgressable, currentProgress, endProgress, 100); - - InputStream dataIn = literalData.getInputStream(); - - long alreadyWritten = 0; - long wholeSize = 0; // TODO inputData.getSize() - inputData.getStreamPosition(); - int length; - byte[] buffer = new byte[1 << 16]; - while ((length = dataIn.read(buffer)) > 0) { - // Log.d(Constants.TAG, "read bytes: " + length); - if (out != null) { - out.write(buffer, 0, length); - } - - // update signature buffer if signature is also present - if (signature != null) { - signature.update(buffer, 0, length); - } - - alreadyWritten += length; - if (wholeSize > 0) { - long progress = 100 * alreadyWritten / wholeSize; - // stop at 100% for wrong file sizes... - if (progress > 100) { - progress = 100; - } - progressScaler.setProgress((int) progress, 100); - } - // TODO: slow annealing to fake a progress? - } - - metadata = new OpenPgpMetadata( - originalFilename, - mimeType, - literalData.getModificationTime().getTime(), - alreadyWritten); - - if (signature != null) { - updateProgress(R.string.progress_verifying_signature, 90, 100); - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_CHECK, indent); - - PGPSignatureList signatureList = (PGPSignatureList) plainFact.nextObject(); - PGPSignature messageSignature = signatureList.get(signatureIndex); - - // TODO: what about binary signatures? - - // Verify signature - boolean validSignature = signature.verify(messageSignature); - if (validSignature) { - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_OK, indent + 1); - } else { - log.add(LogType.MSG_DC_CLEAR_SIGNATURE_BAD, indent + 1); - } - - // check for insecure hash algorithms - if (!PgpSecurityConstants.isSecureHashAlgorithm(signature.getHashAlgorithm())) { - log.add(LogType.MSG_DC_INSECURE_HASH_ALGO, indent + 1); - signatureResultBuilder.setInsecure(true); - } - - signatureResultBuilder.setValidSignature(validSignature); - } - - indent -= 1; - } else { - // If there is no literalData, we don't have any metadata - metadata = null; - } - - if (encryptedData.isIntegrityProtected()) { - updateProgress(R.string.progress_verifying_integrity, 95, 100); - - if (encryptedData.verify()) { - log.add(LogType.MSG_DC_INTEGRITY_CHECK_OK, indent); - } else { - log.add(LogType.MSG_DC_ERROR_INTEGRITY_CHECK, indent); - return new DecryptVerifyResult(DecryptVerifyResult.RESULT_ERROR, log); - } - } else { - // If no valid signature is present: - // Handle missing integrity protection like failed integrity protection! - // The MDC packet can be stripped by an attacker! - Log.d(Constants.TAG, "MDC fail"); - if (!signatureResultBuilder.isValidSignature()) { - log.add(LogType.MSG_DC_INSECURE_MDC_MISSING, indent); - decryptionResultBuilder.setInsecure(true); - } - } - - updateProgress(R.string.progress_done, 100, 100); - - log.add(LogType.MSG_DC_OK, indent); - - // Return a positive result, with metadata and verification info - DecryptVerifyResult result = new DecryptVerifyResult(DecryptVerifyResult.RESULT_OK, log); - result.setCachedCryptoInputParcel(cryptoInput); - result.setSignatureResult(signatureResultBuilder.build()); - result.setCharset(charset); - result.setDecryptionResult(decryptionResultBuilder.build()); - result.setDecryptionMetadata(metadata); return result; } -- cgit v1.2.3 From 950409ce55f2df1aecdb61a7fecfc599b541d89c Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Thu, 10 Sep 2015 22:36:07 +0200 Subject: complain about mdc insecurity status only if data actually was encrypted --- .../keychain/pgp/PgpDecryptVerifyOperation.java | 31 +++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java index a538c9bd1..005f04e70 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java @@ -382,12 +382,14 @@ public class PgpDecryptVerifyOperation extends BaseOperation Date: Fri, 11 Sep 2015 01:57:17 +0200 Subject: perform fingerprint check after canonicalization (OKC-01-009) --- .../keychain/operations/ImportOperation.java | 13 +------------ .../keychain/operations/results/OperationResult.java | 4 ++-- .../keychain/pgp/CanonicalizedKeyRing.java | 12 ++++++++++++ .../sufficientlysecure/keychain/pgp/UncachedKeyRing.java | 11 ----------- .../keychain/provider/ProviderHelper.java | 15 +++++++++++++-- 5 files changed, 28 insertions(+), 27 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java index 7b224fe8e..29264b5a2 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java @@ -254,17 +254,6 @@ public class ImportOperation extends BaseOperation { continue; } - // If we have an expected fingerprint, make sure it matches - if (entry.mExpectedFingerprint != null) { - if (!key.containsSubkey(entry.mExpectedFingerprint)) { - log.add(LogType.MSG_IMPORT_FINGERPRINT_ERROR, 2); - badKeys += 1; - continue; - } else { - log.add(LogType.MSG_IMPORT_FINGERPRINT_OK, 2); - } - } - // Another check if we have been cancelled if (checkCancelled()) { cancelled = true; @@ -283,7 +272,7 @@ public class ImportOperation extends BaseOperation { } else { result = mProviderHelper.savePublicKeyRing(key, new ProgressScaler(progressable, (int) (position * progSteps), - (int) ((position + 1) * progSteps), 100)); + (int) ((position + 1) * progSteps), 100), entry.mExpectedFingerprint); } } if (!result.success()) { 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 46852d783..4e528f73e 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 @@ -289,6 +289,8 @@ public abstract class OperationResult implements Parcelable { MSG_IP_ERROR_IO_EXC (LogLevel.ERROR, R.string.msg_ip_error_io_exc), MSG_IP_ERROR_OP_EXC (LogLevel.ERROR, R.string.msg_ip_error_op_exc), MSG_IP_ERROR_REMOTE_EX (LogLevel.ERROR, R.string.msg_ip_error_remote_ex), + MSG_IP_FINGERPRINT_ERROR (LogLevel.ERROR, R.string.msg_ip_fingerprint_error), + MSG_IP_FINGERPRINT_OK (LogLevel.INFO, R.string.msg_ip_fingerprint_ok), MSG_IP_INSERT_KEYRING (LogLevel.DEBUG, R.string.msg_ip_insert_keyring), MSG_IP_INSERT_SUBKEYS (LogLevel.DEBUG, R.string.msg_ip_insert_keys), MSG_IP_PREPARE (LogLevel.DEBUG, R.string.msg_ip_prepare), @@ -712,8 +714,6 @@ public abstract class OperationResult implements Parcelable { MSG_IMPORT_KEYSERVER (LogLevel.DEBUG, R.string.msg_import_keyserver), MSG_IMPORT_MERGE (LogLevel.DEBUG, R.string.msg_import_merge), MSG_IMPORT_MERGE_ERROR (LogLevel.ERROR, R.string.msg_import_merge_error), - MSG_IMPORT_FINGERPRINT_ERROR (LogLevel.ERROR, R.string.msg_import_fingerprint_error), - MSG_IMPORT_FINGERPRINT_OK (LogLevel.DEBUG, R.string.msg_import_fingerprint_ok), MSG_IMPORT_ERROR (LogLevel.ERROR, R.string.msg_import_error), MSG_IMPORT_ERROR_IO (LogLevel.ERROR, R.string.msg_import_error_io), MSG_IMPORT_PARTIAL (LogLevel.ERROR, R.string.msg_import_partial), diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java index 770e8de91..18a27dd96 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/CanonicalizedKeyRing.java @@ -21,6 +21,7 @@ package org.sufficientlysecure.keychain.pgp; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPPublicKey; import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException; +import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.IterableIterator; import java.io.IOException; @@ -28,6 +29,7 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.Date; import java.util.HashSet; +import java.util.Iterator; import java.util.Set; @@ -152,4 +154,14 @@ public abstract class CanonicalizedKeyRing extends KeyRing { return getRing().getEncoded(); } + public boolean containsSubkey(String expectedFingerprint) { + for (CanonicalizedPublicKey key : publicKeyIterator()) { + if (KeyFormattingUtils.convertFingerprintToHex( + key.getFingerprint()).equalsIgnoreCase(expectedFingerprint)) { + return true; + } + } + return false; + } + } 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 a7baddf8b..ca98882d8 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/UncachedKeyRing.java @@ -216,17 +216,6 @@ public class UncachedKeyRing implements Serializable { } - public boolean containsSubkey(String expectedFingerprint) { - Iterator it = mRing.getPublicKeys(); - while (it.hasNext()) { - if (KeyFormattingUtils.convertFingerprintToHex( - it.next().getFingerprint()).equalsIgnoreCase(expectedFingerprint)) { - return true; - } - } - return false; - } - public interface IteratorWithIOThrow { public boolean hasNext() throws IOException; public E next() throws IOException; 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 d9ef4f3c8..6f452bfd1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -878,7 +878,7 @@ public class ProviderHelper { } public SaveKeyringResult savePublicKeyRing(UncachedKeyRing keyRing) { - return savePublicKeyRing(keyRing, new ProgressScaler()); + return savePublicKeyRing(keyRing, new ProgressScaler(), null); } /** @@ -887,7 +887,7 @@ public class ProviderHelper { * This is a high level method, which takes care of merging all new information into the old and * keep public and secret keyrings in sync. */ - public SaveKeyringResult savePublicKeyRing(UncachedKeyRing publicRing, Progressable progress) { + public SaveKeyringResult savePublicKeyRing(UncachedKeyRing publicRing, Progressable progress, String expectedFingerprint) { try { long masterKeyId = publicRing.getMasterKeyId(); @@ -960,6 +960,17 @@ public class ProviderHelper { canSecretRing = null; } + + // If we have an expected fingerprint, make sure it matches + if (expectedFingerprint != null) { + if (!canPublicRing.containsSubkey(expectedFingerprint)) { + log(LogType.MSG_IP_FINGERPRINT_ERROR); + return new SaveKeyringResult(SaveKeyringResult.RESULT_ERROR, mLog, null); + } else { + log(LogType.MSG_IP_FINGERPRINT_OK); + } + } + int result = saveCanonicalizedPublicKeyRing(canPublicRing, progress, canSecretRing != null); // Save the saved keyring (if any) -- cgit v1.2.3 From b76aa7fe11f953d10688fbc01daa65676448c78a Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 11 Sep 2015 02:05:17 +0200 Subject: never import secret keys from keyserver (OKC-01-001) --- .../keychain/operations/ImportOperation.java | 11 +++++++++-- .../keychain/operations/results/OperationResult.java | 5 +++-- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java index 29264b5a2..89575338f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/ImportOperation.java @@ -211,7 +211,7 @@ public class ImportOperation extends BaseOperation { } } catch (Keyserver.QueryFailedException e) { Log.d(Constants.TAG, "query failed", e); - log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER_ERROR, 3, e.getMessage()); + log.add(LogType.MSG_IMPORT_FETCH_ERROR_KEYSERVER, 3, e.getMessage()); } } @@ -243,7 +243,7 @@ public class ImportOperation extends BaseOperation { } catch (Keyserver.QueryFailedException e) { // download failed, too bad. just proceed Log.e(Constants.TAG, "query failed", e); - log.add(LogType.MSG_IMPORT_FETCH_KEYSERVER_ERROR, 3, e.getMessage()); + log.add(LogType.MSG_IMPORT_FETCH_ERROR_KEYSERVER, 3, e.getMessage()); } } } @@ -254,6 +254,13 @@ public class ImportOperation extends BaseOperation { continue; } + // never import secret keys from keyserver! + if (entry.mBytes == null && key.isSecret()) { + log.add(LogType.MSG_IMPORT_FETCH_ERROR_KEYSERVER_SECRET, 2); + badKeys += 1; + continue; + } + // Another check if we have been cancelled if (checkCancelled()) { cancelled = true; 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 4e528f73e..6e9aca30d 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 @@ -707,10 +707,11 @@ public abstract class OperationResult implements Parcelable { MSG_IMPORT_FETCH_ERROR (LogLevel.ERROR, R.string.msg_import_fetch_error), MSG_IMPORT_FETCH_ERROR_DECODE (LogLevel.ERROR, R.string.msg_import_fetch_error_decode), + MSG_IMPORT_FETCH_ERROR_KEYSERVER(LogLevel.ERROR, R.string.msg_import_fetch_error_keyserver), + MSG_IMPORT_FETCH_ERROR_KEYSERVER_SECRET (LogLevel.ERROR, R.string.msg_import_fetch_error_keyserver_secret), + MSG_IMPORT_FETCH_KEYBASE (LogLevel.INFO, R.string.msg_import_fetch_keybase), MSG_IMPORT_FETCH_KEYSERVER (LogLevel.INFO, R.string.msg_import_fetch_keyserver), MSG_IMPORT_FETCH_KEYSERVER_OK (LogLevel.DEBUG, R.string.msg_import_fetch_keyserver_ok), - MSG_IMPORT_FETCH_KEYSERVER_ERROR (LogLevel.ERROR, R.string.msg_import_fetch_keyserver_error), - MSG_IMPORT_FETCH_KEYBASE (LogLevel.INFO, R.string.msg_import_fetch_keybase), MSG_IMPORT_KEYSERVER (LogLevel.DEBUG, R.string.msg_import_keyserver), MSG_IMPORT_MERGE (LogLevel.DEBUG, R.string.msg_import_merge), MSG_IMPORT_MERGE_ERROR (LogLevel.ERROR, R.string.msg_import_merge_error), -- cgit v1.2.3 From cdf67c3296b9a7aadf3528ed3eed29cdac752dde Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 11 Sep 2015 03:08:53 +0200 Subject: share rather than save log files (OKC-01-015) --- .../operations/results/OperationResult.java | 80 +++++++-- .../keychain/ui/LogDisplayFragment.java | 189 ++++----------------- 2 files changed, 94 insertions(+), 175 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 6e9aca30d..41691933e 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 @@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.operations.results; import android.app.Activity; import android.content.Intent; +import android.content.res.Resources; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.NonNull; @@ -52,6 +53,8 @@ import java.util.List; */ public abstract class OperationResult implements Parcelable { + final static String INDENTATION_WHITESPACE = " "; + public static final String EXTRA_RESULT = "operation_result"; /** @@ -166,6 +169,27 @@ public abstract class OperationResult implements Parcelable { ", mIndent=" + mIndent + '}'; } + + StringBuilder getPrintableLogEntry(Resources resources, int indent) { + + StringBuilder result = new StringBuilder(); + int padding = mIndent +indent; + if (padding > INDENTATION_WHITESPACE.length()) { + padding = INDENTATION_WHITESPACE.length(); + } + result.append(INDENTATION_WHITESPACE, 0, padding); + result.append(LOG_LEVEL_NAME[mType.mLevel.ordinal()]).append(' '); + + // special case: first parameter may be a quantity + if (mParameters != null && mParameters.length > 0 && mParameters[0] instanceof Integer) { + result.append(resources.getQuantityString(mType.getMsgId(), (Integer) mParameters[0], mParameters)); + } else { + result.append(resources.getString(mType.getMsgId(), mParameters)); + } + + return result; + } + } public static class SubLogEntryParcel extends LogEntryParcel { @@ -202,6 +226,17 @@ public abstract class OperationResult implements Parcelable { dest.writeParcelable(mSubResult, 0); } + @Override + StringBuilder getPrintableLogEntry(Resources resources, int indent) { + + LogEntryParcel subEntry = mSubResult.getLog().getLast(); + if (subEntry != null) { + return subEntry.getPrintableLogEntry(resources, mIndent +indent); + } else { + return super.getPrintableLogEntry(resources, indent); + } + } + } public Showable createNotify(final Activity activity) { @@ -245,15 +280,15 @@ public abstract class OperationResult implements Parcelable { } return Notify.create(activity, logText, Notify.LENGTH_LONG, style, - new ActionListener() { - @Override - public void onAction() { - Intent intent = new Intent( - activity, LogDisplayActivity.class); - intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResult.this); - activity.startActivity(intent); - } - }, R.string.snackbar_details); + new ActionListener() { + @Override + public void onAction() { + Intent intent = new Intent( + activity, LogDisplayActivity.class); + intent.putExtra(LogDisplayFragment.EXTRA_RESULT, OperationResult.this); + activity.startActivity(intent); + } + }, R.string.snackbar_details); } @@ -803,14 +838,7 @@ 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_LV_FETCH_ERROR_NOTHING (LogLevel.ERROR, R.string.msg_lv_fetch_error_nothing); public final int mMsgId; public final LogLevel mLevel; @@ -833,6 +861,10 @@ public abstract class OperationResult implements Parcelable { OK, // should occur once at the end of a successful operation CANCELLED, // should occur once at the end of a cancelled operation } + // for print of debug log. keep those in sync with above! + static final String[] LOG_LEVEL_NAME = new String[] { + "[DEBUG]", "[INFO]", "[WARN]", "[ERROR]", "[START]", "[OK]", "[CANCEL]" + }; @Override public int describeContents() { @@ -931,6 +963,20 @@ public abstract class OperationResult implements Parcelable { public Iterator iterator() { return mParcels.iterator(); } + + /** + * returns an indented String of an entire OperationLog + * @param indent padding to add at the start of all log entries, made for use with SubLogs + * @return printable, indented version of passed operationLog + */ + public String getPrintableOperationLog(Resources resources, int indent) { + StringBuilder log = new StringBuilder(); + for (LogEntryParcel entry : this) { + log.append(entry.getPrintableLogEntry(resources, indent)).append("\n"); + } + return log.toString().substring(0, log.length() -1); // get rid of extra new line + } + } } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java index 4de83337e..43c8d2643 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java @@ -18,11 +18,12 @@ package org.sufficientlysecure.keychain.ui; +import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Color; +import android.net.Uri; import android.os.Bundle; -import android.os.Parcel; import android.support.v4.app.ListFragment; import android.util.TypedValue; import android.view.LayoutInflater; @@ -37,19 +38,19 @@ import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; -import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogEntryParcel; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogLevel; import org.sufficientlysecure.keychain.operations.results.OperationResult.SubLogEntryParcel; +import org.sufficientlysecure.keychain.provider.TemporaryStorageProvider; import org.sufficientlysecure.keychain.ui.util.FormattingUtils; -import org.sufficientlysecure.keychain.util.FileHelper; -import org.sufficientlysecure.keychain.util.Log; +import org.sufficientlysecure.keychain.ui.util.Notify; +import org.sufficientlysecure.keychain.ui.util.Notify.Style; + +import java.io.IOException; +import java.io.OutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.PrintWriter; public class LogDisplayFragment extends ListFragment implements OnItemClickListener { @@ -60,6 +61,8 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe public static final String EXTRA_RESULT = "log"; protected int mTextColor; + private Uri mLogTempFile; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -118,170 +121,40 @@ public class LogDisplayFragment extends ListFragment implements OnItemClickListe public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_log_display_export_log: - exportLog(); + shareLog(); break; } return super.onOptionsItemSelected(item); } - private void exportLog() { - showExportLogDialog(new File(Constants.Path.APP_DIR, "export.log")); - } - - private void writeToLogFile(final OperationResult.OperationLog operationLog, final File f) { - OperationResult.OperationLog currLog = new OperationResult.OperationLog(); - currLog.add(OperationResult.LogType.MSG_EXPORT_LOG, 0); - - boolean error = false; - - PrintWriter pw = null; - try { - pw = new PrintWriter(f); - pw.print(getPrintableOperationLog(operationLog, "")); - if (pw.checkError()) {//IOException - Log.e(Constants.TAG, "Log Export I/O Exception " + f.getAbsolutePath()); - currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_ERROR_WRITING, 1); - error = true; - } - } catch (FileNotFoundException e) { - Log.e(Constants.TAG, "File not found for exporting log " + f.getAbsolutePath()); - currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_ERROR_FOPEN, 1); - error = true; - } - if (pw != null) { - pw.close(); - if (!error && pw.checkError()) {//check if it is only pw.close() which generated error - currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_ERROR_WRITING, 1); - error = true; - } - } - - if (!error) { - currLog.add(OperationResult.LogType.MSG_EXPORT_LOG_EXPORT_SUCCESS, 1); - } - - int opResultCode = error ? OperationResult.RESULT_ERROR : OperationResult.RESULT_OK; - OperationResult opResult = new LogExportResult(opResultCode, currLog); - opResult.createNotify(getActivity()).show(); - } - - /** - * returns an indented String of an entire OperationLog - * - * @param opLog log to be converted to indented, printable format - * @param basePadding padding to add at the start of all log entries, made for use with SubLogs - * @return printable, indented version of passed operationLog - */ - private String getPrintableOperationLog(OperationResult.OperationLog opLog, String basePadding) { - String log = ""; - for (LogEntryParcel anOpLog : opLog) { - log += getPrintableLogEntry(anOpLog, basePadding) + "\n"; - } - log = log.substring(0, log.length() - 1);//gets rid of extra new line - return log; - } - - /** - * returns an indented String of a LogEntryParcel including any sub-logs it may contain - * - * @param entryParcel log entryParcel whose String representation is to be obtained - * @return indented version of passed log entryParcel in a readable format - */ - private String getPrintableLogEntry(OperationResult.LogEntryParcel entryParcel, - String basePadding) { - - final String indent = " ";//4 spaces = 1 Indent level - - String padding = basePadding; - for (int i = 0; i < entryParcel.mIndent; i++) { - padding += indent; - } - String logText = padding; - - switch (entryParcel.mType.mLevel) { - case DEBUG: - logText += "[DEBUG]"; - break; - case INFO: - logText += "[INFO]"; - break; - case WARN: - logText += "[WARN]"; - break; - case ERROR: - logText += "[ERROR]"; - break; - case START: - logText += "[START]"; - break; - case OK: - logText += "[OK]"; - break; - case CANCELLED: - logText += "[CANCELLED]"; - break; - } + private void shareLog() { - // special case: first parameter may be a quantity - if (entryParcel.mParameters != null && entryParcel.mParameters.length > 0 - && entryParcel.mParameters[0] instanceof Integer) { - logText += getResources().getQuantityString(entryParcel.mType.getMsgId(), - (Integer) entryParcel.mParameters[0], - entryParcel.mParameters); - } else { - logText += getResources().getString(entryParcel.mType.getMsgId(), - entryParcel.mParameters); + Activity activity = getActivity(); + if (activity == null) { + return; } - if (entryParcel instanceof SubLogEntryParcel) { - OperationResult subResult = ((SubLogEntryParcel) entryParcel).getSubResult(); - LogEntryParcel subEntry = subResult.getLog().getLast(); - if (subEntry != null) { - //the first line of log of subResult is same as entryParcel, so replace logText - logText = getPrintableOperationLog(subResult.getLog(), padding); + String log = mResult.getLog().getPrintableOperationLog(getResources(), 0); + + // if there is no log temp file yet, create one + if (mLogTempFile == null) { + mLogTempFile = TemporaryStorageProvider.createFile(getActivity(), "openkeychain_log.txt", "text/plain"); + try { + OutputStream outputStream = activity.getContentResolver().openOutputStream(mLogTempFile); + outputStream.write(log.getBytes()); + } catch (IOException e) { + Notify.create(activity, R.string.error_log_share_internal, Style.ERROR).show(); + return; } } - return logText; - } - - private void showExportLogDialog(final File exportFile) { - - String title = this.getString(R.string.title_export_log); - - String message = this.getString(R.string.specify_file_to_export_log_to); - - FileHelper.saveDocumentDialog(new FileHelper.FileDialogCallback() { - @Override - public void onFileSelected(File file, boolean checked) { - writeToLogFile(mResult.getLog(), file); - } - }, this.getActivity().getSupportFragmentManager(), title, message, exportFile, null); - } - - private static class LogExportResult extends OperationResult { - - public static Creator CREATOR = new Creator() { - public LogExportResult createFromParcel(final Parcel source) { - return new LogExportResult(source); - } - - public LogExportResult[] newArray(final int size) { - return new LogExportResult[size]; - } - }; - - public LogExportResult(int result, OperationLog log) { - super(result, log); - } + Intent intent = new Intent(Intent.ACTION_SEND); + intent.putExtra(Intent.EXTRA_STREAM, mLogTempFile); + intent.setType("text/plain"); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + startActivity(intent); - /** - * trivial but necessary to implement the Parcelable protocol. - */ - public LogExportResult(Parcel source) { - super(source); - } } @Override -- cgit v1.2.3 From aad606c47ffea495d314bf1e537f1e2981b0dd88 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 11 Sep 2015 03:30:10 +0200 Subject: strip encrypted filenames to basenames (OKC-01-004) --- .../org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java | 3 +++ 1 file changed, 3 insertions(+) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java index 005f04e70..dda15f382 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java @@ -512,6 +512,9 @@ public class PgpDecryptVerifyOperation extends BaseOperation Date: Fri, 11 Sep 2015 03:56:07 +0200 Subject: sort confirmed keys before unconfirmed primary ones (OKC-01-011) --- .../org/sufficientlysecure/keychain/provider/ProviderHelper.java | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') 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 6f452bfd1..a6823d3ac 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -771,6 +771,11 @@ public class ProviderHelper { if (type != o.type) { return type == null ? -1 : 1; } + // if one is *trusted* but the other isn't, that one comes first + // this overrides the primary attribute, even! + if ( (trustedCerts.size() == 0) != (o.trustedCerts.size() == 0) ) { + return trustedCerts.size() > o.trustedCerts.size() ? -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; -- cgit v1.2.3 From fd6141f0d2564eee1e6c87fe6db5bf3bbb1c4bcf Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 11 Sep 2015 04:51:55 +0200 Subject: add share item to decrypt file list (#1524) --- .../keychain/ui/DecryptListFragment.java | 74 ++++++++++++---------- 1 file changed, 39 insertions(+), 35 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java index 9c0122b7b..92470218b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java @@ -375,7 +375,7 @@ public class DecryptListFragment onFileClick = new OnClickListener() { @Override public void onClick(View view) { - displayWithViewIntent(uri); + displayWithViewIntent(uri, false); } }; } @@ -400,7 +400,7 @@ public class DecryptListFragment } - public void displayWithViewIntent(final Uri uri) { + public void displayWithViewIntent(final Uri uri, boolean share) { Activity activity = getActivity(); if (activity == null || mCurrentInputUri != null) { return; @@ -419,49 +419,50 @@ public class DecryptListFragment // OpenKeychain's internal viewer if ("text/plain".equals(metadata.getMimeType())) { - // this is a significant i/o operation, use an asynctask - new AsyncTask() { + if (share) { + try { + String plaintext = FileHelper.readTextFromUri(activity, outputUri, result.getCharset()); - @Override - protected Intent doInBackground(Void... params) { - - Activity activity = getActivity(); - if (activity == null) { - return null; - } + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType(metadata.getMimeType()); + intent.putExtra(Intent.EXTRA_TEXT, plaintext); + startActivity(intent); - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(outputUri, "text/plain"); - intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - return intent; + } catch (IOException e) { + Notify.create(activity, R.string.error_preparing_data, Style.ERROR).show(); } - @Override - protected void onPostExecute(Intent intent) { - // for result so we can possibly get a snackbar error from internal viewer - Activity activity = getActivity(); - if (intent == null || activity == null) { - return; - } + return; + } - LabeledIntent internalIntent = new LabeledIntent( - new Intent(intent) - .setClass(activity, DisplayTextActivity.class) - .putExtra(DisplayTextActivity.EXTRA_METADATA, result), - BuildConfig.APPLICATION_ID, R.string.view_internal, R.mipmap.ic_launcher); + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(outputUri, metadata.getMimeType()); - Intent chooserIntent = Intent.createChooser(intent, getString(R.string.intent_show)); - chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, - new Parcelable[] { internalIntent }); + // for result so we can possibly get a snackbar error from internal viewer + LabeledIntent internalIntent = new LabeledIntent( + new Intent(intent) + .setClass(activity, DisplayTextActivity.class) + .putExtra(DisplayTextActivity.EXTRA_METADATA, result), + BuildConfig.APPLICATION_ID, R.string.view_internal, R.mipmap.ic_launcher); - activity.startActivity(chooserIntent); - } + Intent chooserIntent = Intent.createChooser(intent, getString(R.string.intent_show)); + chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, + new Parcelable[] { internalIntent }); - }.execute(); + activity.startActivity(chooserIntent); } else { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(outputUri, metadata.getMimeType()); + + Intent intent; + if (share) { + intent = new Intent(Intent.ACTION_SEND); + intent.setType(metadata.getMimeType()); + intent.putExtra(Intent.EXTRA_STREAM, outputUri); + } else { + intent = new Intent(Intent.ACTION_VIEW); + intent.setDataAndType(outputUri, metadata.getMimeType()); + } + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Intent chooserIntent = Intent.createChooser(intent, getString(R.string.intent_show)); @@ -515,6 +516,9 @@ public class DecryptListFragment intent.putExtra(LogDisplayFragment.EXTRA_RESULT, result); activity.startActivity(intent); return true; + case R.id.decrypt_share: + displayWithViewIntent(model.mInputUri, true); + return true; case R.id.decrypt_save: OpenPgpMetadata metadata = result.getDecryptionMetadata(); if (metadata == null) { -- cgit v1.2.3 From 0d46a589e5161aeb762bc7eab2a71b69d33fedce Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 11 Sep 2015 04:59:00 +0200 Subject: no chooser before DisplayTextActivity, can select from there (#1524) --- .../keychain/ui/DecryptListFragment.java | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java index 92470218b..ddaf40010 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java @@ -28,7 +28,6 @@ import android.app.Activity; import android.content.ClipDescription; import android.content.Context; import android.content.Intent; -import android.content.pm.LabeledIntent; import android.content.pm.ResolveInfo; import android.graphics.Bitmap; import android.graphics.Point; @@ -37,7 +36,6 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; -import android.os.Parcelable; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; @@ -57,7 +55,6 @@ import android.widget.ViewAnimator; import org.openintents.openpgp.OpenPgpMetadata; import org.openintents.openpgp.OpenPgpSignatureResult; -import org.sufficientlysecure.keychain.BuildConfig; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; @@ -435,21 +432,11 @@ public class DecryptListFragment return; } - Intent intent = new Intent(Intent.ACTION_VIEW); + Intent intent = new Intent(activity, DisplayTextActivity.class); + intent.setAction(Intent.ACTION_VIEW); intent.setDataAndType(outputUri, metadata.getMimeType()); - - // for result so we can possibly get a snackbar error from internal viewer - LabeledIntent internalIntent = new LabeledIntent( - new Intent(intent) - .setClass(activity, DisplayTextActivity.class) - .putExtra(DisplayTextActivity.EXTRA_METADATA, result), - BuildConfig.APPLICATION_ID, R.string.view_internal, R.mipmap.ic_launcher); - - Intent chooserIntent = Intent.createChooser(intent, getString(R.string.intent_show)); - chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, - new Parcelable[] { internalIntent }); - - activity.startActivity(chooserIntent); + intent.putExtra(DisplayTextActivity.EXTRA_METADATA, result); + activity.startActivity(intent); } else { -- cgit v1.2.3 From d5dd6a49c8156a699b3fbbbeef06658e1c232c16 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Fri, 11 Sep 2015 13:29:59 +0200 Subject: fix up navigation in keyserver select (fixes #1482) --- .../sufficientlysecure/keychain/ui/SettingsActivity.java | 6 +++--- .../keychain/ui/SettingsKeyServerActivity.java | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java index 21d0f1dbd..eb9ee05af 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsActivity.java @@ -77,7 +77,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { String action = getIntent().getAction(); - if (action != null && action.equals(ACTION_PREFS_CLOUD)) { + if (ACTION_PREFS_CLOUD.equals(action)) { addPreferencesFromResource(R.xml.cloud_search_prefs); mKeyServerPreference = (PreferenceScreen) findPreference(Constants.Pref.KEY_SERVERS); @@ -100,7 +100,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { (SwitchPreference) findPreference(Constants.Pref.SEARCH_KEYBASE) ); - } else if (action != null && action.equals(ACTION_PREFS_ADV)) { + } else if (ACTION_PREFS_ADV.equals(action)) { addPreferencesFromResource(R.xml.passphrase_preferences); initializePassphraseCacheSubs( @@ -115,7 +115,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { initializeUseNumKeypadForYubiKeyPin( (CheckBoxPreference) findPreference(Constants.Pref.USE_NUMKEYPAD_FOR_YUBIKEY_PIN)); - } else if (action != null && action.equals(ACTION_PREFS_GUI)) { + } else if (ACTION_PREFS_GUI.equals(action)) { addPreferencesFromResource(R.xml.gui_preferences); initializeTheme((ListPreference) findPreference(Constants.Pref.THEME)); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java index f61ada84f..7dd92c45f 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/SettingsKeyServerActivity.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui; import android.content.Intent; import android.os.Bundle; +import android.view.MenuItem; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.base.BaseActivity; @@ -34,6 +35,19 @@ public class SettingsKeyServerActivity extends BaseActivity { Intent intent = getIntent(); String servers[] = intent.getStringArrayExtra(EXTRA_KEY_SERVERS); loadFragment(savedInstanceState, servers); + + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + } + return super.onOptionsItemSelected(item); } @Override -- cgit v1.2.3