From 212ee222c3cf66adda0842176a17f9302549f51b Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sun, 2 Feb 2014 22:14:07 +0000 Subject: [PATCH 001/253] replace spinner with non-functioning checkboxes --- .../keychain/ui/widget/KeyEditor.java | 44 +++++++-------- .../src/main/res/layout/edit_key_key_item.xml | 54 +++++++++++++++++-- .../src/main/res/values/strings.xml | 9 ++-- 3 files changed, 77 insertions(+), 30 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index 6c265057e..16e868a2c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -89,18 +89,18 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { mKeyId = (TextView) findViewById(R.id.keyId); mCreationDate = (TextView) findViewById(R.id.creation); mExpiryDateButton = (BootstrapButton) findViewById(R.id.expiry); - mUsage = (Spinner) findViewById(R.id.usage); - Choice choices[] = { - new Choice(Id.choice.usage.sign_only, getResources().getString( - R.string.choice_sign_only)), - new Choice(Id.choice.usage.encrypt_only, getResources().getString( - R.string.choice_encrypt_only)), - new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString( - R.string.choice_sign_and_encrypt)), }; - ArrayAdapter adapter = new ArrayAdapter(getContext(), - android.R.layout.simple_spinner_item, choices); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - mUsage.setAdapter(adapter); + //mUsage = (Spinner) findViewById(R.id.usage); + //Choice choices[] = { + // new Choice(Id.choice.usage.sign_only, getResources().getString( + // R.string.choice_sign_only)), + // new Choice(Id.choice.usage.encrypt_only, getResources().getString( + // R.string.choice_encrypt_only)), + // new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString( + // R.string.choice_sign_and_encrypt)), }; + //ArrayAdapter adapter = new ArrayAdapter(getContext(), + // android.R.layout.simple_spinner_item, choices); + //adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + //mUsage.setAdapter(adapter); mDeleteButton = (BootstrapButton) findViewById(R.id.delete); mDeleteButton.setOnClickListener(this); @@ -139,7 +139,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { public void setCanEdit(boolean bCanEdit) { if (!bCanEdit) { mDeleteButton.setVisibility(View.INVISIBLE); - mUsage.setEnabled(false); + //mUsage.setEnabled(false); mExpiryDateButton.setEnabled(false); } } @@ -161,22 +161,22 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT); boolean isDSAKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.DSA); if (!isElGamalKey) { - choices.add(new Choice(Id.choice.usage.sign_only, getResources().getString( - R.string.choice_sign_only))); + //choices.add(new Choice(Id.choice.usage.sign_only, getResources().getString( + // R.string.choice_sign_only))); } if (!mIsMasterKey && !isDSAKey) { - choices.add(new Choice(Id.choice.usage.encrypt_only, getResources().getString( - R.string.choice_encrypt_only))); + //choices.add(new Choice(Id.choice.usage.encrypt_only, getResources().getString( + // R.string.choice_encrypt_only))); } if (!isElGamalKey && !isDSAKey) { - choices.add(new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString( - R.string.choice_sign_and_encrypt))); + //choices.add(new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString( + // R.string.choice_sign_and_encrypt))); } ArrayAdapter adapter = new ArrayAdapter(getContext(), android.R.layout.simple_spinner_item, choices); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - mUsage.setAdapter(adapter); + //mUsage.setAdapter(adapter); // Set value in choice dropdown to key int selectId = 0; @@ -198,7 +198,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { for (int i = 0; i < choices.size(); ++i) { if (choices.get(i).getId() == selectId) { - mUsage.setSelection(i); + //mUsage.setSelection(i); break; } } @@ -249,7 +249,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { } public int getUsage() { - return ((Choice) mUsage.getSelectedItem()).getId(); + return 1; //((Choice) mUsage.getSelectedItem()).getId(); } } diff --git a/OpenPGP-Keychain/src/main/res/layout/edit_key_key_item.xml b/OpenPGP-Keychain/src/main/res/layout/edit_key_key_item.xml index 4bf4aa38a..ef913b039 100644 --- a/OpenPGP-Keychain/src/main/res/layout/edit_key_key_item.xml +++ b/OpenPGP-Keychain/src/main/res/layout/edit_key_key_item.xml @@ -97,11 +97,54 @@ android:layout_gravity="center_vertical" android:paddingRight="10dip" android:text="@string/label_usage" /> + + - + + + + + + + + + + + + + + + + @@ -122,4 +165,5 @@ android:layout_height="1dip" android:background="?android:attr/listDivider" /> - \ No newline at end of file + + diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index fb7b60b7c..438a5384c 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -153,9 +153,6 @@ None - Sign only - Encrypt only - Sign and Encrypt 15 secs 1 min 3 mins @@ -176,6 +173,12 @@ Error Error: %s + + Certify + Sign + Encrypt + Authenticate + Wrong passphrase. Using clipboard content. From a90b748611126cd122e39ef944f4c268798419f0 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sun, 2 Feb 2014 23:56:53 +0000 Subject: [PATCH 002/253] set checkboxes from key properties --- .../keychain/ui/widget/KeyEditor.java | 74 +++++++++---------- .../src/main/res/layout/edit_key_key_item.xml | 17 +++-- 2 files changed, 49 insertions(+), 42 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index 16e868a2c..f845b53a7 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -39,9 +39,12 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ArrayAdapter; +import android.widget.CheckBox; import android.widget.DatePicker; import android.widget.LinearLayout; import android.widget.Spinner; +import android.widget.TableLayout; +import android.widget.TableRow; import android.widget.TextView; import com.beardedhen.androidbootstrap.BootstrapButton; @@ -59,6 +62,10 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { TextView mCreationDate; BootstrapButton mExpiryDateButton; GregorianCalendar mExpiryDate; + CheckBox mChkCertify; + CheckBox mChkSign; + CheckBox mChkEncrypt; + CheckBox mChkAuthenticate; private int mDatePickerResultCount = 0; private DatePickerDialog.OnDateSetListener mExpiryDateSetListener = new DatePickerDialog.OnDateSetListener() { @@ -89,21 +96,13 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { mKeyId = (TextView) findViewById(R.id.keyId); mCreationDate = (TextView) findViewById(R.id.creation); mExpiryDateButton = (BootstrapButton) findViewById(R.id.expiry); - //mUsage = (Spinner) findViewById(R.id.usage); - //Choice choices[] = { - // new Choice(Id.choice.usage.sign_only, getResources().getString( - // R.string.choice_sign_only)), - // new Choice(Id.choice.usage.encrypt_only, getResources().getString( - // R.string.choice_encrypt_only)), - // new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString( - // R.string.choice_sign_and_encrypt)), }; - //ArrayAdapter adapter = new ArrayAdapter(getContext(), - // android.R.layout.simple_spinner_item, choices); - //adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - //mUsage.setAdapter(adapter); mDeleteButton = (BootstrapButton) findViewById(R.id.delete); mDeleteButton.setOnClickListener(this); + mChkCertify = (CheckBox) findViewById(R.id.chkCertify); + mChkSign = (CheckBox) findViewById(R.id.chkSign); + mChkEncrypt = (CheckBox) findViewById(R.id.chkEncrypt); + mChkAuthenticate = (CheckBox) findViewById(R.id.chkAuthenticate); setExpiryDate(null); @@ -139,8 +138,10 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { public void setCanEdit(boolean bCanEdit) { if (!bCanEdit) { mDeleteButton.setVisibility(View.INVISIBLE); - //mUsage.setEnabled(false); mExpiryDateButton.setEnabled(false); + mChkSign.setEnabled(false); //certify is always disabled + mChkEncrypt.setEnabled(false); + mChkAuthenticate.setEnabled(false); } } @@ -160,17 +161,26 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { Vector choices = new Vector(); boolean isElGamalKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT); boolean isDSAKey = (key.getPublicKey().getAlgorithm() == PGPPublicKey.DSA); - if (!isElGamalKey) { - //choices.add(new Choice(Id.choice.usage.sign_only, getResources().getString( - // R.string.choice_sign_only))); + if (isElGamalKey) { + mChkSign.setVisibility(View.INVISIBLE); + TableLayout table = (TableLayout)findViewById(R.id.table_keylayout); + TableRow row = (TableRow)findViewById(R.id.row_sign); + table.removeView(row); } - if (!mIsMasterKey && !isDSAKey) { - //choices.add(new Choice(Id.choice.usage.encrypt_only, getResources().getString( - // R.string.choice_encrypt_only))); + if (isDSAKey) { + mChkEncrypt.setVisibility(View.INVISIBLE); + TableLayout table = (TableLayout)findViewById(R.id.table_keylayout); + TableRow row = (TableRow)findViewById(R.id.row_encrypt); + table.removeView(row); } - if (!isElGamalKey && !isDSAKey) { - //choices.add(new Choice(Id.choice.usage.sign_and_encrypt, getResources().getString( - // R.string.choice_sign_and_encrypt))); + if (!mIsMasterKey) { + mChkCertify.setVisibility(View.INVISIBLE); + TableLayout table = (TableLayout)findViewById(R.id.table_keylayout); + TableRow row = (TableRow)findViewById(R.id.row_certify); + table.removeView(row); + } else { + TextView mLabelUsage2= (TextView) findViewById(R.id.label_usage2); + mLabelUsage2.setVisibility(View.INVISIBLE); } ArrayAdapter adapter = new ArrayAdapter(getContext(), @@ -180,21 +190,11 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { // Set value in choice dropdown to key int selectId = 0; - if (PgpKeyHelper.isEncryptionKey(key)) { - if (PgpKeyHelper.isSigningKey(key)) { - selectId = Id.choice.usage.sign_and_encrypt; - } else { - selectId = Id.choice.usage.encrypt_only; - } - } else { - // set usage if it is predefined - if (usage != -1) { - selectId = usage; - } else { - selectId = Id.choice.usage.sign_only; - } - - } + if (key.isMasterKey()) + mChkCertify.setChecked(PgpKeyHelper.isCertificationKey(key)); + mChkSign.setChecked(PgpKeyHelper.isSigningKey(key)); + mChkEncrypt.setChecked(PgpKeyHelper.isEncryptionKey(key)); + // TODO: use usage argument? for (int i = 0; i < choices.size(); ++i) { if (choices.get(i).getId() == selectId) { diff --git a/OpenPGP-Keychain/src/main/res/layout/edit_key_key_item.xml b/OpenPGP-Keychain/src/main/res/layout/edit_key_key_item.xml index ef913b039..499fd5aa9 100644 --- a/OpenPGP-Keychain/src/main/res/layout/edit_key_key_item.xml +++ b/OpenPGP-Keychain/src/main/res/layout/edit_key_key_item.xml @@ -11,6 +11,7 @@ android:orientation="horizontal" > - + - + + android:paddingRight="10dip" + android:text="@string/label_usage" /> - + - + Date: Mon, 3 Feb 2014 00:05:45 +0000 Subject: [PATCH 003/253] include authentication keys --- .../keychain/pgp/PgpKeyHelper.java | 30 +++++++++++++++++++ .../keychain/ui/widget/KeyEditor.java | 1 + 2 files changed, 31 insertions(+) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index 3fc63cda1..bef41ce64 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -397,6 +397,36 @@ public class PgpKeyHelper { return false; } + public static boolean isAuthenticationKey(PGPSecretKey key) { + return isAuthenticationKey(key.getPublicKey()); + } + + @SuppressWarnings("unchecked") + public static boolean isAuthenticationKey(PGPPublicKey key) { + if (key.getVersion() <= 3) { + return true; + } + + for (PGPSignature sig : new IterableIterator(key.getSignatures())) { + if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) { + continue; + } + PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); + + if (hashed != null && (hashed.getKeyFlags() & KeyFlags.AUTHENTICATION) != 0) { + return true; + } + + PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets(); + + if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.AUTHENTICATION) != 0) { + return true; + } + } + + return false; + } + public static boolean isCertificationKey(PGPSecretKey key) { return isCertificationKey(key.getPublicKey()); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index f845b53a7..b05963385 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -194,6 +194,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { mChkCertify.setChecked(PgpKeyHelper.isCertificationKey(key)); mChkSign.setChecked(PgpKeyHelper.isSigningKey(key)); mChkEncrypt.setChecked(PgpKeyHelper.isEncryptionKey(key)); + mChkAuthenticate.setChecked(PgpKeyHelper.isAuthenticationKey(key)); // TODO: use usage argument? for (int i = 0; i < choices.size(); ++i) { From 089a70fe323ee0628b05709f2f831d6aa32e0281 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Mon, 3 Feb 2014 01:15:29 +0000 Subject: [PATCH 004/253] save flags from checkboxes --- .../keychain/pgp/PgpKeyOperation.java | 22 +++++----------- .../keychain/ui/widget/KeyEditor.java | 26 +++++++++---------- 2 files changed, 18 insertions(+), 30 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 9782d1ac2..d2793a859 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -237,8 +237,8 @@ public class PgpKeyOperation { updateProgress(R.string.progress_preparing_master_key, 10, 100); int usageId = keysUsages.get(0); - boolean canSign = (usageId == Id.choice.usage.sign_only || usageId == Id.choice.usage.sign_and_encrypt); - boolean canEncrypt = (usageId == Id.choice.usage.encrypt_only || usageId == Id.choice.usage.sign_and_encrypt); + boolean canSign = (usageId & KeyFlags.SIGN_DATA) > 0; + boolean canEncrypt = (usageId & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) > 0; String mainUserId = userIds.get(0); @@ -287,11 +287,7 @@ public class PgpKeyOperation { PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - int keyFlags = KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA; - if (canEncrypt) { - keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; - } - hashedPacketsGen.setKeyFlags(true, keyFlags); + hashedPacketsGen.setKeyFlags(true, usageId); hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS); hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS); @@ -349,14 +345,11 @@ public class PgpKeyOperation { hashedPacketsGen = new PGPSignatureSubpacketGenerator(); unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - keyFlags = 0; - usageId = keysUsages.get(i); - canSign = (usageId == Id.choice.usage.sign_only || usageId == Id.choice.usage.sign_and_encrypt); - canEncrypt = (usageId == Id.choice.usage.encrypt_only || usageId == Id.choice.usage.sign_and_encrypt); + canSign = (usageId & KeyFlags.SIGN_DATA) > 0; //todo - separate function for this + canEncrypt = (usageId & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) > 0; if (canSign) { Date todayDate = new Date(); //both sig times the same - keyFlags |= KeyFlags.SIGN_DATA; // cross-certify signing keys hashedPacketsGen.setSignatureCreationTime(false, todayDate); //set outer creation time PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); @@ -371,10 +364,7 @@ public class PgpKeyOperation { subPublicKey); unhashedPacketsGen.setEmbeddedSignature(false, certification); } - if (canEncrypt) { - keyFlags |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; - } - hashedPacketsGen.setKeyFlags(false, keyFlags); + hashedPacketsGen.setKeyFlags(false, usageId); if (keysExpiryDates.get(i) != null) { GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC")); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index b05963385..2bedafc9f 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -23,6 +23,7 @@ import java.util.GregorianCalendar; import java.util.TimeZone; import java.util.Vector; +import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSecretKey; import org.sufficientlysecure.keychain.Id; @@ -183,12 +184,6 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { mLabelUsage2.setVisibility(View.INVISIBLE); } - ArrayAdapter adapter = new ArrayAdapter(getContext(), - android.R.layout.simple_spinner_item, choices); - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); - //mUsage.setAdapter(adapter); - - // Set value in choice dropdown to key int selectId = 0; if (key.isMasterKey()) mChkCertify.setChecked(PgpKeyHelper.isCertificationKey(key)); @@ -197,13 +192,6 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { mChkAuthenticate.setChecked(PgpKeyHelper.isAuthenticationKey(key)); // TODO: use usage argument? - for (int i = 0; i < choices.size(); ++i) { - if (choices.get(i).getId() == selectId) { - //mUsage.setSelection(i); - break; - } - } - GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); cal.setTime(PgpKeyHelper.getCreationDate(key)); mCreationDate.setText(DateFormat.getDateInstance().format(cal.getTime())); @@ -250,7 +238,17 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { } public int getUsage() { - return 1; //((Choice) mUsage.getSelectedItem()).getId(); + int result = 0; // TODO: preserve other flags + if (mChkCertify.isChecked()) + result |= KeyFlags.CERTIFY_OTHER; + if (mChkSign.isChecked()) //TODO: fix what happens when we remove sign flag from master - should still be able to certify + result |= KeyFlags.SIGN_DATA; + if (mChkEncrypt.isChecked()) + result |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; + if (mChkAuthenticate.isChecked()) + result |= KeyFlags.AUTHENTICATION; + + return result; } } From 82b94838c3132064c8011a041466fea04a602b19 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Tue, 4 Feb 2014 00:38:19 +0000 Subject: [PATCH 005/253] fix certify keys without sign flag --- .../keychain/provider/ProviderHelper.java | 12 +++++------- .../keychain/ui/EditKeyActivity.java | 2 +- .../keychain/ui/widget/KeyEditor.java | 1 - 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index a4992163a..12bc33995 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -23,8 +23,6 @@ import java.util.ArrayList; import java.util.Date; import org.spongycastle.bcpg.ArmoredOutputStream; -import org.spongycastle.bcpg.UserAttributePacket; -import org.spongycastle.bcpg.UserAttributeSubpacket; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPPublicKeyRing; @@ -348,7 +346,7 @@ public class ProviderHelper { values.put(Keys.KEY_SIZE, key.getPublicKey().getBitStrength()); values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key) && has_private)); values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key) && has_private)); - values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key)); + values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key) && has_private); values.put(Keys.IS_REVOKED, key.getPublicKey().isRevoked()); values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000); Date expiryDate = PgpKeyHelper.getExpiryDate(key); @@ -441,21 +439,21 @@ public class ProviderHelper { /** * Get empty status of master key of keyring by its row id */ - public static boolean getSecretMasterKeyCanSign(Context context, long keyRingRowId) { + public static boolean getSecretMasterKeyCanCertify(Context context, long keyRingRowId) { Uri queryUri = KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowId)); - return getMasterKeyCanSign(context, queryUri, keyRingRowId); + return getMasterKeyCanCertify(context, queryUri, keyRingRowId); } /** * Private helper method to get master key private empty status of keyring by its row id */ - private static boolean getMasterKeyCanSign(Context context, Uri queryUri, long keyRingRowId) { + private static boolean getMasterKeyCanCertify(Context context, Uri queryUri, long keyRingRowId) { String[] projection = new String[]{ KeyRings.MASTER_KEY_ID, "(SELECT COUNT(sign_keys." + Keys._ID + ") FROM " + Tables.KEYS + " AS sign_keys WHERE sign_keys." + Keys.KEY_RING_ROW_ID + " = " + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID - + " AND sign_keys." + Keys.CAN_SIGN + " = '1' AND " + Keys.IS_MASTER_KEY + + " AND sign_keys." + Keys.CAN_CERTIFY + " = '1' AND " + Keys.IS_MASTER_KEY + " = 1) AS sign",}; ContentResolver cr = context.getContentResolver(); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 73426e32d..2c49d4921 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -256,7 +256,7 @@ public class EditKeyActivity extends ActionBarActivity { // get master key id using row id long masterKeyId = ProviderHelper.getSecretMasterKeyId(this, keyRingRowId); - masterCanSign = ProviderHelper.getSecretMasterKeyCanSign(this, keyRingRowId); + masterCanSign = ProviderHelper.getSecretMasterKeyCanCertify(this, keyRingRowId); finallyEdit(masterKeyId, masterCanSign); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index 2bedafc9f..2ea044e66 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -59,7 +59,6 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { BootstrapButton mDeleteButton; TextView mAlgorithm; TextView mKeyId; - Spinner mUsage; TextView mCreationDate; BootstrapButton mExpiryDateButton; GregorianCalendar mExpiryDate; From 64aa2b7701fc6fa1992540f32f18bad34e011b31 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Tue, 4 Feb 2014 00:46:03 +0000 Subject: [PATCH 006/253] slight clarification --- OpenPGP-Keychain/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index 438a5384c..becf2417e 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -56,8 +56,8 @@ Delete None Okay - Change Passphrase - Set Passphrase + Change New Passphrase + Set New Passphrase Search Upload To Key Server Next From fe3db8f0e68b7490bf98e356474a631dec32530c Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Tue, 4 Feb 2014 01:18:54 +0000 Subject: [PATCH 007/253] preserve keys that we don\'t use in the app --- .../keychain/pgp/PgpKeyHelper.java | 27 ++++++++++++++++--- .../keychain/ui/widget/KeyEditor.java | 18 ++++++------- 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index bef41ce64..78d42cbf9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -209,9 +209,8 @@ public class PgpKeyHelper { Calendar calendar = GregorianCalendar.getInstance(); calendar.setTime(creationDate); calendar.add(Calendar.DATE, key.getValidDays()); - Date expiryDate = calendar.getTime(); - return expiryDate; + return calendar.getTime(); } public static Date getExpiryDate(PGPSecretKey key) { @@ -291,6 +290,28 @@ public class PgpKeyHelper { return userId; } + public static int getKeyUsage(PGPSecretKey key) + { + return getKeyUsage(key.getPublicKey()); + } + + @SuppressWarnings("unchecked") + private static int getKeyUsage(PGPPublicKey key) { + int usage = 0; + if (key.getVersion() >= 4) { + for (PGPSignature sig : new IterableIterator(key.getSignatures())) { + if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) continue; + + PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets(); + if (hashed != null) usage |= hashed.getKeyFlags(); + + PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets(); + if (unhashed != null) usage |= unhashed.getKeyFlags(); + } + } + return usage; + } + @SuppressWarnings("unchecked") public static boolean isEncryptionKey(PGPPublicKey key) { if (!key.isEncryptionKey()) { @@ -440,7 +461,7 @@ public class PgpKeyHelper { } public static String getAlgorithmInfo(int algorithm, int keySize) { - String algorithmStr = null; + String algorithmStr; switch (algorithm) { case PGPPublicKey.RSA_ENCRYPT: diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index 2ea044e66..8443895e8 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -66,6 +66,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { CheckBox mChkSign; CheckBox mChkEncrypt; CheckBox mChkAuthenticate; + int mUsage; private int mDatePickerResultCount = 0; private DatePickerDialog.OnDateSetListener mExpiryDateSetListener = new DatePickerDialog.OnDateSetListener() { @@ -189,6 +190,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { mChkSign.setChecked(PgpKeyHelper.isSigningKey(key)); mChkEncrypt.setChecked(PgpKeyHelper.isEncryptionKey(key)); mChkAuthenticate.setChecked(PgpKeyHelper.isAuthenticationKey(key)); + mUsage = PgpKeyHelper.getKeyUsage(key); // TODO: use usage argument? GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); @@ -237,17 +239,13 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { } public int getUsage() { - int result = 0; // TODO: preserve other flags - if (mChkCertify.isChecked()) - result |= KeyFlags.CERTIFY_OTHER; - if (mChkSign.isChecked()) //TODO: fix what happens when we remove sign flag from master - should still be able to certify - result |= KeyFlags.SIGN_DATA; - if (mChkEncrypt.isChecked()) - result |= KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE; - if (mChkAuthenticate.isChecked()) - result |= KeyFlags.AUTHENTICATION; + mUsage = (mUsage & ~KeyFlags.CERTIFY_OTHER) | (mChkCertify.isChecked() ? KeyFlags.CERTIFY_OTHER : 0); + mUsage = (mUsage & ~KeyFlags.SIGN_DATA) | (mChkSign.isChecked() ? KeyFlags.SIGN_DATA : 0); + mUsage = (mUsage & ~KeyFlags.ENCRYPT_COMMS) | (mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_COMMS : 0); + mUsage = (mUsage & ~KeyFlags.ENCRYPT_STORAGE) | (mChkEncrypt.isChecked() ? KeyFlags.ENCRYPT_STORAGE : 0); + mUsage = (mUsage & ~KeyFlags.AUTHENTICATION) | (mChkAuthenticate.isChecked() ? KeyFlags.AUTHENTICATION : 0); - return result; + return mUsage; } } From 6d40bc3c0c5e14b77f7de734e57e351eacd6e98c Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Tue, 4 Feb 2014 22:02:53 +0000 Subject: [PATCH 008/253] use usage argument if needed --- .../sufficientlysecure/keychain/ui/widget/KeyEditor.java | 8 +++++--- .../keychain/ui/widget/SectionView.java | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index 8443895e8..2e56c65bc 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -146,7 +146,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { } } - public void setValue(PGPSecretKey key, boolean isMasterKey, int usage) { + public void setValue(PGPSecretKey key, boolean isMasterKey, int usage, boolean isNewKey) { mKey = key; mIsMasterKey = isMasterKey; @@ -190,8 +190,10 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { mChkSign.setChecked(PgpKeyHelper.isSigningKey(key)); mChkEncrypt.setChecked(PgpKeyHelper.isEncryptionKey(key)); mChkAuthenticate.setChecked(PgpKeyHelper.isAuthenticationKey(key)); - mUsage = PgpKeyHelper.getKeyUsage(key); - // TODO: use usage argument? + if (isNewKey) + mUsage = usage; + else + mUsage = PgpKeyHelper.getKeyUsage(key); GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); cal.setTime(PgpKeyHelper.getCreationDate(key)); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 9d3643914..df69ff60b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -272,7 +272,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor false); view.setEditorListener(this); boolean isMasterKey = (mEditors.getChildCount() == 0); - view.setValue(list.get(i), isMasterKey, usages.get(i)); + view.setValue(list.get(i), isMasterKey, usages.get(i), false); view.setCanEdit(canEdit); mEditors.addView(view); } @@ -344,7 +344,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, mEditors, false); view.setEditorListener(SectionView.this); - view.setValue(newKey, newKey.isMasterKey(), -1); + view.setValue(newKey, newKey.isMasterKey(), -1, true); mEditors.addView(view); SectionView.this.updateEditorsVisible(); } From 8d7cc6755349d06dcd9a2c28dc556c7adb71b8a4 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Wed, 5 Feb 2014 21:04:20 +0000 Subject: [PATCH 009/253] save --- .../keychain/ui/EditKeyActivity.java | 13 ++++++- .../ui/PreferencesKeyServerActivity.java | 7 +++- .../keychain/ui/widget/Editor.java | 4 +- .../keychain/ui/widget/KeyEditor.java | 37 ++++++++++++++++--- .../keychain/ui/widget/KeyServerEditor.java | 2 +- .../keychain/ui/widget/SectionView.java | 29 ++++++++++++++- .../keychain/ui/widget/UserIdEditor.java | 7 +++- 7 files changed, 87 insertions(+), 12 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 2c49d4921..b28ed8922 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -38,9 +38,11 @@ import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; +import org.sufficientlysecure.keychain.ui.widget.Editor; import org.sufficientlysecure.keychain.ui.widget.KeyEditor; import org.sufficientlysecure.keychain.ui.widget.SectionView; import org.sufficientlysecure.keychain.ui.widget.UserIdEditor; +import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -67,7 +69,7 @@ import android.widget.Toast; import com.beardedhen.androidbootstrap.BootstrapButton; -public class EditKeyActivity extends ActionBarActivity { +public class EditKeyActivity extends ActionBarActivity implements EditorListener { // Actions for internal use only: public static final String ACTION_CREATE_KEY = Constants.INTENT_PREFIX + "CREATE_KEY"; @@ -106,6 +108,15 @@ public class EditKeyActivity extends ActionBarActivity { ExportHelper mExportHelper; + public void onDeleted(Editor e, boolean wasNewItem) + { + } + + public void onEdited() + { + + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java index b5ac739ae..b39b93f52 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/PreferencesKeyServerActivity.java @@ -91,10 +91,15 @@ public class PreferencesKeyServerActivity extends ActionBarActivity implements O } } - public void onDeleted(Editor editor) { + public void onDeleted(Editor editor, boolean wasNewItem) { // nothing to do } + @Override + public void onEdited() { + + } + public void onClick(View v) { KeyServerEditor view = (KeyServerEditor) mInflater.inflate(R.layout.key_server_editor, mEditors, false); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java index 1cf510d3a..7b21c189d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/Editor.java @@ -18,8 +18,10 @@ package org.sufficientlysecure.keychain.ui.widget; public interface Editor { public interface EditorListener { - public void onDeleted(Editor editor); + public void onDeleted(Editor editor, boolean wasNewItem); + public void onEdited(); } public void setEditorListener(EditorListener listener); + public boolean needsSaving(); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index 2e56c65bc..c941ea908 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -26,7 +26,6 @@ import java.util.Vector; import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSecretKey; -import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.util.Choice; @@ -39,11 +38,9 @@ import android.util.AttributeSet; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; -import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.DatePicker; import android.widget.LinearLayout; -import android.widget.Spinner; import android.widget.TableLayout; import android.widget.TableRow; import android.widget.TextView; @@ -62,11 +59,14 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { TextView mCreationDate; BootstrapButton mExpiryDateButton; GregorianCalendar mExpiryDate; + GregorianCalendar mOriginalExpiryDate = null; CheckBox mChkCertify; CheckBox mChkSign; CheckBox mChkEncrypt; CheckBox mChkAuthenticate; int mUsage; + int mOriginalUsage; + boolean mIsNewKey; private int mDatePickerResultCount = 0; private DatePickerDialog.OnDateSetListener mExpiryDateSetListener = new DatePickerDialog.OnDateSetListener() { @@ -190,10 +190,13 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { mChkSign.setChecked(PgpKeyHelper.isSigningKey(key)); mChkEncrypt.setChecked(PgpKeyHelper.isEncryptionKey(key)); mChkAuthenticate.setChecked(PgpKeyHelper.isAuthenticationKey(key)); + mIsNewKey = isNewKey; if (isNewKey) mUsage = usage; - else + else { mUsage = PgpKeyHelper.getKeyUsage(key); + mOriginalUsage = mUsage; + } GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); cal.setTime(PgpKeyHelper.getCreationDate(key)); @@ -205,6 +208,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { } else { cal.setTime(PgpKeyHelper.getExpiryDate(key)); setExpiryDate(cal); + mOriginalExpiryDate = cal; // TODO: ensure time doesn't matter when selecting the same date as before } } @@ -218,7 +222,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { if (v == mDeleteButton) { parent.removeView(this); if (mEditorListener != null) { - mEditorListener.onDeleted(this); + mEditorListener.onDeleted(this, mIsNewKey); } } } @@ -250,4 +254,27 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { return mUsage; } + public boolean needsSaving() + { + if (mIsNewKey) + return true; + + boolean retval = (getUsage() != mOriginalUsage); + + boolean dateChanged; + boolean mOEDNull = (mOriginalExpiryDate == null); + boolean mEDNull = (mExpiryDate == null); + if (mOEDNull != mEDNull) { + dateChanged = true; + } else { + if(mOEDNull) //both null, no change + dateChanged = false; + else + dateChanged = ((mExpiryDate.compareTo(mOriginalExpiryDate)) != 0); + } + retval |= dateChanged; + + return retval; + } + } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java index 01259ccd1..a75aafc11 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java @@ -68,7 +68,7 @@ public class KeyServerEditor extends LinearLayout implements Editor, OnClickList if (v == mDeleteButton) { parent.removeView(this); if (mEditorListener != null) { - mEditorListener.onDeleted(this); + mEditorListener.onDeleted(this, false); } } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index df69ff60b..19926abd3 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -50,21 +50,27 @@ import android.widget.TextView; import com.beardedhen.androidbootstrap.BootstrapButton; -public class SectionView extends LinearLayout implements OnClickListener, EditorListener { +public class SectionView extends LinearLayout implements OnClickListener, EditorListener, Editor { private LayoutInflater mInflater; private BootstrapButton mPlusButton; private ViewGroup mEditors; private TextView mTitle; private int mType = 0; + private EditorListener mEditorListener = null; private Choice mNewKeyAlgorithmChoice; private int mNewKeySize; private boolean canEdit = true; + private boolean oldItemDeleted = false; private ActionBarActivity mActivity; private ProgressDialogFragment mGeneratingDialog; + public void setEditorListener(EditorListener listener) { + mEditorListener = listener; + } + public SectionView(Context context) { super(context); mActivity = (ActionBarActivity) context; @@ -124,15 +130,34 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor } /** {@inheritDoc} */ - public void onDeleted(Editor editor) { + public void onDeleted(Editor editor, boolean wasNewItem) { + oldItemDeleted |= !wasNewItem; this.updateEditorsVisible(); } + @Override + public void onEdited() { + if (mEditorListener != null) { + mEditorListener.onEdited(); + } + } + protected void updateEditorsVisible() { final boolean hasChildren = mEditors.getChildCount() > 0; mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE); } + public boolean needsSaving() + { + //check each view for needs saving, take account of deleted items + boolean ret = oldItemDeleted; + for (int i = 0; i < mEditors.getChildCount(); ++i) { + Editor editor = (Editor) mEditors.getChildAt(i); + ret |= editor.needsSaving(); + } + return ret; + } + /** {@inheritDoc} */ public void onClick(View v) { if (canEdit) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java index 5428b626e..a56340582 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java @@ -175,7 +175,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene boolean wasMainUserId = mIsMainUserId.isChecked(); parent.removeView(this); if (mEditorListener != null) { - mEditorListener.onDeleted(this); + mEditorListener.onDeleted(this, false); } if (wasMainUserId && parent.getChildCount() > 0) { UserIdEditor editor = (UserIdEditor) parent.getChildAt(0); @@ -204,4 +204,9 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene public void setEditorListener(EditorListener listener) { mEditorListener = listener; } + + @Override + public boolean needsSaving() { + return false; + } } From 262425c6ad7cfa6509dc53150e592b3f6fd366c1 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Wed, 5 Feb 2014 21:36:11 +0000 Subject: [PATCH 010/253] edit ui knows it it has been changed --- .../keychain/ui/widget/SectionView.java | 5 +---- .../keychain/ui/widget/UserIdEditor.java | 22 +++++++++++++++++-- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 19926abd3..bf8a3f96b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -273,10 +273,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor UserIdEditor view = (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item, mEditors, false); view.setEditorListener(this); - view.setValue(userId); - if (mEditors.getChildCount() == 0) { - view.setIsMainUserId(true); - } + view.setValue(userId, mEditors.getChildCount() == 0); view.setCanEdit(canEdit); mEditors.addView(view); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java index a56340582..4cd1392c2 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java @@ -38,8 +38,12 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene private BootstrapButton mDeleteButton; private RadioButton mIsMainUserId; private EditText mName; + private String mOriginalName; private EditText mEmail; + private String mOriginalEmail; private EditText mComment; + private String mOriginalComment; + private boolean mOriginallyMainUserID; // see http://www.regular-expressions.info/email.html // RFC 2822 if we omit the syntax using double quotes and square brackets @@ -108,17 +112,22 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene super.onFinishInflate(); } - public void setValue(String userId) { + public void setValue(String userId, boolean isMainID) { mName.setText(""); mComment.setText(""); mEmail.setText(""); + //TODO: update this file for blank email/name? + Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$"); Matcher matcher = withComment.matcher(userId); if (matcher.matches()) { mName.setText(matcher.group(1)); + mOriginalName = matcher.group(1); mComment.setText(matcher.group(2)); + mOriginalComment = matcher.group(2); mEmail.setText(matcher.group(3)); + mOriginalEmail = matcher.group(3); return; } @@ -126,9 +135,14 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene matcher = withoutComment.matcher(userId); if (matcher.matches()) { mName.setText(matcher.group(1)); + mOriginalName = matcher.group(1); mEmail.setText(matcher.group(2)); + mOriginalEmail = matcher.group(2); + mOriginalComment = ""; return; } + mOriginallyMainUserID = isMainID; + setIsMainUserId(isMainID); } public String getValue() throws NoNameException, NoEmailException, InvalidEmailException { @@ -207,6 +221,10 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene @Override public boolean needsSaving() { - return false; + boolean retval = (mOriginallyMainUserID != isMainUserId()); + retval |= (mOriginalName.equals( ("" + mName.getText()).trim() ) ); + retval |= (mOriginalEmail.equals( ("" + mEmail.getText()).trim() ) ); + retval |= (mOriginalComment.equals( ("" + mComment.getText()).trim() ) ); + return retval; } } From 83514b82c0861b90f33a2b32f9dfc1dd54f2cde4 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Wed, 5 Feb 2014 22:19:07 +0000 Subject: [PATCH 011/253] notify of changes --- .../keychain/ui/widget/KeyEditor.java | 24 +++++++++++++++++++ .../keychain/ui/widget/UserIdEditor.java | 24 ++++++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index c941ea908..0ec1bad92 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -39,6 +39,7 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.CheckBox; +import android.widget.CompoundButton; import android.widget.DatePicker; import android.widget.LinearLayout; import android.widget.TableLayout; @@ -68,6 +69,19 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { int mOriginalUsage; boolean mIsNewKey; + private CheckBox.OnCheckedChangeListener mCheckChanged = new CheckBox.OnCheckedChangeListener() + { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) + { + if (mEditorListener != null) { + mEditorListener.onEdited(); + } + } + + } + + private int mDatePickerResultCount = 0; private DatePickerDialog.OnDateSetListener mExpiryDateSetListener = new DatePickerDialog.OnDateSetListener() { public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { @@ -76,6 +90,9 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { GregorianCalendar date = new GregorianCalendar(TimeZone.getTimeZone("UTC")); date.set(year, monthOfYear, dayOfMonth); setExpiryDate(date); + if (mEditorListener != null) { + mEditorListener.onEdited(); + } } } }; @@ -101,9 +118,13 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { mDeleteButton = (BootstrapButton) findViewById(R.id.delete); mDeleteButton.setOnClickListener(this); mChkCertify = (CheckBox) findViewById(R.id.chkCertify); + mChkCertify.setOnCheckedChangeListener(mCheckChanged); mChkSign = (CheckBox) findViewById(R.id.chkSign); + mChkSign.setOnCheckedChangeListener(mCheckChanged); mChkEncrypt = (CheckBox) findViewById(R.id.chkEncrypt); + mChkEncrypt.setOnCheckedChangeListener(mCheckChanged); mChkAuthenticate = (CheckBox) findViewById(R.id.chkAuthenticate); + mChkAuthenticate.setOnCheckedChangeListener(mCheckChanged); setExpiryDate(null); @@ -126,6 +147,9 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { // Note: Ignore results after the first one - android sends multiples. if (mDatePickerResultCount++ == 0) { setExpiryDate(null); + if (mEditorListener != null) { + mEditorListener.onEdited(); + } } } }); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java index 4cd1392c2..4b550c580 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java @@ -22,6 +22,8 @@ import java.util.regex.Pattern; import org.sufficientlysecure.keychain.R; import android.content.Context; +import android.text.Editable; +import android.text.TextWatcher; import android.util.AttributeSet; import android.view.View; import android.view.View.OnClickListener; @@ -106,6 +108,23 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene mIsMainUserId.setOnClickListener(this); mName = (EditText) findViewById(R.id.name); + mName.addTextChangedListener(new TextWatcher() { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void afterTextChanged(Editable s) + { + if (mEditorListener != null) { + mEditorListener.onEdited(); + } + } + }); mEmail = (EditText) findViewById(R.id.email); mComment = (EditText) findViewById(R.id.comment); @@ -189,7 +208,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene boolean wasMainUserId = mIsMainUserId.isChecked(); parent.removeView(this); if (mEditorListener != null) { - mEditorListener.onDeleted(this, false); + mEditorListener.onDeleted(this, false); //TODO: WAS THIS A NEW ITEM } if (wasMainUserId && parent.getChildCount() > 0) { UserIdEditor editor = (UserIdEditor) parent.getChildAt(0); @@ -204,6 +223,9 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene editor.setIsMainUserId(false); } } + if (mEditorListener != null) { + mEditorListener.onEdited(); + } } } From 603665976a3a75918fb9838361b7194c5def6968 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Thu, 6 Feb 2014 09:00:36 +0000 Subject: [PATCH 012/253] new Ids need to be saved --- .../sufficientlysecure/keychain/ui/widget/SectionView.java | 6 ++---- .../sufficientlysecure/keychain/ui/widget/UserIdEditor.java | 5 ++++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index bf8a3f96b..e0d31fa3f 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -166,9 +166,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor UserIdEditor view = (UserIdEditor) mInflater.inflate( R.layout.edit_key_user_id_item, mEditors, false); view.setEditorListener(this); - if (mEditors.getChildCount() == 0) { - view.setIsMainUserId(true); - } + view.setValue("", mEditors.getChildCount() == 0, true); mEditors.addView(view); break; } @@ -273,7 +271,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor UserIdEditor view = (UserIdEditor) mInflater.inflate(R.layout.edit_key_user_id_item, mEditors, false); view.setEditorListener(this); - view.setValue(userId, mEditors.getChildCount() == 0); + view.setValue(userId, mEditors.getChildCount() == 0, false); view.setCanEdit(canEdit); mEditors.addView(view); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java index 4b550c580..0509e69f2 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java @@ -46,6 +46,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene private EditText mComment; private String mOriginalComment; private boolean mOriginallyMainUserID; + private boolean mIsNewId; // see http://www.regular-expressions.info/email.html // RFC 2822 if we omit the syntax using double quotes and square brackets @@ -131,10 +132,11 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene super.onFinishInflate(); } - public void setValue(String userId, boolean isMainID) { + public void setValue(String userId, boolean isMainID, boolean isNewId) { mName.setText(""); mComment.setText(""); mEmail.setText(""); + mIsNewId = isNewId; //TODO: update this file for blank email/name? @@ -247,6 +249,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene retval |= (mOriginalName.equals( ("" + mName.getText()).trim() ) ); retval |= (mOriginalEmail.equals( ("" + mEmail.getText()).trim() ) ); retval |= (mOriginalComment.equals( ("" + mComment.getText()).trim() ) ); + retval |= mIsNewId; return retval; } } From a5aae930e5e857005563c21fb2c7ca86d6e235da Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Thu, 6 Feb 2014 13:28:59 +0000 Subject: [PATCH 013/253] allow blank names and emails --- .../keychain/ui/widget/UserIdEditor.java | 67 +++++-------------- 1 file changed, 17 insertions(+), 50 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java index 0509e69f2..641b710a9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java @@ -20,6 +20,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import android.content.Context; import android.text.Editable; @@ -56,14 +57,6 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene "[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?", Pattern.CASE_INSENSITIVE); - public static class NoNameException extends Exception { - static final long serialVersionUID = 0xf812773343L; - - public NoNameException(String message) { - super(message); - } - } - public void setCanEdit(boolean bCanEdit) { if (!bCanEdit) { mDeleteButton.setVisibility(View.INVISIBLE); @@ -74,14 +67,6 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene } } - public static class NoEmailException extends Exception { - static final long serialVersionUID = 0xf812773344L; - - public NoEmailException(String message) { - super(message); - } - } - public static class InvalidEmailException extends Exception { static final long serialVersionUID = 0xf812773345L; @@ -133,40 +118,31 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene } public void setValue(String userId, boolean isMainID, boolean isNewId) { + mName.setText(""); mComment.setText(""); mEmail.setText(""); mIsNewId = isNewId; - //TODO: update this file for blank email/name? - - Pattern withComment = Pattern.compile("^(.*) [(](.*)[)] <(.*)>$"); - Matcher matcher = withComment.matcher(userId); - if (matcher.matches()) { - mName.setText(matcher.group(1)); - mOriginalName = matcher.group(1); - mComment.setText(matcher.group(2)); - mOriginalComment = matcher.group(2); - mEmail.setText(matcher.group(3)); - mOriginalEmail = matcher.group(3); - return; + String[] result = PgpKeyHelper.splitUserId(userId); + if (result[0] != null) { + mName.setText(result[0]); + mOriginalName = result[0]; + } + if (result[1] != null) { + mComment.setText(result[1]); + mOriginalComment = result[1]; + } + if (result[2] != null) { + mEmail.setText(result[2]); + mOriginalEmail = result[2]; } - Pattern withoutComment = Pattern.compile("^(.*) <(.*)>$"); - matcher = withoutComment.matcher(userId); - if (matcher.matches()) { - mName.setText(matcher.group(1)); - mOriginalName = matcher.group(1); - mEmail.setText(matcher.group(2)); - mOriginalEmail = matcher.group(2); - mOriginalComment = ""; - return; - } mOriginallyMainUserID = isMainID; setIsMainUserId(isMainID); } - public String getValue() throws NoNameException, NoEmailException, InvalidEmailException { + public String getValue() throws InvalidEmailException { String name = ("" + mName.getText()).trim(); String email = ("" + mEmail.getText()).trim(); String comment = ("" + mComment.getText()).trim(); @@ -191,16 +167,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene // ok, empty one... return userId; } - - // otherwise make sure that name and email exist - if (name.equals("")) { - throw new NoNameException("need a name"); - } - - if (email.equals("")) { - throw new NoEmailException("need an email"); - } - + //TODO: check gpg accepts an entirely empty ID packet. specs say this is allowed return userId; } @@ -210,7 +177,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene boolean wasMainUserId = mIsMainUserId.isChecked(); parent.removeView(this); if (mEditorListener != null) { - mEditorListener.onDeleted(this, false); //TODO: WAS THIS A NEW ITEM + mEditorListener.onDeleted(this, mIsNewId); } if (wasMainUserId && parent.getChildCount() > 0) { UserIdEditor editor = (UserIdEditor) parent.getChildAt(0); From e7ebbc5ef662ecfe4f43ae203731c6d25cc26e0d Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Thu, 6 Feb 2014 13:56:34 +0000 Subject: [PATCH 014/253] final link in chain, fix compile --- .../keychain/ui/EditKeyActivity.java | 12 ++++++------ .../keychain/ui/widget/KeyEditor.java | 3 +-- .../keychain/ui/widget/KeyServerEditor.java | 5 +++++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index b28ed8922..d8489ce65 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -96,6 +96,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener private String mNewPassPhrase = null; private String mSavedNewPassPhrase = null; private boolean mIsPassPhraseSet; + private boolean mNeedsSaving; private BootstrapButton mChangePassPhrase; @@ -114,7 +115,9 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener public void onEdited() { - + mNeedsSaving = mUserIdsView.needsSaving(); + mNeedsSaving |= mKeysView.needsSaving(); + Toast.makeText(this, "Needs saving: " + Boolean.toString(mNeedsSaving), Toast.LENGTH_LONG).show(); } @Override @@ -437,11 +440,13 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener mUserIdsView.setType(Id.type.user_id); mUserIdsView.setCanEdit(masterCanSign); mUserIdsView.setUserIds(mUserIds); + mUserIdsView.setEditorListener(this); container.addView(mUserIdsView); mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); mKeysView.setType(Id.type.key); mKeysView.setCanEdit(masterCanSign); mKeysView.setKeys(mKeys, mKeysUsages); + mKeysView.setEditorListener(this); container.addView(mKeysView); updatePassPhraseButtonText(); @@ -597,11 +602,6 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener String userId = null; try { userId = editor.getValue(); - } catch (UserIdEditor.NoNameException e) { - throw new PgpGeneralException(this.getString(R.string.error_user_id_needs_a_name)); - } catch (UserIdEditor.NoEmailException e) { - throw new PgpGeneralException( - this.getString(R.string.error_user_id_needs_an_email_address)); } catch (UserIdEditor.InvalidEmailException e) { throw new PgpGeneralException(e.getMessage()); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index 0ec1bad92..009b0cb10 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -78,8 +78,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { mEditorListener.onEdited(); } } - - } + }; private int mDatePickerResultCount = 0; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java index a75aafc11..47238cd56 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyServerEditor.java @@ -73,6 +73,11 @@ public class KeyServerEditor extends LinearLayout implements Editor, OnClickList } } + @Override + public boolean needsSaving() { + return false; + } + public void setEditorListener(EditorListener listener) { mEditorListener = listener; } From d6726fe9d6a2efccca394567424342df9a941f2e Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Thu, 6 Feb 2014 14:25:09 +0000 Subject: [PATCH 015/253] some fixes --- .../keychain/ui/widget/UserIdEditor.java | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java index 641b710a9..e3747aeb9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java @@ -83,6 +83,24 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene super(context, attrs); } + TextWatcher mTextWatcher = new TextWatcher() { + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void afterTextChanged(Editable s) + { + if (mEditorListener != null) { + mEditorListener.onEdited(); + } + } + }; + @Override protected void onFinishInflate() { setDrawingCacheEnabled(true); @@ -94,25 +112,11 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene mIsMainUserId.setOnClickListener(this); mName = (EditText) findViewById(R.id.name); - mName.addTextChangedListener(new TextWatcher() { - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } - - @Override - public void afterTextChanged(Editable s) - { - if (mEditorListener != null) { - mEditorListener.onEdited(); - } - } - }); + mName.addTextChangedListener(mTextWatcher); mEmail = (EditText) findViewById(R.id.email); + mEmail.addTextChangedListener(mTextWatcher); mComment = (EditText) findViewById(R.id.comment); + mComment.addTextChangedListener(mTextWatcher); super.onFinishInflate(); } @@ -120,8 +124,11 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene public void setValue(String userId, boolean isMainID, boolean isNewId) { mName.setText(""); + mOriginalName = ""; mComment.setText(""); + mOriginalComment = ""; mEmail.setText(""); + mOriginalEmail = ""; mIsNewId = isNewId; String[] result = PgpKeyHelper.splitUserId(userId); @@ -213,9 +220,9 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene @Override public boolean needsSaving() { boolean retval = (mOriginallyMainUserID != isMainUserId()); - retval |= (mOriginalName.equals( ("" + mName.getText()).trim() ) ); - retval |= (mOriginalEmail.equals( ("" + mEmail.getText()).trim() ) ); - retval |= (mOriginalComment.equals( ("" + mComment.getText()).trim() ) ); + retval |= !(mOriginalName.equals( ("" + mName.getText()).trim() ) ); + retval |= !(mOriginalEmail.equals( ("" + mEmail.getText()).trim() ) ); + retval |= !(mOriginalComment.equals( ("" + mComment.getText()).trim() ) ); retval |= mIsNewId; return retval; } From 53dc044ab4da1e177f4259863cb7fb0266521f6e Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Thu, 6 Feb 2014 14:33:29 +0000 Subject: [PATCH 016/253] pass message when key added --- .../sufficientlysecure/keychain/ui/widget/SectionView.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index e0d31fa3f..072c04f03 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -168,6 +168,9 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor view.setEditorListener(this); view.setValue("", mEditors.getChildCount() == 0, true); mEditors.addView(view); + if (mEditorListener != null) { + mEditorListener.onEdited(); + } break; } @@ -367,5 +370,8 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor view.setValue(newKey, newKey.isMasterKey(), -1, true); mEditors.addView(view); SectionView.this.updateEditorsVisible(); + if (mEditorListener != null) { + mEditorListener.onEdited(); + } } } From 6dbf48275518b6089d4f4a92d20e460d0a2f96ba Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Thu, 6 Feb 2014 15:21:29 +0000 Subject: [PATCH 017/253] passphrase changes update need to save --- .../keychain/ui/EditKeyActivity.java | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index d8489ce65..b3df52726 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -109,15 +109,22 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener ExportHelper mExportHelper; + public void somethingChanged() + { + mNeedsSaving = mUserIdsView.needsSaving(); + mNeedsSaving |= mKeysView.needsSaving(); + mNeedsSaving |= hasPassphraseChanged(); + Toast.makeText(this, "Needs saving: " + Boolean.toString(mNeedsSaving) + "(" + Boolean.toString(mUserIdsView.needsSaving()) + ", " + Boolean.toString(mKeysView.needsSaving()) + ")", Toast.LENGTH_LONG).show(); + } + public void onDeleted(Editor e, boolean wasNewItem) { + somethingChanged(); } public void onEdited() { - mNeedsSaving = mUserIdsView.needsSaving(); - mNeedsSaving |= mKeysView.needsSaving(); - Toast.makeText(this, "Needs saving: " + Boolean.toString(mNeedsSaving), Toast.LENGTH_LONG).show(); + somethingChanged(); } @Override @@ -400,6 +407,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener .getString(SetPassphraseDialogFragment.MESSAGE_NEW_PASSPHRASE); updatePassPhraseButtonText(); + somethingChanged(); } } }; @@ -471,6 +479,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener mNewPassPhrase = mSavedNewPassPhrase; mChangePassPhrase.setVisibility(View.VISIBLE); } + somethingChanged(); } }); } @@ -493,6 +502,15 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener } } + public boolean hasPassphraseChanged() + { + if (mNoPassphrase.isChecked()) { + return mIsPassPhraseSet; + } else { + return (mNewPassPhrase != null && !mNewPassPhrase.equals("")); + } + } + private void saveClicked() { long masterKeyId = getMasterKeyId(); try { From d4d6de1bc5c538c17613f5918e70ee0932637915 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Thu, 6 Feb 2014 20:07:04 +0000 Subject: [PATCH 018/253] select old date doesn't need save --- .../sufficientlysecure/keychain/ui/widget/KeyEditor.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index 009b0cb10..ba10c7603 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -88,7 +88,11 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { if (mDatePickerResultCount++ == 0) { GregorianCalendar date = new GregorianCalendar(TimeZone.getTimeZone("UTC")); date.set(year, monthOfYear, dayOfMonth); - setExpiryDate(date); + long numDays = (date.getTimeInMillis() / 86400000) - (mOriginalExpiryDate.getTimeInMillis() / 86400000); + if (numDays == 0) + setExpiryDate(mOriginalExpiryDate); + else + setExpiryDate(date); if (mEditorListener != null) { mEditorListener.onEdited(); } @@ -231,7 +235,7 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { } else { cal.setTime(PgpKeyHelper.getExpiryDate(key)); setExpiryDate(cal); - mOriginalExpiryDate = cal; // TODO: ensure time doesn't matter when selecting the same date as before + mOriginalExpiryDate = cal; } } From aee4ec6d1f5b41d882dea747915af93cd7d84652 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Thu, 6 Feb 2014 22:16:41 +0000 Subject: [PATCH 019/253] action bar and a small fix --- .../keychain/ui/EditKeyActivity.java | 57 ++++++++++--------- .../keychain/ui/widget/SectionView.java | 3 + .../keychain/ui/widget/UserIdEditor.java | 8 +-- .../src/main/res/menu/key_edit.xml | 6 ++ 4 files changed, 44 insertions(+), 30 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index b3df52726..89af2d683 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -55,6 +55,7 @@ import android.os.Handler; import android.os.Message; import android.os.Messenger; import android.support.v7.app.ActionBarActivity; +import android.support.v4.app.ActivityCompat; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -97,6 +98,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener private String mSavedNewPassPhrase = null; private boolean mIsPassPhraseSet; private boolean mNeedsSaving; + private MenuItem mSaveButton; private BootstrapButton mChangePassPhrase; @@ -109,12 +111,19 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener ExportHelper mExportHelper; - public void somethingChanged() + public boolean needsSaving() { mNeedsSaving = mUserIdsView.needsSaving(); mNeedsSaving |= mKeysView.needsSaving(); mNeedsSaving |= hasPassphraseChanged(); - Toast.makeText(this, "Needs saving: " + Boolean.toString(mNeedsSaving) + "(" + Boolean.toString(mUserIdsView.needsSaving()) + ", " + Boolean.toString(mKeysView.needsSaving()) + ")", Toast.LENGTH_LONG).show(); + return mNeedsSaving; + } + + + public void somethingChanged() + { + ActivityCompat.invalidateOptionsMenu(this); + //Toast.makeText(this, "Needs saving: " + Boolean.toString(mNeedsSaving) + "(" + Boolean.toString(mUserIdsView.needsSaving()) + ", " + Boolean.toString(mKeysView.needsSaving()) + ")", Toast.LENGTH_LONG).show(); } public void onDeleted(Editor e, boolean wasNewItem) @@ -133,6 +142,10 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener mExportHelper = new ExportHelper(this); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setIcon(android.R.color.transparent); + getSupportActionBar().setHomeButtonEnabled(true); + mUserIds = new Vector(); mKeys = new Vector(); mKeysUsages = new Vector(); @@ -153,20 +166,6 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener * @param intent */ private void handleActionCreateKey(Intent intent) { - // Inflate a "Done"/"Cancel" custom action bar - ActionBarHelper.setDoneCancelView(getSupportActionBar(), R.string.btn_save, - new View.OnClickListener() { - @Override - public void onClick(View v) { - saveClicked(); - } - }, R.string.btn_do_not_save, new View.OnClickListener() { - @Override - public void onClick(View v) { - cancelClicked(); - } - }); - Bundle extras = intent.getExtras(); mCurrentPassPhrase = ""; @@ -255,15 +254,6 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener * @param intent */ private void handleActionEditKey(Intent intent) { - // Inflate a "Done"/"Cancel" custom action bar - ActionBarHelper.setDoneView(getSupportActionBar(), R.string.btn_save, - new View.OnClickListener() { - @Override - public void onClick(View v) { - saveClicked(); - } - }); - mDataUri = intent.getData(); if (mDataUri == null) { Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!"); @@ -325,12 +315,17 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.key_edit, menu); + mSaveButton = (MenuItem) menu.findItem(R.id.menu_key_edit_save); + mSaveButton.setEnabled(needsSaving()); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { + case android.R.id.home: + cancelClicked(); + return true; case R.id.menu_key_edit_cancel: cancelClicked(); return true; @@ -353,6 +348,9 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener mExportHelper.deleteKey(mDataUri, Id.type.secret_key, returnHandler); return true; } + case R.id.menu_key_edit_save: + saveClicked(); + return true; } return super.onOptionsItemSelected(item); } @@ -373,8 +371,15 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener Toast.makeText(this, R.string.error_no_secret_key_found, Toast.LENGTH_LONG).show(); } if (masterKey != null) { + boolean isSet = false; for (String userId : new IterableIterator(masterKey.getUserIDs())) { Log.d(Constants.TAG, "Added userId " + userId); + if (!isSet) { + isSet = true; + String[] parts = PgpKeyHelper.splitUserId(userId); + if (parts[0] != null) + setTitle(parts[0]); + } mUserIds.add(userId); } } @@ -465,7 +470,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener } }); - // disable passphrase when no passphrase checkobox is checked! + // disable passphrase when no passphrase checkbox is checked! mNoPassphrase.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 072c04f03..21ebff102 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -133,6 +133,9 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor public void onDeleted(Editor editor, boolean wasNewItem) { oldItemDeleted |= !wasNewItem; this.updateEditorsVisible(); + if (mEditorListener != null) { + mEditorListener.onEdited(); + } } @Override diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java index e3747aeb9..b499a429a 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java @@ -137,12 +137,12 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene mOriginalName = result[0]; } if (result[1] != null) { - mComment.setText(result[1]); - mOriginalComment = result[1]; + mEmail.setText(result[1]); + mOriginalEmail = result[1]; } if (result[2] != null) { - mEmail.setText(result[2]); - mOriginalEmail = result[2]; + mComment.setText(result[2]); + mOriginalComment = result[2]; } mOriginallyMainUserID = isMainID; diff --git a/OpenPGP-Keychain/src/main/res/menu/key_edit.xml b/OpenPGP-Keychain/src/main/res/menu/key_edit.xml index 16992affb..ede1d03c0 100644 --- a/OpenPGP-Keychain/src/main/res/menu/key_edit.xml +++ b/OpenPGP-Keychain/src/main/res/menu/key_edit.xml @@ -2,6 +2,12 @@ + + Date: Thu, 6 Feb 2014 22:35:27 +0000 Subject: [PATCH 020/253] ask for save --- .../keychain/ui/EditKeyActivity.java | 33 +++++++++++++++++-- .../src/main/res/values/strings.xml | 1 + 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 89af2d683..49713db71 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -46,8 +46,10 @@ import org.sufficientlysecure.keychain.ui.widget.Editor.EditorListener; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; +import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; @@ -604,8 +606,35 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener } private void cancelClicked() { - setResult(RESULT_CANCELED); - finish(); + if (mNeedsSaving && masterCanSign) { //ask if we want to save + AlertDialog.Builder alert = new AlertDialog.Builder( + EditKeyActivity.this); + + alert.setIcon(android.R.drawable.ic_dialog_alert); + alert.setTitle(R.string.warning); + alert.setMessage(EditKeyActivity.this.getString(R.string.ask_save_changed_key)); + + alert.setPositiveButton(EditKeyActivity.this.getString(android.R.string.yes), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + saveClicked(); + } + }); + alert.setNegativeButton(this.getString(android.R.string.no), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + setResult(RESULT_CANCELED); + finish(); + } + }); + alert.setCancelable(false); + alert.create().show(); + } else { + setResult(RESULT_CANCELED); + finish(); + } } /** diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index becf2417e..69a941b02 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -204,6 +204,7 @@ Do you really want to delete the key \'%s\'?\nYou can\'t undo this! Do you really want to delete all selected keys?\nYou can\'t undo this! Do you really want to delete the SECRET key \'%s\'?\nYou can\'t undo this! + You have made changes to the keyring, would you like to save it? Successfully added %d key From 899fadb9169e93105f633c0925835cce9c413b24 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Thu, 6 Feb 2014 22:43:59 +0000 Subject: [PATCH 021/253] can change passwords --- .../org/sufficientlysecure/keychain/ui/EditKeyActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 49713db71..694e52f71 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -606,7 +606,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener } private void cancelClicked() { - if (mNeedsSaving && masterCanSign) { //ask if we want to save + if (mNeedsSaving) { //ask if we want to save AlertDialog.Builder alert = new AlertDialog.Builder( EditKeyActivity.this); From 8d1047d05c3a0e1e1310fb1a47dc6d4f80b2c0f4 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Thu, 6 Feb 2014 22:52:22 +0000 Subject: [PATCH 022/253] fix a crash --- .../keychain/ui/widget/KeyEditor.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index ba10c7603..fa220313c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -88,11 +88,15 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { if (mDatePickerResultCount++ == 0) { GregorianCalendar date = new GregorianCalendar(TimeZone.getTimeZone("UTC")); date.set(year, monthOfYear, dayOfMonth); - long numDays = (date.getTimeInMillis() / 86400000) - (mOriginalExpiryDate.getTimeInMillis() / 86400000); - if (numDays == 0) - setExpiryDate(mOriginalExpiryDate); - else + if (mOriginalExpiryDate != null) { + long numDays = (date.getTimeInMillis() / 86400000) - (mOriginalExpiryDate.getTimeInMillis() / 86400000); + if (numDays == 0) + setExpiryDate(mOriginalExpiryDate); + else + setExpiryDate(date); + } else { setExpiryDate(date); + } if (mEditorListener != null) { mEditorListener.onEdited(); } From cd2b493ea8d25796bf666b46597e6b6bd416f64b Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sat, 8 Feb 2014 12:12:17 +0000 Subject: [PATCH 023/253] small extra check --- .../keychain/ui/EditKeyActivity.java | 38 ++++++++++--------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 694e52f71..d53a1f836 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -326,7 +326,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: - cancelClicked(); + cancelClicked(); //TODO: why isn't this triggered on my tablet - one of many ui problems I've had with this device. A code compatibility issue or a Samsung fail? return true; case R.id.menu_key_edit_cancel: cancelClicked(); @@ -520,25 +520,27 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener private void saveClicked() { long masterKeyId = getMasterKeyId(); - try { - if (!isPassphraseSet()) { - throw new PgpGeneralException(this.getString(R.string.set_a_passphrase)); - } + if (needsSaving()) { //make sure, as some versions don't support invalidateOptionsMenu + try { + if (!isPassphraseSet()) { + throw new PgpGeneralException(this.getString(R.string.set_a_passphrase)); + } - String passphrase = null; - if (mIsPassPhraseSet) - passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId); - else - passphrase = ""; - if (passphrase == null) { - showPassphraseDialog(masterKeyId, masterCanSign); - } else { - mCurrentPassPhrase = passphrase; - finallySaveClicked(); + String passphrase = null; + if (mIsPassPhraseSet) + passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId); + else + passphrase = ""; + if (passphrase == null) { + showPassphraseDialog(masterKeyId, masterCanSign); + } else { + mCurrentPassPhrase = passphrase; + finallySaveClicked(); + } + } catch (PgpGeneralException e) { + Toast.makeText(this, getString(R.string.error_message, e.getMessage()), + Toast.LENGTH_SHORT).show(); } - } catch (PgpGeneralException e) { - //Toast.makeText(this, getString(R.string.error_message, e.getMessage()), - // Toast.LENGTH_SHORT).show(); } } From b74ed4643453cd089ec885bb3f56169629f8cacf Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sat, 8 Feb 2014 15:35:44 +0000 Subject: [PATCH 024/253] disable export if unsaved --- .../sufficientlysecure/keychain/ui/EditKeyActivity.java | 8 ++++++-- OpenPGP-Keychain/src/main/res/values/strings.xml | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index d53a1f836..66f23e22e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -332,8 +332,12 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener cancelClicked(); return true; case R.id.menu_key_edit_export_file: - mExportHelper.showExportKeysDialog(mDataUri, Id.type.secret_key, Constants.path.APP_DIR - + "/secexport.asc"); + if (needsSaving()) { + Toast.makeText(this, R.string.error_save_first, Toast.LENGTH_LONG).show(); + } else { + mExportHelper.showExportKeysDialog(mDataUri, Id.type.secret_key, Constants.path.APP_DIR + + "/secexport.asc"); + } return true; case R.id.menu_key_edit_delete: { // Message is received after key is deleted diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index 69a941b02..64c9be32d 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -286,6 +286,7 @@ NFC is not available on your device! Nothing to import! expiry date must come after creation date + please save the keyring first done. From 44ee7137632c2fd8e6d97a8f05a213467ecf1dc6 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sat, 8 Feb 2014 15:51:11 +0000 Subject: [PATCH 025/253] fix create keyring crash --- .../keychain/ui/EditKeyActivity.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 66f23e22e..802d29a44 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -115,8 +115,8 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener public boolean needsSaving() { - mNeedsSaving = mUserIdsView.needsSaving(); - mNeedsSaving |= mKeysView.needsSaving(); + mNeedsSaving = (mUserIdsView == null) ? false : mUserIdsView.needsSaving(); + mNeedsSaving |= (mKeysView == null) ? false : mKeysView.needsSaving(); mNeedsSaving |= hasPassphraseChanged(); return mNeedsSaving; } @@ -515,10 +515,14 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener public boolean hasPassphraseChanged() { - if (mNoPassphrase.isChecked()) { - return mIsPassPhraseSet; - } else { - return (mNewPassPhrase != null && !mNewPassPhrase.equals("")); + if (mNoPassphrase != null) { + if (mNoPassphrase.isChecked()) { + return mIsPassPhraseSet; + } else { + return (mNewPassPhrase != null && !mNewPassPhrase.equals("")); + } + }else { + return false; } } From 5def251e62d383f023c62fea1734977e11b0fa27 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sat, 8 Feb 2014 15:59:44 +0000 Subject: [PATCH 026/253] keep track of brand new keys, they need saving --- .../org/sufficientlysecure/keychain/ui/EditKeyActivity.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 802d29a44..9ddfadef1 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -100,6 +100,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener private String mSavedNewPassPhrase = null; private boolean mIsPassPhraseSet; private boolean mNeedsSaving; + private boolean mIsBrandNewKeyring = false; private MenuItem mSaveButton; private BootstrapButton mChangePassPhrase; @@ -118,6 +119,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener mNeedsSaving = (mUserIdsView == null) ? false : mUserIdsView.needsSaving(); mNeedsSaving |= (mKeysView == null) ? false : mKeysView.needsSaving(); mNeedsSaving |= hasPassphraseChanged(); + mNeedsSaving |= mIsBrandNewKeyring; return mNeedsSaving; } @@ -171,6 +173,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener Bundle extras = intent.getExtras(); mCurrentPassPhrase = ""; + mIsBrandNewKeyring = true; if (extras != null) { // if userId is given, prefill the fields @@ -616,7 +619,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener } private void cancelClicked() { - if (mNeedsSaving) { //ask if we want to save + if (needsSaving()) { //ask if we want to save AlertDialog.Builder alert = new AlertDialog.Builder( EditKeyActivity.this); From 33f41e66a0a39ab7b8eb48c1055ba16ce8d784f5 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sat, 8 Feb 2014 16:18:30 +0000 Subject: [PATCH 027/253] consistent ui --- .../keychain/ui/EditKeyActivity.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 9ddfadef1..a0c2871ae 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -306,22 +306,19 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener } } - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - // show menu only on edit - if (mDataUri != null) { - return super.onPrepareOptionsMenu(menu); - } else { - return false; - } - } - @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.key_edit, menu); mSaveButton = (MenuItem) menu.findItem(R.id.menu_key_edit_save); mSaveButton.setEnabled(needsSaving()); + //totally get rid of some actions for new keys + if (mDataUri == null) { + MenuItem mButton = (MenuItem) menu.findItem(R.id.menu_key_edit_export_file); + mButton.setVisible(false); + mButton = (MenuItem) menu.findItem(R.id.menu_key_edit_delete); + mButton.setVisible(false); + } return true; } From adc6a3ea64c00f35e881e856d2877c2ea88c7e51 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sat, 8 Feb 2014 16:33:37 +0000 Subject: [PATCH 028/253] some AS Inspect Code fixes --- .../keychain/ui/EditKeyActivity.java | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index a0c2871ae..d12a5479d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -26,7 +26,6 @@ import org.spongycastle.openpgp.PGPSecretKeyRing; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.ActionBarHelper; import org.sufficientlysecure.keychain.helper.ExportHelper; import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; @@ -218,10 +217,10 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { // get new key from data bundle returned from service Bundle data = message.getData(); - PGPSecretKey masterKey = (PGPSecretKey) PgpConversionHelper + PGPSecretKey masterKey = PgpConversionHelper .BytesToPGPSecretKey(data .getByteArray(KeychainIntentService.RESULT_NEW_KEY)); - PGPSecretKey subKey = (PGPSecretKey) PgpConversionHelper + PGPSecretKey subKey = PgpConversionHelper .BytesToPGPSecretKey(data .getByteArray(KeychainIntentService.RESULT_NEW_KEY2)); @@ -235,7 +234,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener buildLayout(); } - }; + } }; // Create a new Messenger for the communication back @@ -263,7 +262,6 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener if (mDataUri == null) { Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!"); finish(); - return; } else { Log.d(Constants.TAG, "uri: " + mDataUri); @@ -273,19 +271,18 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener long masterKeyId = ProviderHelper.getSecretMasterKeyId(this, keyRingRowId); masterCanSign = ProviderHelper.getSecretMasterKeyCanCertify(this, keyRingRowId); - finallyEdit(masterKeyId, masterCanSign); + finallyEdit(masterKeyId); } } - private void showPassphraseDialog(final long masterKeyId, final boolean masterCanSign) { + private void showPassphraseDialog(final long masterKeyId) { // Message is received after passphrase is cached Handler returnHandler = new Handler() { @Override public void handleMessage(Message message) { if (message.what == PassphraseDialogFragment.MESSAGE_OKAY) { - String passPhrase = PassphraseCacheService.getCachedPassphrase( + mCurrentPassPhrase = PassphraseCacheService.getCachedPassphrase( EditKeyActivity.this, masterKeyId); - mCurrentPassPhrase = passPhrase; finallySaveClicked(); } } @@ -310,13 +307,13 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); getMenuInflater().inflate(R.menu.key_edit, menu); - mSaveButton = (MenuItem) menu.findItem(R.id.menu_key_edit_save); + mSaveButton = menu.findItem(R.id.menu_key_edit_save); mSaveButton.setEnabled(needsSaving()); //totally get rid of some actions for new keys if (mDataUri == null) { - MenuItem mButton = (MenuItem) menu.findItem(R.id.menu_key_edit_export_file); + MenuItem mButton = menu.findItem(R.id.menu_key_edit_export_file); mButton.setVisible(false); - mButton = (MenuItem) menu.findItem(R.id.menu_key_edit_delete); + mButton = menu.findItem(R.id.menu_key_edit_delete); mButton.setVisible(false); } return true; @@ -362,7 +359,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener } @SuppressWarnings("unchecked") - private void finallyEdit(final long masterKeyId, final boolean masterCanSign) { + private void finallyEdit(final long masterKeyId) { if (masterKeyId != 0) { PGPSecretKey masterKey = null; mKeyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId(this, masterKeyId); @@ -427,7 +424,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener Messenger messenger = new Messenger(returnHandler); // set title based on isPassphraseSet() - int title = -1; + int title; if (isPassphraseSet()) { title = R.string.title_change_pass_phrase; } else { @@ -534,13 +531,13 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener throw new PgpGeneralException(this.getString(R.string.set_a_passphrase)); } - String passphrase = null; + String passphrase; if (mIsPassPhraseSet) passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId); else passphrase = ""; if (passphrase == null) { - showPassphraseDialog(masterKeyId, masterCanSign); + showPassphraseDialog(masterKeyId); } else { mCurrentPassPhrase = passphrase; finallySaveClicked(); @@ -598,7 +595,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener setResult(RESULT_OK, data); finish(); } - }; + } }; // Create a new Messenger for the communication back @@ -661,7 +658,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener boolean gotMainUserId = false; for (int i = 0; i < userIdEditors.getChildCount(); ++i) { UserIdEditor editor = (UserIdEditor) userIdEditors.getChildAt(i); - String userId = null; + String userId; try { userId = editor.getValue(); } catch (UserIdEditor.InvalidEmailException e) { From 75640934a080d21113f501ed604b85de3eab6b51 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sat, 8 Feb 2014 21:04:38 +0000 Subject: [PATCH 029/253] saving fixes --- .../keychain/ui/EditKeyActivity.java | 26 +++++++++---------- .../keychain/ui/widget/KeyEditor.java | 20 +++++++++----- .../keychain/ui/widget/SectionView.java | 10 ++++--- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index d12a5479d..132141bc5 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.GregorianCalendar; import java.util.Vector; +import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKeyRing; import org.sufficientlysecure.keychain.Constants; @@ -224,15 +225,17 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener .BytesToPGPSecretKey(data .getByteArray(KeychainIntentService.RESULT_NEW_KEY2)); + //We must set the key flags here as they are not set when we make the + //key pair. Because we are not generating hashed packets there... // add master key mKeys.add(masterKey); - mKeysUsages.add(Id.choice.usage.sign_only); //TODO: get from key flags + mKeysUsages.add(KeyFlags.CERTIFY_OTHER); // add sub key mKeys.add(subKey); - mKeysUsages.add(Id.choice.usage.encrypt_only); //TODO: get from key flags + mKeysUsages.add(KeyFlags.ENCRYPT_COMMS + KeyFlags.ENCRYPT_STORAGE); - buildLayout(); + buildLayout(true); } } }; @@ -248,7 +251,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener } } } else { - buildLayout(); + buildLayout(false); } } @@ -390,7 +393,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener mCurrentPassPhrase = ""; - buildLayout(); + buildLayout(false); mIsPassPhraseSet = PassphraseCacheService.hasPassphrase(this, masterKeyId); if (!mIsPassPhraseSet) { // check "no passphrase" checkbox and remove button @@ -440,8 +443,9 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener /** * Build layout based on mUserId, mKeys and mKeysUsages Vectors. It creates Views for every user * id and key. + * @param newKeys */ - private void buildLayout() { + private void buildLayout(boolean newKeys) { setContentView(R.layout.edit_key_activity); // find views @@ -461,7 +465,7 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener mKeysView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); mKeysView.setType(Id.type.key); mKeysView.setCanEdit(masterCanSign); - mKeysView.setKeys(mKeys, mKeysUsages); + mKeysView.setKeys(mKeys, mKeysUsages, newKeys); mKeysView.setEditorListener(this); container.addView(mKeysView); @@ -607,8 +611,8 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener // start service with intent startService(intent); } catch (PgpGeneralException e) { - //Toast.makeText(this, getString(R.string.error_message, e.getMessage()), - // Toast.LENGTH_SHORT).show(); + Toast.makeText(this, getString(R.string.error_message, e.getMessage()), + Toast.LENGTH_SHORT).show(); } } @@ -665,10 +669,6 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener throw new PgpGeneralException(e.getMessage()); } - if (userId.equals("")) { - continue; - } - if (editor.isMainUserId()) { userIds.add(0, userId); gotMainUserId = true; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index fa220313c..5ce5d89d7 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -24,6 +24,7 @@ import java.util.TimeZone; import java.util.Vector; import org.spongycastle.bcpg.sig.KeyFlags; +import org.spongycastle.openpgp.PGPKeyFlags; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSecretKey; import org.sufficientlysecure.keychain.R; @@ -216,17 +217,22 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { } int selectId = 0; - if (key.isMasterKey()) - mChkCertify.setChecked(PgpKeyHelper.isCertificationKey(key)); - mChkSign.setChecked(PgpKeyHelper.isSigningKey(key)); - mChkEncrypt.setChecked(PgpKeyHelper.isEncryptionKey(key)); - mChkAuthenticate.setChecked(PgpKeyHelper.isAuthenticationKey(key)); mIsNewKey = isNewKey; - if (isNewKey) + if (isNewKey) { mUsage = usage; - else { + mChkCertify.setChecked((usage &= KeyFlags.CERTIFY_OTHER) == KeyFlags.CERTIFY_OTHER); + mChkSign.setChecked((usage &= KeyFlags.SIGN_DATA) == KeyFlags.SIGN_DATA); + mChkEncrypt.setChecked(((usage &= KeyFlags.ENCRYPT_COMMS) == KeyFlags.ENCRYPT_COMMS) || + ((usage &= KeyFlags.ENCRYPT_STORAGE) == KeyFlags.ENCRYPT_STORAGE)); + mChkAuthenticate.setChecked((usage &= KeyFlags.AUTHENTICATION) == KeyFlags.AUTHENTICATION); + } else { mUsage = PgpKeyHelper.getKeyUsage(key); mOriginalUsage = mUsage; + if (key.isMasterKey()) + mChkCertify.setChecked(PgpKeyHelper.isCertificationKey(key)); + mChkSign.setChecked(PgpKeyHelper.isSigningKey(key)); + mChkEncrypt.setChecked(PgpKeyHelper.isEncryptionKey(key)); + mChkAuthenticate.setChecked(PgpKeyHelper.isAuthenticationKey(key)); } GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC")); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 21ebff102..315a8faba 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -18,6 +18,7 @@ package org.sufficientlysecure.keychain.ui.widget; import java.util.Vector; +import org.spongycastle.openpgp.PGPKeyFlags; import org.spongycastle.openpgp.PGPSecretKey; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; @@ -285,7 +286,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor this.updateEditorsVisible(); } - public void setKeys(Vector list, Vector usages) { + public void setKeys(Vector list, Vector usages, boolean newKeys) { if (mType != Id.type.key) { return; } @@ -298,7 +299,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor false); view.setEditorListener(this); boolean isMasterKey = (mEditors.getChildCount() == 0); - view.setValue(list.get(i), isMasterKey, usages.get(i), false); + view.setValue(list.get(i), isMasterKey, usages.get(i), newKeys); view.setCanEdit(canEdit); mEditors.addView(view); } @@ -370,7 +371,10 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor KeyEditor view = (KeyEditor) mInflater.inflate(R.layout.edit_key_key_item, mEditors, false); view.setEditorListener(SectionView.this); - view.setValue(newKey, newKey.isMasterKey(), -1, true); + int usage = 0; + if (mEditors.getChildCount() == 0) + usage = PGPKeyFlags.CAN_CERTIFY; + view.setValue(newKey, newKey.isMasterKey(), usage, true); mEditors.addView(view); SectionView.this.updateEditorsVisible(); if (mEditorListener != null) { From 5bd0a2ebc8d4738d53d5ba3e112069ca6a0d049a Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sat, 8 Feb 2014 21:38:56 +0000 Subject: [PATCH 030/253] fix Android Studio analysis issues --- .../keychain/pgp/PgpKeyOperation.java | 54 ++++++------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index d2793a859..7ab5df613 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -23,6 +23,7 @@ import java.security.InvalidAlgorithmParameterException; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; +import java.security.PublicKey; import java.security.SecureRandom; import java.security.SignatureException; import java.util.ArrayList; @@ -56,6 +57,7 @@ import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; import org.spongycastle.openpgp.operator.PGPDigestCalculator; import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyConverter; import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; @@ -71,8 +73,8 @@ import org.sufficientlysecure.keychain.util.ProgressDialogUpdater; import android.content.Context; public class PgpKeyOperation { - private Context mContext; - private ProgressDialogUpdater mProgress; + private final Context mContext; + private final ProgressDialogUpdater mProgress; private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[] { SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192, @@ -90,34 +92,18 @@ public class PgpKeyOperation { this.mProgress = progress; } - public void updateProgress(int message, int current, int total) { + void updateProgress(int message, int current, int total) { if (mProgress != null) { mProgress.setProgress(message, current, total); } } - public void updateProgress(int current, int total) { + void updateProgress(int current, int total) { if (mProgress != null) { mProgress.setProgress(current, total); } } - /** - * Creates new secret key. - * - * @param algorithmChoice - * @param keySize - * @param passPhrase - * @param isMasterKey - * @return - * @throws NoSuchAlgorithmException - * @throws PGPException - * @throws NoSuchProviderException - * @throws PgpGeneralException - * @throws InvalidAlgorithmParameterException - */ - - // TODO: key flags? public PGPSecretKey createKey(int algorithmChoice, int keySize, String passPhrase, boolean isMasterKey) throws NoSuchAlgorithmException, PGPException, NoSuchProviderException, PgpGeneralException, InvalidAlgorithmParameterException { @@ -130,8 +116,8 @@ public class PgpKeyOperation { passPhrase = ""; } - int algorithm = 0; - KeyPairGenerator keyGen = null; + int algorithm; + KeyPairGenerator keyGen; switch (algorithmChoice) { case Id.choice.algorithm.dsa: { @@ -183,15 +169,13 @@ public class PgpKeyOperation { PGPEncryptedData.CAST5, sha1Calc) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passPhrase.toCharArray()); - PGPSecretKey secKey = new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(), + return new PGPSecretKey(keyPair.getPrivateKey(), keyPair.getPublicKey(), sha1Calc, isMasterKey, keyEncryptor); - return secKey; } public void changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase, - String newPassPhrase) throws IOException, PGPException, PGPException, - NoSuchProviderException { + String newPassPhrase) throws IOException, PGPException { updateProgress(R.string.progress_building_key, 0, 100); if (oldPassPhrase == null) { @@ -219,9 +203,8 @@ public class PgpKeyOperation { public void buildSecretKey(ArrayList userIds, ArrayList keys, ArrayList keysUsages, ArrayList keysExpiryDates, - long masterKeyId, String oldPassPhrase, - String newPassPhrase) throws PgpGeneralException, NoSuchProviderException, - PGPException, NoSuchAlgorithmException, SignatureException, IOException { + String oldPassPhrase, String newPassPhrase) throws PgpGeneralException, + PGPException, SignatureException, IOException { Log.d(Constants.TAG, "userIds: " + userIds.toString()); @@ -237,17 +220,16 @@ public class PgpKeyOperation { updateProgress(R.string.progress_preparing_master_key, 10, 100); int usageId = keysUsages.get(0); - boolean canSign = (usageId & KeyFlags.SIGN_DATA) > 0; - boolean canEncrypt = (usageId & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) > 0; - + boolean canSign; String mainUserId = userIds.get(0); PGPSecretKey masterKey = keys.get(0); // this removes all userIds and certifications previously attached to the masterPublicKey PGPPublicKey tmpKey = masterKey.getPublicKey(); - PGPPublicKey masterPublicKey = new PGPPublicKey(tmpKey.getAlgorithm(), - tmpKey.getKey(new BouncyCastleProvider()), tmpKey.getCreationTime()); + PublicKey tmpPuK = new JcaPGPKeyConverter().setProvider(new BouncyCastleProvider()).getPublicKey(tmpKey); + PGPPublicKey masterPublicKey = new JcaPGPKeyConverter().getPGPPublicKey(tmpKey.getAlgorithm(), + tmpPuK, tmpKey.getCreationTime()); // already done by code above: // PGPPublicKey masterPublicKey = masterKey.getPublicKey(); @@ -347,7 +329,6 @@ public class PgpKeyOperation { usageId = keysUsages.get(i); canSign = (usageId & KeyFlags.SIGN_DATA) > 0; //todo - separate function for this - canEncrypt = (usageId & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) > 0; if (canSign) { Date todayDate = new Date(); //both sig times the same // cross-certify signing keys @@ -396,8 +377,7 @@ public class PgpKeyOperation { } public PGPPublicKeyRing signKey(long masterKeyId, long pubKeyId, String passphrase) - throws PgpGeneralException, NoSuchAlgorithmException, NoSuchProviderException, - PGPException, SignatureException { + throws PgpGeneralException, PGPException, SignatureException { if (passphrase == null) { throw new PgpGeneralException("Unable to obtain passphrase"); } else { From e150004e32d4f4278950dc2b24a9cf70885c468f Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sat, 8 Feb 2014 21:42:04 +0000 Subject: [PATCH 031/253] should have used refactor... --- .../keychain/service/KeychainIntentService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 657d40a5a..a31ddc53e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -542,7 +542,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId), oldPassPhrase, newPassPhrase); } else { - keyOperations.buildSecretKey(userIds, keys, keysUsages, keysExpiryDates, masterKeyId, + keyOperations.buildSecretKey(userIds, keys, keysUsages, keysExpiryDates, oldPassPhrase, newPassPhrase); } PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase); From 076a7ec4a16a6db5df0bbe5490d44cbd514f10c1 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Thu, 13 Feb 2014 22:05:36 +0000 Subject: [PATCH 032/253] save work, this code doesn't work... --- .../keychain/pgp/PgpKeyOperation.java | 34 +++++++++---------- .../service/KeychainIntentService.java | 11 ++++-- .../keychain/ui/EditKeyActivity.java | 15 ++++++++ .../keychain/ui/widget/SectionView.java | 19 +++++++++++ 4 files changed, 58 insertions(+), 21 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 7ab5df613..505d3ba55 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -23,7 +23,6 @@ import java.security.InvalidAlgorithmParameterException; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; -import java.security.PublicKey; import java.security.SecureRandom; import java.security.SignatureException; import java.util.ArrayList; @@ -35,7 +34,6 @@ import org.spongycastle.bcpg.CompressionAlgorithmTags; import org.spongycastle.bcpg.HashAlgorithmTags; import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags; import org.spongycastle.bcpg.sig.KeyFlags; -import org.spongycastle.jce.provider.BouncyCastleProvider; import org.spongycastle.jce.spec.ElGamalParameterSpec; import org.spongycastle.openpgp.PGPEncryptedData; import org.spongycastle.openpgp.PGPException; @@ -57,7 +55,6 @@ import org.spongycastle.openpgp.operator.PGPContentSignerBuilder; import org.spongycastle.openpgp.operator.PGPDigestCalculator; import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; -import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyConverter; import org.spongycastle.openpgp.operator.jcajce.JcaPGPKeyPair; import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; @@ -201,9 +198,7 @@ public class PgpKeyOperation { } - public void buildSecretKey(ArrayList userIds, ArrayList keys, - ArrayList keysUsages, ArrayList keysExpiryDates, - String oldPassPhrase, String newPassPhrase) throws PgpGeneralException, + public void buildSecretKey(ArrayList userIds, ArrayList OriginalIDs, ArrayList deletedIDs, ArrayList keys, boolean[] modded_keys, String newPassPhrase, ArrayList keysExpiryDates, String oldPassPhrase, ArrayList keysUsages) throws PgpGeneralException, PGPException, SignatureException, IOException { Log.d(Constants.TAG, "userIds: " + userIds.toString()); @@ -226,10 +221,7 @@ public class PgpKeyOperation { PGPSecretKey masterKey = keys.get(0); // this removes all userIds and certifications previously attached to the masterPublicKey - PGPPublicKey tmpKey = masterKey.getPublicKey(); - PublicKey tmpPuK = new JcaPGPKeyConverter().setProvider(new BouncyCastleProvider()).getPublicKey(tmpKey); - PGPPublicKey masterPublicKey = new JcaPGPKeyConverter().getPGPPublicKey(tmpKey.getAlgorithm(), - tmpPuK, tmpKey.getCreationTime()); + PGPPublicKey masterPublicKey = masterKey.getPublicKey(); // already done by code above: // PGPPublicKey masterPublicKey = masterKey.getPublicKey(); @@ -243,6 +235,8 @@ public class PgpKeyOperation { // masterPublicKey = masterPublicKeyRmCert; // } + + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor); @@ -250,18 +244,22 @@ public class PgpKeyOperation { updateProgress(R.string.progress_certifying_master_key, 20, 100); // TODO: if we are editing a key, keep old certs, don't remake certs we don't have to. - + int user_id_index = 0; for (String userId : userIds) { - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); + if (OriginalIDs[user_id_index]) { + PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( + masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); + sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); - PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); + PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); - masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification); + masterPublicKey = PGPPublicKey.removeCertification(); + masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification); + } + user_id_index++; } PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index a31ddc53e..4cace2658 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -54,7 +54,6 @@ import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry; import org.sufficientlysecure.keychain.util.HkpKeyServer; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; -import org.sufficientlysecure.keychain.util.PositionAwareInputStream; import org.sufficientlysecure.keychain.util.ProgressDialogUpdater; import android.app.IntentService; @@ -135,6 +134,9 @@ public class KeychainIntentService extends IntentService implements ProgressDial public static final String SAVE_KEYRING_KEYS_EXPIRY_DATES = "keys_expiry_dates"; public static final String SAVE_KEYRING_MASTER_KEY_ID = "master_key_id"; public static final String SAVE_KEYRING_CAN_SIGN = "can_sign"; + public static final String SAVE_KEYRING_ORIGINAL_IDS = "original_ids"; + public static final String SAVE_KEYRING_DELETED_IDS = "deleted_ids"; + public static final String SAVE_KEYRING_MODDED_KEYS = "modified_keys"; // generate key public static final String GENERATE_KEY_ALGORITHM = "algorithm"; @@ -532,6 +534,9 @@ public class KeychainIntentService extends IntentService implements ProgressDial .getByteArray(SAVE_KEYRING_KEYS)); ArrayList keysUsages = data.getIntegerArrayList(SAVE_KEYRING_KEYS_USAGES); ArrayList keysExpiryDates = (ArrayList) data.getSerializable(SAVE_KEYRING_KEYS_EXPIRY_DATES); + ArrayList original_ids = data.getStringArrayList(SAVE_KEYRING_ORIGINAL_IDS); + ArrayList deleted_ids = data.getStringArrayList(SAVE_KEYRING_DELETED_IDS); + boolean[] modded_keys = data.getBooleanArray(SAVE_KEYRING_MODDED_KEYS); long masterKeyId = data.getLong(SAVE_KEYRING_MASTER_KEY_ID); @@ -542,8 +547,8 @@ public class KeychainIntentService extends IntentService implements ProgressDial ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId), oldPassPhrase, newPassPhrase); } else { - keyOperations.buildSecretKey(userIds, keys, keysUsages, keysExpiryDates, - oldPassPhrase, newPassPhrase); + keyOperations.buildSecretKey(userIds, original_ids, deleted_ids, keys, modded_keys, + newPassPhrase, keysExpiryDates, oldPassPhrase, keysUsages); } PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 132141bc5..5adb65342 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui; import java.util.ArrayList; import java.util.GregorianCalendar; +import java.util.List; import java.util.Vector; import org.spongycastle.bcpg.sig.KeyFlags; @@ -553,6 +554,15 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener } } + private boolean[] toPrimitiveArray(final List booleanList) { + final boolean[] primitives = new boolean[booleanList.size()]; + int index = 0; + for (Boolean object : booleanList) { + primitives[index++] = object; + } + return primitives; + } + private void finallySaveClicked() { try { // Send all information needed to service to edit key in other thread @@ -576,6 +586,11 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener getKeysExpiryDates(mKeysView)); data.putLong(KeychainIntentService.SAVE_KEYRING_MASTER_KEY_ID, getMasterKeyId()); data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, masterCanSign); + data.putStringArrayList(KeychainIntentService.SAVE_KEYRING_ORIGINAL_IDS, ); + data.putBooleanArray(KeychainIntentService.SAVE_KEYRING_ORIGINAL_IDS, + toPrimitiveArray(mUserIdsView.getNeedsSavingArray())); + data.putBooleanArray(KeychainIntentService.SAVE_KEYRING_MODDED_KEYS, + toPrimitiveArray(mKeysView.getNeedsSavingArray())); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 315a8faba..5eaf54841 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -16,6 +16,8 @@ package org.sufficientlysecure.keychain.ui.widget; +import java.util.ArrayList; +import java.util.List; import java.util.Vector; import org.spongycastle.openpgp.PGPKeyFlags; @@ -162,6 +164,23 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor return ret; } + public List getNeedsSavingArray() + { + ArrayList mList = new ArrayList(); + for (int i = 0; i < mEditors.getChildCount(); ++i) { + Editor editor = (Editor) mEditors.getChildAt(i); + if (mType == Id.type.user_id) { + try { + if (((UserIdEditor)editor).getValue().equals("")) //other code ignores empty user id + continue; + } catch (UserIdEditor.InvalidEmailException e) { + } + } + mList.add(editor.needsSaving()); + } + return mList; + } + /** {@inheritDoc} */ public void onClick(View v) { if (canEdit) { From ba62d30563ee5dd316528decd272f1bbdcbee5bd Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Fri, 14 Feb 2014 17:40:11 +0000 Subject: [PATCH 033/253] propgate through --- .../keychain/ui/EditKeyActivity.java | 7 +++-- .../keychain/ui/widget/SectionView.java | 29 ++++++++++++++----- .../keychain/ui/widget/UserIdEditor.java | 7 +++++ 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 5adb65342..9ebe73197 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -586,9 +586,10 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener getKeysExpiryDates(mKeysView)); data.putLong(KeychainIntentService.SAVE_KEYRING_MASTER_KEY_ID, getMasterKeyId()); data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, masterCanSign); - data.putStringArrayList(KeychainIntentService.SAVE_KEYRING_ORIGINAL_IDS, ); - data.putBooleanArray(KeychainIntentService.SAVE_KEYRING_ORIGINAL_IDS, - toPrimitiveArray(mUserIdsView.getNeedsSavingArray())); + data.putStringArrayList(KeychainIntentService.SAVE_KEYRING_DELETED_IDS, + mUserIdsView.getDeletedIDs()); + data.putStringArrayList(KeychainIntentService.SAVE_KEYRING_ORIGINAL_IDS, + mUserIdsView.getOriginalIDs()); data.putBooleanArray(KeychainIntentService.SAVE_KEYRING_MODDED_KEYS, toPrimitiveArray(mKeysView.getNeedsSavingArray())); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 5eaf54841..788a80a60 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -65,6 +65,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor private int mNewKeySize; private boolean canEdit = true; private boolean oldItemDeleted = false; + private ArrayList mDeletedIDs = new ArrayList(); private ActionBarActivity mActivity; @@ -135,6 +136,8 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor /** {@inheritDoc} */ public void onDeleted(Editor editor, boolean wasNewItem) { oldItemDeleted |= !wasNewItem; + if (oldItemDeleted && mType == Id.type.user_id) + mDeletedIDs.add(((UserIdEditor)editor).getOriginalID()); this.updateEditorsVisible(); if (mEditorListener != null) { mEditorListener.onEdited(); @@ -164,18 +167,30 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor return ret; } + public ArrayList getOriginalIDs() + { + ArrayList orig = new ArrayList(); + if (mType == Id.type.user_id) { + for (int i = 0; i < mEditors.getChildCount(); ++i) { + UserIdEditor editor = (UserIdEditor) mEditors.getChildAt(i); + orig.add(editor.getOriginalID()); + } + return orig; + } else { + return null; + } + } + + public ArrayList getDeletedIDs() + { + return mDeletedIDs; + } + public List getNeedsSavingArray() { ArrayList mList = new ArrayList(); for (int i = 0; i < mEditors.getChildCount(); ++i) { Editor editor = (Editor) mEditors.getChildAt(i); - if (mType == Id.type.user_id) { - try { - if (((UserIdEditor)editor).getValue().equals("")) //other code ignores empty user id - continue; - } catch (UserIdEditor.InvalidEmailException e) { - } - } mList.add(editor.needsSaving()); } return mList; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java index b499a429a..37ab0e051 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java @@ -40,6 +40,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene private BootstrapButton mDeleteButton; private RadioButton mIsMainUserId; + private String mOriginalID; private EditText mName; private String mOriginalName; private EditText mEmail; @@ -130,6 +131,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene mEmail.setText(""); mOriginalEmail = ""; mIsNewId = isNewId; + mOriginalID = userId; String[] result = PgpKeyHelper.splitUserId(userId); if (result[0] != null) { @@ -226,4 +228,9 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene retval |= mIsNewId; return retval; } + + public String getOriginalID() + { + return mOriginalID; + } } From c9d9c800b609d4bc9ed4b9f20a4085d4ad3f13bc Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sat, 15 Feb 2014 14:45:57 +0000 Subject: [PATCH 034/253] pass through deleted keys --- .../keychain/pgp/PgpKeyOperation.java | 2 +- .../keychain/service/KeychainIntentService.java | 5 ++++- .../keychain/ui/EditKeyActivity.java | 3 +++ .../keychain/ui/widget/SectionView.java | 15 +++++++++++++-- 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 505d3ba55..c57718540 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -198,7 +198,7 @@ public class PgpKeyOperation { } - public void buildSecretKey(ArrayList userIds, ArrayList OriginalIDs, ArrayList deletedIDs, ArrayList keys, boolean[] modded_keys, String newPassPhrase, ArrayList keysExpiryDates, String oldPassPhrase, ArrayList keysUsages) throws PgpGeneralException, + public void buildSecretKey(ArrayList userIds, ArrayList OriginalIDs, ArrayList deletedIDs, ArrayList keys, boolean[] modded_keys, ArrayList deleted_keys, ArrayList keysExpiryDates, ArrayList keysUsages, String newPassPhrase, String oldPassPhrase) throws PgpGeneralException, PGPException, SignatureException, IOException { Log.d(Constants.TAG, "userIds: " + userIds.toString()); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 4cace2658..5e5735c88 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -137,6 +137,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial public static final String SAVE_KEYRING_ORIGINAL_IDS = "original_ids"; public static final String SAVE_KEYRING_DELETED_IDS = "deleted_ids"; public static final String SAVE_KEYRING_MODDED_KEYS = "modified_keys"; + public static final String SAVE_KEYRING_DELETED_KEYS = "deleted_keys"; // generate key public static final String GENERATE_KEY_ALGORITHM = "algorithm"; @@ -537,6 +538,8 @@ public class KeychainIntentService extends IntentService implements ProgressDial ArrayList original_ids = data.getStringArrayList(SAVE_KEYRING_ORIGINAL_IDS); ArrayList deleted_ids = data.getStringArrayList(SAVE_KEYRING_DELETED_IDS); boolean[] modded_keys = data.getBooleanArray(SAVE_KEYRING_MODDED_KEYS); + ArrayList deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(data + .getByteArray(SAVE_KEYRING_DELETED_KEYS)); long masterKeyId = data.getLong(SAVE_KEYRING_MASTER_KEY_ID); @@ -548,7 +551,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial oldPassPhrase, newPassPhrase); } else { keyOperations.buildSecretKey(userIds, original_ids, deleted_ids, keys, modded_keys, - newPassPhrase, keysExpiryDates, oldPassPhrase, keysUsages); + deletedKeys, keysExpiryDates, keysUsages, newPassPhrase, oldPassPhrase); } PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 9ebe73197..ec7dd1330 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -580,6 +580,9 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener ArrayList keys = getKeys(mKeysView); data.putByteArray(KeychainIntentService.SAVE_KEYRING_KEYS, PgpConversionHelper.PGPSecretKeyArrayListToBytes(keys)); + ArrayList dKeys = mKeysView.getDeletedKeys(); + data.putByteArray(KeychainIntentService.SAVE_KEYRING_DELETED_KEYS, + PgpConversionHelper.PGPSecretKeyArrayListToBytes(dKeys)); data.putIntegerArrayList(KeychainIntentService.SAVE_KEYRING_KEYS_USAGES, getKeysUsages(mKeysView)); data.putSerializable(KeychainIntentService.SAVE_KEYRING_KEYS_EXPIRY_DATES, diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 788a80a60..369288d2c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -66,6 +66,7 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor private boolean canEdit = true; private boolean oldItemDeleted = false; private ArrayList mDeletedIDs = new ArrayList(); + private ArrayList mDeletedKeys = new ArrayList(); private ActionBarActivity mActivity; @@ -136,8 +137,13 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor /** {@inheritDoc} */ public void onDeleted(Editor editor, boolean wasNewItem) { oldItemDeleted |= !wasNewItem; - if (oldItemDeleted && mType == Id.type.user_id) - mDeletedIDs.add(((UserIdEditor)editor).getOriginalID()); + if (oldItemDeleted) { + if (mType == Id.type.user_id) + mDeletedIDs.add(((UserIdEditor)editor).getOriginalID()); + else if (mType == Id.type.key) + mDeletedKeys.add(((KeyEditor)editor).getValue()); + + } this.updateEditorsVisible(); if (mEditorListener != null) { mEditorListener.onEdited(); @@ -186,6 +192,11 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor return mDeletedIDs; } + public ArrayList getDeletedKeys() + { + return mDeletedKeys; + } + public List getNeedsSavingArray() { ArrayList mList = new ArrayList(); From 034deaacba5cdccf422fbba26d6244c0a4cf2554 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Fri, 21 Feb 2014 15:01:59 +0000 Subject: [PATCH 035/253] initial split into two functions --- .../keychain/pgp/PgpKeyOperation.java | 184 +++++++++++++++--- 1 file changed, 157 insertions(+), 27 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index c57718540..6055aebfc 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -198,22 +198,9 @@ public class PgpKeyOperation { } - public void buildSecretKey(ArrayList userIds, ArrayList OriginalIDs, ArrayList deletedIDs, ArrayList keys, boolean[] modded_keys, ArrayList deleted_keys, ArrayList keysExpiryDates, ArrayList keysUsages, String newPassPhrase, String oldPassPhrase) throws PgpGeneralException, + private void buildNewSecretKey(ArrayList userIds, ArrayList keys, ArrayList keysExpiryDates, ArrayList keysUsages, String newPassPhrase, String oldPassPhrase) throws PgpGeneralException, PGPException, SignatureException, IOException { - Log.d(Constants.TAG, "userIds: " + userIds.toString()); - - updateProgress(R.string.progress_building_key, 0, 100); - - if (oldPassPhrase == null) { - oldPassPhrase = ""; - } - if (newPassPhrase == null) { - newPassPhrase = ""; - } - - updateProgress(R.string.progress_preparing_master_key, 10, 100); - int usageId = keysUsages.get(0); boolean canSign; String mainUserId = userIds.get(0); @@ -223,19 +210,163 @@ public class PgpKeyOperation { // this removes all userIds and certifications previously attached to the masterPublicKey PGPPublicKey masterPublicKey = masterKey.getPublicKey(); - // already done by code above: - // PGPPublicKey masterPublicKey = masterKey.getPublicKey(); - // // Somehow, the PGPPublicKey already has an empty certification attached to it when the - // // keyRing is generated the first time, we remove that when it exists, before adding the - // new - // // ones - // PGPPublicKey masterPublicKeyRmCert = PGPPublicKey.removeCertification(masterPublicKey, - // ""); - // if (masterPublicKeyRmCert != null) { - // masterPublicKey = masterPublicKeyRmCert; - // } + PGPSecretKeyRing mKR = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, masterKey.getKeyID()); + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); + PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor); + updateProgress(R.string.progress_certifying_master_key, 20, 100); + int user_id_index = 0; + for (String userId : userIds) { + PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( + masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); + + sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); + + PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); + masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification); + user_id_index++; + } + + PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); + + PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); + PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); + + hashedPacketsGen.setKeyFlags(true, usageId); + + hashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS); + hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS); + hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS); + + if (keysExpiryDates.get(0) != null) { + GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + creationDate.setTime(masterPublicKey.getCreationTime()); + GregorianCalendar expiryDate = keysExpiryDates.get(0); + //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c + //here we purposefully ignore partial days in each date - long type has no fractional part! + long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000); + if (numDays <= 0) + throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation)); + hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400); + } else { + hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding, + //this happens anyway + } + + updateProgress(R.string.progress_building_master_key, 30, 100); + + // define hashing and signing algos + PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build().get( + HashAlgorithmTags.SHA1); + PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder( + masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); + + // Build key encrypter based on passphrase + PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( + PGPEncryptedData.CAST5, sha1Calc) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( + newPassPhrase.toCharArray()); + + PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, + masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), + unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); + + updateProgress(R.string.progress_adding_sub_keys, 40, 100); + + for (int i = 1; i < keys.size(); ++i) { + updateProgress(40 + 50 * (i - 1) / (keys.size() - 1), 100); + + PGPSecretKey subKey = keys.get(i); + PGPPublicKey subPublicKey = subKey.getPublicKey(); + + PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( + oldPassPhrase.toCharArray()); + PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2); + + // TODO: now used without algorithm and creation time?! (APG 1) + PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey); + + hashedPacketsGen = new PGPSignatureSubpacketGenerator(); + unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); + + usageId = keysUsages.get(i); + canSign = (usageId & KeyFlags.SIGN_DATA) > 0; //todo - separate function for this + if (canSign) { + Date todayDate = new Date(); //both sig times the same + // cross-certify signing keys + hashedPacketsGen.setSignatureCreationTime(false, todayDate); //set outer creation time + PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); + subHashedPacketsGen.setSignatureCreationTime(false, todayDate); //set inner creation time + PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( + subPublicKey.getAlgorithm(), PGPUtil.SHA1) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); + sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey); + sGen.setHashedSubpackets(subHashedPacketsGen.generate()); + PGPSignature certification = sGen.generateCertification(masterPublicKey, + subPublicKey); + unhashedPacketsGen.setEmbeddedSignature(false, certification); + } + hashedPacketsGen.setKeyFlags(false, usageId); + + if (keysExpiryDates.get(i) != null) { + GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + creationDate.setTime(subPublicKey.getCreationTime()); + GregorianCalendar expiryDate = keysExpiryDates.get(i); + //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c + //here we purposefully ignore partial days in each date - long type has no fractional part! + long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000); + if (numDays <= 0) + throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation)); + hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400); + } else { + hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding, + //this happens anyway + } + + keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate()); + } + + PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing(); + PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing(); + + updateProgress(R.string.progress_saving_key_ring, 90, 100); + + ProviderHelper.saveKeyRing(mContext, secretKeyRing); + ProviderHelper.saveKeyRing(mContext, publicKeyRing); + + updateProgress(R.string.progress_done, 100, 100); + } + + public void buildSecretKey(ArrayList userIds, ArrayList OriginalIDs, ArrayList deletedIDs, ArrayList keys, boolean[] modded_keys, ArrayList deleted_keys, ArrayList keysExpiryDates, ArrayList keysUsages, String newPassPhrase, String oldPassPhrase) throws PgpGeneralException, + PGPException, SignatureException, IOException { + + updateProgress(R.string.progress_building_key, 0, 100); + PGPSecretKey masterKey = keys.get(0); + + PGPSecretKeyRing mKR = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, masterKey.getKeyID()); + + if (oldPassPhrase == null) { + oldPassPhrase = ""; + } + if (newPassPhrase == null) { + newPassPhrase = ""; + } + + if (mKR == null) + buildNewSecretKey(userIds, keys, keysExpiryDates, keysUsages, newPassPhrase, oldPassPhrase); //new Keyring + + masterKey = mKR.getSecretKey(); + PGPPublicKey masterPublicKey = masterKey.getPublicKey(); + + int usageId = keysUsages.get(0); + boolean canSign; + String mainUserId = userIds.get(0); PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); @@ -243,10 +374,9 @@ public class PgpKeyOperation { updateProgress(R.string.progress_certifying_master_key, 20, 100); - // TODO: if we are editing a key, keep old certs, don't remake certs we don't have to. int user_id_index = 0; for (String userId : userIds) { - if (OriginalIDs[user_id_index]) { + if (!OriginalIDs.get(user_id_index).equals(userIds.get(user_id_index))) { PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); From c3c311152ef33a6887909dbbeae40e8d01f96a9d Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Fri, 21 Feb 2014 15:09:49 +0000 Subject: [PATCH 036/253] put main original id at the top --- .../sufficientlysecure/keychain/ui/widget/SectionView.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 369288d2c..2e06f3f34 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -179,7 +179,10 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor if (mType == Id.type.user_id) { for (int i = 0; i < mEditors.getChildCount(); ++i) { UserIdEditor editor = (UserIdEditor) mEditors.getChildAt(i); - orig.add(editor.getOriginalID()); + if (editor.isMainUserId()) + orig.add(0, editor.getOriginalID()); + else + orig.add(editor.getOriginalID()); } return orig; } else { From 1a28a4e9214add62b2b1e23b4579e0a4d585f52e Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sat, 22 Feb 2014 13:54:59 +0000 Subject: [PATCH 037/253] change how primary id changing is passed through --- .../keychain/pgp/PgpKeyOperation.java | 4 ++-- .../keychain/service/KeychainIntentService.java | 7 +++++-- .../keychain/ui/EditKeyActivity.java | 2 ++ .../keychain/ui/widget/SectionView.java | 13 +++++++++++++ .../keychain/ui/widget/UserIdEditor.java | 7 ++++++- 5 files changed, 28 insertions(+), 5 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 80831d35f..cc9384821 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -343,7 +343,7 @@ public class PgpKeyOperation { updateProgress(R.string.progress_done, 100, 100); } - public void buildSecretKey(ArrayList userIds, ArrayList OriginalIDs, ArrayList deletedIDs, ArrayList keys, boolean[] modded_keys, ArrayList deleted_keys, ArrayList keysExpiryDates, ArrayList keysUsages, String newPassPhrase, String oldPassPhrase) throws PgpGeneralException, + public void buildSecretKey(ArrayList userIds, ArrayList OriginalIDs, ArrayList deletedIDs, boolean primaryIDChanged, boolean[] modded_keys, ArrayList deleted_keys, ArrayList keysExpiryDates, ArrayList keysUsages, String newPassPhrase, String oldPassPhrase, ArrayList keys) throws PgpGeneralException, PGPException, SignatureException, IOException { updateProgress(R.string.progress_building_key, 0, 100); @@ -386,7 +386,7 @@ public class PgpKeyOperation { PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); - masterPublicKey = PGPPublicKey.removeCertification(); + //masterPublicKey = PGPPublicKey.removeCertification(); masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification); } user_id_index++; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 9e517b93e..73de7ca6e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -128,6 +128,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial public static final String SAVE_KEYRING_NEW_PASSPHRASE = "new_passphrase"; public static final String SAVE_KEYRING_CURRENT_PASSPHRASE = "current_passphrase"; public static final String SAVE_KEYRING_USER_IDS = "user_ids"; + public static final String SAVE_KEYRING_PRIMARY_ID_CHANGED = "primary_id_changed"; public static final String SAVE_KEYRING_KEYS = "keys"; public static final String SAVE_KEYRING_KEYS_USAGES = "keys_usages"; public static final String SAVE_KEYRING_KEYS_EXPIRY_DATES = "keys_expiry_dates"; @@ -549,6 +550,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial boolean[] modded_keys = data.getBooleanArray(SAVE_KEYRING_MODDED_KEYS); ArrayList deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(data .getByteArray(SAVE_KEYRING_DELETED_KEYS)); + boolean primaryChanged = data.getBoolean(SAVE_KEYRING_PRIMARY_ID_CHANGED); long masterKeyId = data.getLong(SAVE_KEYRING_MASTER_KEY_ID); @@ -559,8 +561,9 @@ public class KeychainIntentService extends IntentService implements ProgressDial ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId), oldPassPhrase, newPassPhrase); } else { - keyOperations.buildSecretKey(userIds, original_ids, deleted_ids, keys, modded_keys, - deletedKeys, keysExpiryDates, keysUsages, newPassPhrase, oldPassPhrase); + keyOperations.buildSecretKey(userIds, original_ids, deleted_ids, primaryChanged, + modded_keys, deletedKeys, keysExpiryDates, keysUsages, newPassPhrase, + oldPassPhrase, keys); } PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index ec7dd1330..09be0dc79 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -595,6 +595,8 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener mUserIdsView.getOriginalIDs()); data.putBooleanArray(KeychainIntentService.SAVE_KEYRING_MODDED_KEYS, toPrimitiveArray(mKeysView.getNeedsSavingArray())); + data.putBoolean(KeychainIntentService.SAVE_KEYRING_PRIMARY_ID_CHANGED, + mUserIdsView.primaryChanged()); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 2e06f3f34..014a891a5 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -169,6 +169,19 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor for (int i = 0; i < mEditors.getChildCount(); ++i) { Editor editor = (Editor) mEditors.getChildAt(i); ret |= editor.needsSaving(); + if (mType == Id.type.user_id) + ret |= ((UserIdEditor)editor).primarySwapped(); + } + return ret; + } + + public boolean primaryChanged() + { + boolean ret = false; + for (int i = 0; i < mEditors.getChildCount(); ++i) { + Editor editor = (Editor) mEditors.getChildAt(i); + if (mType == Id.type.user_id) + ret |= ((UserIdEditor)editor).primarySwapped(); } return ret; } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java index 37ab0e051..5098c8713 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/UserIdEditor.java @@ -221,7 +221,7 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene @Override public boolean needsSaving() { - boolean retval = (mOriginallyMainUserID != isMainUserId()); + boolean retval = false; //(mOriginallyMainUserID != isMainUserId()); retval |= !(mOriginalName.equals( ("" + mName.getText()).trim() ) ); retval |= !(mOriginalEmail.equals( ("" + mEmail.getText()).trim() ) ); retval |= !(mOriginalComment.equals( ("" + mComment.getText()).trim() ) ); @@ -229,6 +229,11 @@ public class UserIdEditor extends LinearLayout implements Editor, OnClickListene return retval; } + public boolean primarySwapped() + { + return (mOriginallyMainUserID != isMainUserId()); + } + public String getOriginalID() { return mOriginalID; From a39c73ca12dabba01412083afbf0b58dfb42a79e Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sat, 22 Feb 2014 15:29:19 +0000 Subject: [PATCH 038/253] first go at saving IDs correctly --- .../keychain/pgp/PgpKeyOperation.java | 74 ++++++++++++++++--- 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index cc9384821..f3b289f66 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -28,6 +28,7 @@ import java.security.SignatureException; import java.util.ArrayList; import java.util.Date; import java.util.GregorianCalendar; +import java.util.Iterator; import java.util.TimeZone; import org.spongycastle.bcpg.CompressionAlgorithmTags; @@ -68,6 +69,7 @@ import org.sufficientlysecure.keychain.util.Primes; import org.sufficientlysecure.keychain.util.ProgressDialogUpdater; import android.content.Context; +import android.util.Pair; public class PgpKeyOperation { private final Context mContext; @@ -361,6 +363,21 @@ public class PgpKeyOperation { if (mKR == null) buildNewSecretKey(userIds, keys, keysExpiryDates, keysUsages, newPassPhrase, oldPassPhrase); //new Keyring + /* + IDs - + remove deleted ids + if the primary ID changed we need to: + remove all of the IDs from the keyring, saving their certifications + add them all in again, updating certs of IDs which have changed + else + remove changed IDs and add in with new certs + + Keys + remove deleted keys + if a key is modified, re-sign it + do we need to remove and add in? + */ + masterKey = mKR.getSecretKey(); PGPPublicKey masterPublicKey = masterKey.getPublicKey(); @@ -374,22 +391,55 @@ public class PgpKeyOperation { updateProgress(R.string.progress_certifying_master_key, 20, 100); + for (String delID : deletedIDs) { + masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, delID); + } + int user_id_index = 0; - for (String userId : userIds) { - if (!OriginalIDs.get(user_id_index).equals(userIds.get(user_id_index))) { - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); + if (primaryIDChanged) { + ArrayList> sigList = new ArrayList>(); + for (String userId : userIds) { + String orig_id = OriginalIDs.get(user_id_index); + if (orig_id.equals(userId)) { + Iterator orig_sigs = masterPublicKey.getSignaturesForID(orig_id); //TODO: make sure this iterator only has signatures we are interested in + while (orig_sigs.hasNext()) { + PGPSignature orig_sig = orig_sigs.next(); + sigList.add(new Pair(orig_id, orig_sig)); + } + } else { + PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( + masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); + sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); - PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); - - //masterPublicKey = PGPPublicKey.removeCertification(); - masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification); + PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); + sigList.add(new Pair(userId, certification)); + } + masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, orig_id); + user_id_index++; + } + for (Pair to_add : sigList) { + masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, to_add.first, to_add.second); + } + } else { + for (String userId : userIds) { + String orig_id = OriginalIDs.get(user_id_index); + if (!orig_id.equals(userId)) { + PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( + masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); + + sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); + + PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); + masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, orig_id); + masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification); + } + user_id_index++; } - user_id_index++; } PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); From fab74590eb0984e055f98ccf3af4390d3e3c0c15 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sat, 22 Feb 2014 15:41:15 +0000 Subject: [PATCH 039/253] remove deleted keys --- .../org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index f3b289f66..ab4408056 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -378,6 +378,10 @@ public class PgpKeyOperation { do we need to remove and add in? */ + for (PGPSecretKey dKey : deleted_keys) { + mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey); + } + masterKey = mKR.getSecretKey(); PGPPublicKey masterPublicKey = masterKey.getPublicKey(); From fa533dda325b5d56db16dee2f84152ec4807a2b2 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sat, 22 Feb 2014 16:08:38 +0000 Subject: [PATCH 040/253] pass through which keys are new --- .../keychain/pgp/PgpKeyOperation.java | 9 +++++++-- .../keychain/service/KeychainIntentService.java | 4 +++- .../keychain/ui/EditKeyActivity.java | 2 ++ .../keychain/ui/widget/KeyEditor.java | 5 +++++ .../keychain/ui/widget/SectionView.java | 12 ++++++++++++ 5 files changed, 29 insertions(+), 3 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index ab4408056..a6ff60442 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -345,7 +345,7 @@ public class PgpKeyOperation { updateProgress(R.string.progress_done, 100, 100); } - public void buildSecretKey(ArrayList userIds, ArrayList OriginalIDs, ArrayList deletedIDs, boolean primaryIDChanged, boolean[] modded_keys, ArrayList deleted_keys, ArrayList keysExpiryDates, ArrayList keysUsages, String newPassPhrase, String oldPassPhrase, ArrayList keys) throws PgpGeneralException, + public void buildSecretKey(ArrayList userIds, ArrayList OriginalIDs, ArrayList deletedIDs, boolean primaryIDChanged, boolean[] modded_keys, ArrayList deleted_keys, ArrayList keysExpiryDates, ArrayList keysUsages, String newPassPhrase, String oldPassPhrase, boolean[] new_keys, ArrayList keys) throws PgpGeneralException, PGPException, SignatureException, IOException { updateProgress(R.string.progress_building_key, 0, 100); @@ -490,6 +490,11 @@ public class PgpKeyOperation { masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); + //updating master is slightly different to updating the others + if (modded_keys[0]) { + + } + updateProgress(R.string.progress_adding_sub_keys, 40, 100); for (int i = 1; i < keys.size(); ++i) { @@ -549,7 +554,7 @@ public class PgpKeyOperation { PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing(); PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing(); - +//must copy with new passphrase... new keys will have an empty passphrase... pass in boolean array to mark new key? updateProgress(R.string.progress_saving_key_ring, 90, 100); ProviderHelper.saveKeyRing(mContext, secretKeyRing); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 73de7ca6e..3610ddc9e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -138,6 +138,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial public static final String SAVE_KEYRING_DELETED_IDS = "deleted_ids"; public static final String SAVE_KEYRING_MODDED_KEYS = "modified_keys"; public static final String SAVE_KEYRING_DELETED_KEYS = "deleted_keys"; + public static final String SAVE_KEYRING_NEW_KEYS = "new_keys"; // generate key public static final String GENERATE_KEY_ALGORITHM = "algorithm"; @@ -548,6 +549,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial ArrayList original_ids = data.getStringArrayList(SAVE_KEYRING_ORIGINAL_IDS); ArrayList deleted_ids = data.getStringArrayList(SAVE_KEYRING_DELETED_IDS); boolean[] modded_keys = data.getBooleanArray(SAVE_KEYRING_MODDED_KEYS); + boolean[] new_keys = data.getBooleanArray(SAVE_KEYRING_NEW_KEYS); ArrayList deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(data .getByteArray(SAVE_KEYRING_DELETED_KEYS)); boolean primaryChanged = data.getBoolean(SAVE_KEYRING_PRIMARY_ID_CHANGED); @@ -563,7 +565,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial } else { keyOperations.buildSecretKey(userIds, original_ids, deleted_ids, primaryChanged, modded_keys, deletedKeys, keysExpiryDates, keysUsages, newPassPhrase, - oldPassPhrase, keys); + oldPassPhrase, new_keys, keys); } PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 09be0dc79..bb38ac979 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -597,6 +597,8 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener toPrimitiveArray(mKeysView.getNeedsSavingArray())); data.putBoolean(KeychainIntentService.SAVE_KEYRING_PRIMARY_ID_CHANGED, mUserIdsView.primaryChanged()); + data.putBooleanArray(KeychainIntentService.SAVE_KEYRING_NEW_KEYS, + toPrimitiveArray(mKeysView.getNewKeysArray())); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java index 5ce5d89d7..68bfc573e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/KeyEditor.java @@ -314,4 +314,9 @@ public class KeyEditor extends LinearLayout implements Editor, OnClickListener { return retval; } + public boolean getIsNewKey() + { + return mIsNewKey; + } + } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index 014a891a5..93c10a2b0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -223,6 +223,18 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor return mList; } + public List getNewKeysArray() + { + ArrayList mList = new ArrayList(); + if (mType == Id.type.key) { + for (int i = 0; i < mEditors.getChildCount(); ++i) { + KeyEditor editor = (KeyEditor) mEditors.getChildAt(i); + mList.add(editor.getIsNewKey()); + } + } + return mList; + } + /** {@inheritDoc} */ public void onClick(View v) { if (canEdit) { From d6b0975f9b666dc30edab36e6f2c68c0022790d1 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sun, 2 Mar 2014 20:34:28 +0000 Subject: [PATCH 041/253] begin adding parcel for save intent --- .../keychain/pgp/PgpKeyOperation.java | 10 ++- .../keychain/service/SaveKeyringParcel.java | 90 +++++++++++++++++++ 2 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index a6ff60442..a52f247c6 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.GregorianCalendar; import java.util.Iterator; +import java.util.List; import java.util.TimeZone; import org.spongycastle.bcpg.CompressionAlgorithmTags; @@ -490,11 +491,14 @@ public class PgpKeyOperation { masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); - //updating master is slightly different to updating the others - if (modded_keys[0]) { + for (int i = 0; i < keys.size(); ++i) { + updateProgress(40 + 50 * (i - 1) / (keys.size() - 1), 100); + if (new_keys[i]) { + } else { + + } } - updateProgress(R.string.progress_adding_sub_keys, 40, 100); for (int i = 1; i < keys.size(); ++i) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java new file mode 100644 index 000000000..bdabc70a2 --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2014 Ash Hughes + * + * 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.service; + +import android.os.Parcel; +import android.os.Parcelable; + +import org.spongycastle.openpgp.PGPSecretKey; +import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; + +import java.util.ArrayList; +import java.util.GregorianCalendar; + +public class SaveKeyringParcel implements Parcelable { + + public ArrayList userIDs; + public ArrayList originalIDs; + public ArrayList deletedIDs; + public boolean primaryIDChanged; + public boolean[] moddedKeys; + public ArrayList deletedKeys; + public ArrayList keysExpiryDates; + public ArrayList keysUsages; + public String newPassPhrase; + public String oldPassPhrase; + public boolean[] newKeys; + public ArrayList keys; + + private SaveKeyringParcel(Parcel source) + { + byte[] tmpB; + userIDs = (ArrayList)source.readSerializable(); + originalIDs = (ArrayList)source.readSerializable(); + deletedIDs = (ArrayList)source.readSerializable(); + primaryIDChanged = source.readByte() != 0; + source.readBooleanArray(moddedKeys); + deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray()); + keysExpiryDates = (ArrayList)source.readSerializable(); + keysUsages = source.readArrayList(Integer.class.getClassLoader()); + } + + @Override + public void writeToParcel(Parcel destination, int flags) + { + destination.writeSerializable(userIDs); + destination.writeSerializable(originalIDs); + destination.writeSerializable(deletedIDs); + destination.writeByte((byte) (primaryIDChanged ? 1 : 0)); + destination.writeBooleanArray(moddedKeys); + destination.writeByteArray(PgpConversionHelper.PGPSecretKeyArrayListToBytes(deletedKeys)); + destination.writeSerializable(keysExpiryDates); + destination.writeList(keysUsages); + destination.writeString(newPassPhrase); + destination.writeString(oldPassPhrase); + destination.writeBooleanArray(newKeys); + destination.writeByteArray(); + } + + public static final Creator CREATOR = new Creator() { + public SaveKeyringParcel createFromParcel(final Parcel source) { + return new SaveKeyringParcel(source); + } + + public SaveKeyringParcel[] newArray(final int size) { + return new SaveKeyringParcel[size]; + } + }; + + @Override + public int describeContents() + { + return 0; + } +} From 8662ca2928ac0b007d4ee5d00f6de3211f5d3d2c Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Thu, 6 Mar 2014 12:25:35 +0000 Subject: [PATCH 042/253] a method, if not the best one, for each property --- .../keychain/service/SaveKeyringParcel.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index bdabc70a2..9e290c1b6 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -53,12 +53,16 @@ public class SaveKeyringParcel implements Parcelable { deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray()); keysExpiryDates = (ArrayList)source.readSerializable(); keysUsages = source.readArrayList(Integer.class.getClassLoader()); + newPassPhrase = source.readString(); + oldPassPhrase = source.readString(); + source.readBooleanArray(newKeys); + keys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray()); } @Override public void writeToParcel(Parcel destination, int flags) { - destination.writeSerializable(userIDs); + destination.writeSerializable(userIDs); //might not be the best method to store. destination.writeSerializable(originalIDs); destination.writeSerializable(deletedIDs); destination.writeByte((byte) (primaryIDChanged ? 1 : 0)); @@ -69,7 +73,7 @@ public class SaveKeyringParcel implements Parcelable { destination.writeString(newPassPhrase); destination.writeString(oldPassPhrase); destination.writeBooleanArray(newKeys); - destination.writeByteArray(); + destination.writeByteArray(PgpConversionHelper.PGPSecretKeyArrayListToBytes(keys)); } public static final Creator CREATOR = new Creator() { From 01951810ae1b3a0e4fefab7d55c090fc8f776ce5 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Thu, 6 Mar 2014 13:00:17 +0000 Subject: [PATCH 043/253] fix saving new key (tmp) --- .../sufficientlysecure/keychain/pgp/PgpKeyOperation.java | 4 +++- .../keychain/service/KeychainIntentService.java | 7 ++++++- .../sufficientlysecure/keychain/ui/EditKeyActivity.java | 5 ++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index a52f247c6..66665df3c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -361,8 +361,10 @@ public class PgpKeyOperation { newPassPhrase = ""; } - if (mKR == null) + if (mKR == null) { buildNewSecretKey(userIds, keys, keysExpiryDates, keysUsages, newPassPhrase, oldPassPhrase); //new Keyring + return; + } /* IDs - diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 3610ddc9e..9d6e24d30 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -550,8 +550,13 @@ public class KeychainIntentService extends IntentService implements ProgressDial ArrayList deleted_ids = data.getStringArrayList(SAVE_KEYRING_DELETED_IDS); boolean[] modded_keys = data.getBooleanArray(SAVE_KEYRING_MODDED_KEYS); boolean[] new_keys = data.getBooleanArray(SAVE_KEYRING_NEW_KEYS); - ArrayList deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(data + byte[] tmp = data.getByteArray(SAVE_KEYRING_DELETED_KEYS); + ArrayList deletedKeys; + if (tmp != null) + deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(data .getByteArray(SAVE_KEYRING_DELETED_KEYS)); + else + deletedKeys = new ArrayList(); boolean primaryChanged = data.getBoolean(SAVE_KEYRING_PRIMARY_ID_CHANGED); long masterKeyId = data.getLong(SAVE_KEYRING_MASTER_KEY_ID); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index bb38ac979..044d15ec7 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -581,8 +581,11 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener data.putByteArray(KeychainIntentService.SAVE_KEYRING_KEYS, PgpConversionHelper.PGPSecretKeyArrayListToBytes(keys)); ArrayList dKeys = mKeysView.getDeletedKeys(); + byte[] tmp = null; + if (dKeys.size() != 0) + tmp = PgpConversionHelper.PGPSecretKeyArrayListToBytes(dKeys); data.putByteArray(KeychainIntentService.SAVE_KEYRING_DELETED_KEYS, - PgpConversionHelper.PGPSecretKeyArrayListToBytes(dKeys)); + tmp); data.putIntegerArrayList(KeychainIntentService.SAVE_KEYRING_KEYS_USAGES, getKeysUsages(mKeysView)); data.putSerializable(KeychainIntentService.SAVE_KEYRING_KEYS_EXPIRY_DATES, From 04fa0e9cc7fbfc117948d60a3ad8bfab0b0060ba Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Thu, 6 Mar 2014 23:47:11 +0000 Subject: [PATCH 044/253] use parcel to save keys, but saving existing keys is disabled, pending a rewrite... --- .../keychain/pgp/PgpKeyOperation.java | 18 +++++--- .../service/KeychainIntentService.java | 43 +++-------------- .../keychain/service/SaveKeyringParcel.java | 18 ++++++-- .../keychain/ui/EditKeyActivity.java | 46 +++++++------------ 4 files changed, 48 insertions(+), 77 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 66665df3c..0073107a0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -65,6 +65,7 @@ import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.service.SaveKeyringParcel; import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Primes; import org.sufficientlysecure.keychain.util.ProgressDialogUpdater; @@ -346,23 +347,24 @@ public class PgpKeyOperation { updateProgress(R.string.progress_done, 100, 100); } - public void buildSecretKey(ArrayList userIds, ArrayList OriginalIDs, ArrayList deletedIDs, boolean primaryIDChanged, boolean[] modded_keys, ArrayList deleted_keys, ArrayList keysExpiryDates, ArrayList keysUsages, String newPassPhrase, String oldPassPhrase, boolean[] new_keys, ArrayList keys) throws PgpGeneralException, + public void buildSecretKey(SaveKeyringParcel saveParcel) throws PgpGeneralException, PGPException, SignatureException, IOException { updateProgress(R.string.progress_building_key, 0, 100); - PGPSecretKey masterKey = keys.get(0); + PGPSecretKey masterKey = saveParcel.keys.get(0); PGPSecretKeyRing mKR = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, masterKey.getKeyID()); - if (oldPassPhrase == null) { - oldPassPhrase = ""; + if (saveParcel.oldPassPhrase == null) { + saveParcel.oldPassPhrase = ""; } - if (newPassPhrase == null) { - newPassPhrase = ""; + if (saveParcel.newPassPhrase == null) { + saveParcel.newPassPhrase = ""; } if (mKR == null) { - buildNewSecretKey(userIds, keys, keysExpiryDates, keysUsages, newPassPhrase, oldPassPhrase); //new Keyring + buildNewSecretKey(saveParcel.userIDs, saveParcel.keys, saveParcel.keysExpiryDates, + saveParcel.keysUsages, saveParcel.newPassPhrase, saveParcel.oldPassPhrase); //new Keyring return; } @@ -381,6 +383,7 @@ public class PgpKeyOperation { do we need to remove and add in? */ + /* for (PGPSecretKey dKey : deleted_keys) { mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey); } @@ -567,6 +570,7 @@ public class PgpKeyOperation { ProviderHelper.saveKeyRing(mContext, publicKeyRing); updateProgress(R.string.progress_done, 100, 100); + */ } public PGPPublicKeyRing certifyKey(long masterKeyId, long pubKeyId, String passphrase) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 9d6e24d30..24bce8eeb 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -125,20 +125,9 @@ public class KeychainIntentService extends IntentService implements ProgressDial public static final String DECRYPT_ASSUME_SYMMETRIC = "assume_symmetric"; // save keyring - public static final String SAVE_KEYRING_NEW_PASSPHRASE = "new_passphrase"; - public static final String SAVE_KEYRING_CURRENT_PASSPHRASE = "current_passphrase"; - public static final String SAVE_KEYRING_USER_IDS = "user_ids"; - public static final String SAVE_KEYRING_PRIMARY_ID_CHANGED = "primary_id_changed"; - public static final String SAVE_KEYRING_KEYS = "keys"; - public static final String SAVE_KEYRING_KEYS_USAGES = "keys_usages"; - public static final String SAVE_KEYRING_KEYS_EXPIRY_DATES = "keys_expiry_dates"; - public static final String SAVE_KEYRING_MASTER_KEY_ID = "master_key_id"; + public static final String SAVE_KEYRING_PARCEL = "save_parcel"; public static final String SAVE_KEYRING_CAN_SIGN = "can_sign"; - public static final String SAVE_KEYRING_ORIGINAL_IDS = "original_ids"; - public static final String SAVE_KEYRING_DELETED_IDS = "deleted_ids"; - public static final String SAVE_KEYRING_MODDED_KEYS = "modified_keys"; - public static final String SAVE_KEYRING_DELETED_KEYS = "deleted_keys"; - public static final String SAVE_KEYRING_NEW_KEYS = "new_keys"; + // generate key public static final String GENERATE_KEY_ALGORITHM = "algorithm"; @@ -530,8 +519,9 @@ public class KeychainIntentService extends IntentService implements ProgressDial } else if (ACTION_SAVE_KEYRING.equals(action)) { try { /* Input */ - String oldPassPhrase = data.getString(SAVE_KEYRING_CURRENT_PASSPHRASE); - String newPassPhrase = data.getString(SAVE_KEYRING_NEW_PASSPHRASE); + SaveKeyringParcel saveParams = data.getParcelable(SAVE_KEYRING_PARCEL); + String oldPassPhrase = saveParams.oldPassPhrase; + String newPassPhrase = saveParams.newPassPhrase; boolean canSign = true; if (data.containsKey(SAVE_KEYRING_CAN_SIGN)) { @@ -541,25 +531,8 @@ public class KeychainIntentService extends IntentService implements ProgressDial if (newPassPhrase == null) { newPassPhrase = oldPassPhrase; } - ArrayList userIds = data.getStringArrayList(SAVE_KEYRING_USER_IDS); - ArrayList keys = PgpConversionHelper.BytesToPGPSecretKeyList(data - .getByteArray(SAVE_KEYRING_KEYS)); - ArrayList keysUsages = data.getIntegerArrayList(SAVE_KEYRING_KEYS_USAGES); - ArrayList keysExpiryDates = (ArrayList) data.getSerializable(SAVE_KEYRING_KEYS_EXPIRY_DATES); - ArrayList original_ids = data.getStringArrayList(SAVE_KEYRING_ORIGINAL_IDS); - ArrayList deleted_ids = data.getStringArrayList(SAVE_KEYRING_DELETED_IDS); - boolean[] modded_keys = data.getBooleanArray(SAVE_KEYRING_MODDED_KEYS); - boolean[] new_keys = data.getBooleanArray(SAVE_KEYRING_NEW_KEYS); - byte[] tmp = data.getByteArray(SAVE_KEYRING_DELETED_KEYS); - ArrayList deletedKeys; - if (tmp != null) - deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(data - .getByteArray(SAVE_KEYRING_DELETED_KEYS)); - else - deletedKeys = new ArrayList(); - boolean primaryChanged = data.getBoolean(SAVE_KEYRING_PRIMARY_ID_CHANGED); - long masterKeyId = data.getLong(SAVE_KEYRING_MASTER_KEY_ID); + long masterKeyId = saveParams.keys.get(0).getKeyID(); PgpKeyOperation keyOperations = new PgpKeyOperation(this, this); /* Operation */ @@ -568,9 +541,7 @@ public class KeychainIntentService extends IntentService implements ProgressDial ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId), oldPassPhrase, newPassPhrase); } else { - keyOperations.buildSecretKey(userIds, original_ids, deleted_ids, primaryChanged, - modded_keys, deletedKeys, keysExpiryDates, keysUsages, newPassPhrase, - oldPassPhrase, new_keys, keys); + keyOperations.buildSecretKey(saveParams); } PassphraseCacheService.addCachedPassphrase(this, masterKeyId, newPassPhrase); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index 9e290c1b6..ae481aa80 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -42,20 +42,25 @@ public class SaveKeyringParcel implements Parcelable { public boolean[] newKeys; public ArrayList keys; + public SaveKeyringParcel() {} + private SaveKeyringParcel(Parcel source) { - byte[] tmpB; userIDs = (ArrayList)source.readSerializable(); originalIDs = (ArrayList)source.readSerializable(); deletedIDs = (ArrayList)source.readSerializable(); primaryIDChanged = source.readByte() != 0; - source.readBooleanArray(moddedKeys); - deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray()); + moddedKeys = source.createBooleanArray(); + byte[] tmp = source.createByteArray(); + if (tmp == null) + deletedKeys = null; + else + deletedKeys = PgpConversionHelper.BytesToPGPSecretKeyList(tmp); keysExpiryDates = (ArrayList)source.readSerializable(); keysUsages = source.readArrayList(Integer.class.getClassLoader()); newPassPhrase = source.readString(); oldPassPhrase = source.readString(); - source.readBooleanArray(newKeys); + newKeys = source.createBooleanArray(); keys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray()); } @@ -67,7 +72,10 @@ public class SaveKeyringParcel implements Parcelable { destination.writeSerializable(deletedIDs); destination.writeByte((byte) (primaryIDChanged ? 1 : 0)); destination.writeBooleanArray(moddedKeys); - destination.writeByteArray(PgpConversionHelper.PGPSecretKeyArrayListToBytes(deletedKeys)); + byte[] tmp = null; + if (deletedKeys.size() != 0) + tmp = PgpConversionHelper.PGPSecretKeyArrayListToBytes(deletedKeys); + destination.writeByteArray(tmp); destination.writeSerializable(keysExpiryDates); destination.writeList(keysUsages); destination.writeString(newPassPhrase); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 044d15ec7..6ce2a8f6b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper; 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.dialog.DeleteKeyDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.SetPassphraseDialogFragment; @@ -570,38 +571,25 @@ public class EditKeyActivity extends ActionBarActivity implements EditorListener intent.setAction(KeychainIntentService.ACTION_SAVE_KEYRING); + SaveKeyringParcel saveParams = new SaveKeyringParcel(); + saveParams.userIDs = getUserIds(mUserIdsView); + saveParams.originalIDs = mUserIdsView.getOriginalIDs(); + saveParams.deletedIDs = mUserIdsView.getDeletedIDs(); + saveParams.primaryIDChanged = mUserIdsView.primaryChanged(); + saveParams.moddedKeys = toPrimitiveArray(mKeysView.getNeedsSavingArray()); + saveParams.deletedKeys = mKeysView.getDeletedKeys(); + saveParams.keysExpiryDates = getKeysExpiryDates(mKeysView); + saveParams.keysUsages = getKeysUsages(mKeysView); + saveParams.newPassPhrase = mNewPassPhrase; + saveParams.oldPassPhrase = mCurrentPassPhrase; + saveParams.newKeys = toPrimitiveArray(mKeysView.getNewKeysArray()); + saveParams.keys = getKeys(mKeysView); + + // fill values for this action Bundle data = new Bundle(); - data.putString(KeychainIntentService.SAVE_KEYRING_CURRENT_PASSPHRASE, - mCurrentPassPhrase); - data.putString(KeychainIntentService.SAVE_KEYRING_NEW_PASSPHRASE, mNewPassPhrase); - data.putStringArrayList(KeychainIntentService.SAVE_KEYRING_USER_IDS, - getUserIds(mUserIdsView)); - ArrayList keys = getKeys(mKeysView); - data.putByteArray(KeychainIntentService.SAVE_KEYRING_KEYS, - PgpConversionHelper.PGPSecretKeyArrayListToBytes(keys)); - ArrayList dKeys = mKeysView.getDeletedKeys(); - byte[] tmp = null; - if (dKeys.size() != 0) - tmp = PgpConversionHelper.PGPSecretKeyArrayListToBytes(dKeys); - data.putByteArray(KeychainIntentService.SAVE_KEYRING_DELETED_KEYS, - tmp); - data.putIntegerArrayList(KeychainIntentService.SAVE_KEYRING_KEYS_USAGES, - getKeysUsages(mKeysView)); - data.putSerializable(KeychainIntentService.SAVE_KEYRING_KEYS_EXPIRY_DATES, - getKeysExpiryDates(mKeysView)); - data.putLong(KeychainIntentService.SAVE_KEYRING_MASTER_KEY_ID, getMasterKeyId()); data.putBoolean(KeychainIntentService.SAVE_KEYRING_CAN_SIGN, masterCanSign); - data.putStringArrayList(KeychainIntentService.SAVE_KEYRING_DELETED_IDS, - mUserIdsView.getDeletedIDs()); - data.putStringArrayList(KeychainIntentService.SAVE_KEYRING_ORIGINAL_IDS, - mUserIdsView.getOriginalIDs()); - data.putBooleanArray(KeychainIntentService.SAVE_KEYRING_MODDED_KEYS, - toPrimitiveArray(mKeysView.getNeedsSavingArray())); - data.putBoolean(KeychainIntentService.SAVE_KEYRING_PRIMARY_ID_CHANGED, - mUserIdsView.primaryChanged()); - data.putBooleanArray(KeychainIntentService.SAVE_KEYRING_NEW_KEYS, - toPrimitiveArray(mKeysView.getNewKeysArray())); + data.putParcelable(KeychainIntentService.SAVE_KEYRING_PARCEL, saveParams); intent.putExtra(KeychainIntentService.EXTRA_DATA, data); From b06e4d827a65838cc7085040ea465c1163f1cdd9 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Fri, 7 Mar 2014 15:36:01 +0000 Subject: [PATCH 045/253] uncomment, use Parcel. Builds, not functional --- .../keychain/pgp/PgpKeyOperation.java | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index f744fbcec..87f8fdebb 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -383,33 +383,32 @@ public class PgpKeyOperation { do we need to remove and add in? */ - /* - for (PGPSecretKey dKey : deleted_keys) { + for (PGPSecretKey dKey : saveParcel.deletedKeys) { mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey); } masterKey = mKR.getSecretKey(); PGPPublicKey masterPublicKey = masterKey.getPublicKey(); - int usageId = keysUsages.get(0); + int usageId = saveParcel.keysUsages.get(0); boolean canSign; - String mainUserId = userIds.get(0); + String mainUserId = saveParcel.userIDs.get(0); PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( - Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray()); + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(saveParcel.oldPassPhrase.toCharArray()); PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor); updateProgress(R.string.progress_certifying_master_key, 20, 100); - for (String delID : deletedIDs) { + for (String delID : saveParcel.deletedIDs) { masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, delID); } int user_id_index = 0; - if (primaryIDChanged) { + if (saveParcel.primaryIDChanged) { ArrayList> sigList = new ArrayList>(); - for (String userId : userIds) { - String orig_id = OriginalIDs.get(user_id_index); + for (String userId : saveParcel.userIDs) { + String orig_id = saveParcel.originalIDs.get(user_id_index); if (orig_id.equals(userId)) { Iterator orig_sigs = masterPublicKey.getSignaturesForID(orig_id); //TODO: make sure this iterator only has signatures we are interested in while (orig_sigs.hasNext()) { @@ -434,8 +433,8 @@ public class PgpKeyOperation { masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, to_add.first, to_add.second); } } else { - for (String userId : userIds) { - String orig_id = OriginalIDs.get(user_id_index); + for (String userId : saveParcel.userIDs) { + String orig_id = saveParcel.originalIDs.get(user_id_index); if (!orig_id.equals(userId)) { PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) @@ -463,10 +462,10 @@ public class PgpKeyOperation { hashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS); hashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS); - if (keysExpiryDates.get(0) != null) { + if (saveParcel.keysExpiryDates.get(0) != null) { GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC")); creationDate.setTime(masterPublicKey.getCreationTime()); - GregorianCalendar expiryDate = keysExpiryDates.get(0); + GregorianCalendar expiryDate = saveParcel.keysExpiryDates.get(0); //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c //here we purposefully ignore partial days in each date - long type has no fractional part! long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000); @@ -490,15 +489,15 @@ public class PgpKeyOperation { PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( PGPEncryptedData.CAST5, sha1Calc) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - newPassPhrase.toCharArray()); + saveParcel.newPassPhrase.toCharArray()); PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); - for (int i = 0; i < keys.size(); ++i) { - updateProgress(40 + 50 * (i - 1) / (keys.size() - 1), 100); - if (new_keys[i]) { + for (int i = 0; i < saveParcel.keys.size(); ++i) { + updateProgress(40 + 50 * (i - 1) / (saveParcel.keys.size() - 1), 100); + if (saveParcel.newKeys[i]) { } else { @@ -506,15 +505,15 @@ public class PgpKeyOperation { } updateProgress(R.string.progress_adding_sub_keys, 40, 100); - for (int i = 1; i < keys.size(); ++i) { - updateProgress(40 + 50 * (i - 1) / (keys.size() - 1), 100); + for (int i = 1; i < saveParcel.keys.size(); ++i) { + updateProgress(40 + 50 * (i - 1) / (saveParcel.keys.size() - 1), 100); - PGPSecretKey subKey = keys.get(i); + PGPSecretKey subKey = saveParcel.keys.get(i); PGPPublicKey subPublicKey = subKey.getPublicKey(); PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - oldPassPhrase.toCharArray()); + saveParcel.oldPassPhrase.toCharArray()); PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2); // TODO: now used without algorithm and creation time?! (APG 1) @@ -523,7 +522,7 @@ public class PgpKeyOperation { hashedPacketsGen = new PGPSignatureSubpacketGenerator(); unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - usageId = keysUsages.get(i); + usageId = saveParcel.keysUsages.get(i); canSign = (usageId & KeyFlags.SIGN_DATA) > 0; //todo - separate function for this if (canSign) { Date todayDate = new Date(); //both sig times the same @@ -543,10 +542,10 @@ public class PgpKeyOperation { } hashedPacketsGen.setKeyFlags(false, usageId); - if (keysExpiryDates.get(i) != null) { + if (saveParcel.keysExpiryDates.get(i) != null) { GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC")); creationDate.setTime(subPublicKey.getCreationTime()); - GregorianCalendar expiryDate = keysExpiryDates.get(i); + GregorianCalendar expiryDate = saveParcel.keysExpiryDates.get(i); //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c //here we purposefully ignore partial days in each date - long type has no fractional part! long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000); @@ -570,7 +569,6 @@ public class PgpKeyOperation { ProviderHelper.saveKeyRing(mContext, publicKeyRing); updateProgress(R.string.progress_done, 100, 100); - */ } public PGPPublicKeyRing certifyKey(long masterKeyId, long pubKeyId, String passphrase) From 6f0a5d39b2931981fb521f533acb5c2c33514734 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Mon, 10 Mar 2014 23:53:28 +0000 Subject: [PATCH 046/253] start modifying save code --- .../keychain/pgp/PgpKeyOperation.java | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 87f8fdebb..89be90ab8 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -73,6 +73,8 @@ import org.sufficientlysecure.keychain.util.ProgressDialogUpdater; import android.content.Context; import android.util.Pair; +import javax.crypto.SecretKey; + public class PgpKeyOperation { private final Context mContext; private final ProgressDialogUpdater mProgress; @@ -354,6 +356,7 @@ public class PgpKeyOperation { PGPSecretKey masterKey = saveParcel.keys.get(0); PGPSecretKeyRing mKR = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, masterKey.getKeyID()); + PGPPublicKeyRing pKR = ProviderHelper.getPGPPublicKeyRingByKeyId(mContext, masterKey.getKeyID()); if (saveParcel.oldPassPhrase == null) { saveParcel.oldPassPhrase = ""; @@ -497,11 +500,27 @@ public class PgpKeyOperation { for (int i = 0; i < saveParcel.keys.size(); ++i) { updateProgress(40 + 50 * (i - 1) / (saveParcel.keys.size() - 1), 100); - if (saveParcel.newKeys[i]) { - + if (saveParcel.moddedKeys[i]) { +//secretkey.replacepublickey with updated public key +//secretkeyring.insertsecretkey with newly signed secret key } else { - +//else nothing, right? } + if (saveParcel.newKeys[i]) { + //set the passphrase to the old one, so we can update the whole keyring passphrase later + PBESecretKeyEncryptor keyEncryptorOld = new JcePBESecretKeyEncryptorBuilder( + PGPEncryptedData.CAST5, sha1Calc) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( + saveParcel.oldPassPhrase.toCharArray()); + PBESecretKeyDecryptor keyDecryptorBlank = new JcePBESecretKeyDecryptorBuilder() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( + saveParcel.oldPassPhrase.toCharArray()); + saveParcel.keys.set(i, PGPSecretKey.copyWithNewPassword(saveParcel.keys.get(i), + keyDecryptorBlank, keyEncryptorOld)); + } + //finally, update the keyrings + mKR = PGPSecretKeyRing.insertSecretKey(mKR, saveParcel.keys.get(i)); + pKR = PGPPublicKeyRing.insertPublicKey(pKR, saveParcel.keys.get(i).getPublicKey()); } updateProgress(R.string.progress_adding_sub_keys, 40, 100); @@ -560,13 +579,12 @@ public class PgpKeyOperation { keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate()); } - PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing(); - PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing(); -//must copy with new passphrase... new keys will have an empty passphrase... pass in boolean array to mark new key? + //update the passphrase + mKR = PGPSecretKeyRing.copyWithNewPassword(mKR, keyDecryptor, keyEncryptor); updateProgress(R.string.progress_saving_key_ring, 90, 100); - ProviderHelper.saveKeyRing(mContext, secretKeyRing); - ProviderHelper.saveKeyRing(mContext, publicKeyRing); + ProviderHelper.saveKeyRing(mContext, mKR); + ProviderHelper.saveKeyRing(mContext, pKR); updateProgress(R.string.progress_done, 100, 100); } From fdb013a0529e4fcf3b602f856c0290f5626789aa Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Tue, 11 Mar 2014 00:00:19 +0000 Subject: [PATCH 047/253] unused imports --- .../org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 89be90ab8..8ef2a7452 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -29,7 +29,6 @@ import java.util.ArrayList; import java.util.Date; import java.util.GregorianCalendar; import java.util.Iterator; -import java.util.List; import java.util.TimeZone; import org.spongycastle.bcpg.CompressionAlgorithmTags; @@ -66,15 +65,12 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.SaveKeyringParcel; -import org.sufficientlysecure.keychain.util.Log; import org.sufficientlysecure.keychain.util.Primes; import org.sufficientlysecure.keychain.util.ProgressDialogUpdater; import android.content.Context; import android.util.Pair; -import javax.crypto.SecretKey; - public class PgpKeyOperation { private final Context mContext; private final ProgressDialogUpdater mProgress; From 5eb12ef88f93bfadb51ac28e31bcf4935647b827 Mon Sep 17 00:00:00 2001 From: Daniel Hammann Date: Wed, 12 Mar 2014 21:36:42 +0100 Subject: [PATCH 048/253] Squashed commit of the following: commit 1df4d4a9e047f70f284183185d81070e49097ad3 Author: Daniel Hammann Date: Wed Mar 12 21:33:13 2014 +0100 Added Windows section to CheckStyle configuration information part (and splitted Linux and MacOSX up). --- README.md | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4b997c59f..8954bdcda 100644 --- a/README.md +++ b/README.md @@ -156,12 +156,21 @@ See http://source.android.com/source/code-style.html See http://www.androidpolice.com/2009/11/04/auto-formatting-android-xml-files-with-eclipse/ ### Automated syntax check with CheckStyle -* Paste the tools/checkstyle.xml file to ~/.AndroidStudioPreview/config/codestyles/ (in Linux/Unix) - or ~/Library/Preferences/AndroidStudioPreview/codestyles (in Mac OSX) -* Go to Settings (or Preferences in Mac OS X) > Code Style > Java, select OpenPgpChecker, - as well as Code Style > XML and select OpenPgpChecker again. -* Start code inspection and see the results by selecting Analyze > Inspect Code from Android-Studio - or you can directly run checkstyle via cli with .tools/checkstyle. Make sure it's executable first. + +####Linux +1. Paste the `tools/checkstyle.xml` file to `~/.AndroidStudioPreview/config/codestyles/` +2. Go to Settings > Code Style > Java, select OpenPgpChecker, as well as Code Style > XML and select OpenPgpChecker again. +3. Start code inspection and see the results by selecting Analyze > Inspect Code from Android-Studio or you can directly run checkstyle via cli with `.tools/checkstyle`. Make sure it's executable first. + +####Mac OSX +1. Paste the `tools/checkstyle.xml` file to `~/Library/Preferences/AndroidStudioPreview/codestyles` +2. Go to Preferences > Code Style > Java, select OpenPgpChecker, as well as Code Style > XML and select OpenPgpChecker again. +3. Start code inspection and see the results by selecting Analyze > Inspect Code from Android-Studio or you can directly run checkstyle via cli with `.tools/checkstyle`. Make sure it's executable first. + +####Windows +1. Paste the `tools/checkstyle.xml` file to `C:\Users\\.AndroidStudioPreview\config\codestyles` +2. Go to File > Settings > Code Style > Java, select OpenPgpChecker, as well as Code Style > XML and select OpenPgpChecker again. +3. Start code inspection and see the results by selecting Analyze > Inspect Code from Android-Studio. ## Licenses OpenPGP Kechain is licensed under GPLv3+. From 3abe17090e1f0a654c50679231d1232516b2d815 Mon Sep 17 00:00:00 2001 From: gogowitczak Date: Fri, 14 Mar 2014 20:24:27 +0100 Subject: [PATCH 049/253] Added FAQ: specifying connection port for Keyserver. --- OpenPGP-Keychain/src/main/res/raw/help_faq.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/src/main/res/raw/help_faq.html b/OpenPGP-Keychain/src/main/res/raw/help_faq.html index b3d5b3a11..bfd43eafd 100644 --- a/OpenPGP-Keychain/src/main/res/raw/help_faq.html +++ b/OpenPGP-Keychain/src/main/res/raw/help_faq.html @@ -5,8 +5,9 @@ And don't add newlines before or after p tags because of transifex --> -

TODO

-

text

+

How can I specify connection port for Keyserver?

+

Add a new Keyserver (or modify existing one) by going to Preferences -> General -> Keyservers. Enter the port number after the Keyserver address and preceded it by a colon. For example, "p80.pool.sks-keyservers.net:80" (without quotation marks) means that server "p80.pool.sks-keyservers.net" is working on a port 80.

+

Default connection port is 11371 and it doesn't need to be specified.

From e387dd7c54ddfdbe8379b57e00fed567510fd863 Mon Sep 17 00:00:00 2001 From: uberspot Date: Sat, 15 Mar 2014 01:51:01 +0200 Subject: [PATCH 050/253] Fix export for new unified key list #409 --- .../org/sufficientlysecure/keychain/Id.java | 1 + .../keychain/helper/ExportHelper.java | 34 +++++---- .../keychain/pgp/PgpImportExport.java | 71 +++++++++++-------- .../service/KeychainIntentService.java | 50 ++++++------- .../keychain/ui/EditKeyActivity.java | 6 +- .../keychain/ui/KeyListActivity.java | 5 +- .../keychain/ui/KeyListFragment.java | 18 +++-- .../keychain/ui/ViewKeyActivity.java | 7 +- .../src/main/res/values/strings.xml | 1 + 9 files changed, 113 insertions(+), 80 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java index 1d79edd43..784ec340e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Id.java @@ -119,6 +119,7 @@ public final class Id { public static final int secret_key = 0x21070002; public static final int user_id = 0x21070003; public static final int key = 0x21070004; + public static final int public_secret_key = 0x21070005; } public static final class choice { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java index 557d75dbf..03cf936ee 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java @@ -30,12 +30,18 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.compatibility.DialogFragmentWorkaround; +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.dialog.DeleteKeyDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.FileDialogFragment; import org.sufficientlysecure.keychain.util.Log; +import java.lang.reflect.Array; +import java.security.Provider; +import java.util.ArrayList; + public class ExportHelper { protected FileDialogFragment mFileDialog; protected String mExportFilename; @@ -62,8 +68,8 @@ public class ExportHelper { /** * Show dialog where to export keys */ - public void showExportKeysDialog(final long[] rowIds, final int keyType, - final String exportFilename) { + public void showExportKeysDialog(final long[] masterKeyIds, final int keyType, + final String exportFilename, final String checkboxString) { mExportFilename = exportFilename; // Message is received after file is selected @@ -72,9 +78,14 @@ public class ExportHelper { public void handleMessage(Message message) { if (message.what == FileDialogFragment.MESSAGE_OKAY) { Bundle data = message.getData(); + int type = keyType; mExportFilename = data.getString(FileDialogFragment.MESSAGE_DATA_FILENAME); - exportKeys(rowIds, keyType); + if( data.getBoolean(FileDialogFragment.MESSAGE_DATA_CHECKED) ) { + type = Id.type.public_secret_key; + } + + exportKeys(masterKeyIds, type); } } }; @@ -85,7 +96,7 @@ public class ExportHelper { DialogFragmentWorkaround.INTERFACE.runnableRunDelayed(new Runnable() { public void run() { String title = null; - if (rowIds == null) { + if (masterKeyIds == null) { // export all keys title = mActivity.getString(R.string.title_export_keys); } else { @@ -93,15 +104,10 @@ public class ExportHelper { title = mActivity.getString(R.string.title_export_key); } - String message = null; - if (keyType == Id.type.public_key) { - message = mActivity.getString(R.string.specify_file_to_export_to); - } else { - message = mActivity.getString(R.string.specify_file_to_export_secret_keys_to); - } + String message = mActivity.getString(R.string.specify_file_to_export_to); mFileDialog = FileDialogFragment.newInstance(messenger, title, message, - exportFilename, null); + exportFilename, checkboxString); mFileDialog.show(mActivity.getSupportFragmentManager(), "fileDialog"); } @@ -111,7 +117,7 @@ public class ExportHelper { /** * Export keys */ - public void exportKeys(long[] rowIds, int keyType) { + public void exportKeys(long[] masterKeyIds, int keyType) { Log.d(Constants.TAG, "exportKeys started"); // Send all information needed to service to export key in other thread @@ -125,10 +131,10 @@ public class ExportHelper { data.putString(KeychainIntentService.EXPORT_FILENAME, mExportFilename); data.putInt(KeychainIntentService.EXPORT_KEY_TYPE, keyType); - if (rowIds == null) { + if (masterKeyIds == null) { data.putBoolean(KeychainIntentService.EXPORT_ALL, true); } else { - data.putLongArray(KeychainIntentService.EXPORT_KEY_RING_ROW_ID, rowIds); + data.putLongArray(KeychainIntentService.EXPORT_KEY_RING_MASTER_KEY_ID, masterKeyIds); } intent.putExtra(KeychainIntentService.EXTRA_DATA, data); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java index dbfa521e5..0e0fdec83 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpImportExport.java @@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.util.KeyServer.AddKeyException; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.security.Provider; import java.util.ArrayList; import java.util.List; @@ -158,60 +159,68 @@ public class PgpImportExport { return returnData; } - public Bundle exportKeyRings(ArrayList keyRingRowIds, int keyType, + public Bundle exportKeyRings(ArrayList publicKeyRingMasterIds, ArrayList secretKeyRingMasterIds, OutputStream outStream) throws PgpGeneralException, PGPException, IOException { Bundle returnData = new Bundle(); - int rowIdsSize = keyRingRowIds.size(); + int masterKeyIdsSize = publicKeyRingMasterIds.size() + secretKeyRingMasterIds.size(); + int progress = 0; updateProgress( mContext.getResources().getQuantityString(R.plurals.progress_exporting_key, - rowIdsSize), 0, 100); + masterKeyIdsSize), 0, 100); if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { throw new PgpGeneralException( mContext.getString(R.string.error_external_storage_not_ready)); } - // For each row id - for (int i = 0; i < rowIdsSize; ++i) { + // For each public masterKey id + for (long pubKeyMasterId : publicKeyRingMasterIds) { + progress++; // Create an output stream ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream); arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext)); - // If the keyType is secret get the PGPSecretKeyRing - // based on the row id and encode it to the output - if (keyType == Id.type.secret_key) { - updateProgress(i * 100 / rowIdsSize / 2, 100); - PGPSecretKeyRing secretKeyRing = - ProviderHelper.getPGPSecretKeyRingByRowId(mContext, keyRingRowIds.get(i)); + updateProgress(progress * 100 / masterKeyIdsSize, 100); + PGPPublicKeyRing publicKeyRing = + ProviderHelper.getPGPPublicKeyRingByMasterKeyId(mContext, pubKeyMasterId); - if (secretKeyRing != null) { - secretKeyRing.encode(arOutStream); - } - if (mKeychainServiceListener.hasServiceStopped()) { - arOutStream.close(); - return null; - } - } else { - updateProgress(i * 100 / rowIdsSize, 100); - PGPPublicKeyRing publicKeyRing = - ProviderHelper.getPGPPublicKeyRingByRowId(mContext, keyRingRowIds.get(i)); + if (publicKeyRing != null) { + publicKeyRing.encode(arOutStream); + } - if (publicKeyRing != null) { - publicKeyRing.encode(arOutStream); - } - - if (mKeychainServiceListener.hasServiceStopped()) { - arOutStream.close(); - return null; - } + if (mKeychainServiceListener.hasServiceStopped()) { + arOutStream.close(); + return null; } arOutStream.close(); } - returnData.putInt(KeychainIntentService.RESULT_EXPORT, rowIdsSize); + // For each secret masterKey id + for (long secretKeyMasterId : secretKeyRingMasterIds) { + progress++; + // Create an output stream + ArmoredOutputStream arOutStream = new ArmoredOutputStream(outStream); + arOutStream.setHeader("Version", PgpHelper.getFullVersion(mContext)); + + updateProgress(progress * 100 / masterKeyIdsSize, 100); + PGPSecretKeyRing secretKeyRing = + ProviderHelper.getPGPSecretKeyRingByMasterKeyId(mContext, secretKeyMasterId); + + if (secretKeyRing != null) { + secretKeyRing.encode(arOutStream); + } + if (mKeychainServiceListener.hasServiceStopped()) { + arOutStream.close(); + return null; + } + + arOutStream.close(); + } + + returnData.putInt(KeychainIntentService.RESULT_EXPORT, masterKeyIdsSize); updateProgress(R.string.progress_done, 100, 100); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 4e5812202..a44b121ec 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -131,7 +131,6 @@ public class KeychainIntentService extends IntentService public static final String EXPORT_KEY_TYPE = "export_key_type"; public static final String EXPORT_ALL = "export_all"; public static final String EXPORT_KEY_RING_MASTER_KEY_ID = "export_key_ring_id"; - public static final String EXPORT_KEY_RING_ROW_ID = "export_key_rind_row_id"; // upload key public static final String UPLOAD_KEY_SERVER = "upload_key_server"; @@ -660,16 +659,11 @@ public class KeychainIntentService extends IntentService if (data.containsKey(EXPORT_KEY_TYPE)) { keyType = data.getInt(EXPORT_KEY_TYPE); } - + long[] masterKeyIds = data.getLongArray(EXPORT_KEY_RING_MASTER_KEY_ID); String outputFile = data.getString(EXPORT_FILENAME); - long[] rowIds = new long[0]; - - // If not exporting all keys get the rowIds of the keys to export from the intent + // If not exporting all keys get the masterKeyIds of the keys to export from the intent boolean exportAll = data.getBoolean(EXPORT_ALL); - if (!exportAll) { - rowIds = data.getLongArray(EXPORT_KEY_RING_ROW_ID); - } /* Operation */ @@ -678,30 +672,38 @@ public class KeychainIntentService extends IntentService throw new PgpGeneralException(getString(R.string.error_external_storage_not_ready)); } - // OutputStream - FileOutputStream outStream = new FileOutputStream(outputFile); + ArrayList publicMasterKeyIds = new ArrayList(); + ArrayList secretMasterKeyIds = new ArrayList(); + ArrayList allPublicMasterKeyIds = ProviderHelper.getPublicKeyRingsMasterKeyIds(this); + ArrayList allSecretMasterKeyIds = ProviderHelper.getSecretKeyRingsMasterKeyIds(this); - ArrayList keyRingRowIds = new ArrayList(); if (exportAll) { - - // get all key ring row ids based on export type - if (keyType == Id.type.public_key) { - keyRingRowIds = ProviderHelper.getPublicKeyRingsRowIds(this); - } else { - keyRingRowIds = ProviderHelper.getSecretKeyRingsRowIds(this); + // get all public key ring MasterKey ids + if (keyType == Id.type.public_key || keyType == Id.type.public_secret_key) { + publicMasterKeyIds = allPublicMasterKeyIds; + } + // get all secret key ring MasterKey ids + if (keyType == Id.type.secret_key || keyType == Id.type.public_secret_key) { + secretMasterKeyIds = allSecretMasterKeyIds; } } else { - for (long rowId : rowIds) { - keyRingRowIds.add(rowId); + + for (long masterKeyId : masterKeyIds) { + if ((keyType == Id.type.public_key || keyType == Id.type.public_secret_key) + && allPublicMasterKeyIds.contains(masterKeyId)) { + publicMasterKeyIds.add(masterKeyId); + } + if ((keyType == Id.type.secret_key || keyType == Id.type.public_secret_key) + && allSecretMasterKeyIds.contains(masterKeyId)) { + secretMasterKeyIds.add(masterKeyId); + } } } - Bundle resultData; - PgpImportExport pgpImportExport = new PgpImportExport(this, this, this); - - resultData = pgpImportExport - .exportKeyRings(keyRingRowIds, keyType, outStream); + Bundle resultData = pgpImportExport + .exportKeyRings(publicMasterKeyIds, secretMasterKeyIds, + new FileOutputStream(outputFile)); if (mIsCanceled) { boolean isDeleted = new File(outputFile).delete(); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index edf980773..7edb61b09 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -322,8 +322,10 @@ public class EditKeyActivity extends ActionBarActivity { cancelClicked(); return true; case R.id.menu_key_edit_export_file: - long[] ids = new long[]{Long.valueOf(mDataUri.getLastPathSegment())}; - mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC); + long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri); + long[] ids = new long[]{masterKeyId}; + mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC, + null); return true; case R.id.menu_key_edit_delete: { // Message is received after key is deleted diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java index 078b998e1..5ff4dbdeb 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java @@ -58,8 +58,7 @@ public class KeyListActivity extends DrawerActivity { return true; case R.id.menu_key_list_export: - // TODO fix this for unified keylist - mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.Path.APP_DIR_FILE_PUB); + mExportHelper.showExportKeysDialog(null, Id.type.public_key, Constants.Path.APP_DIR_FILE_PUB, null); return true; case R.id.menu_key_list_create: @@ -71,7 +70,7 @@ public class KeyListActivity extends DrawerActivity { return true; case R.id.menu_key_list_secret_export: - mExportHelper.showExportKeysDialog(null, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC); + mExportHelper.showExportKeysDialog(null, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC, null); return true; default: diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index 5ac59965d..cac8b7046 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -49,6 +49,7 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; import org.sufficientlysecure.keychain.provider.KeychainDatabase; +import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.ui.adapter.HighlightQueryCursorAdapter; import org.sufficientlysecure.keychain.ui.dialog.DeleteKeyDialogFragment; import org.sufficientlysecure.keychain.util.Log; @@ -183,13 +184,22 @@ public class KeyListFragment extends Fragment break; } case R.id.menu_key_list_multi_export: { - // todo: public/secret needs to be handled differently here ids = mStickyList.getWrappedList().getCheckedItemIds(); + long[] masterKeyIds = new long[2*ids.length]; + ArrayList allPubRowIds = + ProviderHelper.getPublicKeyRingsRowIds(getActivity()); + for (int i = 0; i < ids.length; i++) { + if (allPubRowIds.contains(ids[i])) { + masterKeyIds[i] = ProviderHelper.getPublicMasterKeyId(getActivity(), ids[i]); + } else { + masterKeyIds[i] = ProviderHelper.getSecretMasterKeyId(getActivity(), ids[i]); + } + } ExportHelper mExportHelper = new ExportHelper((ActionBarActivity) getActivity()); mExportHelper - .showExportKeysDialog(ids, - Id.type.public_key, - Constants.Path.APP_DIR_FILE_PUB); + .showExportKeysDialog(masterKeyIds, Id.type.public_key, + Constants.Path.APP_DIR_FILE_PUB, + getString(R.string.also_export_secret_keys)); break; } case R.id.menu_key_list_multi_select_all: { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index 93bb83003..c4097403c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -124,8 +124,11 @@ public class ViewKeyActivity extends ActionBarActivity { uploadToKeyserver(mDataUri); return true; case R.id.menu_key_view_export_file: - long[] ids = new long[]{Long.valueOf(mDataUri.getLastPathSegment())}; - mExportHelper.showExportKeysDialog(ids, Id.type.public_key, Constants.Path.APP_DIR_FILE_PUB); + long masterKeyId = + ProviderHelper.getPublicMasterKeyId(this, Long.valueOf(mDataUri.getLastPathSegment())); + long[] ids = new long[]{masterKeyId}; + mExportHelper.showExportKeysDialog(ids, Id.type.public_key, + Constants.Path.APP_DIR_FILE_PUB, null); return true; case R.id.menu_key_view_share_default_fingerprint: shareKey(mDataUri, true); diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index 77891d6c7..947801217 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -219,6 +219,7 @@ Do you really want to delete the key \'%s\'?\nYou can\'t undo this! Do you really want to delete all selected keys?\nYou can\'t undo this! Do you really want to delete the SECRET key \'%s\'?\nYou can\'t undo this! + Also export secret keys? Successfully added %d key From 207010dd86c11db2db04f426d434b68784baacbe Mon Sep 17 00:00:00 2001 From: gogowitczak Date: Sat, 15 Mar 2014 21:16:34 +0100 Subject: [PATCH 051/253] Keyserver query now uses machine readable output for search and get. Added separate function for converting algorithm integer ID to String. --- .../keychain/pgp/PgpKeyHelper.java | 4 +- .../ui/adapter/ImportKeysListEntry.java | 45 +++-- .../keychain/util/HkpKeyServer.java | 191 +++++++++++------- 3 files changed, 149 insertions(+), 91 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index 436c26700..b93c68677 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -477,7 +477,7 @@ public class PgpKeyHelper { * @return */ public static String convertKeyIdToHex(long keyId) { - return "0x" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId); + return "0x" + ((keyId >> 32) > 0 ? convertKeyIdToHex32bit(keyId >> 32) : "") + convertKeyIdToHex32bit(keyId); } private static String convertKeyIdToHex32bit(long keyId) { @@ -498,7 +498,7 @@ public class PgpKeyHelper { int len = hexString.length(); String s2 = hexString.substring(len - 8); String s1 = hexString.substring(0, len - 8); - return (Long.parseLong(s1, 16) << 32) | Long.parseLong(s2, 16); + return ((!s1.isEmpty() ? Long.parseLong(s1, 16) << 32 : 0) | Long.parseLong(s2, 16)); } /** diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java index 19f0d1eaf..13309435d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui.adapter; import android.os.Parcel; import android.os.Parcelable; +import android.util.SparseArray; import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSecretKeyRing; @@ -171,20 +172,34 @@ public class ImportKeysListEntry implements Serializable, Parcelable { .getFingerprint(), true); this.hexKeyId = PgpKeyHelper.convertKeyIdToHex(keyId); this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength(); - int algorithm = pgpKeyRing.getPublicKey().getAlgorithm(); - if (algorithm == PGPPublicKey.RSA_ENCRYPT || algorithm == PGPPublicKey.RSA_GENERAL - || algorithm == PGPPublicKey.RSA_SIGN) { - this.algorithm = "RSA"; - } else if (algorithm == PGPPublicKey.DSA) { - this.algorithm = "DSA"; - } else if (algorithm == PGPPublicKey.ELGAMAL_ENCRYPT - || algorithm == PGPPublicKey.ELGAMAL_GENERAL) { - this.algorithm = "ElGamal"; - } else if (algorithm == PGPPublicKey.EC || algorithm == PGPPublicKey.ECDSA) { - this.algorithm = "ECC"; - } else { - // TODO: with resources - this.algorithm = "unknown"; - } + final int algorithm = pgpKeyRing.getPublicKey().getAlgorithm(); + this.algorithm = getAlgorithmFromId(algorithm); } + + /** + * Based on OpenPGP Message Format + */ + private final static SparseArray ALGORITHM_IDS = new SparseArray() {{ + put(-1, "unknown"); // TODO: with resources + put(0, "unencrypted"); + put(PGPPublicKey.RSA_GENERAL, "RSA"); + put(PGPPublicKey.RSA_ENCRYPT, "RSA"); + put(PGPPublicKey.RSA_SIGN, "RSA"); + put(PGPPublicKey.ELGAMAL_ENCRYPT, "ElGamal"); + put(PGPPublicKey.ELGAMAL_GENERAL, "ElGamal"); + put(PGPPublicKey.DSA, "DSA"); + put(PGPPublicKey.EC, "ECC"); + put(PGPPublicKey.ECDSA, "ECC"); + put(PGPPublicKey.ECDH, "ECC"); + }}; + + /** + * Based on OpenPGP Message Format + */ + public static String getAlgorithmFromId(int algorithmId) { + return (ALGORITHM_IDS.get(algorithmId) != null ? ALGORITHM_IDS.get(algorithmId) : ALGORITHM_IDS.get(-1)); + } + } + + diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java index 42fb03a3e..7ec532f5b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java @@ -43,6 +43,8 @@ import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry.getAlgorithmFromId; + /** * TODO: * rewrite to use machine readable output. @@ -74,16 +76,55 @@ public class HkpKeyServer extends KeyServer { private String mHost; private short mPort; - // example: - // pub 2048R/9F5C9090 2009-08-17 Jörg Runge - // <joerg@joergrunge.de> - public static final Pattern PUB_KEY_LINE = Pattern - .compile( - "pub +([0-9]+)([a-z]+)/.*?0x([0-9a-z]+).*? +([0-9-]+) +(.+)[\n\r]+((?: +.+[\n\r]+)*)", - Pattern.CASE_INSENSITIVE); - public static final Pattern USER_ID_LINE = Pattern.compile("^ +(.+)$", Pattern.MULTILINE - | Pattern.CASE_INSENSITIVE); + /** + * pub:%keyid%:%algo%:%keylen%:%creationdate%:%expirationdate%:%flags% + *
    + *
  • %keyid% = this is either the fingerprint or the key ID of the key. + * Either the 16-digit or 8-digit key IDs are acceptable, but obviously the fingerprint is best.
  • + *
  • %algo% = the algorithm number, (i.e. 1==RSA, 17==DSA, etc). See RFC-2440
  • + *
  • %keylen% = the key length (i.e. 1024, 2048, 4096, etc.)
  • + *
  • %creationdate% = creation date of the key in standard RFC-2440 form (i.e. number of seconds since 1/1/1970 UTC time)
  • + *
  • %expirationdate% = expiration date of the key in standard RFC-2440 form (i.e. number of seconds since 1/1/1970 UTC time)
  • + *
  • %flags% = letter codes to indicate details of the key, if any. Flags may be in any order. The + * meaning of "disabled" is implementation-specific. Note that individual flags may be unimplemented, so + * the absence of a given flag does not necessarily mean the absence of the detail. + *
      + *
    • r == revoked
    • + *
    • d == disabled
    • + *
    • e == expired
    • + *
    + *
  • + *
+ * + * + * @see 5.2. Machine Readable Indexes in Internet-Draft OpenPGP HTTP Keyserver Protocol Document + */ + public static final Pattern PUB_KEY_LINE = Pattern + .compile("pub:([0-9a-fA-F]+):([0-9]+):([0-9]+):([0-9]+):([0-9]*):([rde]*)[ \n\r]*" // pub line + + "(uid:(.*):([0-9]+):([0-9]*):([rde]*))+", // one or more uid lines + Pattern.CASE_INSENSITIVE); + + /** + * uid:%escaped uid string%:%creationdate%:%expirationdate%:%flags% + *
    + *
  • %escaped uid string% = the user ID string, with HTTP %-escaping for anything that isn't 7-bit + * safe as well as for the ":" character. Any other characters may be escaped, as desired.
  • + *
  • %creationdate% = creation date of the key in standard RFC-2440 form (i.e. number of seconds since 1/1/1970 UTC time)
  • + *
  • %expirationdate% = expiration date of the key in standard RFC-2440 form (i.e. number of seconds since 1/1/1970 UTC time)
  • + *
  • %flags% = letter codes to indicate details of the key, if any. Flags may be in any order. The + * meaning of "disabled" is implementation-specific. Note that individual flags may be unimplemented, so + * the absence of a given flag does not necessarily mean the absence of the detail. + *
      + *
    • r == revoked
    • + *
    • d == disabled
    • + *
    • e == expired
    • + *
    + *
  • + *
+ */ + public static final Pattern UID_LINE = Pattern + .compile("uid:(.*):([0-9]+):([0-9]*):([rde]*)", + Pattern.CASE_INSENSITIVE); private static final short PORT_DEFAULT = 11371; @@ -158,82 +199,84 @@ public class HkpKeyServer extends KeyServer { throw new QueryException("querying server(s) for '" + mHost + "' failed"); } - @Override - public ArrayList search(String query) throws QueryException, TooManyResponses, - InsufficientQuery { - ArrayList results = new ArrayList(); + @Override + public ArrayList search(String query) throws QueryException, TooManyResponses, + InsufficientQuery { + ArrayList results = new ArrayList(); - if (query.length() < 3) { - throw new InsufficientQuery(); - } + if (query.length() < 3) { + throw new InsufficientQuery(); + } - String encodedQuery; - try { - encodedQuery = URLEncoder.encode(query, "utf8"); - } catch (UnsupportedEncodingException e) { - return null; - } - String request = "/pks/lookup?op=index&search=" + encodedQuery; + String encodedQuery; + try { + encodedQuery = URLEncoder.encode(query, "utf8"); + } catch (UnsupportedEncodingException e) { + return null; + } + String request = "/pks/lookup?op=index&search=" + encodedQuery + "&options=mr"; - String data = null; - try { - data = query(request); - } catch (HttpError e) { - if (e.getCode() == 404) { - return results; - } else { - if (e.getData().toLowerCase(Locale.US).contains("no keys found")) { - return results; - } else if (e.getData().toLowerCase(Locale.US).contains("too many")) { - throw new TooManyResponses(); - } else if (e.getData().toLowerCase(Locale.US).contains("insufficient")) { - throw new InsufficientQuery(); - } - } - throw new QueryException("querying server(s) for '" + mHost + "' failed"); - } + String data = null; + try { + data = query(request); + } catch (HttpError e) { + if (e.getCode() == 404) { + return results; + } else { + if (e.getData().toLowerCase(Locale.US).contains("no keys found")) { + return results; + } else if (e.getData().toLowerCase(Locale.US).contains("too many")) { + throw new TooManyResponses(); + } else if (e.getData().toLowerCase(Locale.US).contains("insufficient")) { + throw new InsufficientQuery(); + } + } + throw new QueryException("querying server(s) for '" + mHost + "' failed"); + } - Matcher matcher = PUB_KEY_LINE.matcher(data); - while (matcher.find()) { - ImportKeysListEntry info = new ImportKeysListEntry(); - info.bitStrength = Integer.parseInt(matcher.group(1)); - info.algorithm = matcher.group(2); - info.hexKeyId = "0x" + matcher.group(3); - info.keyId = PgpKeyHelper.convertHexToKeyId(matcher.group(3)); - String chunks[] = matcher.group(4).split("-"); + final Matcher matcher = PUB_KEY_LINE.matcher(data); + while (matcher.find()) { + final ImportKeysListEntry info = new ImportKeysListEntry(); + info.bitStrength = Integer.parseInt(matcher.group(3)); + final int algorithmId = Integer.decode(matcher.group(2)); + info.algorithm = getAlgorithmFromId(algorithmId); - GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC")); - tmpGreg.set(Integer.parseInt(chunks[0]), Integer.parseInt(chunks[1]), - Integer.parseInt(chunks[2])); - info.date = tmpGreg.getTime(); - info.userIds = new ArrayList(); - if (matcher.group(5).startsWith("*** KEY")) { - info.revoked = true; - } else { - String tmp = matcher.group(5).replaceAll("<.*?>", ""); - tmp = Html.fromHtml(tmp).toString(); - info.userIds.add(tmp); - } - if (matcher.group(6).length() > 0) { - Matcher matcher2 = USER_ID_LINE.matcher(matcher.group(6)); - while (matcher2.find()) { - String tmp = matcher2.group(1).replaceAll("<.*?>", ""); - tmp = Html.fromHtml(tmp).toString(); - info.userIds.add(tmp); - } - } - results.add(info); - } + info.hexKeyId = "0x" + matcher.group(1); + info.keyId = PgpKeyHelper.convertHexToKeyId(matcher.group(1)); - return results; - } + final long creationDate = Long.parseLong(matcher.group(4)); + final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + tmpGreg.setTimeInMillis(creationDate*1000); + info.date = tmpGreg.getTime(); + + info.revoked = matcher.group(6).contains("r"); + info.userIds = new ArrayList(); + + final String uidLines = matcher.group(7); + final Matcher uidMatcher = UID_LINE.matcher(uidLines); + while (uidMatcher.find()) { + String tmp = uidMatcher.group(1).replaceAll("<.*?>", ""); + tmp = Html.fromHtml(tmp).toString().trim(); + if (tmp.contains("%")) + { + try { + tmp = (URLDecoder.decode(tmp, "UTF8")); // converts String like "Universit%C3%A4t" to a proper form "Universität". + } catch (UnsupportedEncodingException ignored) { + } + } + info.userIds.add(tmp); + } + results.add(info); + } + return results; + } @Override public String get(long keyId) throws QueryException { HttpClient client = new DefaultHttpClient(); try { HttpGet get = new HttpGet("http://" + mHost + ":" + mPort - + "/pks/lookup?op=get&search=" + PgpKeyHelper.convertKeyIdToHex(keyId)); + + "/pks/lookup?op=get&search=" + PgpKeyHelper.convertKeyIdToHex(keyId) + "&options=mr"); HttpResponse response = client.execute(get); if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { From 5277e4c1e76e185132716b0a807066c5abbf340c Mon Sep 17 00:00:00 2001 From: gogowitczak Date: Sat, 15 Mar 2014 22:01:37 +0100 Subject: [PATCH 052/253] removed TODO comment --- .../org/sufficientlysecure/keychain/util/HkpKeyServer.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java index 7ec532f5b..0839fc494 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java @@ -45,13 +45,6 @@ import java.util.regex.Pattern; import static org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry.getAlgorithmFromId; -/** - * TODO: - * rewrite to use machine readable output. - *

- * see http://tools.ietf.org/html/draft-shaw-openpgp-hkp-00#section-5 - * https://github.com/openpgp-keychain/openpgp-keychain/issues/259 - */ public class HkpKeyServer extends KeyServer { private static class HttpError extends Exception { private static final long serialVersionUID = 1718783705229428893L; From cea62b1857a9b5dbade8dcff6009f72e309b7469 Mon Sep 17 00:00:00 2001 From: uberspot Date: Sun, 16 Mar 2014 23:35:14 +0200 Subject: [PATCH 053/253] Lock drawer as open in tablets --- .../keychain/helper/ActionBarHelper.java | 1 - .../keychain/ui/DrawerActivity.java | 84 ++-- .../keychain/ui/KeyListActivity.java | 3 + .../layout-large/api_apps_list_activity.xml | 20 + .../res/layout-large/decrypt_activity.xml | 19 + .../res/layout-large/encrypt_activity.xml | 19 + .../res/layout-large/import_keys_activity.xml | 21 + .../res/layout-large/key_list_activity.xml | 18 + .../res/layout/api_apps_list_activity.xml | 11 +- .../main/res/layout/api_apps_list_content.xml | 14 + .../src/main/res/layout/decrypt_activity.xml | 201 +-------- .../src/main/res/layout/decrypt_content.xml | 205 +++++++++ .../src/main/res/layout/drawer_list.xml | 2 +- .../src/main/res/layout/encrypt_activity.xml | 394 +---------------- .../src/main/res/layout/encrypt_content.xml | 398 ++++++++++++++++++ .../main/res/layout/import_keys_activity.xml | 53 +-- .../main/res/layout/import_keys_content.xml | 56 +++ .../src/main/res/layout/key_list_activity.xml | 11 +- .../src/main/res/layout/key_list_content.xml | 14 + .../src/main/res/values-large/dimens.xml | 4 + .../src/main/res/values/dimens.xml | 5 + 21 files changed, 862 insertions(+), 691 deletions(-) create mode 100644 OpenPGP-Keychain/src/main/res/layout-large/api_apps_list_activity.xml create mode 100644 OpenPGP-Keychain/src/main/res/layout-large/decrypt_activity.xml create mode 100644 OpenPGP-Keychain/src/main/res/layout-large/encrypt_activity.xml create mode 100644 OpenPGP-Keychain/src/main/res/layout-large/import_keys_activity.xml create mode 100644 OpenPGP-Keychain/src/main/res/layout-large/key_list_activity.xml create mode 100644 OpenPGP-Keychain/src/main/res/layout/api_apps_list_content.xml create mode 100644 OpenPGP-Keychain/src/main/res/layout/decrypt_content.xml create mode 100644 OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml create mode 100644 OpenPGP-Keychain/src/main/res/layout/import_keys_content.xml create mode 100644 OpenPGP-Keychain/src/main/res/layout/key_list_content.xml create mode 100644 OpenPGP-Keychain/src/main/res/values-large/dimens.xml create mode 100644 OpenPGP-Keychain/src/main/res/values/dimens.xml diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java index 91e50637e..a26df556d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ActionBarHelper.java @@ -37,7 +37,6 @@ public class ActionBarHelper { * @param activity */ public static void setBackButton(ActionBarActivity activity) { - // set actionbar without home button if called from another app final ActionBar actionBar = activity.getSupportActionBar(); Log.d(Constants.TAG, "calling package (only set when using startActivityForResult)=" + activity.getCallingPackage()); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java index c0fd53007..985f6c309 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java @@ -21,18 +21,17 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.res.Configuration; +import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.ActionBarDrawerToggle; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarActivity; import android.view.*; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ListView; -import android.widget.TextView; +import android.widget.*; import com.beardedhen.androidbootstrap.FontAwesomeText; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper; import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity; public class DrawerActivity extends ActionBarActivity { @@ -42,6 +41,7 @@ public class DrawerActivity extends ActionBarActivity { private CharSequence mDrawerTitle; private CharSequence mTitle; + private boolean mIsDrawerLocked = false; private static Class[] mItemsClass = new Class[]{KeyListActivity.class, EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class, @@ -55,10 +55,22 @@ public class DrawerActivity extends ActionBarActivity { mDrawerTitle = getString(R.string.app_name); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerList = (ListView) findViewById(R.id.left_drawer); + ViewGroup viewGroup = (ViewGroup) findViewById(R.id.content_frame); + int leftMarginLoaded = ((ViewGroup.MarginLayoutParams) viewGroup.getLayoutParams()).leftMargin; + int leftMarginInTablets = (int) getResources().getDimension(R.dimen.drawer_size); + int errorInMarginAllowed = 5; - // set a custom shadow that overlays the main content when the drawer - // opens - mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); + // if the left margin of the loaded layout is close to the + // one used in tablets then set drawer as open and locked + if( Math.abs(leftMarginLoaded - leftMarginInTablets) < errorInMarginAllowed) { + mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN, mDrawerList); + mDrawerLayout.setScrimColor(Color.TRANSPARENT); + mIsDrawerLocked = true; + } else { + // set a custom shadow that overlays the main content when the drawer opens + mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); + mIsDrawerLocked = false; + } NavItem mItemIconTexts[] = new NavItem[]{ new NavItem("fa-user", getString(R.string.nav_contacts)), @@ -73,8 +85,11 @@ public class DrawerActivity extends ActionBarActivity { mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); // enable ActionBar app icon to behave as action to toggle nav drawer - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setHomeButtonEnabled(true); + // if the drawer is not locked + if ( !mIsDrawerLocked ) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + getSupportActionBar().setHomeButtonEnabled(true); + } // ActionBarDrawerToggle ties together the the proper interactions // between the sliding drawer and the action bar app icon @@ -86,19 +101,8 @@ public class DrawerActivity extends ActionBarActivity { ) { public void onDrawerClosed(View view) { getSupportActionBar().setTitle(mTitle); - // creates call to onPrepareOptionsMenu() - supportInvalidateOptionsMenu(); - // call intent activity if selected - if (mSelectedItem != null) { - finish(); - overridePendingTransition(0, 0); - - Intent intent = new Intent(DrawerActivity.this, mSelectedItem); - startActivity(intent); - // disable animation of activity start - overridePendingTransition(0, 0); - } + callIntentForSelectedItem(); } public void onDrawerOpened(View drawerView) { @@ -108,13 +112,37 @@ public class DrawerActivity extends ActionBarActivity { supportInvalidateOptionsMenu(); } }; - mDrawerLayout.setDrawerListener(mDrawerToggle); + if ( !mIsDrawerLocked ) { + mDrawerLayout.setDrawerListener(mDrawerToggle); + } + if ( mIsDrawerLocked ) { + // If the drawer is locked open make it un-focusable + // so that it doesn't consume all the Back button presses + mDrawerLayout.setFocusableInTouchMode(false); + } // if (savedInstanceState == null) { // selectItem(0); // } } + private void callIntentForSelectedItem() { + // creates call to onPrepareOptionsMenu() + supportInvalidateOptionsMenu(); + + // call intent activity if selected + if (mSelectedItem != null) { + finish(); + overridePendingTransition(0, 0); + + Intent intent = new Intent(this, mSelectedItem); + startActivity(intent); + + // disable animation of activity start + overridePendingTransition(0, 0); + } + } + @Override public boolean onCreateOptionsMenu(Menu menu) { menu.add(42, MENU_ID_PREFERENCE, 100, R.string.menu_preferences); @@ -185,10 +213,18 @@ public class DrawerActivity extends ActionBarActivity { private void selectItem(int position) { // update selected item and title, then close the drawer mDrawerList.setItemChecked(position, true); - // setTitle(mDrawerTitles[position]); - mDrawerLayout.closeDrawer(mDrawerList); // set selected class mSelectedItem = mItemsClass[position]; + + // setTitle(mDrawerTitles[position]); + // If drawer isn't locked just close the drawer and + // it will move to the selected item by itself (via drawer toggle listener) + if ( !mIsDrawerLocked ) { + mDrawerLayout.closeDrawer(mDrawerList); + // else move to the selected item yourself + } else { + callIntentForSelectedItem(); + } } /** diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java index 5ff4dbdeb..57709350e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java @@ -53,8 +53,11 @@ public class KeyListActivity extends DrawerActivity { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_key_list_import: + + overridePendingTransition(0, 0); Intent intentImport = new Intent(this, ImportKeysActivity.class); startActivityForResult(intentImport, 0); + overridePendingTransition(0, 0); return true; case R.id.menu_key_list_export: diff --git a/OpenPGP-Keychain/src/main/res/layout-large/api_apps_list_activity.xml b/OpenPGP-Keychain/src/main/res/layout-large/api_apps_list_activity.xml new file mode 100644 index 000000000..c0021261e --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout-large/api_apps_list_activity.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/layout-large/decrypt_activity.xml b/OpenPGP-Keychain/src/main/res/layout-large/decrypt_activity.xml new file mode 100644 index 000000000..26aed0831 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout-large/decrypt_activity.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/layout-large/encrypt_activity.xml b/OpenPGP-Keychain/src/main/res/layout-large/encrypt_activity.xml new file mode 100644 index 000000000..7d0d44074 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout-large/encrypt_activity.xml @@ -0,0 +1,19 @@ + + + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/layout-large/import_keys_activity.xml b/OpenPGP-Keychain/src/main/res/layout-large/import_keys_activity.xml new file mode 100644 index 000000000..2cb408441 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout-large/import_keys_activity.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/layout-large/key_list_activity.xml b/OpenPGP-Keychain/src/main/res/layout-large/key_list_activity.xml new file mode 100644 index 000000000..6636f12ff --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout-large/key_list_activity.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/layout/api_apps_list_activity.xml b/OpenPGP-Keychain/src/main/res/layout/api_apps_list_activity.xml index 71fbcfb12..9f95e9f3b 100644 --- a/OpenPGP-Keychain/src/main/res/layout/api_apps_list_activity.xml +++ b/OpenPGP-Keychain/src/main/res/layout/api_apps_list_activity.xml @@ -4,16 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" > - - - - + diff --git a/OpenPGP-Keychain/src/main/res/layout/api_apps_list_content.xml b/OpenPGP-Keychain/src/main/res/layout/api_apps_list_content.xml new file mode 100644 index 000000000..b8606b929 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/api_apps_list_content.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml b/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml index 25c7c000c..c4709a67e 100644 --- a/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml +++ b/OpenPGP-Keychain/src/main/res/layout/decrypt_activity.xml @@ -5,206 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/OpenPGP-Keychain/src/main/res/layout/decrypt_content.xml b/OpenPGP-Keychain/src/main/res/layout/decrypt_content.xml new file mode 100644 index 000000000..a847d9e46 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/decrypt_content.xml @@ -0,0 +1,205 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/layout/drawer_list.xml b/OpenPGP-Keychain/src/main/res/layout/drawer_list.xml index 81ceba20c..ab00c0073 100644 --- a/OpenPGP-Keychain/src/main/res/layout/drawer_list.xml +++ b/OpenPGP-Keychain/src/main/res/layout/drawer_list.xml @@ -9,7 +9,7 @@ --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml b/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml new file mode 100644 index 000000000..d6a05f0d4 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml @@ -0,0 +1,398 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/layout/import_keys_activity.xml b/OpenPGP-Keychain/src/main/res/layout/import_keys_activity.xml index d7794ace3..b11f99757 100644 --- a/OpenPGP-Keychain/src/main/res/layout/import_keys_activity.xml +++ b/OpenPGP-Keychain/src/main/res/layout/import_keys_activity.xml @@ -5,58 +5,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" > - - - - - - - - - - - - - - - - - - - + diff --git a/OpenPGP-Keychain/src/main/res/layout/import_keys_content.xml b/OpenPGP-Keychain/src/main/res/layout/import_keys_content.xml new file mode 100644 index 000000000..fae8147e5 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/import_keys_content.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_activity.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_activity.xml index 65d246d7b..fcb376fa8 100644 --- a/OpenPGP-Keychain/src/main/res/layout/key_list_activity.xml +++ b/OpenPGP-Keychain/src/main/res/layout/key_list_activity.xml @@ -4,16 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" > - - - - + diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_content.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_content.xml new file mode 100644 index 000000000..e58e42961 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/key_list_content.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/values-large/dimens.xml b/OpenPGP-Keychain/src/main/res/values-large/dimens.xml new file mode 100644 index 000000000..192a4bb99 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/values-large/dimens.xml @@ -0,0 +1,4 @@ + + + 240dp + diff --git a/OpenPGP-Keychain/src/main/res/values/dimens.xml b/OpenPGP-Keychain/src/main/res/values/dimens.xml new file mode 100644 index 000000000..e1a7749f0 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 240dp + 0dp + \ No newline at end of file From e01c99a193528df717d5cf5bd9fe20c649297375 Mon Sep 17 00:00:00 2001 From: uberspot Date: Mon, 17 Mar 2014 00:27:52 +0200 Subject: [PATCH 054/253] make loading of drawer items a bit more dynamic --- .../keychain/Constants.java | 14 ++++++++++ .../keychain/ui/DrawerActivity.java | 27 +++++++++---------- .../keychain/ui/KeyListActivity.java | 6 +---- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index 011cd9663..ff4abe56a 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -19,6 +19,11 @@ package org.sufficientlysecure.keychain; import android.os.Environment; import org.spongycastle.jce.provider.BouncyCastleProvider; +import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity; +import org.sufficientlysecure.keychain.ui.DecryptActivity; +import org.sufficientlysecure.keychain.ui.EncryptActivity; +import org.sufficientlysecure.keychain.ui.ImportKeysActivity; +import org.sufficientlysecure.keychain.ui.KeyListActivity; public final class Constants { @@ -63,4 +68,13 @@ public final class Constants { public static final String KEY_SERVERS = "pool.sks-keyservers.net, subkeys.pgp.net, pgp.mit.edu"; } + public static final class DrawerItems { + public static final Class KEY_LIST = KeyListActivity.class; + public static final Class ENCRYPT = EncryptActivity.class; + public static final Class DECRYPT = DecryptActivity.class; + public static final Class IMPORT_KEYS = ImportKeysActivity.class; + public static final Class REGISTERED_APPS_LIST = RegisteredAppsListActivity.class; + public static final Class[] ARRAY = new Class[]{KEY_LIST, ENCRYPT, DECRYPT, + IMPORT_KEYS, REGISTERED_APPS_LIST}; + } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java index 985f6c309..f01e67449 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DrawerActivity.java @@ -30,9 +30,8 @@ import android.support.v7.app.ActionBarActivity; import android.view.*; import android.widget.*; import com.beardedhen.androidbootstrap.FontAwesomeText; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.ActionBarHelper; -import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity; public class DrawerActivity extends ActionBarActivity { private DrawerLayout mDrawerLayout; @@ -43,9 +42,6 @@ public class DrawerActivity extends ActionBarActivity { private CharSequence mTitle; private boolean mIsDrawerLocked = false; - private static Class[] mItemsClass = new Class[]{KeyListActivity.class, - EncryptActivity.class, DecryptActivity.class, ImportKeysActivity.class, - RegisteredAppsListActivity.class}; private Class mSelectedItem; private static final int MENU_ID_PREFERENCE = 222; @@ -102,7 +98,7 @@ public class DrawerActivity extends ActionBarActivity { public void onDrawerClosed(View view) { getSupportActionBar().setTitle(mTitle); - callIntentForSelectedItem(); + callIntentForDrawerItem(mSelectedItem); } public void onDrawerOpened(View drawerView) { @@ -112,11 +108,10 @@ public class DrawerActivity extends ActionBarActivity { supportInvalidateOptionsMenu(); } }; + if ( !mIsDrawerLocked ) { mDrawerLayout.setDrawerListener(mDrawerToggle); - } - - if ( mIsDrawerLocked ) { + } else { // If the drawer is locked open make it un-focusable // so that it doesn't consume all the Back button presses mDrawerLayout.setFocusableInTouchMode(false); @@ -126,16 +121,20 @@ public class DrawerActivity extends ActionBarActivity { // } } - private void callIntentForSelectedItem() { + /** + * Uses startActivity to call the Intent of the given class + * @param drawerItem the class of the drawer item you want to load. Based on Constants.DrawerItems.* + */ + public void callIntentForDrawerItem(Class drawerItem) { // creates call to onPrepareOptionsMenu() supportInvalidateOptionsMenu(); // call intent activity if selected - if (mSelectedItem != null) { + if (drawerItem != null) { finish(); overridePendingTransition(0, 0); - Intent intent = new Intent(this, mSelectedItem); + Intent intent = new Intent(this, drawerItem); startActivity(intent); // disable animation of activity start @@ -214,7 +213,7 @@ public class DrawerActivity extends ActionBarActivity { // update selected item and title, then close the drawer mDrawerList.setItemChecked(position, true); // set selected class - mSelectedItem = mItemsClass[position]; + mSelectedItem = Constants.DrawerItems.ARRAY[position]; // setTitle(mDrawerTitles[position]); // If drawer isn't locked just close the drawer and @@ -223,7 +222,7 @@ public class DrawerActivity extends ActionBarActivity { mDrawerLayout.closeDrawer(mDrawerList); // else move to the selected item yourself } else { - callIntentForSelectedItem(); + callIntentForDrawerItem(mSelectedItem); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java index 57709350e..06df6f12d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListActivity.java @@ -53,11 +53,7 @@ public class KeyListActivity extends DrawerActivity { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_key_list_import: - - overridePendingTransition(0, 0); - Intent intentImport = new Intent(this, ImportKeysActivity.class); - startActivityForResult(intentImport, 0); - overridePendingTransition(0, 0); + callIntentForDrawerItem(Constants.DrawerItems.IMPORT_KEYS); return true; case R.id.menu_key_list_export: From b39d37f59c5d1633f22a45a37c7dc94d6c37d010 Mon Sep 17 00:00:00 2001 From: Daniel Hammann Date: Mon, 17 Mar 2014 22:17:05 +0100 Subject: [PATCH 055/253] #429 4) In the clicking for a particular contact , two icon in the actionbar show the same hint ('Share'). Second icon hint can be 'Keyserver' or just only 'server' --- OpenPGP-Keychain/src/main/res/menu/key_view.xml | 2 +- OpenPGP-Keychain/src/main/res/values/arrays.xml | 2 +- OpenPGP-Keychain/src/main/res/values/strings.xml | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/OpenPGP-Keychain/src/main/res/menu/key_view.xml b/OpenPGP-Keychain/src/main/res/menu/key_view.xml index cd84cc91a..105368cbb 100644 --- a/OpenPGP-Keychain/src/main/res/menu/key_view.xml +++ b/OpenPGP-Keychain/src/main/res/menu/key_view.xml @@ -52,7 +52,7 @@ android:id="@+id/menu_key_keyserver" android:icon="@drawable/ic_action_import_export" app:showAsAction="always" - android:title="@string/menu_share"> + android:title="@string/menu_key_server">

@string/key_size_4096 - @string/menu_key_server + @string/menu_import_from_key_server @string/menu_import_from_file @string/menu_import_from_qr_code @string/import_from_clipboard diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index 77891d6c7..d6550d63d 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -84,10 +84,11 @@ Create key Create key (expert) Search - Import from keyserver + Keyserver + Keyserver… Update from keyserver Upload to key server - Share + Share… Share fingerprint… Share whole key… with… From fee18bd33e56a90f3d0df9d39dc70b3a2bf30132 Mon Sep 17 00:00:00 2001 From: gogowitczak Date: Tue, 18 Mar 2014 15:44:20 +0100 Subject: [PATCH 056/253] Fixed coding style. --- .../ui/adapter/ImportKeysListEntry.java | 49 ++-- .../keychain/util/HkpKeyServer.java | 232 +++++++++--------- 2 files changed, 144 insertions(+), 137 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java index 13309435d..05521b0c9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java @@ -173,33 +173,30 @@ public class ImportKeysListEntry implements Serializable, Parcelable { this.hexKeyId = PgpKeyHelper.convertKeyIdToHex(keyId); this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength(); final int algorithm = pgpKeyRing.getPublicKey().getAlgorithm(); - this.algorithm = getAlgorithmFromId(algorithm); + this.algorithm = getAlgorithmFromId(algorithm); } - /** - * Based on OpenPGP Message Format - */ - private final static SparseArray ALGORITHM_IDS = new SparseArray() {{ - put(-1, "unknown"); // TODO: with resources - put(0, "unencrypted"); - put(PGPPublicKey.RSA_GENERAL, "RSA"); - put(PGPPublicKey.RSA_ENCRYPT, "RSA"); - put(PGPPublicKey.RSA_SIGN, "RSA"); - put(PGPPublicKey.ELGAMAL_ENCRYPT, "ElGamal"); - put(PGPPublicKey.ELGAMAL_GENERAL, "ElGamal"); - put(PGPPublicKey.DSA, "DSA"); - put(PGPPublicKey.EC, "ECC"); - put(PGPPublicKey.ECDSA, "ECC"); - put(PGPPublicKey.ECDH, "ECC"); - }}; - - /** - * Based on OpenPGP Message Format - */ - public static String getAlgorithmFromId(int algorithmId) { - return (ALGORITHM_IDS.get(algorithmId) != null ? ALGORITHM_IDS.get(algorithmId) : ALGORITHM_IDS.get(-1)); - } + /** + * Based on OpenPGP Message Format + */ + private final static SparseArray ALGORITHM_IDS = new SparseArray() {{ + put(-1, "unknown"); // TODO: with resources + put(0, "unencrypted"); + put(PGPPublicKey.RSA_GENERAL, "RSA"); + put(PGPPublicKey.RSA_ENCRYPT, "RSA"); + put(PGPPublicKey.RSA_SIGN, "RSA"); + put(PGPPublicKey.ELGAMAL_ENCRYPT, "ElGamal"); + put(PGPPublicKey.ELGAMAL_GENERAL, "ElGamal"); + put(PGPPublicKey.DSA, "DSA"); + put(PGPPublicKey.EC, "ECC"); + put(PGPPublicKey.ECDSA, "ECC"); + put(PGPPublicKey.ECDH, "ECC"); + }}; + /** + * Based on OpenPGP Message Format + */ + public static String getAlgorithmFromId(int algorithmId) { + return (ALGORITHM_IDS.get(algorithmId) != null ? ALGORITHM_IDS.get(algorithmId) : ALGORITHM_IDS.get(-1)); + } } - - diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java index 0839fc494..a354d19e8 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java @@ -69,55 +69,64 @@ public class HkpKeyServer extends KeyServer { private String mHost; private short mPort; - /** - * pub:%keyid%:%algo%:%keylen%:%creationdate%:%expirationdate%:%flags% - *
    - *
  • %keyid% = this is either the fingerprint or the key ID of the key. - * Either the 16-digit or 8-digit key IDs are acceptable, but obviously the fingerprint is best.
  • - *
  • %algo% = the algorithm number, (i.e. 1==RSA, 17==DSA, etc). See RFC-2440
  • - *
  • %keylen% = the key length (i.e. 1024, 2048, 4096, etc.)
  • - *
  • %creationdate% = creation date of the key in standard RFC-2440 form (i.e. number of seconds since 1/1/1970 UTC time)
  • - *
  • %expirationdate% = expiration date of the key in standard RFC-2440 form (i.e. number of seconds since 1/1/1970 UTC time)
  • - *
  • %flags% = letter codes to indicate details of the key, if any. Flags may be in any order. The - * meaning of "disabled" is implementation-specific. Note that individual flags may be unimplemented, so - * the absence of a given flag does not necessarily mean the absence of the detail. - *
      - *
    • r == revoked
    • - *
    • d == disabled
    • - *
    • e == expired
    • - *
    - *
  • - *
- * - * - * @see 5.2. Machine Readable Indexes in Internet-Draft OpenPGP HTTP Keyserver Protocol Document - */ - public static final Pattern PUB_KEY_LINE = Pattern - .compile("pub:([0-9a-fA-F]+):([0-9]+):([0-9]+):([0-9]+):([0-9]*):([rde]*)[ \n\r]*" // pub line - + "(uid:(.*):([0-9]+):([0-9]*):([rde]*))+", // one or more uid lines - Pattern.CASE_INSENSITIVE); + /** + * pub:%keyid%:%algo%:%keylen%:%creationdate%:%expirationdate%:%flags% + *
    + *
  • %keyid% = this is either the fingerprint or the key ID of the key. Either the 16-digit or 8-digit + * key IDs are acceptable, but obviously the fingerprint is best.
  • + *
  • %algo% = the algorithm number, (i.e. 1==RSA, 17==DSA, etc). + * See RFC-2440
  • + *
  • %keylen% = the key length (i.e. 1024, 2048, 4096, etc.)
  • + *
  • %creationdate% = creation date of the key in standard + * RFC-2440 form (i.e. number of seconds since + * 1/1/1970 UTC time)
  • + *
  • %expirationdate% = expiration date of the key in standard + * RFC-2440 form (i.e. number of seconds since + * 1/1/1970 UTC time)
  • + *
  • %flags% = letter codes to indicate details of the key, if any. Flags may be in any order. The + * meaning of "disabled" is implementation-specific. Note that individual flags may be unimplemented, so + * the absence of a given flag does not necessarily mean the absence of the detail. + *
      + *
    • r == revoked
    • + *
    • d == disabled
    • + *
    • e == expired
    • + *
    + *
  • + *
+ * + * @see 5.2. Machine Readable Indexes + * in Internet-Draft OpenPGP HTTP Keyserver Protocol Document + */ + public static final Pattern PUB_KEY_LINE = Pattern + .compile("pub:([0-9a-fA-F]+):([0-9]+):([0-9]+):([0-9]+):([0-9]*):([rde]*)[ \n\r]*" // pub line + + "(uid:(.*):([0-9]+):([0-9]*):([rde]*))+", // one or more uid lines + Pattern.CASE_INSENSITIVE); - /** - * uid:%escaped uid string%:%creationdate%:%expirationdate%:%flags% - *
    - *
  • %escaped uid string% = the user ID string, with HTTP %-escaping for anything that isn't 7-bit - * safe as well as for the ":" character. Any other characters may be escaped, as desired.
  • - *
  • %creationdate% = creation date of the key in standard RFC-2440 form (i.e. number of seconds since 1/1/1970 UTC time)
  • - *
  • %expirationdate% = expiration date of the key in standard RFC-2440 form (i.e. number of seconds since 1/1/1970 UTC time)
  • - *
  • %flags% = letter codes to indicate details of the key, if any. Flags may be in any order. The - * meaning of "disabled" is implementation-specific. Note that individual flags may be unimplemented, so - * the absence of a given flag does not necessarily mean the absence of the detail. - *
      - *
    • r == revoked
    • - *
    • d == disabled
    • - *
    • e == expired
    • - *
    - *
  • - *
- */ - public static final Pattern UID_LINE = Pattern - .compile("uid:(.*):([0-9]+):([0-9]*):([rde]*)", - Pattern.CASE_INSENSITIVE); + /** + * uid:%escaped uid string%:%creationdate%:%expirationdate%:%flags% + *
    + *
  • %escaped uid string% = the user ID string, with HTTP %-escaping for anything that isn't 7-bit + * safe as well as for the ":" character. Any other characters may be escaped, as desired.
  • + *
  • %creationdate% = creation date of the key in standard + * RFC-2440 form (i.e. number of seconds since + * 1/1/1970 UTC time)
  • + *
  • %expirationdate% = expiration date of the key in standard + * RFC-2440 form (i.e. number of seconds since + * 1/1/1970 UTC time)
  • + *
  • %flags% = letter codes to indicate details of the key, if any. Flags may be in any order. The + * meaning of "disabled" is implementation-specific. Note that individual flags may be unimplemented, so + * the absence of a given flag does not necessarily mean the absence of the detail. + *
      + *
    • r == revoked
    • + *
    • d == disabled
    • + *
    • e == expired
    • + *
    + *
  • + *
+ */ + public static final Pattern UID_LINE = Pattern + .compile("uid:(.*):([0-9]+):([0-9]*):([rde]*)", + Pattern.CASE_INSENSITIVE); private static final short PORT_DEFAULT = 11371; @@ -192,77 +201,78 @@ public class HkpKeyServer extends KeyServer { throw new QueryException("querying server(s) for '" + mHost + "' failed"); } - @Override - public ArrayList search(String query) throws QueryException, TooManyResponses, - InsufficientQuery { - ArrayList results = new ArrayList(); + @Override + public ArrayList search(String query) throws QueryException, TooManyResponses, + InsufficientQuery { + ArrayList results = new ArrayList(); - if (query.length() < 3) { - throw new InsufficientQuery(); - } + if (query.length() < 3) { + throw new InsufficientQuery(); + } - String encodedQuery; - try { - encodedQuery = URLEncoder.encode(query, "utf8"); - } catch (UnsupportedEncodingException e) { - return null; - } - String request = "/pks/lookup?op=index&search=" + encodedQuery + "&options=mr"; + String encodedQuery; + try { + encodedQuery = URLEncoder.encode(query, "utf8"); + } catch (UnsupportedEncodingException e) { + return null; + } + String request = "/pks/lookup?op=index&search=" + encodedQuery + "&options=mr"; - String data = null; - try { - data = query(request); - } catch (HttpError e) { - if (e.getCode() == 404) { - return results; - } else { - if (e.getData().toLowerCase(Locale.US).contains("no keys found")) { - return results; - } else if (e.getData().toLowerCase(Locale.US).contains("too many")) { - throw new TooManyResponses(); - } else if (e.getData().toLowerCase(Locale.US).contains("insufficient")) { - throw new InsufficientQuery(); - } - } - throw new QueryException("querying server(s) for '" + mHost + "' failed"); - } + String data = null; + try { + data = query(request); + } catch (HttpError e) { + if (e.getCode() == 404) { + return results; + } else { + if (e.getData().toLowerCase(Locale.US).contains("no keys found")) { + return results; + } else if (e.getData().toLowerCase(Locale.US).contains("too many")) { + throw new TooManyResponses(); + } else if (e.getData().toLowerCase(Locale.US).contains("insufficient")) { + throw new InsufficientQuery(); + } + } + throw new QueryException("querying server(s) for '" + mHost + "' failed"); + } - final Matcher matcher = PUB_KEY_LINE.matcher(data); - while (matcher.find()) { - final ImportKeysListEntry info = new ImportKeysListEntry(); - info.bitStrength = Integer.parseInt(matcher.group(3)); - final int algorithmId = Integer.decode(matcher.group(2)); - info.algorithm = getAlgorithmFromId(algorithmId); + final Matcher matcher = PUB_KEY_LINE.matcher(data); + while (matcher.find()) { + final ImportKeysListEntry info = new ImportKeysListEntry(); + info.bitStrength = Integer.parseInt(matcher.group(3)); + final int algorithmId = Integer.decode(matcher.group(2)); + info.algorithm = getAlgorithmFromId(algorithmId); - info.hexKeyId = "0x" + matcher.group(1); - info.keyId = PgpKeyHelper.convertHexToKeyId(matcher.group(1)); + info.hexKeyId = "0x" + matcher.group(1); + info.keyId = PgpKeyHelper.convertHexToKeyId(matcher.group(1)); - final long creationDate = Long.parseLong(matcher.group(4)); - final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC")); - tmpGreg.setTimeInMillis(creationDate*1000); - info.date = tmpGreg.getTime(); + final long creationDate = Long.parseLong(matcher.group(4)); + final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + tmpGreg.setTimeInMillis(creationDate * 1000); + info.date = tmpGreg.getTime(); - info.revoked = matcher.group(6).contains("r"); - info.userIds = new ArrayList(); + info.revoked = matcher.group(6).contains("r"); + info.userIds = new ArrayList(); - final String uidLines = matcher.group(7); - final Matcher uidMatcher = UID_LINE.matcher(uidLines); - while (uidMatcher.find()) { - String tmp = uidMatcher.group(1).replaceAll("<.*?>", ""); - tmp = Html.fromHtml(tmp).toString().trim(); - if (tmp.contains("%")) - { - try { - tmp = (URLDecoder.decode(tmp, "UTF8")); // converts String like "Universit%C3%A4t" to a proper form "Universität". - } catch (UnsupportedEncodingException ignored) { - } - } - info.userIds.add(tmp); - } - results.add(info); - } - return results; - } + final String uidLines = matcher.group(7); + final Matcher uidMatcher = UID_LINE.matcher(uidLines); + while (uidMatcher.find()) { + String tmp = uidMatcher.group(1).replaceAll("<.*?>", ""); + tmp = Html.fromHtml(tmp).toString().trim(); + if (tmp.contains("%")) { + try { + // converts Strings like "Universit%C3%A4t" to a proper encoding form "Universität". + tmp = (URLDecoder.decode(tmp, "UTF8")); + } catch (UnsupportedEncodingException ignored) { + // will never happen, because "UTF8" is supported + } + } + info.userIds.add(tmp); + } + results.add(info); + } + return results; + } @Override public String get(long keyId) throws QueryException { From fa439c1a721320eb6c4bb17532ff96937bbb4a17 Mon Sep 17 00:00:00 2001 From: Sreeram Boyapati Date: Tue, 18 Mar 2014 07:17:05 +0530 Subject: [PATCH 057/253] Can_Encrypt Check added to hide Button --- .../sufficientlysecure/keychain/ui/ViewKeyMainFragment.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index eeb17fea2..b2f9c9f40 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -266,7 +266,10 @@ public class ViewKeyMainFragment extends Fragment implements if (data.moveToFirst()) { // get key id from MASTER_KEY_ID long keyId = data.getLong(KEYS_INDEX_KEY_ID); - + long can_encrypt = data.getLong(KEYS_INDEX_CAN_ENCRYPT); + if(can_encrypt == 0){ + mActionEncrypt.setVisibility(View.GONE); + } String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId); mKeyId.setText(keyIdStr); From 93447bf3f866efe35d3fe7713693ccb03ec2628e Mon Sep 17 00:00:00 2001 From: Daniel Hammann Date: Tue, 18 Mar 2014 16:07:17 +0100 Subject: [PATCH 058/253] Show revocation status in SelectPublicKeyFragment #375 Added revocation status in Key List View (graphical symbol and red coloring) --- .../keychain/ui/ViewKeyMainFragment.java | 11 +- .../ui/adapter/ViewKeyKeysAdapter.java | 13 +- .../res/drawable-hdpi/revoked_key_small.png | Bin 0 -> 2509 bytes .../main/res/drawable/revoked_key_small.png | Bin 0 -> 1793 bytes .../main/res/layout/view_key_keys_item.xml | 4 + Resources/graphics/revokedKey.png | Bin 0 -> 68871 bytes Resources/graphics/revokedKey.svg | 14909 ++++++++++++++++ 7 files changed, 14931 insertions(+), 6 deletions(-) create mode 100644 OpenPGP-Keychain/src/main/res/drawable-hdpi/revoked_key_small.png create mode 100644 OpenPGP-Keychain/src/main/res/drawable/revoked_key_small.png create mode 100644 Resources/graphics/revokedKey.png create mode 100644 Resources/graphics/revokedKey.svg diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index eeb17fea2..c5f2e0d8e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -192,8 +192,8 @@ public class ViewKeyMainFragment extends Fragment implements KeychainContract.Keys.IS_MASTER_KEY, KeychainContract.Keys.ALGORITHM, KeychainContract.Keys.KEY_SIZE, KeychainContract.Keys.CAN_CERTIFY, KeychainContract.Keys.CAN_SIGN, KeychainContract.Keys.CAN_ENCRYPT, - KeychainContract.Keys.CREATION, KeychainContract.Keys.EXPIRY, - KeychainContract.Keys.FINGERPRINT}; + KeychainContract.Keys.IS_REVOKED, KeychainContract.Keys.CREATION, + KeychainContract.Keys.EXPIRY, KeychainContract.Keys.FINGERPRINT}; static final String KEYS_SORT_ORDER = KeychainContract.Keys.RANK + " ASC"; static final int KEYS_INDEX_ID = 0; static final int KEYS_INDEX_KEY_ID = 1; @@ -203,9 +203,10 @@ public class ViewKeyMainFragment extends Fragment implements static final int KEYS_INDEX_CAN_CERTIFY = 5; static final int KEYS_INDEX_CAN_SIGN = 6; static final int KEYS_INDEX_CAN_ENCRYPT = 7; - static final int KEYS_INDEX_CREATION = 8; - static final int KEYS_INDEX_EXPIRY = 9; - static final int KEYS_INDEX_FINGERPRINT = 10; + static final int KEY_INDEX_IS_REVOKED = 8; + static final int KEYS_INDEX_CREATION = 9; + static final int KEYS_INDEX_EXPIRY = 10; + static final int KEYS_INDEX_FINGERPRINT = 11; public Loader onCreateLoader(int id, Bundle args) { switch (id) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java index 153a3f266..5f911275d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui.adapter; import android.content.Context; import android.database.Cursor; +import android.graphics.Color; import android.support.v4.widget.CursorAdapter; import android.view.LayoutInflater; import android.view.View; @@ -39,6 +40,7 @@ public class ViewKeyKeysAdapter extends CursorAdapter { private int mIndexCanCertify; private int mIndexCanEncrypt; private int mIndexCanSign; + private int mIndexRevokedKey; public ViewKeyKeysAdapter(Context context, Cursor c, int flags) { super(context, c, flags); @@ -70,6 +72,7 @@ public class ViewKeyKeysAdapter extends CursorAdapter { mIndexCanCertify = cursor.getColumnIndexOrThrow(Keys.CAN_CERTIFY); mIndexCanEncrypt = cursor.getColumnIndexOrThrow(Keys.CAN_ENCRYPT); mIndexCanSign = cursor.getColumnIndexOrThrow(Keys.CAN_SIGN); + mIndexRevokedKey = cursor.getColumnIndexOrThrow(Keys.IS_REVOKED); } } @@ -81,13 +84,13 @@ public class ViewKeyKeysAdapter extends CursorAdapter { ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey); ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey); ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey); + ImageView revokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey); String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId)); String algorithmStr = PgpKeyHelper.getAlgorithmInfo(cursor.getInt(mIndexAlgorithm), cursor.getInt(mIndexKeySize)); keyId.setText(keyIdStr); - keyDetails.setText("(" + algorithmStr + ")"); if (cursor.getInt(mIndexIsMasterKey) != 1) { @@ -113,6 +116,14 @@ public class ViewKeyKeysAdapter extends CursorAdapter { } else { signIcon.setVisibility(View.VISIBLE); } + + if (cursor.getInt(mIndexRevokedKey) > 0) { + revokedKeyIcon.setVisibility(View.VISIBLE); + keyId.setTextColor(Color.RED); + keyDetails.setTextColor(Color.RED); + } else { + revokedKeyIcon.setVisibility(View.GONE); + } } @Override diff --git a/OpenPGP-Keychain/src/main/res/drawable-hdpi/revoked_key_small.png b/OpenPGP-Keychain/src/main/res/drawable-hdpi/revoked_key_small.png new file mode 100644 index 0000000000000000000000000000000000000000..75f45eb54a3af919caec2b8fdff1ddd44fc65e8a GIT binary patch literal 2509 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 wtn=W!8=IiaQtfZra zLZOsBJzV_}#7HMv0a>T{jvENbLoS|yXk;XyBQucwUXDj7gkX?#%EZ1iSwW0Luv-Wm zz>b3X6fS_`^Z9s?6$8;I901Se((f%f=pZDFl;q5VquE^4DK3Q-4?-~%YygMNf&fOe zt|;{NO#)O?#Nt#B4uF%K_-9-05~w#%EH3d-ql|Zq46jZW~tl+21o-CO(6m`4hK;P$MF_~ z3n}LwBX_yw>FONlaV&RfBey8C$V;wF+OUDx2Bd^7YiB z{g#hX`}_899cVG;Dk}M`FL}R?lb1lAmV8Q1-QwUa7fm_yXKsbBo*u;Eex9rojy~`0 zTj}!m@9A7Ois&m?ZS|4-X>aT9VQwC^*I^o z>e~|hx>Y|@U9yoYntGIHSlWAY@hr<2FEC?P(Iz`qhPEA1-wPhgeGYeGjsDtvo}_j< zzp!OGyYo!6e!F||iJ>r=eBF={%k&lc*3;^wIVXu)LR*;r^)*W}&$6$#j_zmwOPhbz z)`v1L^uV#wMf0Adner2Nj-u`Bem-D48FFx|B+LAHj=Q3Uq+;&7>~v70QbjSYq9Q|} zdM;?su#lP>lJ@m`FWcVsnAXq8F!&@PE?fv;0lecz^z5V>!L4&B$QhT0AG8H6+c^b6B~ z=d#Wi2a1-XPv-_GJ`wL4x|=POTh}DrEuXSlo!Z~|Z418pkbh6nz&RUOwW?%JVsF{- zG@LPR=u1l9P`i;Mk)`t9h;)9Zl3?og?B(L$?KamI9t!cpy0S@%F2=@TpEWpPj=B*S z{?S;n9vT?;RrLK!EfL!SrBIhoRY8#^KH`vi1>uy>LIZdu2S@HJxIH%DoI+_xH1Alv zCwo)L66({8B3hHoA~%gr7N@qSe^e*(Ugo&P&wHUK!r3-oM&U28`p4T&n6d zgjG2k4)@(`E60u6rDFAdq@vr7W&%+UR3uwo+7G0jWPWRLub=xbhylQuos0`%^-~fl)_C*AbE1 z81ZeA+>gu{at!E9O3arqK5liDPQ?9`BIsVIX>*Jnt(}d0A){{IEt$GfPCBYvnl-9o zuDE;sl<-aJhG{E>JX8I!Zc)Ruuzv7e(;ta)3u^(zH9NyTPa3~z7*>d@FUtA1$B`Y! z`QJTCB0oH9N8Z!fZyoL_*YJ~Nvt2U6#f@A2rs`q(-SZ?2PGy-eal~g9rdfPKK3SBf Lo42dT + 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 z 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 literal 0 HcmV?d00001 diff --git a/Resources/graphics/revokedKey.svg b/Resources/graphics/revokedKey.svg new file mode 100644 index 000000000..c7f3f2f76 --- /dev/null +++ b/Resources/graphics/revokedKey.svg @@ -0,0 +1,14909 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + email + send + mail + user + not available + + + + Sept 2009 + + + Franziska Sponsel + + + + + Franziska Sponsel + + + + + RRZE + + + + + Beate Kaspar, Hendrik Eggers + + + uses < http://ftp.uni-erlangen.de/pub/rrze/tango/rrze-icon-set/tango/scalable/categories/email.svg>, < http://ftp.uni-erlangen.de/pub/rrze/tango/rrze-icon-set/tango/scalable/actions/action-undo.svg> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From f34b3ee5316b95bbef9c9ac30b753d1708c4dbc4 Mon Sep 17 00:00:00 2001 From: Sreeram Boyapati Date: Tue, 18 Mar 2014 20:40:09 +0530 Subject: [PATCH 059/253] Button Text changes to Change Passphrase if Passphrase is set --- .../sufficientlysecure/keychain/ui/EditKeyActivity.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index edf980773..93099427d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -368,9 +368,8 @@ public class EditKeyActivity extends ActionBarActivity { } mCurrentPassphrase = ""; - - buildLayout(); mIsPassPhraseSet = PassphraseCacheService.hasPassphrase(this, masterKeyId); + buildLayout(); if (!mIsPassPhraseSet) { // check "no passphrase" checkbox and remove button mNoPassphrase.setChecked(true); @@ -425,11 +424,14 @@ public class EditKeyActivity extends ActionBarActivity { // find views mChangePassphrase = (BootstrapButton) findViewById(R.id.edit_key_btn_change_passphrase); mNoPassphrase = (CheckBox) findViewById(R.id.edit_key_no_passphrase); - // Build layout based on given userIds and keys + LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); LinearLayout container = (LinearLayout) findViewById(R.id.edit_key_container); + if(mIsPassPhraseSet){ + mChangePassphrase.setText(getString(R.string.btn_change_passphrase)); + } mUserIdsView = (SectionView) inflater.inflate(R.layout.edit_key_section, container, false); mUserIdsView.setType(Id.type.user_id); mUserIdsView.setCanEdit(mMasterCanSign); From eebf1c171a0ba70a92d76f5affb93476f6813e30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 18 Mar 2014 16:29:40 +0100 Subject: [PATCH 060/253] add mr option in keyserver class before query (code simplification) --- .../org/sufficientlysecure/keychain/util/HkpKeyServer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java index a354d19e8..0c07e9b06 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java @@ -216,9 +216,9 @@ public class HkpKeyServer extends KeyServer { } catch (UnsupportedEncodingException e) { return null; } - String request = "/pks/lookup?op=index&search=" + encodedQuery + "&options=mr"; + String request = "/pks/lookup?op=index&options=mr&search=" + encodedQuery; - String data = null; + String data; try { data = query(request); } catch (HttpError e) { @@ -279,7 +279,7 @@ public class HkpKeyServer extends KeyServer { HttpClient client = new DefaultHttpClient(); try { HttpGet get = new HttpGet("http://" + mHost + ":" + mPort - + "/pks/lookup?op=get&search=" + PgpKeyHelper.convertKeyIdToHex(keyId) + "&options=mr"); + + "/pks/lookup?op=get&options=mr&search=" + PgpKeyHelper.convertKeyIdToHex(keyId)); HttpResponse response = client.execute(get); if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { From 2d3f47eb99c18b188ed6e4f396605d6b0340de16 Mon Sep 17 00:00:00 2001 From: Daniel Hammann Date: Tue, 18 Mar 2014 16:07:17 +0100 Subject: [PATCH 061/253] Show revocation status in SelectPublicKeyFragment #375 Added revocation status in Key List View (graphical symbol and red coloring) --- .../keychain/ui/ViewKeyMainFragment.java | 11 +- .../ui/adapter/ViewKeyKeysAdapter.java | 13 +- .../res/drawable-hdpi/revoked_key_small.png | Bin 0 -> 2509 bytes .../main/res/drawable/revoked_key_small.png | Bin 0 -> 1793 bytes .../main/res/layout/view_key_keys_item.xml | 4 + Resources/graphics/revokedKey.png | Bin 0 -> 68871 bytes Resources/graphics/revokedKey.svg | 14909 ++++++++++++++++ 7 files changed, 14931 insertions(+), 6 deletions(-) create mode 100644 OpenPGP-Keychain/src/main/res/drawable-hdpi/revoked_key_small.png create mode 100644 OpenPGP-Keychain/src/main/res/drawable/revoked_key_small.png create mode 100644 Resources/graphics/revokedKey.png create mode 100644 Resources/graphics/revokedKey.svg diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index eeb17fea2..7ff4e0c57 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -192,8 +192,8 @@ public class ViewKeyMainFragment extends Fragment implements KeychainContract.Keys.IS_MASTER_KEY, KeychainContract.Keys.ALGORITHM, KeychainContract.Keys.KEY_SIZE, KeychainContract.Keys.CAN_CERTIFY, KeychainContract.Keys.CAN_SIGN, KeychainContract.Keys.CAN_ENCRYPT, - KeychainContract.Keys.CREATION, KeychainContract.Keys.EXPIRY, - KeychainContract.Keys.FINGERPRINT}; + KeychainContract.Keys.IS_REVOKED, KeychainContract.Keys.CREATION, + KeychainContract.Keys.EXPIRY, KeychainContract.Keys.FINGERPRINT}; static final String KEYS_SORT_ORDER = KeychainContract.Keys.RANK + " ASC"; static final int KEYS_INDEX_ID = 0; static final int KEYS_INDEX_KEY_ID = 1; @@ -203,9 +203,10 @@ public class ViewKeyMainFragment extends Fragment implements static final int KEYS_INDEX_CAN_CERTIFY = 5; static final int KEYS_INDEX_CAN_SIGN = 6; static final int KEYS_INDEX_CAN_ENCRYPT = 7; - static final int KEYS_INDEX_CREATION = 8; - static final int KEYS_INDEX_EXPIRY = 9; - static final int KEYS_INDEX_FINGERPRINT = 10; + static final int KEYS_INDEX_IS_REVOKED = 8; + static final int KEYS_INDEX_CREATION = 9; + static final int KEYS_INDEX_EXPIRY = 10; + static final int KEYS_INDEX_FINGERPRINT = 11; public Loader onCreateLoader(int id, Bundle args) { switch (id) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java index 153a3f266..5f911275d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.ui.adapter; import android.content.Context; import android.database.Cursor; +import android.graphics.Color; import android.support.v4.widget.CursorAdapter; import android.view.LayoutInflater; import android.view.View; @@ -39,6 +40,7 @@ public class ViewKeyKeysAdapter extends CursorAdapter { private int mIndexCanCertify; private int mIndexCanEncrypt; private int mIndexCanSign; + private int mIndexRevokedKey; public ViewKeyKeysAdapter(Context context, Cursor c, int flags) { super(context, c, flags); @@ -70,6 +72,7 @@ public class ViewKeyKeysAdapter extends CursorAdapter { mIndexCanCertify = cursor.getColumnIndexOrThrow(Keys.CAN_CERTIFY); mIndexCanEncrypt = cursor.getColumnIndexOrThrow(Keys.CAN_ENCRYPT); mIndexCanSign = cursor.getColumnIndexOrThrow(Keys.CAN_SIGN); + mIndexRevokedKey = cursor.getColumnIndexOrThrow(Keys.IS_REVOKED); } } @@ -81,13 +84,13 @@ public class ViewKeyKeysAdapter extends CursorAdapter { ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey); ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey); ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey); + ImageView revokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey); String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId)); String algorithmStr = PgpKeyHelper.getAlgorithmInfo(cursor.getInt(mIndexAlgorithm), cursor.getInt(mIndexKeySize)); keyId.setText(keyIdStr); - keyDetails.setText("(" + algorithmStr + ")"); if (cursor.getInt(mIndexIsMasterKey) != 1) { @@ -113,6 +116,14 @@ public class ViewKeyKeysAdapter extends CursorAdapter { } else { signIcon.setVisibility(View.VISIBLE); } + + if (cursor.getInt(mIndexRevokedKey) > 0) { + revokedKeyIcon.setVisibility(View.VISIBLE); + keyId.setTextColor(Color.RED); + keyDetails.setTextColor(Color.RED); + } else { + revokedKeyIcon.setVisibility(View.GONE); + } } @Override diff --git a/OpenPGP-Keychain/src/main/res/drawable-hdpi/revoked_key_small.png b/OpenPGP-Keychain/src/main/res/drawable-hdpi/revoked_key_small.png new file mode 100644 index 0000000000000000000000000000000000000000..75f45eb54a3af919caec2b8fdff1ddd44fc65e8a GIT binary patch literal 2509 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 wtn=W!8=IiaQtfZra zLZOsBJzV_}#7HMv0a>T{jvENbLoS|yXk;XyBQucwUXDj7gkX?#%EZ1iSwW0Luv-Wm zz>b3X6fS_`^Z9s?6$8;I901Se((f%f=pZDFl;q5VquE^4DK3Q-4?-~%YygMNf&fOe zt|;{NO#)O?#Nt#B4uF%K_-9-05~w#%EH3d-ql|Zq46jZW~tl+21o-CO(6m`4hK;P$MF_~ z3n}LwBX_yw>FONlaV&RfBey8C$V;wF+OUDx2Bd^7YiB z{g#hX`}_899cVG;Dk}M`FL}R?lb1lAmV8Q1-QwUa7fm_yXKsbBo*u;Eex9rojy~`0 zTj}!m@9A7Ois&m?ZS|4-X>aT9VQwC^*I^o z>e~|hx>Y|@U9yoYntGIHSlWAY@hr<2FEC?P(Iz`qhPEA1-wPhgeGYeGjsDtvo}_j< zzp!OGyYo!6e!F||iJ>r=eBF={%k&lc*3;^wIVXu)LR*;r^)*W}&$6$#j_zmwOPhbz z)`v1L^uV#wMf0Adner2Nj-u`Bem-D48FFx|B+LAHj=Q3Uq+;&7>~v70QbjSYq9Q|} zdM;?su#lP>lJ@m`FWcVsnAXq8F!&@PE?fv;0lecz^z5V>!L4&B$QhT0AG8H6+c^b6B~ z=d#Wi2a1-XPv-_GJ`wL4x|=POTh}DrEuXSlo!Z~|Z418pkbh6nz&RUOwW?%JVsF{- zG@LPR=u1l9P`i;Mk)`t9h;)9Zl3?og?B(L$?KamI9t!cpy0S@%F2=@TpEWpPj=B*S z{?S;n9vT?;RrLK!EfL!SrBIhoRY8#^KH`vi1>uy>LIZdu2S@HJxIH%DoI+_xH1Alv zCwo)L66({8B3hHoA~%gr7N@qSe^e*(Ugo&P&wHUK!r3-oM&U28`p4T&n6d zgjG2k4)@(`E60u6rDFAdq@vr7W&%+UR3uwo+7G0jWPWRLub=xbhylQuos0`%^-~fl)_C*AbE1 z81ZeA+>gu{at!E9O3arqK5liDPQ?9`BIsVIX>*Jnt(}d0A){{IEt$GfPCBYvnl-9o zuDE;sl<-aJhG{E>JX8I!Zc)Ruuzv7e(;ta)3u^(zH9NyTPa3~z7*>d@FUtA1$B`Y! z`QJTCB0oH9N8Z!fZyoL_*YJ~Nvt2U6#f@A2rs`q(-SZ?2PGy-eal~g9rdfPKK3SBf Lo42dT + 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 z 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 literal 0 HcmV?d00001 diff --git a/Resources/graphics/revokedKey.svg b/Resources/graphics/revokedKey.svg new file mode 100644 index 000000000..c7f3f2f76 --- /dev/null +++ b/Resources/graphics/revokedKey.svg @@ -0,0 +1,14909 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + email + send + mail + user + not available + + + + Sept 2009 + + + Franziska Sponsel + + + + + Franziska Sponsel + + + + + RRZE + + + + + Beate Kaspar, Hendrik Eggers + + + uses < http://ftp.uni-erlangen.de/pub/rrze/tango/rrze-icon-set/tango/scalable/categories/email.svg>, < http://ftp.uni-erlangen.de/pub/rrze/tango/rrze-icon-set/tango/scalable/actions/action-undo.svg> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 163e9fddb9f16d50efe2f936c096bcfec1de2117 Mon Sep 17 00:00:00 2001 From: Sreeram Boyapati Date: Tue, 18 Mar 2014 21:10:09 +0530 Subject: [PATCH 062/253] Code Style fix --- .../sufficientlysecure/keychain/ui/ViewKeyMainFragment.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index b2f9c9f40..4844c4028 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -266,8 +266,8 @@ public class ViewKeyMainFragment extends Fragment implements if (data.moveToFirst()) { // get key id from MASTER_KEY_ID long keyId = data.getLong(KEYS_INDEX_KEY_ID); - long can_encrypt = data.getLong(KEYS_INDEX_CAN_ENCRYPT); - if(can_encrypt == 0){ + long canEncrypt = data.getLong(KEYS_INDEX_CAN_ENCRYPT); + if(canEncrypt == 0){ mActionEncrypt.setVisibility(View.GONE); } String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId); From ea648e5a431e20bf7a49ff7e6f41ae89465db8c1 Mon Sep 17 00:00:00 2001 From: uberspot Date: Tue, 18 Mar 2014 19:24:04 +0200 Subject: [PATCH 063/253] fix emails not showing up in import keyserver search --- .../org/sufficientlysecure/keychain/util/HkpKeyServer.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java index 0c07e9b06..9d6850027 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java @@ -257,8 +257,7 @@ public class HkpKeyServer extends KeyServer { final String uidLines = matcher.group(7); final Matcher uidMatcher = UID_LINE.matcher(uidLines); while (uidMatcher.find()) { - String tmp = uidMatcher.group(1).replaceAll("<.*?>", ""); - tmp = Html.fromHtml(tmp).toString().trim(); + String tmp = uidMatcher.group(1).trim(); if (tmp.contains("%")) { try { // converts Strings like "Universit%C3%A4t" to a proper encoding form "Universität". From 4aa24413deaa46fc5a583d6e380231a8ce630e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 18 Mar 2014 18:35:59 +0100 Subject: [PATCH 064/253] revert ca_necrypt patch --- .../sufficientlysecure/keychain/ui/ViewKeyMainFragment.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index 1958d3bb8..e140cb21e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -286,10 +286,6 @@ public class ViewKeyMainFragment extends Fragment implements if (data.moveToFirst()) { // get key id from MASTER_KEY_ID long keyId = data.getLong(KEYS_INDEX_KEY_ID); - long canEncrypt = data.getLong(KEYS_INDEX_CAN_ENCRYPT); - if(canEncrypt == 0){ - mActionEncrypt.setVisibility(View.GONE); - } String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId); mKeyId.setText(keyIdStr); From 654c3029610f932d3d3396ce70e4bf2702427b38 Mon Sep 17 00:00:00 2001 From: uberspot Date: Tue, 18 Mar 2014 20:13:33 +0200 Subject: [PATCH 065/253] CertifyKeyActivity sets error if no key is selected --- .../keychain/ui/SelectSecretKeyLayoutFragment.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java index 3d22ca9f6..6b57fc568 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java @@ -119,8 +119,8 @@ public class SelectSecretKeyLayoutFragment extends Fragment { } public void setError(String error) { - mKeyUserId.requestFocus(); - mKeyUserId.setError(error); + mNoKeySelected.requestFocus(); + mNoKeySelected.setError(error); } /** From 9d60224980e33552993baa20e99c656b1203ab55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 18 Mar 2014 19:36:59 +0100 Subject: [PATCH 066/253] Cleanup SelectSecretKeyActivity and also return Uri --- .../keychain/ui/SelectSecretKeyActivity.java | 89 +++++-------------- .../keychain/ui/SelectSecretKeyFragment.java | 12 +-- 2 files changed, 26 insertions(+), 75 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java index 1509bc88c..40771b90d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java @@ -1,36 +1,32 @@ /* - * Copyright (C) 2012 Dominik Schürmann - * Copyright (C) 2010 Thialfihar + * Copyright (C) 2012-2014 Dominik Schürmann * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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. * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 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.net.Uri; import android.os.Bundle; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; -import android.view.Menu; -import org.sufficientlysecure.keychain.Constants; + import org.sufficientlysecure.keychain.R; public class SelectSecretKeyActivity extends ActionBarActivity { - // Actions for internal use only: - public static final String ACTION_SELECT_SECRET_KEY = Constants.INTENT_PREFIX - + "SELECT_SECRET_KEYRING"; - public static final String EXTRA_FILTER_CERTIFY = "filter_certify"; public static final String RESULT_EXTRA_MASTER_KEY_ID = "master_key_id"; @@ -50,23 +46,8 @@ public class SelectSecretKeyActivity extends ActionBarActivity { actionBar.setDisplayHomeAsUpEnabled(false); actionBar.setHomeButtonEnabled(false); - setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); - - // TODO: reimplement! - // mFilterLayout = findViewById(R.id.layout_filter); - // mFilterInfo = (TextView) mFilterLayout.findViewById(R.id.filterInfo); - // mClearFilterButton = (Button) mFilterLayout.findViewById(R.id.btn_clear); - // - // mClearFilterButton.setOnClickListener(new OnClickListener() { - // public void onClick(View v) { - // handleIntent(new Intent()); - // } - // }); - mFilterCertify = getIntent().getBooleanExtra(EXTRA_FILTER_CERTIFY, false); - handleIntent(getIntent()); - // Check that the activity is using the layout version with // the fragment_container FrameLayout if (findViewById(R.id.select_secret_key_fragment_container) != null) { @@ -90,48 +71,18 @@ public class SelectSecretKeyActivity extends ActionBarActivity { /** * This is executed by SelectSecretKeyFragment after clicking on an item * - * @param masterKeyId - * @param userId + * @param selectedUri */ - public void afterListSelection(long masterKeyId, String userId) { + public void afterListSelection(Uri selectedUri) { Intent data = new Intent(); + data.setData(selectedUri); + + // TODO: deprecate RESULT_EXTRA_MASTER_KEY_ID! + long masterKeyId = Long.valueOf(selectedUri.getLastPathSegment()); data.putExtra(RESULT_EXTRA_MASTER_KEY_ID, masterKeyId); - data.putExtra(RESULT_EXTRA_USER_ID, (String) userId); + setResult(RESULT_OK, data); finish(); } - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - handleIntent(intent); - } - - private void handleIntent(Intent intent) { - // TODO: reimplement! - - // String searchString = null; - // if (Intent.ACTION_SEARCH.equals(intent.getAction())) { - // searchString = intent.getStringExtra(SearchManager.QUERY); - // if (searchString != null && searchString.trim().length() == 0) { - // searchString = null; - // } - // } - - // if (searchString == null) { - // mFilterLayout.setVisibility(View.GONE); - // } else { - // mFilterLayout.setVisibility(View.VISIBLE); - // mFilterInfo.setText(getString(R.string.filterInfo, searchString)); - // } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // TODO: reimplement! - // menu.add(0, Id.menu.option.search, 0, R.string.menu_search).setIcon( - // android.R.drawable.ic_menu_search); - return true; - } - } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java index 47a3fbad3..2efa7d33a 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Dominik Schürmann + * Copyright (C) 2012-2014 Dominik Schürmann * Copyright (C) 2010 Thialfihar * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,6 +28,7 @@ import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; + import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; @@ -55,10 +56,9 @@ public class SelectSecretKeyFragment extends ListFragment implements */ public static SelectSecretKeyFragment newInstance(boolean filterCertify) { SelectSecretKeyFragment frag = new SelectSecretKeyFragment(); + Bundle args = new Bundle(); - args.putBoolean(ARG_FILTER_CERTIFY, filterCertify); - frag.setArguments(args); return frag; @@ -85,10 +85,10 @@ public class SelectSecretKeyFragment extends ListFragment implements @Override public void onItemClick(AdapterView adapterView, View view, int position, long id) { long masterKeyId = mAdapter.getMasterKeyId(position); - String userId = mAdapter.getUserId(position); + Uri result = KeyRings.buildSecretKeyRingsByMasterKeyIdUri(String.valueOf(masterKeyId)); // return data to activity, which results in finishing it - mActivity.afterListSelection(masterKeyId, userId); + mActivity.afterListSelection(result); } }); @@ -141,7 +141,7 @@ public class SelectSecretKeyFragment extends ListFragment implements + Keys.IS_REVOKED + " = '0' AND valid_keys." + Keys.CAN_SIGN + " = '1' AND valid_keys." + Keys.CREATION + " <= '" + now + "' AND " + "(valid_keys." + Keys.EXPIRY + " IS NULL OR valid_keys." + Keys.EXPIRY - + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID, }; + + " >= '" + now + "')) AS " + SelectKeyCursorAdapter.PROJECTION_ROW_VALID,}; String orderBy = UserIds.USER_ID + " ASC"; From 2435794279bbf8ee8775f4a2629c91827b0a278e Mon Sep 17 00:00:00 2001 From: gogowitczak Date: Tue, 18 Mar 2014 21:52:44 +0100 Subject: [PATCH 067/253] Blocked ok button for generating weak (<1024) RSA key. --- .../ui/dialog/CreateKeyDialogFragment.java | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java index a41bc2bee..ffb45afc5 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/CreateKeyDialogFragment.java @@ -25,6 +25,7 @@ import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentActivity; import android.view.LayoutInflater; import android.view.View; +import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Spinner; import org.sufficientlysecure.keychain.Id; @@ -113,21 +114,8 @@ public class CreateKeyDialogFragment extends DialogFragment { public void onClick(DialogInterface di, int id) { di.dismiss(); try { - int nKeyIndex = keySize.getSelectedItemPosition(); - switch (nKeyIndex) { - case 0: - mNewKeySize = 512; - break; - case 1: - mNewKeySize = 1024; - break; - case 2: - mNewKeySize = 2048; - break; - case 3: - mNewKeySize = 4096; - break; - } + final String selectedItem = (String) keySize.getSelectedItem(); + mNewKeySize = Integer.parseInt(selectedItem); } catch (NumberFormatException e) { mNewKeySize = 0; } @@ -145,7 +133,26 @@ public class CreateKeyDialogFragment extends DialogFragment { } }); - return dialog.create(); + final AlertDialog alertDialog = dialog.create(); + + final AdapterView.OnItemSelectedListener weakRsaListener = new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + final Choice selectedAlgorithm = (Choice)algorithm.getSelectedItem(); + final int selectedKeySize = Integer.parseInt((String)keySize.getSelectedItem()); + final boolean isWeakRsa = (selectedAlgorithm.getId() == Id.choice.algorithm.rsa && selectedKeySize <= 1024); + alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(!isWeakRsa); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }; + + keySize.setOnItemSelectedListener(weakRsaListener); + algorithm.setOnItemSelectedListener(weakRsaListener); + + return alertDialog; } } From 19e962481d171efa587e928ce2339ee42de251c7 Mon Sep 17 00:00:00 2001 From: gogowitczak Date: Tue, 18 Mar 2014 23:30:42 +0100 Subject: [PATCH 068/253] Note about blocked possibility to generate weak (<1024) RSA key. --- OpenPGP-Keychain/src/main/res/layout/create_key_dialog.xml | 7 +++++++ OpenPGP-Keychain/src/main/res/values/strings.xml | 1 + 2 files changed, 8 insertions(+) diff --git a/OpenPGP-Keychain/src/main/res/layout/create_key_dialog.xml b/OpenPGP-Keychain/src/main/res/layout/create_key_dialog.xml index a2e908433..57a1b865f 100644 --- a/OpenPGP-Keychain/src/main/res/layout/create_key_dialog.xml +++ b/OpenPGP-Keychain/src/main/res/layout/create_key_dialog.xml @@ -17,6 +17,13 @@ android:padding="4dp" android:text="@string/key_creation_el_gamal_info" /> + + Successfully exported %d keys. No keys exported. Note: only subkeys support ElGamal, and for ElGamal the nearest keysize of 1536, 2048, 3072, 4096, or 8192 will be used. + Note: generating RSA key with length 1024-bit and less is considered unsafe and it\'s disabled for generating new keys. Couldn\'t find key %08X. From 69d26496a22dbc341fc9e646c13516626007aa08 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Wed, 19 Mar 2014 03:32:09 +0100 Subject: [PATCH 069/253] use correct LongSparseArray for minimum api level --- .../keychain/service/PassphraseCacheService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java index 176d09c1a..5825db01b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/PassphraseCacheService.java @@ -26,7 +26,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.*; import android.util.Log; -import android.util.LongSparseArray; +import android.support.v4.util.LongSparseArray; import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPSecretKey; From 12b97d251a345cbea644c830fb323b90fa24fdbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 19 Mar 2014 16:22:10 +0100 Subject: [PATCH 070/253] Cleanup --- .../keychain/ui/SelectSecretKeyActivity.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java index 40771b90d..82a3c2e8e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java @@ -30,9 +30,8 @@ public class SelectSecretKeyActivity extends ActionBarActivity { public static final String EXTRA_FILTER_CERTIFY = "filter_certify"; public static final String RESULT_EXTRA_MASTER_KEY_ID = "master_key_id"; - public static final String RESULT_EXTRA_USER_ID = "user_id"; - private boolean mFilterCertify = false; + private boolean mFilterCertify; private SelectSecretKeyFragment mSelectFragment; @Override From 8059c78e55b4a29c9a8e66a7614c27732447c926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 19 Mar 2014 16:22:22 +0100 Subject: [PATCH 071/253] Remove oxygen text from icon --- .../src/main/res/drawable-hdpi/icon.png | Bin 5101 -> 5093 bytes .../src/main/res/drawable-ldpi/icon.png | Bin 1969 -> 1967 bytes .../src/main/res/drawable-mdpi/icon.png | Bin 2906 -> 2896 bytes .../src/main/res/drawable-xhdpi/icon.png | Bin 7895 -> 7870 bytes .../src/main/res/drawable-xxhdpi/icon.png | Bin 14188 -> 14153 bytes .../src/main/res/drawable-xxxhdpi/icon.png | Bin 20919 -> 20825 bytes Resources/graphics/icon.png | Bin 78773 -> 77995 bytes Resources/graphics/icon.svg | 14 -------------- Resources/graphics/update-icon.sh | 2 +- 9 files changed, 1 insertion(+), 15 deletions(-) diff --git a/OpenPGP-Keychain/src/main/res/drawable-hdpi/icon.png b/OpenPGP-Keychain/src/main/res/drawable-hdpi/icon.png index 571634090f17b680a01d194fb44a6838d6651754..f5487599b83b55d4834bb8ccb6216e4c8dee6462 100644 GIT binary patch delta 4745 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 delta 4713 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 MSl>*Kq~j9vKLry3Z~y=R diff --git a/OpenPGP-Keychain/src/main/res/drawable-ldpi/icon.png b/OpenPGP-Keychain/src/main/res/drawable-ldpi/icon.png index 63bdba2093716a5c5bfb75da8407bd97e93be2c5..7cd482bff9862e36d1882842a62a27dd11d3c0be 100644 GIT binary patch delta 1878 zcmV-c2dVh653di9ZhtIEL_t(oh3%J5Y!lfX$G`8r8P9lZPaGR3c5rM8WhJ)N_CU2l zJs_bTy5+R3RJgDQDB{vxZJ<;OS)?dj*o78pVT%Z=grMr7p)0j&g{t;IrAoVMcULR^ zyrukRX zG(S9k{P^FWwtxARD_3Io?%jK5W@hHSiHQmS$dMzZ_3PK)-Mo1-3&Rl6o`eugPfsHZ z!^Kvsb!2>e{NfWpM~@!eoyla*Boc`MN+~o=gCt2^pdbhk1OZ&v?JO@ZU-EtbZ2-GW z)BMA!Q>Qv7PoDe*A>>WY`9nof24q?G7-Mb5SUR0fGk;1c=I7_(I1cjpJak=05CqUP z4b^JZ+`oT+sta`J(4klRGwVJ|CbeS6>?{C4GMQMH(sfBu6bQ~iDT5FkuIIsV9QVQf zdu_|I*5z^o4?3OB>%+st|11`Zvgdj5JP(voG@DHR?_PCixO6e5#ESLB#lKmm`NrhrWaG7+JI4$8!bn`xm=cRY zRaIzO9I`BfF{b1O29;DQ1;$vp-EM!uBnj1O6_sig{r&wI$mNhsCZX#(2q6f=5RT(u zZf*{YF$@n6Bb7=)k|d{AtGx>V5&-mNGH<9_;(ui?2)uRh(5y_^@Ygn!5QZUUX6}mm;*yiiX5!^?2}6YfG))8N96|`#wvFZG zWz1FQk?Kn$nM#2W0!nB%6pw6u1mXJucWisk^W2-3X@2~##f9Hox^!uFt(GAHfPV;l zksHbZ02US&P+zPQArLnV1C$VCGa0C=3eGvAb|8d6v(-Yg(SXbtq?o+o2|xg_t)9G{}nx~Y)wr$Jz{Rf1Qq^|43(OeJ&LVuQJVwz^H z)oT5A-@bhZ0f24Yy7fDSLg9d_szj0`sH%$o{(i`^j8dtD4I4H$+X&-m|awdG&bpTFbK9 zw{PFxA=$P)%Q^3cClq5;7N`_%vGfmTwWjP~+h};Q6NO#=(Td#&;h(e)&p?{&FHRpRZDhST` z6XR%pYHI3FLWl`EIXP*%t~(uF3)XUNuceGJFvgzv@LKw1vU=^UTetpv=gyrUjg5_Q z3IL5p{qDSdof zURfhLozAVZXV3n=*Jvpk7>4mvNs_UsQutfw_LQwV4(3M)XLREX964I`=yQ>uf zav%zS5<7{LW=!ny{Ga!FAY&XF7OC2b9{5Q=>5bo;`F(!B_ka8S-ZK$G;QvYJiw1aF zUIIN&UIIN&UI-8&gd9D3bmO{n&no-T(a~2VNqU1ZCR0i!N-6Vwe`0ue`1XGTasK@I z(*S<#x-NX*Z)P%?0mhga1VMdiX=(S!$jHpPK*PhsuNa2$PAZl9zN)HuLI`4YUAJLb z*5575`rzcrlYcj!W&G;ZtBL#f@4r1gJ^k+Z_;_&a*s=1aO`Gm**|LR2QH0ngAp}!X zQ;4EyvDs`M8yy|JvlqeK@j{5 zz#hx8{&f2E>Gr8pr+!5U`6=i8k)kNQvMl?Iu@+-2lYhx%7^M_*b8~QA7llFrhG8HK zLui_YTCHXsJa|y=03A7UFvdVB zg=w0o*MI9UP4m{+*x0KQ0PNqtU+o_#UrH#6QhjmpAGT$^IWaNOczyTo(PE)El+-k) zBoa_n6`GcWEX!bwDf!+$Mb~vO#wx8=>kB4HsMTty)@s$;eoodshI0|Nulbsdr;hMsH=hS7ru4ruUxtE$(MG{ZlIK=ZAhonV2r`GZTOytD2fn0MzMj{WlVdkp}%aN)uqlF8&Q z&Uw9Bt?t!zeeclF(1~s!a_G<@xlkxH`uh4(j4{m5&*R388xTSukw{?c)~(pKZ5z_* zbSFZTQV1d7x-OQMmM}Ot2+#AZFbrFnOr{vmQ%d1DjvWNSLqbT}FpPnCE`JO|Af#W#vJP)4dfpacXO7oP`p19L<&Orzvt4`~wRO;K;u3a;C?AY-s z85$b;Nx5A9Ael^dI%vE_l7A$DtyT$$Nc;}TCEl;l}g7@x5h_= z5Mm8LHyEW9nM_8PB%KJ-u=jFAglR8BuRo0qVpo9 zPj1VrYec) SEr|gD0000Y1*^k*>Hf)7xt4Os~ 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 QQ~&?~07*qoM6N<$g5?rN3jhEB delta 2825 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 Ky;Y`e8}>g delta 6884 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 V3K3f0CLrL_)qJE;qwetXe*v>gR?h$c diff --git a/OpenPGP-Keychain/src/main/res/drawable-xxhdpi/icon.png b/OpenPGP-Keychain/src/main/res/drawable-xxhdpi/icon.png index ac8190c934ae1ecdf7ca8eb96404e31ed6e80055..b2922309f278445d4f0cd94b8254396674c22590 100644 GIT binary patch literal 14153 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 literal 14188 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 e{ug#W(8oS{V+kNX-T`;Uft2Odos7wqg%=GCr{ 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 Dfyh|} delta 19627 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 KHGOL0oBS`+?RX^s diff --git a/Resources/graphics/icon.png b/Resources/graphics/icon.png index de58949ebc10726f1e93581b1b3fc43e19552087..a7b133eb9ba3e14e356289af7159ce6f98392e8a 100644 GIT binary patch delta 36190 zcmX6_byQUC*PWqb=tiVFhj@|hmR3NJE&)LrB`*!qjWp67gMdhbG=h|Lmvnc1*Wc&z z57z>R`<#28v(MRkAL_SJ%2!cpoC|=;hN`l|`74jBMJH4#T2f3#S~I2iU^%W#iUC}W zVDT6@7v~~QLR>G=y3-6+EEV;<#hqD~^|hICFApJir#w;@?^sODbGmri?-&W8*isHC zwK)Efn@ArI>KhhfX!xEISv0bT2P>*RoVS+n` zSxKcG2u`;$Zi;ss3!uNLo%@mqh)Yn;idYCNCP(z6K6dt^- zs2rdg+1bEpc8S%^I&V?{>${wvaie`xi@%~^j)u`h!1l}8QtGj;GW^G|Wwl9Ziq?kv zdc3gq>cCk_<~nT#FV<+bx1GEE5ODx5GhnSe&a5sYc9V@UZGI+iHVwQ95Mg*I;)hx) z{I{gOS-#GVrAtq7fs~S0Q#V%okty+Ab^M@~vPF&T+ytQ~%L|Q$QOk>?=KQE*6Jz5?DcJ{zsjKU&PXXl^fsj;g zlg_O_x=b{XZUOm(lqR4lh5Y6Cx%e`f`!d&%+%IQSwyqg>qn4B2XW#uEL_V}${88yL z2({ambY7FIvY?hWx;ejlNOJi}E?Yc7pnu*QaF4RVTlDRl=XRsRv$M0aG0RC&Lq$_Q z^nV`#q4oLi`j9&mW5tuZ3tubEWGISbQ(1=-8vmC?`PM>7-2kBbbZ<0*Dm6_#)em>I zKHMMVX?{tuDYTrVx6!X_zwrkkSw!>^ z(s30hboB76x3?GgW>ZK}DBman}jeq4L-8Pz$Ed1Gk!b9`KT z>yu8+vLG3cv8v9(%S))jAx3K_gz5xKbMjsBd2iBUoXjKDjK}M&QO#iYv5eIls2Bj5 z=hLd5(!QC6ntFRb<7qu7^t*fq=#~x(%?iDC7I=Md?-m2(z3$>u$k+NH*vU7B7sWd~ zIvRNomH#pci<=&4aENE|_j7EVKYr0@O2K?D6uyk8ARg)&7|JQb=JP)(NP)!qqU2o# z6(f-O7)~~D38n_fb50dxRB)r7#oiYq=H9TIYgDXZ50j(v$i=@hdz=&0Z=J|w%={B> z9#A&3+kFcZblcL++-M%YE5P|X40U<7iyXu_)8nC|wYt+Q=BIA%8rHHq`h)iG1DZlN zT|+}duxlO9<`lX{*O`oDqV)@uVByqBT_Fo^tMMFUU{qb5P&)Vg@qh!)OhBUsV?vkBgC%s7kEr!#< zTs`sKiV*IP#Sb4S*2K=e#1DK$U;>mloIE`14_C`!whCM69~y&9U9lJNv18$FaJlh3fpkduhPbR6lHBLp8 z4G(%=)cyn_nGWX5i-OAi|7Z@lG4*ch$n#GhQY?;l#5PYcl*GePeYBdLEwXvvGjdJ9 z6z;w8QEDfQ<@WGGk(CnTb1z)?ud5mKS0$T)+S+&%Y@bkuFlBOo{J7_qr_7&1M2P0x z(@(cEgV6nYjRju32&;QHx*O|*!P#LW(Dlr*CH~|XuN%L$GgnH8L@RNVBry1J;_2`Q zwc)gEzHtyk<)0whj`HfZ2>y2?rkc1i3FYgsD zph|Qx^!Ft)QurZwe4+RSFh&(5zWKb(Oz8;u!wX`q*3H?Oyf02!+uqqo0LdQ@uR^1P!?35;Lt&Mr)moQMM=C9}yyoct&sW1pRb z!}axESm+aeul^+AgtXLMDP6vvlkImYL7mXcLUTh|p-0*K?5R@H%9EI&a6bu@Q2i zeJ99BDdDL_bP%wigqlVdJnDS%1$vFYG+P95~l+?x7LU%qq;^-)`EivKN?Zu*!Jd(yhC9; zg4?#-%Vcb7=ruOuswa=Vy*-uB{Q)d2(3cIe!j2s0>t)p}*>io?e?gP!3Vcd`^W?fc zqLyUfL5b0gU)}!brEncp8!DRo?@BAXiUa&+Wz2UI6i*FyxqZAXj|4%eWfPx>vrcfRg;1OM$smk}z(jyf9QKmeqS?Z4xRr z!a~D=5(@3j*X)G7f31?sJgRH=Uv}Nwv3d=AEUEbS*+m>|(iHCpPG&5F-FL;xa)+zZ z`?ARpJJM|*tOyDm@JjTQjlM8yX=z10+6Y9jK75(G5@Vy5EV}6a0*p^54Lv+#^teBA zu(v1P#>iO>?Rg~jCufJ!0!dj9{z6%{!$>xE|E+^C)TC~|<4PSxvCk#!Ir^a7%z;NO7^~LS*4G(5 zJ@O83jLViYu_XcMqDsq{&Fct*-z0ct(|hzPw6!-y)xQw*#Y4{ZxP!UFGg;!^r2k$^ z=5?P`t8uK)i$HuSe?~0-pfp=j|2id1IDRr#AVF^fK*}*5Z{AUG#g0WcJc{vRDvW0m9nDe&|qL(1Mp*H`%2B4g#67G<%I8a8h%7? zCDuXw0d2G=K1yj+K~Se=c5uEIyY#m~yt;T5M+PJA7kejS-D;nGIUtNP`8?wuJ6At< zcy6eZT~+5FVxNnK-rU?^{t$THa<$~0W_a+o4PNru*iFwTzn9&txDH+hz)HFEal|P0tKR-Xm z3Vr^n;4+mQw&;g$W=&7J7yV)hvaN-v(5?Gine`mrOOF~VJJ}A8`nxFg`WgOLXpPVf zUh0+XIk8*>G>jm$G<6J)Gn*7u7Qsvh7^>h;0EQ%=JY$g8M>DYall1n41=@F4SdJ3s zUg%m|nh0t8Xh*uuGWL)M`h>NAR#idn&t@_}_XlFO%&?e4{ZSO)vJx9U_ zC}o(igbKcRaq@V#b4ysYZ6>5``}9}-m#5C=^Z&N1{$6Bikt2vWkcql-8g8W_FA1Z- zP=KY3{qV;gH=C=)0CCQVU!^W~v;wkTwej=jb{}Kf^!@?E4)XE>$hlW*5`T`v{9lje zCaeHlX?%=79Rk06S#a3-JsKP=Y;7@tRBj_A3#~)HJ_MY+r%;sw^V1+pPQ?}j;1AbD zT1&3ZMEBQyJmLL$6-QlTSMWn!AHU+2aR%>tzW48!6d0e9yd`&K$C)RZsY=-sRyLH4 z7(Kkx{Kz;}%aGpL$Bb!SIm?c1N8O+nbZl@OBKYVW259+^dtoypxE>x2>z4We;y*#c zYcULb%b`s#NLy_1pQlc+Epv-V8FrGjNPu^Ix{MNdn;yq!s-&TtzE$RDw`eV8XgnLNTbQ0%ihX=?@51S1O+HiD zwdi<31b?x5Xq%_|Ow9hnhoS~RIEG>TO2K!iv3oGrQtCXXj3GrC^_MW278Z(%%A{RA zoE%nDU$4W;SQPM~G4lO%6=n!HAee7_=oHA;RE}Te5ugu~N=d@8UI?z$i9)F}~^2O$)(83YPwpgta2i`0eqd zprByHS<2|~*Pq0ykXuEA0?|!FA@LA1@}4_%+s(R)MUybw8pyw%)hY)YY#3UNPnt#k zSm}?QPdq1IcQ%6bi~y9u0lH6XD;P4kuh_|5cgC4Gxws00ozrTcMJwb%602|uk96KM z4FCG2Be(-wXJp)fDelWQFLG~^i;%N^H8(v1tKJyi(9KP& zma`F2tu>ZzseEI_e)MBx=9VkAF+5~BN>ggGd(3F~9JOS&79a;fkId1-V%R{dg({5= z-WYL?K1MC`p3xZ-Y|r;>KA)gIKS8beCQDOnRdX-CsK429GD0}Y!?AqY6%ucqD>yp# zraz5G){osG;e>}bwDvjh@#Blr$>k;GyI*jnZ*8&ge$JI;#UF-;Jtr(#tKBgJBoHsm z?;FUugzn=n6#!=B|27<*owYDN2O$PX&EVvgx|KnzniXO$TElVXoDb-8$Wt0OFcJ#7F zrI*S?4Gt_LKWm1YYQ3c`69uy=*_^F}2lHh@hcgu*knyV)HS03cy6&? zMpvznLN;0T8e2n-{AYh5Me=tKyr1@k-n$T-(L7LB#xs1?7?viMV0~8dx|c0KCB}2{ zr)Zkqr;>1{FW0_*L@xr!B#~W;m7rSB(HJ}eJUC35`4m+p>%QyO8a-3 z#$jE#8ogj}%J{`Bk`;4YsJWx_JX}2P40)#Exz+{~v-e1oDUD@M{=|-{5LL<`vQu0{ zu^IUref`c5Y%*{XP@Rx#H4dT)g(H&+_Xh@1U_OjC5op~eEapVgOVfSYrzJlPE=~oA zP$(!m(<HFne zy9P<+-Nq1Mgv>%Vo2q<3j82 z&(P?QB>kn!=Z*K-5cIk=RXcqaaxQ|%`hHJ zPPOlO+O8hW)Nnmp@wfc~r;^36nBZer4t2U`f3aifk!Yq6Vh#hk{X54K3(Vx!U-B#b za?Ycd%Jh4If1RW&Y)}{?O^!(-N0hxj33pjcLXsj%i1{Uy#z=;|utB%1gE|2G&l-95v?KOYz*qKBr87Q{zBN$O-+)&4~TT zC>W+qnuv(Qcq+RRGX=2`|KF=<8HhRH)_yPec}A!IZ~yg*Ss!@^qgzIDJ&(e6(@-|#!K>nXW4WS zT<@ZKI8+E9m1}cX`{jQrS8b0MzLbHoa@=p6*wq_^AnBnmU-5gAg8ydEpuLcfp`?q; z@wfM=;RLY3KgstvtSVB1qVV3|HUjJ{*zBSnZdoqrrb7Z|Z%GxZu&McKKGoyl(h79^ zdczAbUxpw{w-^*1(cwX@<*NL} zn8a~O3a3^G6!NCp{@g-Qm|>0u*2QWqx&FO}{{}rlLVEuh zP>UAtn!D}oJTUXOO|EMh|1Me~KMAR(u8x6kN-fa3PXC7kc_>8yww#q~>5@N82fk5{ zPR~@V637ri7)PSrOP-RF5?q&iC^pe?ddg?)cVp$8oxVeDvLZ6W?m6$Js?__OR?J%VefYW#j!lKwwRn8qQdi*s;RR&ui~>cP=_vqV@lKyqBnuZU2&y^}HS={0qrpLO z7zewP!28sdfe+bV6>7U{^68PEK>{$uqxE2a{d|}G;>E=)1+l<;V@oDkR^&3NcPD3q zB~JtIE;@GYj7hg3a-Eq;^uu)z`Ec=zymPbo^`mD~oim>kk6~e-h55CC2=lOQxF*pt zqyv?aqfGMtyGZ01!eJCfJ}SJXpWm*0U)p$j9S8)Ij(CVv`(~4DogxJg zhqs&!T)k@UnjXexn{R1&^2%uYupQ^R-?QoRv7xaJvop9Omj7!}PCJ*}rprLgQ+m}4 z{$VHL8b>sL2Ey}!$Ry3DsN)RGl-ps8s>m^i^Yoe$})7t_U9)5@S%#}V|~9}?BEs^ zEE8i#AdsQ9%JUW30tObk?_L*nWi;|7YuF$$(BsTC6bP)|)Hq;gA2G;bFbK%W(8hDJ z7F{#&Lm(pz5wijeTM>#J8~7)vV0& z3h?$uZyUTkwz2p8qQ8pVu}qISdbb45pq)a|!|-mXg*NI6A!Hd2!xl?-LjBd3zpumY zD>8E8n9iC$V>qqm=@pjw?$(MDO>8;V{O_XWrXx8_y%KFiRx_Xy4&!UEU%p!3)6o4h zFwQbN3Wv!Z27kkfkio+V!X!n4@dB?pdQ>C`AC38VDPR~3?Wn15ur6-bjl@Sa-7gc_pDqSVb?r`;RQL({Id?mrRb58nMVrl8`)3(v>Vr|yW;9#{EH|^HPxd6%`c)6~?u;KX&!6MV7^!Mpl1td$k&?=vM&Kdw<_qc; zW%3ggYiQ)Bb)iI!q({XM!wRKZ5HNFMaEA8JH2;T1GI1e#$5~LfU#gNR(ZzkI)Ki9? z`ZTniLX1@O?hi2Dmzo3_Kqtt3XO1^NAOHL4;9`6Wg_=!E3`XFjAwJEWC(yhsXW93( z^Q8(*N1V^f6p)`^{TtZARWB3RsDG9A*^)1@)RS4O*47b7 zZ-I5okzZ>~wSF%>@;tV%>wv4iX?QL#4G)DMhonK{Sb=-;D66xnax;KRECRp(T*&o8 zOLPR`Jusr5sj>gJD6;R!O{8SX?w>lV@?3U=gSL?;kljaYv^$wYPkUdwe5z|Q;4{D=>d}>km5Ref6WlfZw+VIdge=wf5 zhKp=~DwVb%HcFNeHS^2_Cz=n2EDe!2t-*p^7DN1!HjTm3%cxO?JKrt-n5FLrcn^JPh|`TO320XpNBbEgvKOAbTDkL3 zdOO`0VXvf%keapvqWDymJ)1?k*VgF4`1G97vm_>5VRuPYY~@Qrl3Zm@f6`n@0$$e0 zCojlh$4kwQ`HMSwi#s-`AKZTJ;Eycu#&zF)i-Kr8chp94KH(U>s(FTepOtkm+5Bsr zL?UEofKugthpAv_^KifX6^Me!=8{+)c#w*>DIsGv!JVf5V9*I_c6WgUk_Q1bbi1dn z8R%!__MxTIKqEb7@d*K`u!t@IEz#$)Vcoxo!gIXdfm`?pYC1NUs)|-XtSOV@c3L}` zI<6G;41plPqT6BHN@xFFxcp0$^aiuP9O2a(15&*z9)5_4p{w{AZpn?Yr%E}oe48cLN>Gunhe&%^ID(GoF)Z8|)fH4@0RrPM-^Uiibu zMQ;82W(@!C3;hnhZ)zIN>BpJb+0KjidbKs5#H{pUMUpBmUkw^YsBhb@(lovd7$y+A z;j${8j?)>Cf{4z)or!)>wTnR>Jp&oL6)%aDU)yUN1ln1K=-Sq^2_(RHm>FpGr#=3vHx9g!1}P4Yc^W@kxFn^^A-bNRAM& zyjRnGJf%>Eu4J;*Zhk|0r@)Dyf0Xu{fj5bFG5utF&<-h$i!y8%4tquC-gfj(w{<&ICD^_wSdMlVt?M}lnAEz*z;jK=*^tjs=q z;MP&rbmQNC16E&eT1io>nIKT*z1W>figOUmSjog@&K>3#KCpq%Y6pI3fAE0ZcszlU zrYKf@7CwSaDtZWXBlN{QC$dWGHo)Ps?3lDxY z0adlAD}^jGbFs@A3sQelcIO1P?;2t!Kd-$O`Kid6bs=q+_0>0clK01FQMKq?LxYT- zUn?9ZLLKI9>J1F&@`6Na%LN^00cI?vOYadgI4Bj$srm*h#oe~ynGj;cI7nyjd>Rbp zneY7Sp2mHF#x{mEW!zlgAY`qYFrijdLSR!kOitwSShf}F*Pb)@3v8$|GBOIq@2yO* z`ka4%Xf)H;4IP=jP+RhNxV;#E$SEp)|M4T4cdNLNI!?XNM@VHh1;1%J&_nGiKS;7| z*iznhWFbJAH)fA{Sx%c6*R|BY9p}SDPWBD>R$YMM)Nv&aGFu9s7au?V)Twa+YDs)lN#Jjx` z4!z~8R~LV+L=k52U^9W8exj`H8w2^kmWpn;>J;p^q0-je&?umrV6i--uq(XwtwU+I zz;%S9`teN4EPzkx`(OJ7&)k!gD=?D|JVf1r(-?W<2SnB~g7e$_!C)HeM2yv#?|T7C za@;WV1Xd;VL8H|>sLyE7-j5mwQFc9$v^!u;`t3|LxmF`HJ1qMjo+bM4*J3~gioK&F zGVe=5ahiUh7x*Nz`|e2A9BYMFO{)0i;N}O5ckjp^{6xZVkxl1GN3w;ZL2dclyaSkl z;a4xwTHn_j;6z47f>LdEkf_Ez$P0Oiy6;NakAP?vRi zcir}MmOu7)yV|I^;dTZLbC2-TyY11O_R_Pv>gsA>6nae~nl%Er@6YgNydm#~Yx^+; z^J%?6S-#r#S$>)v85t=C<*BFHe;+Ll{uEbqcP{>d+U2w-bSZ&>A!5%~fQCwzh&D zd^71q)V8Pi;z)k?zgohNr`~`ln?@BfJDyS;` zEk*dkONj6XYY6y}Q><1&HTR8yflM(l(b?72HS;kN@itq?842RFJ6XzO-15oUoN1-D zkNQq;XlSUt9f6=9G=7`HM4S&ID+vCa+}wVLpa|TKXlr{+JKG*zP7;2ao}ShajX7I< zRjj@!0h%^KLQuaMaM9hL<%W78_3KE5b<=U1v_*Yryc^@ttMYO5^?fkO1|)$;K)7Ql zovzo9#Qz9cAF3^Q2#jO8jQ{-4-x&HE6O1MPPm(Kbj}^>2+Bs4t6AD)w?%DJ5K>Lzl zJ~P)j38!r3Tm}v@t3$4OP_mhuIRM>6gR`0>GcG0@uF-o$@)Up9oCb4mD%mC z=xnnTBPh!#oA5q}qI5ehN5q}o(7=Fi{yX5CL@q`|kHi|=+JMKEUqvVt)zvxnf*GTr z)P4C5e8o=p*UoQJm=62CzcaB)bDOsn)5wvC21_w{Xs2EM+|zvsk5A`gkmPmHli6UZ zK+r#HgUQqFG>_Q*arZQ=8{aX@VIE!gFx6!Une_l!Dpy%W2V!GB{GMU(mKw77mRv)P z=Ikxmqk$Ws{=+!$vreZbTK6jc+qZAo0@So%*|!fquEDGGBnA?H|M{cK zKIlJ;8q`T^g%&q&r3%l)zLc`2q3Y{&5}Ld$T>N0+>RNYRuO6XUc7%_42PZs+Vt$l0 zOr)!d{7jN+uN|M`-up$<=T(#x)hWyT?%6j8b)Cs;fc>MlXck4n*>_fg$S%xRVV@=1 zaaJlsx3aV^%!A?-4ueiAE@F}cd#WLv%PdJY;h z3zmVW9W=n*SAPCZye~S!_Zgrzv4BEL z><{)Xr4!j3atgDvlgh1FZZOEKr)^hK7Lp`>fBB!T#iUeagU-zW-5^gatS!TbENZ8FS6!L!PcS(ZEav1u@X3 zG-i{Of%Ch;0n~82>;op8(1<75ibu6}mxp&HF;@D_+@f|^c3%s<_rW#CQqrPjqIX9} zCBoOKStxXA5}p5hace6G&Pd2nLRJkf5A%?=AjV&Z9D93vc%acv()<1zI3bO`11bG4 zX&dk4V@#c?6n(CIO&HxG=2NwisDb+vp2~`f`=$H)mN%Bf;*#0hoo8aJGhSeo*o4Cz zv6xJKeSMpz>>g%!CW;+F0PeoO7gNZOZ_o`>?;zS}D&hpKQ{X%7SF~OJUv(A_HOmAi zDw^skyEXk2-zR`fA%Cp9)%eJyvby?71M(OPRukZFnb6@&=)X4t>sCs>E;le~8%CMk zannxQVlI3NsAvjgZVOVm@%pV4O@lcc_+VSF!DF39*y{OPcq@5_CxF>)ka@;NpjQGr zQ}L^JSs-)jP30V!K@XDB>`h;#aX+Wq?{M8qInN*kvkk;fa0Ph&N6a;6N+UCYcS|vYz7+QDu!ppR;DYVwq{y-Qb^%J^F(*g)TCi`w8 zqu{vL@V1gP{TK7?Lz|N}>c*OZ*Qe|-gG%O0yg(^A?>LKw3;Kj^L*M;NU=2_rJ<1aEJbgFtL^lfKAJHhl zD5LOSxsw26d$QiQb4|nn1z%~-d%+A1@F7GtQkjKa&Ee#r>$176vGJwrg<77}h{zH8|y72uAe3G_4z ze)Up3Y=5QrkOfF-ziZ1;LFKIeHuUpsZ~tEIM)$-bmldkNlTU?el@9~c+d4Tt>;VCZ z;w5LEy-d4~^mJFIQQE$rzQHKLf~b8*q(NZhF`-&EOX?df#oFxv8ow!J(^efBLG>}z z=c9#%Y=(q0hzw*PQ&PvGo@vu)zy-PxUJKhqT+pPR1)(@s>Jm*o3+w)>i3BmVvf4bk zS_Wv!2&Fil)QcH{+wTqKByc|TQX~<}>0{C|`@8zdvi}QCpT+7^JcwLiC-J>1L%TtE zo8oN2j|2h?1}pa!AhyBE4Ms8N>3;iZ0z|0#4~_4+!J>l7*!t|JpkRiYVXGHOg9C6o zSO0$5z{KR~;$NEDjLRIofE{by+<_SSnpK11TiJwdZlb0t$cGwNV|&H!dlFl_)p0-z zj(>kqqpZ}jnySVgLXJB<l&56bA!<9bb*pDrQow-s@}i}w?x+^WOJFuUJ!@-3AY8~uCdM(Job};48?6(k9@?6=&Ryh00|Vih7~RJvK96TPeKe1& zG%-3z5>GdyPx&6r4}FUtfN3$mdm+2}^afV)0lWxm=y4=y<9YZqH#g_#b2DV}sL2Wi zEso%+xFAoMzVD)v)dvhnqiSo*RfiDlR;U}97JhybOi@&QrAY*bCFX|96c%h{dLCsJ zUiS#3)YD%>>NW^2twobX2`b!Ik7$O?|#6;kKBoXe%n%eA?H6EOT+N?HvM4P9wDl1Y%gYoi540zP{cfT4B8z#H*GJ z-N)?hgSXdu(6k9lg+FIvX1EYY@64Njb@<^0*vCGB09Xl1K%LH14!D@K;>$|RVdx<7 zaQRgW3DiLDE#z!zp;DH5$VKbdD3)i~z~4F51j0{X40RB?x{%Xb7k(-C>-p;LBK7SKF2p16g5 z#>oc-@AKv`PTSa(GzW^>K;Rox>k0 z=`|1qlBa1))lq~sKYrYr7EjzfffjNjlE<>#>DI6$BG4oE+0Pr?pE#R+gZbU|IWmhj z>c4_D70HZ;I81QT-`fqRec1<`Eq}jN8)-V zNTaHBl%4N8XJ_ZOiJjb8du!d_0!vofuTnjE*eLy_%9Z(a5{L=_G3Khh zqTbSBPs=mXddxs6PSY;9VJCqF5_n3H3$KTHuvA|C`t|GU`~g+~+nr!ZvZni&1|%uX zqW~*?A@^P4n_>(mQ!opO4m|EOn!z^M(^LQ2JkH{4#h3DJon`cY3UywG;K&URSUJV( z8X7KVGjuS&Btfr7q2cjBxxqv$JF93$%%#zrHy!owr`hX}%8;Zy`q^2TwIIGprf~@hF14M zfaUL@A~miG6oNpS6Hv<(aaVyuW5q9LRLZArN5OP*wX%|zvnV-mJBu9*9k|nyp&z+H zeo;pVFGT{5Gfq>*w%Z@7a+QU^y{Cw0vKJkjpHo`I%KcfDL}I{>|F6HTQ8zl~ZnGJA z+HoIZC;co2`)~ItYfLjtY*e;-`X%NYEbJCgS+qrOg~P`7F0|vH3pgj&kCnSgreOknWzQa26fMFt&ou41mrTgCZFY#L0m5P-JQW>qAk(L%^1 z%M%YILwcX-A5KJLO>L=>{IPjE`#$j(*RG0w!$czO^S+|~!4$h~9_-Px|Luo(OU*(y zB}k1}*zgf`^h`|SA=HGx>$9L3-!Euj*J`J(M(|e)%e31xo}M-YsVvasAv4UY6}KB+ z12&N~8AUftkUKso_;gDXAm)ooO16Ij=TtCv-i?@?N2%UrO|U`l%;733Q{6|vyn)ql68&zlM&Xz z0uA5iP0Ic_7x#Ry1kER#h}`O98D)kz_;WxSC<(py3+rj#-e`0Q%3rMKS)w051vC3eoloIYO|ls0i-w$WME(QUISBKxWRJ{~7*Jm4|LEh^v+>y`ik=tsNu06+${NGwo@iO+m zFzaAc?xXHk?=JT0@OCLEoCD5 z!L2}%tSG1OS3*ePCHCMeZ)(#2ZZOonv_x&}wNp6R04@(MTrWRU!^}0<3IpHd)@!q^)Cs0Rh+7E)2?Xrma?cAe~ zTdWDcx&yy07<1+N1XyQ#OPBWC=2@+oG=9{7L9abyJUl-Bwrvd2)OesEjIO>j`g)_| zG}2S2KH%neJCXBmu6h4{FN+qV4S8j8JS$qOdYb0#n>RwvV3aBTB*&)uF03V_Pv`U%+oN%HszJz`l;qDZiq3ydq|fA^{s@15 z$LQ@>MPz!baS83WfNlV71}P!n{?F?EY#m5s7jjsX4TjjKCijK&HXMDtgzt|`2&I6) zb2AKwfva?pDuEM+ZGe*V3q+pO^6}Q^)P8)Ymo8_HpdWog*mg$VqFM3}kCQKY zZfBKxF}raOA%cmx(ije#wy9gL*CI`$9!?Jr5C0dJEiK>`QW+nlowIQ^cDfKZX1$}~ zKn2%VAbzF+r_Y()Dx3iv20-IJnqzxbQV27&j04kaG8r-15 z$~g%bR+q{f{_Wwu@8-$m_P~Bdg`+5oVn*8qkuNieIMLXI_HRLQN9BoHxNQj+uatuW z_lIBJjhpEA{DqSQ*=E1o)~bpTx8Ur~rtZz8#|9wDC|g>AKQs%r-3`CzL^1ja*RXt+X zEfFWBe=oe!7LG077m+M7a#SZLlbqlAg0r}eeg3hG5$mj$4iX5!*F$qVMjsddtBsD` zU-v{x9m#)L$x(cie8m;0l7Tcub^iKCjxJ7_8gf|9H=Y^=YBbNZ0d~?Gt;KiG&6T6)Gw%o=DFM z?N`yZmecA}b!z5bwiFcMF^yS4ozZ+gsC-Pw?n(boPtY&0=J<5!ND#VOeE0B}BLN!7 z+iyTCx%^wYvl^{rsF3CzFjfZG3Q<{W|3vCoTQgTVW4QALYn}66Wn%=V3rfWMc7i1E zU60ua@QsLr>fSrHU6YUEG3!zF7l{_xWwc;VkX&}jl4HVE_fiYZ>K@hYV29l9kF2}4 zrrx{!F;9m_Csi6IgT~1maN7zfWl;CzBpX zs`bs~xvB+r{|y>pF>T-GAUs`pu-P)H=K^DA7&Enaa!y91fs) z_^Ws_4LnRkj(3c!uZm;AQ%Z_qsYuzPu8b+t7(tw9d>b8_5#mis82Focf7UL=c-HGH zt=}brXZnU~VATPTcR&BQ5Rh}f!9u?fUCa8^VG92;Gj^OSbRxA;ky>!Ofsi}ttprA3*S>uLiYn54!%2Ls4+>Zj&8;t3SeoMRJl{~(P)=|; zrtaku0V%1e;Ha;?Lv?x)_bT!b72Lbbi^Gm%RRI|UD+LPlY4_U3##Dpnq=i|#97d~Ick^m&miycb`L!dIeOq5FJ;V%7sHz(oS_Jmb}RiFd+Zd) z&PJp+0VI{z>tz8cJ^X_$@(lw@vmQFPdZZJ&kV{wUtWvAFGo>XvitCk- zCq^Xix$QD9>fTdXCUvfQ z$hlE)SWm>;k;7i{XK#Oe9JeHOs{rYP=EEP@|MpMqn6`z$#W$tR%F(^Al2FGr0$$qVYxnhH&hPvv6yi5Nr5 zwrky_Z4|e`V_H@=3&D3!z{*r zoLym17+qJKta$w(A>AEH2-3}e_k7klR9uh@!hVm&pzCQFJ(mj4F_Kbk?_>30eXho$C?ZDW zV}*=yxm0H{(B**i=>>osg+M2?S8NMSWqNgJD}x=uo$uqA1##q@tjThtlS6-REz1(v z_0lJPCWYX)w<8MGhEtj^VaQl{qtP_?&(Hj6uG){@kSy0PRfq^5RWYSi2!Z-u7u{}? z!g~hQs=j^}olTwxn-e2jCFK1b&LLrj^0ntRoW$tRW4I11F6_92v5M zj@WIl_Yt34{rA#EBdp4$_P8X)&g2_;AF)cd$oqIOg>Q#W zO5Qp+K+IMJ)wBCW`Dj|9Elo{PBO}VxuG<09XL`T5c>rPxzsRHP*|x0k6BoBKpP!#! z^ZmQ2+d?L7`z}zE{W`kTw8-~}Oj%U1r${%CBym<5hz{8lxcX@$1_(Y#X~oxQeRKR2 zep??0dm5SYSrA-P@2p6}WU~)I`AbMd)Q=89%Y`f$D2t752Xi%3ZgK*u2%ulu479T{ zoLB^r=D9i*Xrac{vod71B(xC+H=z3uzR&%Q9d_x-#DmEI`Y--u(ou$gl5i_Ggm2ZU zG3*3CK#+_$?0V)F6+Fd< z8YN6`fXeOqrEt}4&s&mMfR_4qKzCaSI1+u5{~DBt)}EG1$r5NV@oFJ2I}`1WW@Y%( z$@<-Gb57r9&wgsZgtk2fy&u{wO1*=%C6mb|hXJeoLrZRX7Q{hZSc^8)NomDN-s`v; zl?GmK90v#ym%BcfwRD^8Z_^dJ;@E2$Pf;R%wWr(F%G>x)OwC)C^#d#|xwX*SFR8G# z`~`LtNroq=Pym)zg(4VKZLk|e2Kq+n+%~SOL6_^$)?iZ6J9xC`wZ^*UC~-i({4~`s zmpsO%dN{P7MtCxUP5XYcT zCi9Xf%u8w$+bIo+9ZBM9jVhE0dzX0Feptig{&@`dtN9mf_bVziZJx7pI$Z zdCnFFsGO8*5^|Dku`rlU27mR14v1F*Pq8?5dJ;%px!EEgH>>`;?n#IlfR#(Ds?+38 zKl`WCr7n6L?@n19)NU7ry)rQCIUaO%Rw5p|n;n2XTV)&o`}q9$5-0izy1{Lzd^>^L zoUh%&h8X*OEqdZ)DCrbHbM`MAoeY)!+f+OM43HM^4GNtkccD`e2&ZcQyVr7ZiFjz@ zwVI4Hpe`MH)4Q8xBEF9G7;PXSJk?o9?$qV80d>H~7*iq}cIh{lnt553m6a28)W+#1 z`PZi!C*@*@K)=H{nP{aCObAeW)C>uQj?#M8IkaOWu(nwO$gVqAgKx(+P{gk!@mDFu z+d7Zl=)*)Qvk#$|(&xG~=(W9kH=6hI&VBM7sBa{{;;%Dcl)ofHS~rX+;Hc7$_X5Di z+R?+`Be39H!$NG{(wb?uor-bj!1-RSNA1I2lk3;ksbbU`>v>5H50CPJ;vPQNfmyn7 zeH0+4Iu+EI(tz|siTX+cP-$E2d_~8?cm1T&86A)zwHi}GiAFo_KCbt%(YYGuVDMxSB=U2rp-|9s4oV75zYZDFNK3T;K;6;n&*~GePAx_gc#kC%16^^J= zKm`RG->LmBh~m)odz;cO&1B#YV@(waj?=8p!B0T>$m)Bu=$WT`6t=G z(XQP~0Z~S!)I!+K37r5(?1~C(sMzgi6RK|8Lj;&WHI_HU@eLCh$k>SAhcn(+(S6Ey z80#@dyAJ=|Z=Pkd5LUDY`O!J~;76QDo)y?<^c-B$0G_(5u`W#D0OQkhj5`w zFij3N%ey}hEmY)nAeJplZnSSEOyFZqP7iciWMf(N$7Sh8LFzkUHuys&JTo|}S&SUw zh?lx~T|qavE>cblKp*bQ_d1K4xeltcnqEqB#}%WIejn*>Sydp~C%k^jZ+yKJ^{;+L zfm>BZ<%oIqK12QWzj)|_u8E(_il_K>cxk3|;gH^9m9T`d?HfNcnkATrd65tfpB1ehl`IIyX||}q35GelxL~X+GaFuK z%FIerN4w*rLE8eQTst5watx_O+hQ4Ow#Tm)U(iJv}+~AwPsy)$O)9AW;Lmt~ysUIw1Gkzbo&Fz4^M( z7z;0Hma{aKvSiP_k9kz z*V4DCls;Q#FDO;2R?&yO6z&kZ0Gn&9ti_jC3>KK3M+M*w-YA*}Y3JZgZ%K?s7M8{Z z$a;=OYP;%FvU7mClhJ8WP|F z^u8E^$M>m|l%mgNwGN@T#eYw*g|CoVz1hRaV7dVa7; zA)EDGI?k)LMmxSlXy%z*9utN;BCSk>l9UX3_Che2$;rZ?NOx{xt`P>kq(CBAVKViy zK&iq9q2jv1zpjg9UO)Ss-2;jHLz6>NP5k9o?$p7XI~$kodN_xwp^n<;YO%(9?z}2adp$GetgSXHTf#!) zwR!(KV;N`45#Vi2J^UUzcs^}1GY>#l;iNWGpJkccvyNK*|J3>%B@VYam#cHSV~>}} zwVxj7XWzQnJyc8gE$DJN3KVFH%Yp16u~8KMr6;dF0Cj?Cpx{n0zII{|pQX}~@uSKL zr{(W_M21t7`3T-t{FXOpo3$%tRH4imibMFi)=H%vfnpjWEW_V-tgnp-m|pehB{xZ0 z2Sop^226*Q6%{mxezzZ-T6MB3|9%Uq*TETlN>N;|fcn+@Bcy1tdT<5lozJxY!=~(; zo~!40(KcAU+avV9;Uu=W`lAXlxdh{OcOf5}Y6w-gRzlWVn5&`lvT5>>y@DU zj;7F4#ymQ+(Ndw+q`JVQn(|8EgM;)12!1~RY%2WxKqe?@p}~ipO%*W3Pl^Rnn;K?g zFUV+`Ar$m+$@YJXrRvvc2ISxL)5dB@rc-Y_hGbdbd)jf7`VsGvz&rbmp&fS`$_IS7j!7n*mDSHDTFFfZ)!!pM55!Cl!JsW?#J+FrD@45N~T#<6W;CP*R6Q54UpFf{j zhM%L$beNPlDsQOv@lb$ifSxt)IqvskzmH~2HH7R`>-2jwD#EgxcKlUsSVkNtX)M<~ zBA|7%5@iByGbb^ohJ{p^uxk28$+7naS+&#lDYchlh%yoBxoKc-#vGgb6atJ#{Jl%y z<_q`ve{>;_gx!#FF%(3S&9mJndMK$zUbU5?Iq%_?Jr$yhq)kGa8j-1lFw7uL&)xT0 zMm*~nz{-HN2l4~0I}b8?ZLZXznH^*P8k|juu(sKKa~m6a+TRRxU6>8Z7Wt>FrTM5D z@Z`MF1qbEJUd9Ip2et(gC+LjZH8>q6I8S0{PU<0ODF~uljapt_4vHT%U$@Q^xr|BY zUi;rtC-B=1;2k+vKj^1Mx^Bm%akfh^Qq<@$IeOiYB{_K8=*V&8N}X0OdzsYvarZ1$ zE2Q*8Ig~vd&w1xDLBA|jNVif)x>AR^@N&BIoq5_b%wjv>s^~guu1^SPITw1CC^$VF z1m7W9YS$C{hMzsu85$u6X~`tB;Ay6U!3S-08vb0YBcHUG@TQ$K{^&M%2wxL%o(7TT z3m{yw9Hz;K8B!$451o}0-~ull;Py~G%N!)?o)fnb{Z{^1wYY{Xq0e(F?yXboYm)X9d5T zR&Od-;bDRMhO6HVT{@6pkI%5B;O1PsMF{L=1hqN#b<#kgRJ@86V zdpPhjk#$YL(_fIj$W^Nes{a)*^hABB^_zuBb?HSVi3;F}i zIDM2H@+R9=?mZ(Z9{H;Rri!@D5K}y&>PX*9T;eq83 z!C9fu27}58T05`9Q1_HW%K;TjPvzu^X1V?1$1RTp`ufv_y~c_-uMeDVqMLu8HKdlg zNs(kU2a=KE>HmKIN^_@ke*ppad(MJZ3tgBw@q$8--=@MjiszVo`swhjR$^-UOQf3YnnnO`D7M9yiRKC3Kbad0LZKJT_SJFk9dlAIr-O=* zJe7r*jAn7piMQ0Ng|-K+Luv;!k1=}~ETwFrbHRcdmVJnxDue8#@AYgk)8Z+pI@c?k z*k3aTdH6pFa09ttY%FNc={E+U1ANlfJ(CbaLdCt+;*ZX|MyDR0kqIQ7si`i@F0397 z=@CLSr7S#s(2na`RaIXLSy9>aqcEBssRy-x*-(IAt~X8dbPC@+A9m*H5+_<%ZsOMh zPJq8nwShp)WumGL3bwI%6Cmz67Of)r1+Y^oQq^fdPme1vv6YjtwV#LXB#3Ie!0}Vx zOF)Hl2|j(%?ECMfGU%pT~)r`rD#C0u}R-C>U6-SUj9sk~O zx8%?YsW>n&y`0^H)l+OQ!#-zL5N>a+)tN4cJ@)-MB~oXNppmhAQJSx}K3XVBE$rGaDQuOr)Ql0&A5uYoN>KEjR|BdRp2iC26z9hr;dyyn30eJ#B4Tf2{(_%CED)C$lbA;HnHr}Kw+mRrn3EO0 zcBqb9Z2AE2D)YdFkWP1u9l!6U8rysNBa^>cnv0ZU#N zU4>CuH`hWYN1IeljEbAcPYjHXsV%2PK0t-nIK+{XXAHlO>yc~}+!Khl*!44s+-qXN z06Qu_cV7D*y~nqD*Tu>v>_Envyj=>LIViKVHS(j|Khps77eU`%g#3 zoLI;G%Zn&C!5wYkE|Gubb{<}hr*_*c&A;3ry-nA;IAc(zlUqsbgvpO4@p}WJMS_9v=>Q0Cd;hxm~88eu#aCfk{+y-_(Z|kx@E`FihhJ=~!)6SX-CgSr~ zZEpjPI^J-d(W{D{PdT)pM62^%?9l>gF0T595K4zU5>gS-%zd_v&or$KTF+N4E+{kn zhthybT!EOkRVGC^m7GkS(k;cZ&+5G^845r%a6qDkoAz;@0reFG>TsD_mU6T;13t&a zFsu1|+;N^5ID&QAY^@kqtsCF8OUH@TxFOMuv$QwcCh@ndsPOIAI~ix^@$Np!Y5=Rz zf5_K_UBmz>g~>WTc`n^}*)(L&*j6o)9$(?jk5~J~bb!L@ca^-3k<}vsEI(|czJEPC zAQ-!8*<1KwuR8dt9H!Mk+~8XNM9_UIqF0PG>;6iTJ)$$K99fSph=npp#}|-!g$`1Y)CgdojTrBopv-ym1wnMW-lv!F z!jEjrmT+iVjgPhV1J|r(_3S^zt}}U&W zt8|VLxwRL7;iWJHPgxJ|s@E@kV6lRFpL70iZOljOhBA^GKzzzv>cO{VCD}|VypAuC zat9CsH9(kCL8x5swSg^<^YwK8bQ(5)t!p}+^(M#l`9Lv4R+1SYn-|5Gq`)_$0?ia) z*z|*i6&p6Uyx2RlLkP&vPoAHV zD3cmNjC8qgqj!vvb&InvRz>54Nc{T2_vZ&xK5_Y>W-0KRAKqHZ8G#6oeVy4#r*?Wo`7<&r zx}W($eKk>jvmAfhwUJJn-B^Tl=YuK^xEbkUUo$PTjnRKOIMqykR_`dhd{uTxGF%La zkG1v7T(dPDtek7j|hK#q-vQ z-hkdTZDqv9Ac(-z{JYF5*Nl{Fc~5{w>8;kNt^-1<;k!QJS?ag_LvBE|Ft zs;=9!N!2dRZ2|4DYsFhQrO&xmi3IKYt#}TjZ=Titka!mN_AM1~<9O=wpdX_Q%93x_ z2=6^XBWbZcTcupsW8#k@CIPH?8;@P$_h*x)mHsPC=n_LXX3vjy+;X>QH!nVtBMI)l z6gEuR;L>*EhQvzi2CGOx`d%?jn$f_entsm_pY>thgI~~k)AjfPGgKIrk1Y(2awH;ueU6G^oQU@vW1Vw*>4hW^H*i#?r~k znKl_B3Md+C+CFl;h{9<~NO4OW``+x$-hLdW0$p{%*78bng8g4c6)0#0fgeZ$6* zuokHvRGs8K9+=dgya2kbteI2r7Qgbn%`N&_aL4c0rIA7Z<_|QtxHRl|9j~gt0 z@iqBpiPP&8BnHd+u6M!>n~uiUpV2XNhyVp%x1^} zkH4Wf1xfh(`+xO2-I4X|a>x{qehGL=J_lAyBVT-|7nv6B!z-ul({gf5pQTR3qVM_p z&3nB%Xag6Yz>`szhOP|BP>$0PP_UOAwT<7e=%HYf=^-D#@&II3PGRBw!H?NSY((|r zY7GXxcXxNu0-@g0)Q0pi;(FXKtel;z@?1X$DCEN|YmRsr7$$l;6;=5!v!t)O!U-`T zsumQO+%uGfVmz?Gj%$Eu`0_54kO4;R2DAJ1>58`wfu_X>TU1o^8wT9`v1DaWY8GY7 zo@JTV_fM>bq$W%k#{P3Yf6T=lGxfQE0)CR^5w8^E@x@^&cPu;yvRNP5&(Tq2{XILO zQ3lvfHKbAYCAyxCFlMgZ&tv$K)|M!6zNqBIO_dsh+3C(mY!LGd#`3t&KEVY-lxPNZ zAx<2n_=?*)PN5nMn@m6r;TReLilm;y#U{fA2KgclN${S&e-9HHbusy&j^C+WhzxCc z&G|Uj&oi8e{qMQd21si!Se|;9o12>#&>y#{caL7QioV7|Fe6j5kVzKmWw%(VP2;1eOt3x z16#&KlF8B$#W>M7G%(nU&U##cu%I_y43?8UodF?luDTZF*Kv4CV0R=Tj2;}q!Z85O zRy{!}yiP2iwm)!0z+;*F6BQ!WI4XPljW9-rcVU%9Y+FO&x>gbAmQ57Ijb}L}+4o0$ zA;;?%?jwQeKxaeLiM5uKz6pf2Yhd?Buej<=$&Qf93t3q)(W;p4gPwoM;B%6~pH79J z!H)nxY^VZyQQuIa7@4PE6MY`uP8i-M?Jv?8B!M+HH_ypwGT@We5!N@hMbaxRo?eT% zw`8`CO5)25?4JJq^GE%tpXFU9_#Z)9Wm-B*N#>Kq%J#zG=?hfBEY)E&u6_DD?1m<1 zO!)T4Svuq`ZrvA!c9qOY#}#DMrBg92+m$X>!x=;uuS|YXN7%aM`FXcZ(@#i$R(5!D zu$9jsTV-ZC)(?z%C}{Qfiv^*34MPfp6W(X8Hxl^_Hx{T!X8!&{dT??8eKI_UZ!#Td z#O3W5b6kImnDozf%e`d|_1K`YQR+Qx4}KICGH2AXG(PF7>?U5fCd{o_q#jPnITT~W zN5_)HY%Z9$T8tg%KIHqpbS5vBgR_Tch{i}5!>lvGZPncQ>h=~)9EKDHETnIj%L92- zWmM^6a9=kB-@lsbvD#C*k@};R?Hm=9N^v3WzcB9u?RI-M3PIWu!0UYxFSL zN^~kJ?K9>HG%n-B+`9=rgoZ*RrTWXI*17fs$at1;y2vCw^%Ep6ywktchp z_kl|j481S>hf8X-#+YTjg(|U{Mf022Ot5!S-;wcJN(;jc(m84%i+6@+$kl(NQf3l*OjzX5p8EA8(=cbZM^zlMz%<(IC;Zty$36!Z*O zvaX9(bNk(82uo+h5^7q)i_$pWC=)xCaJp47rTXb7DkF z<$WD`Ww9E52v>77c=p~;#jfdcJhv_#)7DvD;!i7cz+muFb&ESPc&Qn6 zaH1~$_^1;-=+5TKGgThLVBJUSo$xT2Zk5qDAg~K#J+(GX=;YeMcJ4;-f$&9%iQ*i> zt?e>oq8ww!wLg)EIxn5QlQMtD5q~N*C&iRA#{ZYzlNXW=C@&;o;|Tpg-5iOv@x@ZV znpJHZS0RzHsBa_HDn+4a?e=}XtxV1UHGX5g$Q?Hv>kvg4Fk#FSAuMgDes%++9nKAW@_X($ zywpekbK6Tl?jql(<7zYRkV^`&q|6jO9~Wb;6QY;EDL%(ij1bZ6y!f$giZkXoP*-^W zP?&EOKx9x!9I=M`U^8y@sVk4RErU$Nmm@m=_luq3|MF1*s~>$vFT>4hK5UHOwRJ>T zh046QSwn+OgqEtR>X%&Z6{LVmsk*pO#j7?G%Zuw~cgKS?B)l}6n;%w{wwE-VLCm*5 zilG1CY|q{wnMh?L&9cDF{zS`QD6ihIxs-QtIV#^RBq;Rj6i%VXUi_X2Q6K&?`gWmc zczYo9>CuYxn!dyC#A6`GSU2&ZW~}9gss7Q(i}sqL-kjhtz%J5T#K$gSsL;VK4_gQJ%N=Zpc4Pc`a>imQK zX*4u3k!5T0ha-no=(xw!Z5*`hcOxk3fBHz%#n{te7OQyd@az5}|8*^;qY)=Yp48}8 zQVbo5BYR|p>Q5ZW7=2fn%iEy|_`5(1^!aNitATc&gR(^AwviXVFmjhQmuj4fhTS2f zPX>wJSJ`l<;xs|F%XadoBA!g~%C-;nL-M3j!F3lk!?m-?%6==s){{(cNuQt*@F9Y3 zSVDAh$c3g-z)b##0)t_b3uz%7FP<1ZXP4lfv*@tvmr6}dotCIpGj9QT%zspuEfpIk zIw=ZBKxkdwOD^OS1cZ6L9fi;zJiKQv8w{3h(otCThna^wZlB&)tDaonp&x};kR`1) z-dK_IRiz+Ij*pnYQnl?2cMZtd#?&);4{uf)jz;gYr6$|+}wiS*w`YL4G5t2as-jD<^D2DTBHPVHvm)h#it16NFB zI7b!gSiL9d<>A+rf{H2d=yCZ|R2mC(D$1l8-GW}$SkV9Uv8e7byDVUiH0ua}16jjg zdH>WxuCgHS6uV0Y(j!O$wUIwR#n$Q5SnR&v*Hu+FtJbP_^>(cP8j8pd9gCG_Ppb!$ z1nB7KW-5@H5k=if%Jj?Od1|K8cPdGW5B88)+F{ObEq-S1@lg}RbN zKW<<0OuxM)lj3%*e5?E8#4(bLf#1AJt$WGwIR#gYFyPW=i33t?ZYl$-M|#$X-jfiZ0xmmoWU^rvdW_f z_d-#>xCZEN$1dht|B2CLS>^gsCcd7^pdy;bak~b_gGB=uzG-tPIq=lK+L4P{^HwXa zowx37gEs1fcxO9Y-pL$_Z6HqipTOIGs#y-c^`UuGEPVTvt?lzi__u7_{Ji{PcW!%0 zC*F@nT}gT1F5S6nor9u$m3FFudyOATv)yn07b+nbYu*=jFOpAWa}qY1yFb|Ti9d$6 z$Md99HTY|L>ZicRh{7f}fInvS`@7K_!|F5GG9J;^ZcW%-!@7jKueH;3)c-;GnuY3m z{(#G-pDm)w(Y9}wSB7TLQ>1`r{*nga%u#u+(+AHZ*U6fO=3fvZsIJIb3yxl?TPf5bcvz5UZiCk8h|Sn4k!BWcP7 zx5Q42KXv1Gl}ZkHGB|{_pEEOm0ILkJ5~?Vvs%H2s2f$1O1Fu|d`r8a8hDBj8*Q?`o zKUf#-n6ipWqm>aa_9k{?Q-6xZnH%A!pDq-Ug&7yND^W%&IWxPbRB{;8TdG1n#{QH_ z*B7n#ZXj`~JSL^7nZmO6&x3SSe%}*Uh*zm@h~QRsGXnNOuJ)OR>}N@-3o#7JbKONq#38UAiK>W*Pz+FE3lK? z_ch3z5~qoCNBCWzSY?Vs6xV?i+}zxxgl;N-pB8#WiC|C<$yO|Eo>t9`DP?jc6gq)pGk&yq?1K5^y4i;X zunq*T^~a(0o~Tt-57lLfMf{MSD4bc?9UcrTXYYJ}*6sGRTDS2R~If2yW2ZgjP)2;uRdX~?db=p#;BblvZW3VGs7u;m=}sXTDMI^Ss%As zMhBy2uyv+PZVdcB2aG3Mlwf&XI&fodd?6Sa*)dRO_hhzoDz{7|N|#hgKijt3_JC%Y z7N~K9zMJB~yL}hO%KF__-<+I7b3HL-MX5N~$ zP!-rlMu9UKPD}XASOfU(o$|zOmG1H&e!IQT!xDbD#S*jdO9I zW+wfoS1ydT-s(X5cL&VY*Rpl6l4OCfvdhp)pY|KQuaI(`NeGXEQpv56*y6?4n2Hg% z?A}5r5B+7*yskkt3=#QddADh|-BI!nv9ap_zVGV@xCEVA_JVr|bxjj`A#;V0^$$iB zoqq&(Wd(yUO1q}UvV^0+zv|m|XyM&4D52D2WjJ)F6=T(vO^AFjvubf?E0--@G?x8p zZ-S=${#QHF=OX8u@mZV;Et$zrMMXsfeohqtq3Cb9G5*e2*?mCB9&$utyHjLq_~<4L zYdm(FURDw>{MX7G&I%cA3#QZkKp@h1Q+IflQqZt^IjZnBC8~bdOVp`_4WW$uN%-yW zPYQ_7{Xn-2o3ff(Zu2S1ySasRa&d5sFaP?EHt3}gzS;#nn!X9rk?29Zo9h>g1c+3? zX^~$@;?6I`s`@_QFjU1Fe#oPaUpZGVJF{*m(obap$3qR-y4ad0qw437eFlYmg$e$} zUO?5w=f*is#GP%Ym$yZ(srN)OI2TOg#S#mKKA!_VZNuT{?APhK@(_=O{!EHc+5WeE znqt#d41_uub~*5Sn($k^Nh?*R6|d|}ukZ&Gg5?b3kNiuIRZi}EQ|5rcQ=ILJRp>B zfewQmtad#L!#dx_DHc5!1F~&*2)o4s}$Z$^-q#cTdX47_)ew++ldj|EN=9I zd+&l0g0k5VqmOlG7yezXn11A)AYAf{2J*F z+I)X^@qFr5kz%SV@RpbxiRU?(Cur<$+K&!+ABkGSGgGc;=UU|SNeAPu{!h|{x@~LW zC%@18b{)?TRaUX3iZ5qCjm*NrgCBnT6YfeB)+G!^E4$3WSmI424AB(j7zx%Easw&e zfumai%Sf!I7$?SB#%?Z59Gfvh`MNP-%4DJ{wMA)v;XBuB55DWeZlPCO#-B1B!>?Ib zFK`t{R0^E>?R4}opSho82U^@^sd-vaDygWv4Ga{~7VLfNHvI*|^fA(?X)%>XIg^Mg znddvNym8)lj`s=5`{}4H6mNu+KZ5+bP4Ykf@G~ZRXsG-PUAB!mCD&$IiJZ>Z0Iwi88=Kn=R6Y=@Hi zD4uibbe+M{Zj-D&vxX-zIP%$@f-Prrgn#wmruQ8xb!i^Oyg0lWZg)H~GIAm-T5H^& zW>9?JULHLPeJKjN{JR{}PYErhm*BCx8lsK&B`A}gwrG_3hF{bRzy&l*0Q?y)!31IC zdE&O5c?s*^0!G-v0c_F)QuZC@;-USKY=xM^hiG;r#DLWGMHLy{zUQTN-@8iObfJcO zLZKo=GEskgFa=Xfc3^=C%~Voy+^=EI@XaqDs^FVh9;%$`yE|z-{ZYG*kKpc%VL6=i zD}Ys*>l@EendZ%Y~D7Pv~@{6?ADTJ z)xjk(p6SD=N*7D0n};Px{Ea;vb2bcE2F-u(LSAbK4GnFYDV2}F&7ZjN0{-5}$oA^^ zL7dU~4zk_Akz*Yk$H7Y8qny-_N?~ZWpm=n%S366<@yHwM5?`Sx1;2mLm~pzJimo`a zF!NU)Z=+GhDGWL?QuyeY^J(?0%%#n&Qe-6DjL$mp<0I)|a3mnrb8vFj&l;uG+9W*VJe({(zn*Z24*8>7$yzMpH2qx#^uNTM11Lw~uSVkdBXFhQ^|=4v z9UkGkyHa-9j9ViNo1Dx3ELwfLMl3V_G|cKUOWXfLI--;8D0R1<7j@&WJo78YiHSy0 z6gHR8oo^fi(}+QH6wmN{8N#nEh3kJm3b@E>NSzq9xFcz5YBJZtT@`)%_U-xy!E-4& z6g^D{X-(`AyE|tX4_E~9Ww6x~D04UvcG0CZ)IcNyyYBmn+us6bChRxQE^HPps6`Qq z$v=;KpNA_IFmk(r)RpIraF+w0^uQF0(_D9oPPR)EzZp1I4%Er@s*ewAYik)!m1($A zpUp-HLco1VL)hV)tw*()2r>oSq}YbWp8s9&vt3C@i)+B8zlYHR!tc_Lu# zxw|{UtHTG!u~0?%uPJ1HD|&}qv3yD(#UpfCyP-Jno^uYKjFZY6Z>At(H`^rLK4H^T z{^{mP>&bASTMvP^U=?GSH!N>I`>-($HF#At&66uw$}oK#)7&-&+3wOw%qI*WMMf>P z_8kfAIM~>u-i2@xxQU5XYGAQ&bv?#{J?dq*`5aMzj#Z0-0BysXkrac90BuJxBMM)Y zgyd>-gH9A7RjK|ZTg<96R2{_nI>f#XbAyl5{X8S{WvQR=K{|n(2@;WAhd3#qEeg18 zL!7G1)BQIJpy^Mh z2PbVE&t-mf$!RfAM|^o^Tm*2w@$qr6Fw*<|?TdXO;IX4reo~(rNXpFd;{{dtt&v}l zv*)bMtY`$wPJthc=NtJ1Qee>L6)#K}c9$vmRFNes6!m4pv3+B3LmN%-*lDk(NTruB@zDQkKyE-`m^wPs4@+POjCAGRpz959=sWbWW=NecHE{cw@z!yDR z0xm#2s!sp+S=12M6k!ia*+9vB>2?jBY_GX*^leJJqNS+0gm{lUd}+{qk%Oa-YiY8| z0>^6I?Of7=qf1hCoBG%gP}_mh86sPGhuhS}p5tAe#{wMg`^6t>mS;9$4|cB95x5gb zo<(3?mX?+thKY%1xOKo_IPLPlm;hz1R0IMWXuQV*>>{+IE6`31roG8y%Vni@`U)1gTpSfI15 z?SB{#;FA$J)V?r#E_ORV=8SUu3Pzc-D}M4Z-fS#eR5GmFTe~enuT=X*;@EDYs=J9l zULq4ITi~{w|vu#;AmQglD$fv;S6 zRO-T*-v&4#=C-!KW$|So_UQcK2?NknVj0a6CbH~kP#pZH-_v>hTV522?Xp29ry{n~ z7s=0Y)~Xu5bQ{fU`S#{@hRidmOs-w#F@pNCf56&&0~qKwLbF?j_zw{Y=L)mTpsw8- z_;?A|9(jZAfCZS`{{dH^{?2acw{S*MKdj#MVLfYx-;?S<6O*iNIpmdO=)XQ}y#70) zZSr4;H=4Vji6){ECxZ8~dUW;XSJ7&lSQ;4W-B!J0GW)EN zK)9M9@n1I%mLsrRVp<8lbz=Q}H2EfutR<;_8H_#a7iO?}UmPx85{H<>pf}mj*{?VM zfR2U?w9nFKo9nmTG}e!9zkpmpE{iRdW`OlQWGJE>D6w?EAQ|~N0gF5u&22C$;iAP0 zNff@~c710g!xxw#9Hd2F!tSO%zIsy?3dWmrUj`3ZE0 zgiaM`skAuS;Ma`)OzzZFN1=L{iaNY!r~Ah1^-Ei&jxA7r$pk(!gWqN2_pgEr69|72 zbccbz9bH5Mn}soGoPKp#?Su~zhaD@P1Oi90r|XIUt;T43%ThR&^=A@vSL4~yDAaQ` z^Xwl1xY1{N%@OsV=;iNZHBDai9o!;vO}YJ ztJH}r>C(>{p#mnCo3d*16QF>Hhj;OZ7A775LPk8EH3YmDw0#rsULGDEs$p00FdexX z;4cpUWYLiIEz~{Quu{50B&fel|(hmq{t_DSZfo`lH?U5lGZ1qIe=fpvpU`|*S#yZQc1rG2MVUM>@= z{9Cw0)O8-s`H2swCFk%F=u6Fl_nji%oix$Xq_nvPmHwxoX9P+xyNp{UxU2c~b@SGH zH0l>n9997gCILZ@t1wqs_nWP2(9k0?@r46Bh|cNusULm}^Q8>>vsQN@qv!Dw21r!z z*P3=WZ}V4zG!ZwA&Z|h^>`tyG7W|}=Kj02Sppt$QPtjXx*mR&M4tLDm)6-L71P>P5 zXLD+xayYxZ6o+4HgFSQxIRP!M@4sG%NIPcL7hVbhZu{EW0?tSwLNcQgH?eEF2R5SA zF**9q7sp5%+YMa;U#3*2VnqxPDf$81{N4P#K5#*S|4m6j(fQ}65-Rn6CRPcI_~!N& zLh5dUb4=WAa-0tXMdZeM6s5_>6(t-@ZTJavXmvN?X3K4<-QVbFK5z;Er7^Kv zmh9jgJ1_Ixw>7`L+HNB}G`6Xy+;P-N1Fh_@jV$)<5dABXLj;D!*g9D64n>|s0J~W} z_5XrlSM0E0FJE5)D7Y0Y3|~C>>cOgge}9it&}q^=wm91ep8jbV;>8~XLQ=x!8Q?~c z5&HE1%3GiWe)33M#~`xPxmM&BqnyB=tx=pGkLdczEh-wj*&n~ScnvQ>(5)L471c$s zBq4>Wzw79W)IB~sduat*h5I_+*$8kwk;l8!--n0B5cPmUk;b@vs60N*ql%ygxl=@b z5{y?!JhKz?r|ao<*p5+=hTBesPDNT;+SjHN-b{q!m{+e}K_pMto`+e=f|&2@>`aB- zQHQ9`&!WGB7rR@>+5A(IV?u2f-MbN;`^~x%T6Oy@QpM%S9!{4V^znu*aQG_H7jC2K z9aNZfsZlX~u}@Zc=*ZXo71vO|WuEBN^F<9ewD2n(GX5F;uK64=lWh z=G_c5WX~c_Hb}XwCGeUH9MBwl?dOHO3S6`O4m~8wz2%M()&-Y4mSPRCJ73`W_7$`@`o2nh z_PZS!eR-VXRMnz0iD2*|&t;ZKH;R0jQsx;`$`HEGz)=NHALat{}KI?2uKs z0PVTjS%CGDT;frg{Nooiz{3yIhYpg`O{SqkF}># z3D{aEM1dXAT%8RLlB;tYQZ$X#d8pj_e-m($2S#H!JD?|#})wu$hnRH z{*&v#FTL5G&nP$TP_y@ zqW|2Nmz9m(I$d9}WkSUY1{24fG530R34Q2hi2Jfscd8f2(0LV)ICJjr+q-dH!$QgJ z`$FWLL>mKM$1L*{vVIUPd4b~}FpHYD`~wb|J({(FCxPKWy_~FUt?{;lKnL#$V9XGF zTJ$rY=|{a#!+hoqcc+^HYi!_&MMr@rAb^|>G+DOQNpY*t1Mk&u7qRbP-R1jY7sEcA zHsE#?;MrQTz-|ABuUwg;Kf}LB@<2W7hr7V&E9H-O;!r%fkm*9wsi#Gp2hJ~8$Mc2p zMepg!=bwLE$X~l+p+L^#bftPs)OMUH&Lk__0uI2CU9PE`gFL-@T z_s+gr>CKxrFLdT9XZ)kjz|J5eDJxt0{+lx}bBKm8-168N^H1PaH&-p^f%|-C!!<-c z{CM2|7*x%C+zwm?xL_;eq+`r8_GHd)1?~q1?#gBWPCV==eJz%EcULQL#QR9^)ybzBFsALaXBG6EK(+w$&q0V`6oDNLVl_GucJ z7&9LLW{e)-A=7N$7&baTUk}DdD$l9#KGCdbwDZ*m?n-h zr#=W}u%4v}T;pdtO-*mA*S+(=)jNjw@3}n8K0ei}o)y^V0v?-@byS!6KqZ4a;~#<2 zh4ni_S%Hr7WD{-BTM(o7oMBH3JJ-SGj5fQL^=8e>ywdtN{_g+!l9G}y$8MQ*3OIE% zayf_x=}rI4Fpup+;K%Qe{{Hu0mw4IaS)lxW;PO@rIgZ~ax8{fdyC?P`4A&%|7FB9D zh;Ine{E^L2FXvpV{kWYmCoiznltG?h#+HA2GtMtwy!f*2s%~J$VPD9!fl+h%X;+5m zj}D3({#ONVzw?~oPVt}lkK3$(^;F?PCSaGeB}Jh2yCg97TXHg1FbDt{k}X`odR1VO zBLQwbNvf%@xSlv?Qr}{zz4Zjbhh`Q-?X4#=o@nKcdO`_=Ru)6;4Iwt6Vs*!p|IDW+ V{4`P5igIKC0#8>zmvv4FO#m4=Bpm<% delta 36974 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 cKX$yF$DV~T)^z$(T@ZY{xA<+Y diff --git a/Resources/graphics/icon.svg b/Resources/graphics/icon.svg index 4a584dcf7..2532ed83c 100644 --- a/Resources/graphics/icon.svg +++ b/Resources/graphics/icon.svg @@ -13,7 +13,6 @@ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" inkscape:export-ydpi="90" inkscape:export-xdpi="90" - inkscape:export-filename="/home/ds1/Projekte/APG Plus/Resources/icon_google_play.png" version="1.0" inkscape:output_extension="org.inkscape.output.svg.inkscape" sodipodi:docname="icon.svg" @@ -3474,19 +3473,6 @@ style="fill:url(#linearGradient9381-884-21);fill-opacity:1;fill-rule:nonzero;stroke:none" d="m 24.453487,40.951785 c -0.278503,0.0893 -0.539938,0.227781 -0.81312,0.325928 l 0.1981,0.739317 1.195316,-1.184717 -0.009,-0.03361 c -0.188236,0.05559 -0.385377,0.09346 -0.571291,0.153077 z m 1.814688,-0.486244 -0.539453,0.540744 0.522263,1.949109 1.428138,-1.391176 -0.351176,-1.310607 c -0.353713,0.06954 -0.70601,0.126491 -1.059772,0.21193 z m 1.682677,-0.306801 0.234117,0.873739 1.078904,-1.081489 c -0.444796,0.05158 -0.866983,0.13148 -1.313021,0.20775 z m -4.897371,1.348266 c -0.284754,0.110431 -0.566288,0.210126 -0.846725,0.334933 0.333398,0.03079 0.654403,0.111242 0.981793,0.169147 l -0.135068,-0.50408 z m 7.196364,-1.640116 -0.17462,0.154843 0.432217,1.613055 1.419136,-1.42478 -0.108054,-0.403263 c -0.521384,0.0068 -1.043367,0.01983 -1.568679,0.06014 z m -5.026967,1.671134 -0.772277,0.747203 C 24.813169,42.390423 25.17,42.509101 25.520027,42.647 l -0.297149,-1.108976 z m 4.346501,-0.984552 -1.204319,1.151112 0.441221,1.646661 1.186311,-1.218322 -0.423213,-1.579451 z m 3.649798,-0.76185 -0.597659,0.592358 0.531267,1.982714 1.161709,-1.175713 -0.36018,-1.344213 c -0.249836,-0.01881 -0.48382,-0.04396 -0.735137,-0.05515 z m 1.394059,0.0947 0.207104,0.772923 0.655864,-0.643973 c -0.283232,-0.03902 -0.577362,-0.09978 -0.862968,-0.12895 z m -2.497563,1.029401 -1.419136,1.42478 0.522262,1.949109 1.419136,-1.424781 -0.522262,-1.949108 z m -4.256459,1.320605 -1.020695,1.029874 c 0.434477,0.233862 0.850384,0.49579 1.253693,0.780634 l 0.199219,-0.197453 -0.432217,-1.613055 z m 8.403743,-2.107704 -1.253522,1.236332 0.432217,1.613055 1.419135,-1.42478 -0.369186,-1.377818 c -0.08434,-0.01531 -0.144054,-0.03236 -0.228644,-0.04679 z m 0.914582,0.187155 0.198099,0.739317 0.564051,-0.583354 c -0.249905,-0.06107 -0.509667,-0.102698 -0.76215,-0.155963 z m -6.995853,2.522859 -1.195314,1.184717 0.234117,0.873738 c 0.239867,0.215264 0.463772,0.445851 0.686583,0.680464 l 0.805881,-0.756206 -0.531267,-1.982713 z m 8.48655,-2.201928 -1.112508,1.090493 0.522262,1.949108 1.204319,-1.151112 -0.495249,-1.848292 c -0.04572,-0.01294 -0.07301,-0.02753 -0.118824,-0.0402 z m -4.173651,1.22638 -1.16171,1.175713 0.432217,1.613056 1.16171,-1.175713 -0.432217,-1.613056 z m 5.005426,-0.945001 0.288145,1.075371 0.73867,-0.738198 c -0.331899,-0.121338 -0.688782,-0.230193 -1.026815,-0.337173 z m -2.450129,1.340854 -1.42814,1.391175 0.531267,1.982713 1.419135,-1.42478 -0.522262,-1.949108 z m -4.222852,1.3116 -1.428141,1.391174 0.441222,1.646662 1.410131,-1.458387 -0.423212,-1.579449 z m 8.31193,-2.047085 -1.170714,1.142107 0.441222,1.646661 1.41013,-1.458385 -0.324163,-1.209792 c -0.118639,-0.04937 -0.236932,-0.07308 -0.356475,-0.120591 z m 1.096438,0.46259 0.09905,0.369659 0.22382,-0.240063 c -0.11157,-0.05308 -0.210394,-0.07821 -0.32287,-0.129596 z m -7.119502,2.195809 -1.161709,1.175713 0.522262,1.949108 1.16171,-1.175712 -0.522263,-1.949109 z m -4.222853,1.311601 -0.573056,0.549749 c 0.314089,0.36607 0.584652,0.770104 0.855256,1.175539 l 0.141013,-0.145838 -0.423213,-1.57945 z m 8.569356,-2.296152 -1.195314,1.184717 0.432217,1.613054 1.18631,-1.218321 -0.423213,-1.57945 z m 3.683403,-0.770856 -0.622259,0.634968 0.522262,1.949108 1.18631,-1.218322 -0.252126,-0.940949 C 43.714301,42.702794 43.417501,42.564812 43.145802,42.42645 z m -9.706465,3.429255 -1.419136,1.42478 0.522262,1.949108 1.42814,-1.391174 -0.531266,-1.982714 z m 8.569356,-2.296152 -1.419135,1.42478 0.522262,1.949108 1.428139,-1.391174 -0.531266,-1.982714 z m -4.256459,1.320605 -1.419135,1.424779 0.432217,1.613057 1.419135,-1.424782 -0.432217,-1.613054 z m 7.524058,-1.223668 -0.340231,0.34329 0.432217,1.613057 1.137109,-1.133103 C 46.107003,44.18387 45.693405,43.926727 45.276292,43.656487 z m -5.192581,1.859582 -1.204319,1.151112 0.531267,1.982714 1.195314,-1.184717 -0.522262,-1.949109 z m 4.337499,-1.018157 -1.195315,1.184719 0.441221,1.646659 1.186311,-1.218322 -0.432217,-1.613056 z m -8.593956,2.338763 -1.170714,1.142108 0.441221,1.646659 1.152705,-1.209318 -0.423212,-1.579449 z m 11.140248,-1.976516 -1.41013,1.458385 0.522262,1.949109 1.41013,-1.458386 -0.522262,-1.949108 z m -8.593956,2.338763 -1.419135,1.424779 0.522262,1.949109 1.419135,-1.42478 -0.522262,-1.949108 z m -4.222852,1.3116 -1.419136,1.424779 0.432217,1.613056 1.419135,-1.424781 -0.432216,-1.613054 z m 8.569355,-2.296152 -1.419134,1.424779 0.432217,1.613056 1.419134,-1.424781 -0.432217,-1.613054 z m 5.059454,-0.74337 0.234117,0.873738 0.331227,-0.376895 C 48.159743,45.798299 47.970076,45.634344 47.779503,45.471 z m -11.339943,3.650835 -1.161709,1.175714 0.522262,1.949108 1.170714,-1.142108 -0.531267,-1.982714 z m 8.602962,-2.305156 -1.195315,1.184718 0.522262,1.949108 1.195315,-1.184717 -0.522262,-1.949109 z M 8.7331833,56.617773 c -0.080856,0.257998 -0.185146,0.528328 -0.2567787,0.789166 L 8.8412374,57.021037 8.7331833,56.617773 z m 32.0528797,-8.480489 -1.195313,1.184718 0.432216,1.613055 1.195314,-1.184717 -0.432217,-1.613056 z m 8.005304,-1.712797 -0.597656,0.592359 0.432217,1.613054 1.195314,-1.184717 c -0.32357,-0.356241 -0.684705,-0.686305 -1.029875,-1.020696 z M 9.0303324,57.726749 8.1998504,58.525565 c -0.1251137,0.570603 -0.2112478,1.15432 -0.2915036,1.73494 l 0.2251128,0.840132 1.419135,-1.424779 -0.5222622,-1.949109 z m 25.7416726,-6.89746 -1.42814,1.391175 0.531267,1.982713 1.419135,-1.42478 -0.522262,-1.949108 z m 8.569356,-2.296152 -1.428139,1.391174 0.531266,1.982714 1.419135,-1.42478 -0.522262,-1.949108 z m 4.337498,-1.018157 -1.419134,1.42478 0.441221,1.64666 1.41013,-1.458386 -0.432217,-1.613054 z m -8.593956,2.338762 -1.42814,1.391174 0.441222,1.64666 1.41013,-1.458385 -0.423212,-1.579449 z m -29.1435218,8.853506 0.1170588,0.43687 0.05821,-0.05161 C 10.05758,58.966431 9.9963541,58.836017 9.9413812,58.707248 z m 40.0599508,-10.589959 -1.186309,1.218323 0.522261,1.949107 1.18631,-1.218321 -0.522262,-1.949109 z m -8.593957,2.338762 -1.195314,1.184718 0.522262,1.949107 1.195314,-1.184716 -0.522262,-1.949109 z m 4.337499,-1.018158 -1.18631,1.218323 0.432217,1.613056 1.18631,-1.218323 -0.432217,-1.613056 z m -8.593956,2.338763 -1.16171,1.175712 0.432217,1.613056 1.152705,-1.209318 -0.423212,-1.57945 z m 13.635401,-3.149341 0.261131,0.974553 0.248421,-0.282672 C 51.124502,49.08394 50.966383,48.85514 50.786319,48.627315 z m -40.364165,11.067671 -0.174619,0.154843 0.432217,1.613056 0.505846,-0.531741 c -0.279386,-0.394934 -0.53067,-0.804288 -0.763444,-1.236158 z m 29.275056,-7.556083 -1.419135,1.42478 0.522262,1.949108 1.428139,-1.391174 -0.531266,-1.982714 z m 8.602961,-2.305157 -1.41013,1.458385 0.513257,1.915503 1.419135,-1.424779 -0.522262,-1.949109 z M 9.7326848,60.347964 8.3135497,61.772743 8.7457669,63.385799 10.173906,61.994625 9.7326848,60.347964 z m 25.7416732,-6.897461 -1.419136,1.42478 0.432217,1.613056 1.419135,-1.424781 -0.432216,-1.613055 z m 8.569355,-2.296152 -1.419134,1.424779 0.432217,1.613057 1.428139,-1.391175 -0.441222,-1.646661 z m -10.827504,3.009276 c -0.0046,0.245127 -0.0022,0.483366 -0.02154,0.726133 l 0.165613,-0.188448 -0.144072,-0.537685 z m 18.458971,-4.369778 -0.44764,0.480125 0.432217,1.613056 0.787871,-0.823417 C 52.194092,50.635328 51.955417,50.201646 51.67518,49.793849 z m -13.902951,4.301574 -1.161709,1.175712 0.522262,1.949108 1.161709,-1.175712 -0.522262,-1.949108 z m 8.602962,-2.305156 -1.195316,1.184716 0.522262,1.949108 1.186311,-1.218322 -0.513257,-1.915502 z m 4.337498,-1.018158 -1.195314,1.184718 0.441221,1.646659 1.18631,-1.218322 -0.432217,-1.613055 z m -42.9049849,11.532375 -0.125417,0.06962 c -0.027907,0.674826 0.00787,1.360371 0.042783,2.041566 L 8.2399208,63.917539 7.8077041,62.304484 z m 25.7416719,-6.89746 -0.456644,0.44652 c -0.10047,0.621903 -0.256883,1.220038 -0.450526,1.813566 l 0.144072,0.537685 1.18631,-1.218322 -0.423212,-1.579449 z m 8.569356,-2.296152 -1.204318,1.151112 0.441221,1.646659 1.195314,-1.184717 -0.432217,-1.613054 z m -30.54482,8.328532 -0.705065,0.729192 0.522261,1.949108 1.16171,-1.175712 -0.153077,-0.57129 c -0.284683,-0.293438 -0.570662,-0.608229 -0.825829,-0.931298 z m -1.219916,1.227326 -1.4191343,1.424781 0.5222619,1.949108 1.4191354,-1.42478 -0.522263,-1.949109 z m 25.741673,-6.89746 -1.419135,1.42478 0.522262,1.949108 1.419135,-1.424779 -0.522262,-1.949109 z m 8.569356,-2.296152 -1.41013,1.458386 0.513257,1.915502 1.419135,-1.424779 -0.522262,-1.949109 z m 4.337498,-1.018157 -1.41013,1.458385 0.432217,1.613056 1.419135,-1.42478 -0.441222,-1.646661 z m -8.593956,2.338762 -1.419135,1.424779 0.432217,1.613057 1.419134,-1.424781 -0.432216,-1.613055 z m 12.378174,-3.136631 -0.928885,0.969255 0.513258,1.915502 1.25352,-1.23633 c -0.249728,-0.558508 -0.545759,-1.118305 -0.837893,-1.648427 z m -39.520973,11.526083 0.414208,1.545844 0.714071,-0.695587 c -0.390445,-0.258687 -0.769263,-0.547599 -1.128279,-0.850257 z m -4.8457568,1.40647 -0.646859,0.677579 c 0.064396,0.720367 0.1518557,1.433511 0.2894373,2.155565 l 0.8886882,-0.85043 -0.5312665,-1.982714 z m 25.7416718,-6.89746 -1.195315,1.184718 0.522262,1.949107 1.20432,-1.151112 -0.531267,-1.982713 z m 8.569356,-2.296152 -1.186309,1.218322 0.513257,1.915503 1.204319,-1.151112 -0.531267,-1.982713 z m 8.602962,-2.305157 -1.18631,1.218322 0.513258,1.915504 1.204318,-1.151112 -0.531266,-1.982714 z m -38.601093,10.523222 -1.161709,1.175713 0.432217,1.613055 1.170714,-1.142108 -0.441222,-1.64666 z m 25.741674,-6.89746 -1.16171,1.175713 0.432217,1.613054 1.170714,-1.142107 -0.441221,-1.64666 z m 8.602961,-2.305157 -1.195315,1.184718 0.432217,1.613054 1.195315,-1.184717 -0.432217,-1.613055 z m -14.824125,4.224238 c -0.01593,0.03765 -0.02389,0.08134 -0.0402,0.118826 l 0.05821,-0.05161 -0.01801,-0.06721 z m 0.207104,0.772923 -1.25352,1.236332 c -0.04927,0.07437 -0.105705,0.14876 -0.15661,0.222053 l 0.513258,1.915503 1.419135,-1.424779 -0.522263,-1.949109 z m 8.569356,-2.296152 -1.419135,1.424779 0.522263,1.949109 1.419134,-1.424779 -0.522262,-1.949109 z m 8.602962,-2.305157 -1.419135,1.424779 0.522262,1.949109 1.419135,-1.424779 -0.522262,-1.949109 z m -38.567487,10.514217 -1.428139,1.391175 0.441221,1.64666 1.419135,-1.424779 -0.432217,-1.613056 z m 25.741673,-6.89746 -1.42814,1.391175 0.441221,1.64666 1.419136,-1.42478 -0.432217,-1.613055 z m 8.569356,-2.296152 -1.428139,1.391175 0.441221,1.64666 1.419135,-1.424779 -0.432217,-1.613056 z m 8.477545,-2.235534 -1.302723,1.321552 0.441221,1.64666 1.419135,-1.424779 -0.144072,-0.537686 c -0.122462,-0.344034 -0.2754,-0.671033 -0.413561,-1.005747 z m -38.939559,10.469843 -1.054302,1.038878 0.531266,1.982713 1.419136,-1.424779 -0.342171,-1.277002 c -0.189223,-0.09925 -0.370192,-0.210796 -0.553929,-0.31981 z m 1.302896,0.695413 0.09905,0.369659 0.257425,-0.249068 c -0.116016,-0.04678 -0.242034,-0.0703 -0.356475,-0.120591 z m -2.863044,0.875205 -1.161709,1.175713 0.522262,1.949109 1.161709,-1.175714 -0.522262,-1.949108 z m 25.741673,-6.89746 -1.152705,1.209317 0.513258,1.915504 1.161709,-1.175714 -0.522262,-1.949107 z m 8.602962,-2.305157 -1.186311,1.218322 0.513258,1.915504 1.195315,-1.184718 -0.522262,-1.949108 z m 4.337498,-1.018157 -1.186309,1.218323 0.441221,1.64666 1.186309,-1.218323 -0.441221,-1.64666 z M 9.1313681,67.244465 8.2180792,68.137505 c 0.06532,0.289689 0.1475965,0.550838 0.2251128,0.840133 0.068354,0.255102 0.1303727,0.522294 0.2071039,0.772922 L 9.5635848,68.85752 9.1313681,67.244465 z M 30.571561,61.499583 c -0.356828,0.448638 -0.75809,0.855854 -1.177307,1.251928 l 0.459231,1.713872 1.195315,-1.184719 -0.477239,-1.781081 z m 4.301479,-1.152578 -1.195315,1.184718 0.432217,1.613054 1.195315,-1.184717 -0.432217,-1.613055 z m 8.569356,-2.296152 -1.195314,1.184717 0.432217,1.613055 1.195314,-1.184717 -0.432217,-1.613055 z m -26.222918,7.386591 -0.714069,0.695587 0.432217,1.613054 1.195315,-1.184717 -0.252127,-0.940949 c -0.22098,-0.06106 -0.444699,-0.109715 -0.661336,-0.182975 z m -5.541818,2.169268 -1.419134,1.42478 0.522262,1.949109 1.428139,-1.391175 -0.531267,-1.982714 z m 25.741673,-6.897461 -1.410131,1.458386 0.513258,1.915504 1.42814,-1.391175 -0.531267,-1.982715 z m 17.172317,-4.601308 -1.410129,1.458385 0.513257,1.915504 1.428139,-1.391175 -0.531267,-1.982714 z m -36.015895,9.686448 0.07204,0.268842 0.19922,-0.197453 c -0.09261,-0.0175 -0.179227,-0.05176 -0.271256,-0.07139 z m 27.421939,-7.347686 -1.419135,1.42478 0.513258,1.915503 1.428139,-1.391174 -0.522262,-1.949109 z m -30.007136,8.18446 -1.419135,1.42478 0.432217,1.613056 1.42814,-1.391175 -0.441222,-1.646661 z m 17.172317,-4.601309 -1.419135,1.424781 0.432217,1.613055 1.428139,-1.391175 -0.441221,-1.646661 z m 8.569356,-2.296152 -1.419135,1.424781 0.432217,1.613055 1.428139,-1.391175 -0.441221,-1.646661 z m 8.602961,-2.305156 -1.419135,1.42478 0.441222,1.64666 1.419134,-1.424779 -0.441221,-1.646661 z m -21.455788,5.821097 c -0.362299,0.303527 -0.742129,0.584795 -1.141935,0.846252 l -0.340232,0.343291 0.522262,1.949109 1.42814,-1.391175 -0.468235,-1.747477 z m -9.312852,2.747497 -0.738671,0.738198 0.522263,1.949109 1.428139,-1.391175 -0.333167,-1.243397 c -0.285691,-0.01577 -0.596661,-0.01699 -0.878564,-0.05273 z m 1.528482,0.05868 0.189095,0.705712 0.738671,-0.738199 c -0.313681,0.0242 -0.616551,0.03276 -0.927766,0.03249 z m 4.810386,-1.000795 c -0.140151,0.06034 -0.266567,0.162508 -0.409855,0.217874 l 0.04502,0.168027 0.364833,-0.385901 z m -2.695018,0.830182 0.396199,1.478634 1.42814,-1.391175 -0.144072,-0.537685 c -0.218378,0.07192 -0.43826,0.153449 -0.663103,0.213696 -0.346732,0.09291 -0.670138,0.175199 -1.017164,0.23653 z m -13.4577222,3.678022 -0.8886882,0.85043 c 0.2083439,0.613006 0.4397248,1.210458 0.6968803,1.794265 L 10.274942,71.51234 9.7526798,69.563232 z m 8.5693562,-2.296152 -1.195315,1.184718 0.513257,1.915502 0.06721,-0.01801 1.13711,-1.133103 -0.522262,-1.949108 z m 8.569356,-2.296152 -1.16171,1.175713 0.513257,1.915502 0.06721,-0.01801 1.103503,-1.124099 -0.522261,-1.949108 z m 8.60296,-2.305156 -1.195315,1.184717 0.513257,1.915503 0.06721,-0.01801 1.137109,-1.133103 -0.522262,-1.949108 z m 17.172317,-4.601309 -1.195314,1.184718 0.522262,1.949107 1.195314,-1.184717 -0.522262,-1.949108 z m -8.593957,2.338762 -1.204318,1.151112 0.513257,1.915503 0.06721,-0.01801 1.137109,-1.133103 -0.513258,-1.915503 z m 4.328495,-1.051762 -1.195315,1.184717 0.450226,1.680266 1.18631,-1.218323 -0.441221,-1.64666 z m -34.33563,9.236222 -1.170714,1.142108 0.441222,1.646661 1.161709,-1.175714 -0.432217,-1.613055 z m 8.602961,-2.305156 -1.204319,1.151112 0.441221,1.646661 1.195315,-1.184719 -0.432217,-1.613054 z m 8.569357,-2.296153 -1.20432,1.151113 0.441221,1.646661 1.195316,-1.184719 -0.432217,-1.613055 z m 8.569355,-2.296151 -1.170714,1.142107 0.441222,1.646661 1.161709,-1.175714 -0.432217,-1.613054 z m -23.19538,7.259706 -1.419135,1.424781 0.162081,0.604895 c 0.100985,-0.05316 0.220969,-0.05921 0.336053,-0.09005 l 1.344213,-0.360181 -0.423212,-1.57945 z m 8.602961,-2.305156 -1.419135,1.42478 0.162081,0.604896 1.680267,-0.450226 -0.423213,-1.57945 z m 8.569355,-2.296152 -1.41013,1.458386 0.153077,0.57129 1.680266,-0.450226 -0.423213,-1.57945 z m 17.172318,-4.601309 -1.41013,1.458386 0.153076,0.57129 0.806528,-0.216108 c 0.342878,-0.09187 0.651309,-0.05658 0.954779,0.06833 l -0.504253,-1.881898 z m -8.593957,2.338763 -1.419135,1.42478 0.153077,0.57129 1.680266,-0.450226 -0.414208,-1.545844 z m -29.97353,8.175455 -1.419134,1.424781 0.432217,1.613055 1.419134,-1.424781 -0.432217,-1.613055 z m 8.569356,-2.296152 -1.419134,1.424781 0.117058,0.436868 1.680266,-0.450226 -0.37819,-1.411423 z m 8.569356,-2.296152 -1.419135,1.424781 0.117059,0.436868 1.680266,-0.450226 -0.37819,-1.411423 z m 8.602961,-2.305157 -1.419135,1.424782 0.117058,0.436868 1.680267,-0.450226 -0.37819,-1.411424 z m 8.569356,-2.296152 -1.419134,1.424782 0.117058,0.436867 1.680266,-0.450225 -0.37819,-1.411424 z m 8.544755,-2.253542 -1.360928,1.373167 0.441221,1.646659 1.152705,-1.209317 c -0.04622,-0.610934 -0.134328,-1.197792 -0.232998,-1.810509 z m -40.566917,12.058469 -1.161709,1.175713 0.522262,1.949109 0.68947,-0.652978 -0.153077,-0.57129 c -0.126204,-0.471001 7.12e-4,-0.930958 0.265136,-1.295657 l -0.162082,-0.604897 z m 8.602962,-2.305156 -0.398439,0.394905 0.470475,-0.126063 -0.07204,-0.268842 z m 8.569356,-2.296152 -0.364833,0.385901 0.436869,-0.117059 -0.07204,-0.268842 z m 17.172317,-4.601309 -0.364833,0.385901 0.436869,-0.117058 -0.07204,-0.268843 z m -8.593957,2.338762 -0.340233,0.343292 0.403264,-0.108055 -0.06303,-0.235237 z m -29.97353,8.175456 -0.622258,0.634969 c 0.217477,0.456 0.434455,0.894051 0.680637,1.330382 l 0.382842,-0.318691 -0.441221,-1.64666 z m 8.569356,-2.296152 -0.141013,0.145838 0.168026,-0.04502 -0.02701,-0.100815 z m 8.569356,-2.296152 -0.141013,0.145838 0.168026,-0.04502 -0.02701,-0.100815 z m 8.60296,-2.305157 -0.141013,0.145839 0.168027,-0.04502 -0.02701,-0.100816 z m 8.569356,-2.296152 -0.141012,0.145838 0.168026,-0.04502 -0.02701,-0.100816 z m 8.602961,-2.305156 -1.195313,1.184717 0.207103,0.772922 0.234118,0.873738 1.195314,-1.184717 -0.441222,-1.64666 z m -40.358692,11.89462 -1.428139,1.391175 0.531265,1.982709 1.419136,-1.424775 -0.522262,-1.949109 z m 42.515552,-11.103862 -1.020696,1.029875 0.52226,1.949102 0.456646,-0.446513 c 0.06462,-0.834666 0.07352,-1.686628 0.04179,-2.532464 z M 11.076344,74.503212 10.843519,74.70967 c 0.139763,0.231452 0.319261,0.441865 0.466942,0.66728 l -0.234117,-0.873738 z m 42.913989,-11.498768 -1.186309,1.218321 0.522261,1.949106 1.186309,-1.218322 -0.522261,-1.949105 z m -39.082336,11.012351 -0.680465,0.686583 0.432217,1.613057 0.680466,-0.686582 -0.432218,-1.613058 z m -1.195315,1.184719 -1.419136,1.424777 0.06303,0.235242 c 0.255635,0.328728 0.485738,0.636179 0.758618,0.949302 l 1.038707,-0.962659 -0.441222,-1.646662 z m 41.644872,-10.186223 -0.141013,0.145836 0.09005,0.336055 c 0.0241,-0.16474 0.03055,-0.31645 0.05097,-0.481891 z m -0.655864,0.643973 -1.195314,1.184718 0.441222,1.646662 0.987089,-1.020873 c 0.03721,-0.155386 0.09327,-0.310064 0.127183,-0.466296 l -0.36018,-1.344211 z m -39.172382,10.676294 -0.680466,0.686582 0.522263,1.949113 1.103504,-1.1241 C 16.102003,77.653875 15.80009,77.34613 15.682386,76.906856 l -0.153078,-0.571297 z m -1.195315,1.184719 -0.763272,0.780804 c 0.39765,0.431059 0.798073,0.82161 1.227328,1.219922 l 0.05821,-0.05161 -0.522263,-1.949113 z m 38.864634,-9.405244 c -0.200355,0.259912 -0.449818,0.444695 -0.792696,0.536568 l -0.806528,0.216109 0.414209,1.545848 1.410128,-1.458391 -0.225113,-0.840134 z m -35.943858,9.955294 0.405204,1.512244 1.419134,-1.424788 -0.144072,-0.537682 -1.344213,0.360181 c -0.115084,0.03084 -0.222019,0.08559 -0.336053,0.09005 z m 2.285162,-0.612307 0.05403,0.201637 0.282025,-0.291683 -0.336053,0.09005 z m 2.016319,-0.540272 0.450226,1.680267 1.419135,-1.424777 -0.189096,-0.705715 -1.680265,0.450225 z m 2.285161,-0.612307 0.09905,0.36966 0.539451,-0.540746 -0.638501,0.171086 z m 2.016319,-0.540271 0.405205,1.512244 1.419133,-1.424787 -0.144071,-0.537683 -1.680267,0.450226 z m 2.285162,-0.612307 0.05403,0.201637 0.282025,-0.291682 -0.336054,0.09004 z m 1.982714,-0.531267 0.450226,1.680267 1.419135,-1.424777 -0.189096,-0.705715 -1.680265,0.450225 z m 2.285161,-0.612307 0.09905,0.36966 0.539452,-0.540746 -0.638502,0.171086 z m 2.016319,-0.540271 0.414209,1.545849 1.419132,-1.424787 -0.153075,-0.571287 -1.680266,0.450225 z m 2.285161,-0.612307 0.06303,0.235242 0.340232,-0.343296 -0.403265,0.108054 z m 2.016319,-0.540271 0.450226,1.680267 1.419137,-1.424778 -0.189096,-0.705715 -1.680267,0.450226 z m 2.285162,-0.612307 0.09905,0.36966 0.53945,-0.540746 -0.6385,0.171086 z m 1.982714,-0.531267 0.414209,1.545849 1.419132,-1.424787 -0.153076,-0.571287 -1.680265,0.450225 z m 2.285162,-0.612307 0.06303,0.235242 0.340229,-0.343296 -0.403262,0.108054 z m 2.016319,-0.540271 0.45923,1.713871 1.419136,-1.424777 -0.1981,-0.73932 -1.680266,0.450226 z m 2.285161,-0.612307 0.09905,0.36966 0.539451,-0.540746 -0.638501,0.171086 z m -28.604717,7.772662 -1.195314,1.184719 0.513258,1.915508 1.204319,-1.151114 -0.522263,-1.949113 z m 8.569356,-2.296152 -1.161709,1.175714 0.513259,1.915509 1.170713,-1.14211 -0.522263,-1.949113 z m 8.60296,-2.305156 -1.195314,1.184719 0.513259,1.915508 1.204319,-1.151114 -0.522264,-1.949113 z m 8.569356,-2.296152 -1.186309,1.218323 0.513259,1.915508 1.195314,-1.184718 -0.522264,-1.949113 z m -30.007133,8.184467 -1.161709,1.175714 0.189093,0.705705 c 0.214273,0.178473 0.387276,0.35082 0.607956,0.52145 l 0.805882,-0.756207 -0.441222,-1.646662 z m 8.602961,-2.305157 -1.195314,1.184719 0.432214,1.613048 1.204322,-1.151105 -0.441222,-1.646662 z m 8.569357,-2.296152 -1.195316,1.184719 0.44122,1.646652 1.204322,-1.151104 -0.450226,-1.680267 z m 8.569355,-2.296152 -1.161709,1.175714 0.441219,1.646653 1.170716,-1.1421 -0.450226,-1.680267 z m 8.602961,-2.305156 -1.18631,1.218323 0.432215,1.613048 1.195317,-1.184709 -0.441222,-1.646662 z m 3.517786,-0.582416 -0.447639,0.480128 0.144074,0.537692 c 0.116139,-0.330894 0.202775,-0.681866 0.303565,-1.01782 z m -0.96249,0.978265 -1.419135,1.424777 0.522263,1.949113 0.141014,-0.145837 c 0.382886,-0.719825 0.730277,-1.432221 1.034997,-2.186293 l -0.279139,-1.04176 z m -34.335629,9.236221 -1.428141,1.391173 0.522264,1.949113 1.419136,-1.42478 -0.513259,-1.915506 z m 8.602961,-2.305157 -1.42814,1.391174 0.522263,1.949112 1.419136,-1.424777 -0.513259,-1.915509 z m 8.569355,-2.296151 -1.428139,1.391173 0.522263,1.949112 1.419135,-1.424777 -0.513259,-1.915508 z m 8.569356,-2.296152 -1.419135,1.424777 0.513259,1.915508 1.428139,-1.391172 -0.522263,-1.949113 z m -21.404175,5.8793 -1.428137,1.391183 0.44122,1.646653 1.419135,-1.424778 -0.432218,-1.613058 z m 8.569356,-2.296152 -1.428137,1.391183 0.450226,1.680266 1.419133,-1.424787 -0.441222,-1.646662 z m 8.602962,-2.305156 -1.428138,1.391182 0.450226,1.680267 1.419133,-1.424787 -0.441221,-1.646662 z m 8.578362,-2.262538 -1.428139,1.391173 0.441222,1.646662 1.419132,-1.424787 -0.432215,-1.613048 z m 2.313467,0.568699 -1.18631,1.218323 0.522263,1.949112 1.18631,-1.218323 -0.522263,-1.949112 z m -34.33563,9.236223 -0.456643,0.44652 c 0.235943,0.17204 0.466314,0.33126 0.708769,0.49443 l -0.252126,-0.94095 z m 8.602961,-2.305158 -1.195314,1.184718 0.513258,1.91551 1.195315,-1.18472 -0.513259,-1.915508 z m 8.569357,-2.296153 -1.195316,1.184719 0.513259,1.915508 1.195316,-1.184718 -0.513259,-1.915509 z m 8.569355,-2.296151 -1.152705,1.209318 0.513259,1.915508 1.161709,-1.175714 -0.522263,-1.949112 z m -21.404175,5.8793 -1.195314,1.184722 0.432217,1.61305 1.195315,-1.18471 -0.432218,-1.613062 z m 8.569356,-2.296152 -1.161709,1.175714 0.441222,1.646662 1.161709,-1.175714 -0.441222,-1.646662 z m 8.602961,-2.305156 -1.195315,1.184718 0.441222,1.646662 1.195314,-1.184718 -0.441221,-1.646662 z m 8.578362,-2.262538 -1.195316,1.184709 0.432217,1.613057 1.195314,-1.184718 -0.432215,-1.613048 z m -23.19538,7.259705 -1.419136,1.424773 0.513259,1.91551 1.428137,-1.39118 -0.52226,-1.949103 z m 8.569356,-2.296152 -1.419136,1.424777 0.513259,1.915508 1.428137,-1.391182 -0.52226,-1.949103 z m 8.602961,-2.305157 -1.410132,1.458382 0.513259,1.915509 1.419134,-1.424788 -0.522261,-1.949103 z m 8.569356,-2.296152 -1.419136,1.424778 0.522264,1.949112 1.419132,-1.424787 -0.52226,-1.949103 z M 19.970555,81.48471 18.934262,82.5908 c 0.429888,0.24941 0.856871,0.47298 1.302895,0.69542 l 0.17462,-0.15484 -0.441222,-1.64667 z m 8.602961,-2.305153 -1.419136,1.424777 0.441222,1.646666 1.42814,-1.39118 -0.450226,-1.680263 z m 8.569355,-2.296152 -1.419135,1.424777 0.441221,1.646662 1.42814,-1.391173 -0.450226,-1.680266 z m 8.57836,-2.262548 -1.419135,1.424778 0.432217,1.613057 1.42814,-1.391173 -0.441222,-1.646662 z m -23.410194,7.533383 -1.204319,1.15111 0.117059,0.43687 c 0.444778,0.19669 0.91894,0.39769 1.376697,0.56758 l 0.223819,-0.24007 -0.513256,-1.91549 z m 8.569356,-2.296157 -1.170714,1.142107 0.522261,1.94911 1.161709,-1.17572 -0.513256,-1.915497 z m 8.60296,-2.305156 -1.195315,1.184718 0.513257,1.915499 1.204321,-1.151104 -0.522263,-1.949113 z m 8.569356,-2.296152 -1.204318,1.151114 0.522261,1.949103 1.20432,-1.151104 -0.522263,-1.949113 z m -21.404175,5.879305 -1.204319,1.15111 0.450226,1.68027 1.195315,-1.18472 -0.441222,-1.64666 z m 8.569357,-2.296157 -1.20432,1.151114 0.450226,1.680263 1.195316,-1.184715 -0.441222,-1.646662 z m 8.578359,-2.262547 -1.170713,1.142109 0.441222,1.646662 1.161709,-1.175714 -0.432218,-1.613057 z m 7.74788,-1.463724 -0.349237,0.309681 0.02701,0.100814 c 0.09969,-0.140033 0.225699,-0.268515 0.322224,-0.410495 z m -22.340296,6.418278 -1.419137,1.42478 0.513257,1.9155 1.419136,-1.42478 -0.513256,-1.9155 z m 8.569355,-2.296148 -1.419136,1.424777 0.522261,1.949101 1.419135,-1.42478 -0.52226,-1.949098 z m 8.569355,-2.296152 -1.419135,1.424777 0.522261,1.949103 1.419135,-1.424777 -0.522261,-1.949103 z m -21.404174,5.8793 -1.419136,1.42478 0.117059,0.43687 c 0.394597,0.12628 0.776933,0.26212 1.179247,0.36837 l 0.564052,-0.58336 -0.441222,-1.64666 z m 8.569356,-2.296152 -1.419136,1.424782 0.441222,1.64666 1.419135,-1.42478 -0.441221,-1.646662 z m 8.611965,-2.271552 -1.419136,1.424778 0.432217,1.613056 1.419137,-1.424776 -0.432218,-1.613058 z m 8.569356,-2.296152 -1.419135,1.424778 0.117059,0.436869 c 0.477746,-0.522483 0.928752,-1.10114 1.356102,-1.660019 l -0.05403,-0.201628 z m -23.419199,7.499776 -1.195315,1.18472 0.189093,0.7057 c 0.438297,0.08759 0.884162,0.19249 1.329264,0.25614 l 0.199218,-0.19746 -0.52226,-1.9491 z m 8.569356,-2.29615 -1.186311,1.21832 0.513256,1.9155 1.195316,-1.18472 -0.522261,-1.9491 z m 8.569355,-2.296156 -1.161709,1.175714 0.522261,1.949102 1.161709,-1.17571 -0.522261,-1.949106 z m -12.834819,3.583146 -1.161709,1.17572 0.441222,1.64666 1.170714,-1.14211 -0.450227,-1.68027 z m 8.611965,-2.27155 -1.195314,1.18472 0.432217,1.61306 1.204319,-1.15112 -0.441222,-1.64666 z m 8.569356,-2.296152 -1.195314,1.184719 0.108055,0.403264 c 0.406645,-0.365398 0.834514,-0.728004 1.213322,-1.117509 l -0.126063,-0.470474 z m -23.186376,7.293312 -0.05821,0.05161 c 0.02091,0.0049 0.04628,-0.02281 0.06721,-0.01801 l -0.009,-0.0336 z m 17.172317,-4.601311 -1.428138,1.391181 0.396198,1.47863 c 0.421481,-0.19782 0.832645,-0.40094 1.23857,-0.62002 l 0.315633,-0.30068 -0.522263,-1.949111 z m -8.593957,2.338761 -1.428137,1.39118 0.387193,1.44503 c 0.232467,-0.01947 0.466108,-0.05312 0.69912,-0.07928 l 0.855083,-0.84142 -0.513259,-1.91551 z m -4.240859,1.2444 -1.428141,1.39117 0.04502,0.16802 c 0.598027,0.06253 1.183809,0.07812 1.790735,0.09647 l 0.03361,-0.009 -0.441222,-1.64666 z m 8.578359,-2.26255 -1.42814,1.39117 0.441222,1.64666 1.419135,-1.42477 -0.432217,-1.61306 z m 8.569356,-2.296152 -1.42814,1.391172 0.03602,0.13442 c 0.512616,-0.370138 1.012852,-0.750913 1.491173,-1.155932 l -0.09905,-0.36966 z m -6.246885,2.898452 -1.195315,1.18472 0.153079,0.5713 c 0.462149,-0.17092 0.920198,-0.35074 1.366399,-0.54622 l -0.324163,-1.2098 z m -8.593954,2.33877 -1.029702,0.99627 c 0.424597,-0.0025 0.854389,-0.03061 1.281828,-0.05532 l -0.252126,-0.94095 z m 4.337499,-1.01816 -1.195316,1.18472 0.117059,0.43687 c 0.476116,-0.08601 0.943997,-0.22979 1.420428,-0.34458 l -0.342171,-1.27701 z m 2.54629,0.36225 -0.456643,0.44652 c 0.172308,-0.05482 0.367383,-0.08585 0.537685,-0.14407 l -0.08104,-0.30245 z" id="path3410" /> - OXYGEN Date: Wed, 19 Mar 2014 18:28:34 +0200 Subject: [PATCH 072/253] add apg's fix for Key id display --- .../org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java | 6 +++++- .../sufficientlysecure/keychain/ui/CertifyKeyActivity.java | 2 +- .../keychain/ui/SelectSecretKeyLayoutFragment.java | 2 +- .../keychain/ui/adapter/SelectKeyCursorAdapter.java | 2 +- .../keychain/ui/adapter/ViewKeyKeysAdapter.java | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index b93c68677..5b00d163a 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -477,7 +477,11 @@ public class PgpKeyHelper { * @return */ public static String convertKeyIdToHex(long keyId) { - return "0x" + ((keyId >> 32) > 0 ? convertKeyIdToHex32bit(keyId >> 32) : "") + convertKeyIdToHex32bit(keyId); + return "0x" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId); + } + + public static String convertKeyIdToHexShort(long keyId) { + return "0x" + convertKeyIdToHex32bit(keyId); } private static String convertKeyIdToHex32bit(long keyId) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java index d8df5ced3..bd14369b0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java @@ -199,7 +199,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements // the first key here is our master key if (data.moveToFirst()) { long keyId = data.getLong(INDEX_MASTER_KEY_ID); - String keyIdStr = PgpKeyHelper.convertKeyIdToHex(keyId); + String keyIdStr = PgpKeyHelper.convertKeyIdToHexShort(keyId); ((TextView) findViewById(R.id.key_id)).setText(keyIdStr); String mainUserId = data.getString(INDEX_USER_ID); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java index 6b57fc568..960b4aafb 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java @@ -71,7 +71,7 @@ public class SelectSecretKeyLayoutFragment extends Fragment { getActivity(), secretKeyId); if (keyRing != null) { PGPSecretKey key = PgpKeyHelper.getMasterKey(keyRing); - String masterkeyIdHex = PgpKeyHelper.convertKeyIdToHex(secretKeyId); + String masterkeyIdHex = PgpKeyHelper.convertKeyIdToHexShort(secretKeyId); if (key != null) { String userId = PgpKeyHelper.getMainUserIdSafe(getActivity(), key); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java index beb76fc10..fbbb9caa4 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/SelectKeyCursorAdapter.java @@ -115,7 +115,7 @@ public class SelectKeyCursorAdapter extends HighlightQueryCursorAdapter { // TODO: needed to key id to no? keyId.setText(R.string.no_key); long masterKeyId = cursor.getLong(mIndexMasterKeyId); - keyId.setText(PgpKeyHelper.convertKeyIdToHex(masterKeyId)); + keyId.setText(PgpKeyHelper.convertKeyIdToHexShort(masterKeyId)); // TODO: needed to set unknown_status? status.setText(R.string.unknown_status); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java index 5f911275d..0064e9f13 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java @@ -86,7 +86,7 @@ public class ViewKeyKeysAdapter extends CursorAdapter { ImageView signIcon = (ImageView) view.findViewById(R.id.ic_signKey); ImageView revokedKeyIcon = (ImageView) view.findViewById(R.id.ic_revokedKey); - String keyIdStr = PgpKeyHelper.convertKeyIdToHex(cursor.getLong(mIndexKeyId)); + String keyIdStr = PgpKeyHelper.convertKeyIdToHexShort(cursor.getLong(mIndexKeyId)); String algorithmStr = PgpKeyHelper.getAlgorithmInfo(cursor.getInt(mIndexAlgorithm), cursor.getInt(mIndexKeySize)); From 62fb1fb57931626e73e9928cc44f6eb60f3c101e Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Thu, 20 Mar 2014 00:29:40 +0100 Subject: [PATCH 073/253] Fix key list views in main key view Since the views are being reused, a revoked key might set the text color of a view and when the view is being reused for a non-revoked key it is still red. So grab the default text colour and set it explicitly when the key is not revoked. --- .../keychain/ui/adapter/ViewKeyKeysAdapter.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java index 0064e9f13..068d6e6e9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java @@ -18,6 +18,7 @@ package org.sufficientlysecure.keychain.ui.adapter; import android.content.Context; +import android.content.res.ColorStateList; import android.database.Cursor; import android.graphics.Color; import android.support.v4.widget.CursorAdapter; @@ -42,6 +43,8 @@ public class ViewKeyKeysAdapter extends CursorAdapter { private int mIndexCanSign; private int mIndexRevokedKey; + private ColorStateList mDefaultTextColor; + public ViewKeyKeysAdapter(Context context, Cursor c, int flags) { super(context, c, flags); @@ -122,13 +125,20 @@ public class ViewKeyKeysAdapter extends CursorAdapter { keyId.setTextColor(Color.RED); keyDetails.setTextColor(Color.RED); } else { + keyId.setTextColor(mDefaultTextColor); + keyDetails.setTextColor(mDefaultTextColor); revokedKeyIcon.setVisibility(View.GONE); } } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(R.layout.view_key_keys_item, null); + View view = mInflater.inflate(R.layout.view_key_keys_item, null); + if (mDefaultTextColor == null) { + TextView keyId = (TextView) view.findViewById(R.id.keyId); + mDefaultTextColor = keyId.getTextColors(); + } + return view; } } From 16ae9c899949b5443c06226993ca2efc23591e72 Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Thu, 20 Mar 2014 14:09:35 +0100 Subject: [PATCH 074/253] Let convertKeyIdToHex handle short key IDs If a short key ID is given, then it should only be displayed as such. --- .../org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index 5b00d163a..d201a1426 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -477,6 +477,11 @@ public class PgpKeyHelper { * @return */ public static String convertKeyIdToHex(long keyId) { + long upper = keyId >> 32; + if (upper == 0) { + // this is a short key id + return convertKeyIdToHexShort(keyId); + } return "0x" + convertKeyIdToHex32bit(keyId >> 32) + convertKeyIdToHex32bit(keyId); } From 3fa3fc444bf8a0f21d5bd2949d2141e555077f50 Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Thu, 20 Mar 2014 14:14:02 +0100 Subject: [PATCH 075/253] Add debug logging for hkp keyserver actions --- .../keychain/util/HkpKeyServer.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java index 9d6850027..43b40a4db 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java @@ -30,9 +30,11 @@ import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry; +import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -179,6 +181,7 @@ public class HkpKeyServer extends KeyServer { for (int i = 0; i < ips.length; ++i) { try { String url = "http://" + ips[i].getHostAddress() + ":" + mPort + request; + Log.d(Constants.TAG, "hkp keyserver query: " + url); URL realUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection(); conn.setConnectTimeout(5000); @@ -277,9 +280,10 @@ public class HkpKeyServer extends KeyServer { public String get(long keyId) throws QueryException { HttpClient client = new DefaultHttpClient(); try { - HttpGet get = new HttpGet("http://" + mHost + ":" + mPort - + "/pks/lookup?op=get&options=mr&search=" + PgpKeyHelper.convertKeyIdToHex(keyId)); - + String query = "http://" + mHost + ":" + mPort + + "/pks/lookup?op=get&options=mr&search=" + PgpKeyHelper.convertKeyIdToHex(keyId); + Log.d(Constants.TAG, "hkp keyserver get: " + query); + HttpGet get = new HttpGet(query); HttpResponse response = client.execute(get); if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) { throw new QueryException("not found"); @@ -305,8 +309,9 @@ public class HkpKeyServer extends KeyServer { public void add(String armoredText) throws AddKeyException { HttpClient client = new DefaultHttpClient(); try { - HttpPost post = new HttpPost("http://" + mHost + ":" + mPort + "/pks/add"); - + String query = "http://" + mHost + ":" + mPort + "/pks/add"; + HttpPost post = new HttpPost(query); + Log.d(Constants.TAG, "hkp keyserver add: " + query); List nameValuePairs = new ArrayList(2); nameValuePairs.add(new BasicNameValuePair("keytext", armoredText)); post.setEntity(new UrlEncodedFormEntity(nameValuePairs)); From 2b774899322ae31f1d22999caae68c3f80985c8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 20 Mar 2014 15:54:49 +0100 Subject: [PATCH 076/253] Dont try to parse key/fingerprint result from keyserver query --- .../keychain/pgp/PgpKeyHelper.java | 19 +--- .../service/KeychainIntentService.java | 2 +- .../ui/adapter/ImportKeysAdapter.java | 6 +- .../ui/adapter/ImportKeysListEntry.java | 43 ++++---- .../keychain/util/HkpKeyServer.java | 98 ++++++++++--------- .../keychain/util/KeyServer.java | 4 +- .../res/layout/import_keys_list_entry.xml | 7 +- 7 files changed, 91 insertions(+), 88 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index d201a1426..09c0b2d8f 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -460,12 +460,16 @@ public class PgpKeyHelper { public static String convertFingerprintToHex(byte[] fingerprint, boolean split) { String hexString = Hex.toHexString(fingerprint); if (split) { - hexString = hexString.replaceAll("(.{4})(?!$)", "$1 "); + hexString = splitFingerprintHex(hexString); } return hexString; } + public static String splitFingerprintHex(String hexString) { + return hexString.replaceAll("(.{4})(?!$)", "$1 "); + } + /** * Convert key id from long to 64 bit hex string *

@@ -497,19 +501,6 @@ public class PgpKeyHelper { return hexString; } - /** - * Used in HkpKeyServer to convert hex encoded key ids back to long. - * - * @param hexString - * @return - */ - public static long convertHexToKeyId(String hexString) { - int len = hexString.length(); - String s2 = hexString.substring(len - 8); - String s1 = hexString.substring(0, len - 8); - return ((!s1.isEmpty() ? Long.parseLong(s1, 16) << 32 : 0) | Long.parseLong(s2, 16)); - } - /** * Splits userId string into naming part, email part, and comment part * diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 6ec6dcf8a..5af81d39d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -749,7 +749,7 @@ public class KeychainIntentService extends IntentService HkpKeyServer server = new HkpKeyServer(keyServer); for (ImportKeysListEntry entry : entries) { - byte[] downloadedKey = server.get(entry.getKeyId()).getBytes(); + byte[] downloadedKey = server.get(entry.getKeyIdHex()).getBytes(); /** * TODO: copied from ImportKeysListLoader diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 0f05af447..37a8dd416 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -134,10 +134,10 @@ public class ImportKeysAdapter extends ArrayAdapter { holder.mMainUserIdRest.setVisibility(View.GONE); } - holder.mKeyId.setText(entry.hexKeyId); + holder.mKeyId.setText(entry.keyIdHex); - if (entry.fingerPrint != null) { - holder.mFingerprint.setText(mActivity.getString(R.string.fingerprint) + " " + entry.fingerPrint); + if (entry.fingerPrintHex != null) { + holder.mFingerprint.setText(mActivity.getString(R.string.fingerprint) + " " + entry.fingerPrintHex); holder.mFingerprint.setVisibility(View.VISIBLE); } else { holder.mFingerprint.setVisibility(View.GONE); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java index 05521b0c9..d7c38213d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java @@ -20,6 +20,7 @@ package org.sufficientlysecure.keychain.ui.adapter; import android.os.Parcel; import android.os.Parcelable; import android.util.SparseArray; + import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPPublicKey; import org.spongycastle.openpgp.PGPSecretKeyRing; @@ -38,10 +39,11 @@ public class ImportKeysListEntry implements Serializable, Parcelable { public ArrayList userIds; public long keyId; + public String keyIdHex; + public boolean revoked; public Date date; // TODO: not displayed - public String fingerPrint; - public String hexKeyId; + public String fingerPrintHex; public int bitStrength; public String algorithm; public boolean secretKey; @@ -55,8 +57,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable { this.keyId = b.keyId; this.revoked = b.revoked; this.date = b.date; - this.fingerPrint = b.fingerPrint; - this.hexKeyId = b.hexKeyId; + this.fingerPrintHex = b.fingerPrintHex; + this.keyIdHex = b.keyIdHex; this.bitStrength = b.bitStrength; this.algorithm = b.algorithm; this.secretKey = b.secretKey; @@ -74,8 +76,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable { dest.writeLong(keyId); dest.writeByte((byte) (revoked ? 1 : 0)); dest.writeSerializable(date); - dest.writeString(fingerPrint); - dest.writeString(hexKeyId); + dest.writeString(fingerPrintHex); + dest.writeString(keyIdHex); dest.writeInt(bitStrength); dest.writeString(algorithm); dest.writeByte((byte) (secretKey ? 1 : 0)); @@ -92,8 +94,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable { vr.keyId = source.readLong(); vr.revoked = source.readByte() == 1; vr.date = (Date) source.readSerializable(); - vr.fingerPrint = source.readString(); - vr.hexKeyId = source.readString(); + vr.fingerPrintHex = source.readString(); + vr.keyIdHex = source.readString(); vr.bitStrength = source.readInt(); vr.algorithm = source.readString(); vr.secretKey = source.readByte() == 1; @@ -109,8 +111,8 @@ public class ImportKeysListEntry implements Serializable, Parcelable { } }; - public long getKeyId() { - return keyId; + public String getKeyIdHex() { + return keyIdHex; } public byte[] getBytes() { @@ -121,6 +123,14 @@ public class ImportKeysListEntry implements Serializable, Parcelable { this.mBytes = bytes; } + public boolean isSelected() { + return mSelected; + } + + public void setSelected(boolean selected) { + this.mSelected = selected; + } + /** * Constructor for later querying from keyserver */ @@ -132,14 +142,6 @@ public class ImportKeysListEntry implements Serializable, Parcelable { userIds = new ArrayList(); } - public boolean isSelected() { - return mSelected; - } - - public void setSelected(boolean selected) { - this.mSelected = selected; - } - /** * Constructor based on key object, used for import from NFC, QR Codes, files */ @@ -165,12 +167,13 @@ public class ImportKeysListEntry implements Serializable, Parcelable { for (String userId : new IterableIterator(pgpKeyRing.getPublicKey().getUserIDs())) { userIds.add(userId); } + this.keyId = pgpKeyRing.getPublicKey().getKeyID(); + this.keyIdHex = PgpKeyHelper.convertKeyIdToHex(keyId); this.revoked = pgpKeyRing.getPublicKey().isRevoked(); - this.fingerPrint = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey() + this.fingerPrintHex = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey() .getFingerprint(), true); - this.hexKeyId = PgpKeyHelper.convertKeyIdToHex(keyId); this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength(); final int algorithm = pgpKeyRing.getPublicKey().getAlgorithm(); this.algorithm = getAlgorithmFromId(algorithm); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java index 43b40a4db..3658ef6c6 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java @@ -18,7 +18,6 @@ package org.sufficientlysecure.keychain.util; -import android.text.Html; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; @@ -34,7 +33,6 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry; -import org.sufficientlysecure.keychain.util.Log; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -74,26 +72,26 @@ public class HkpKeyServer extends KeyServer { /** * pub:%keyid%:%algo%:%keylen%:%creationdate%:%expirationdate%:%flags% *

* * @see 5.2. Machine Readable Indexes @@ -107,23 +105,23 @@ public class HkpKeyServer extends KeyServer { /** * uid:%escaped uid string%:%creationdate%:%expirationdate%:%flags% *
    - *
  • %escaped uid string% = the user ID string, with HTTP %-escaping for anything that isn't 7-bit - * safe as well as for the ":" character. Any other characters may be escaped, as desired.
  • - *
  • %creationdate% = creation date of the key in standard - * RFC-2440 form (i.e. number of seconds since - * 1/1/1970 UTC time)
  • - *
  • %expirationdate% = expiration date of the key in standard - * RFC-2440 form (i.e. number of seconds since - * 1/1/1970 UTC time)
  • - *
  • %flags% = letter codes to indicate details of the key, if any. Flags may be in any order. The - * meaning of "disabled" is implementation-specific. Note that individual flags may be unimplemented, so - * the absence of a given flag does not necessarily mean the absence of the detail. - *
      - *
    • r == revoked
    • - *
    • d == disabled
    • - *
    • e == expired
    • - *
    - *
  • + *
  • %escaped uid string% = the user ID string, with HTTP %-escaping for anything that isn't 7-bit + * safe as well as for the ":" character. Any other characters may be escaped, as desired.
  • + *
  • %creationdate% = creation date of the key in standard + * RFC-2440 form (i.e. number of seconds since + * 1/1/1970 UTC time)
  • + *
  • %expirationdate% = expiration date of the key in standard + * RFC-2440 form (i.e. number of seconds since + * 1/1/1970 UTC time)
  • + *
  • %flags% = letter codes to indicate details of the key, if any. Flags may be in any order. The + * meaning of "disabled" is implementation-specific. Note that individual flags may be unimplemented, so + * the absence of a given flag does not necessarily mean the absence of the detail. + *
      + *
    • r == revoked
    • + *
    • d == disabled
    • + *
    • e == expired
    • + *
    + *
  • *
*/ public static final Pattern UID_LINE = Pattern @@ -246,8 +244,18 @@ public class HkpKeyServer extends KeyServer { final int algorithmId = Integer.decode(matcher.group(2)); info.algorithm = getAlgorithmFromId(algorithmId); - info.hexKeyId = "0x" + matcher.group(1); - info.keyId = PgpKeyHelper.convertHexToKeyId(matcher.group(1)); + // group 1 contains the full fingerprint (v4) or the long key id if available + // see https://bitbucket.org/skskeyserver/sks-keyserver/pull-request/12/fixes-for-machine-readable-indexes/diff + // and https://github.com/openpgp-keychain/openpgp-keychain/issues/259#issuecomment-38168176 + String fingerprintOrKeyId = matcher.group(1); + if (fingerprintOrKeyId.length() > 16) { + info.fingerPrintHex = "0x" + PgpKeyHelper.splitFingerprintHex(fingerprintOrKeyId); + info.keyIdHex = "0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length() + - 16, fingerprintOrKeyId.length()); + } else { + // set key id only + info.keyIdHex = "0x" + fingerprintOrKeyId; + } final long creationDate = Long.parseLong(matcher.group(4)); final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC")); @@ -277,11 +285,11 @@ public class HkpKeyServer extends KeyServer { } @Override - public String get(long keyId) throws QueryException { + public String get(String keyIdHex) throws QueryException { HttpClient client = new DefaultHttpClient(); try { String query = "http://" + mHost + ":" + mPort + - "/pks/lookup?op=get&options=mr&search=" + PgpKeyHelper.convertKeyIdToHex(keyId); + "/pks/lookup?op=get&options=mr&search=" + keyIdHex; Log.d(Constants.TAG, "hkp keyserver get: " + query); HttpGet get = new HttpGet(query); HttpResponse response = client.execute(get); @@ -306,14 +314,14 @@ public class HkpKeyServer extends KeyServer { } @Override - public void add(String armoredText) throws AddKeyException { + public void add(String armoredKey) throws AddKeyException { HttpClient client = new DefaultHttpClient(); try { String query = "http://" + mHost + ":" + mPort + "/pks/add"; HttpPost post = new HttpPost(query); Log.d(Constants.TAG, "hkp keyserver add: " + query); List nameValuePairs = new ArrayList(2); - nameValuePairs.add(new BasicNameValuePair("keytext", armoredText)); + nameValuePairs.add(new BasicNameValuePair("keytext", armoredKey)); post.setEntity(new UrlEncodedFormEntity(nameValuePairs)); HttpResponse response = client.execute(post); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java index a31fdc5ae..7f70867a5 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/KeyServer.java @@ -46,7 +46,7 @@ public abstract class KeyServer { abstract List search(String query) throws QueryException, TooManyResponses, InsufficientQuery; - abstract String get(long keyId) throws QueryException; + abstract String get(String keyIdHex) throws QueryException; - abstract void add(String armoredText) throws AddKeyException; + abstract void add(String armoredKey) throws AddKeyException; } diff --git a/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml b/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml index 3cc0bc6dc..fb092793b 100644 --- a/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml +++ b/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml @@ -77,10 +77,11 @@ + android:typeface="monospace" + android:layout_weight="1" /> Date: Thu, 20 Mar 2014 16:04:05 +0100 Subject: [PATCH 077/253] Use getter and setter --- .../ui/adapter/ImportKeysAdapter.java | 51 +++++++------ .../ui/adapter/ImportKeysListEntry.java | 71 ++++++++++++++++++- .../keychain/util/HkpKeyServer.java | 29 ++++---- 3 files changed, 111 insertions(+), 40 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 37a8dd416..d174bd0c8 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -43,13 +43,12 @@ public class ImportKeysAdapter extends ArrayAdapter { protected List mData; static class ViewHolder { - private TextView mMainUserId; - private TextView mMainUserIdRest; - private TextView mKeyId; - private TextView mFingerprint; - private TextView mAlgorithm; - private TextView mStatus; - + private TextView mainUserId; + private TextView mainUserIdRest; + private TextView keyId; + private TextView fingerprint; + private TextView algorithm; + private TextView status; } public ImportKeysAdapter(Activity activity) { @@ -100,12 +99,12 @@ public class ImportKeysAdapter extends ArrayAdapter { if (convertView == null) { holder = new ViewHolder(); convertView = mInflater.inflate(R.layout.import_keys_list_entry, null); - holder.mMainUserId = (TextView) convertView.findViewById(R.id.mainUserId); - holder.mMainUserIdRest = (TextView) convertView.findViewById(R.id.mainUserIdRest); - holder.mKeyId = (TextView) convertView.findViewById(R.id.keyId); - holder.mFingerprint = (TextView) convertView.findViewById(R.id.fingerprint); - holder.mAlgorithm = (TextView) convertView.findViewById(R.id.algorithm); - holder.mStatus = (TextView) convertView.findViewById(R.id.status); + holder.mainUserId = (TextView) convertView.findViewById(R.id.mainUserId); + holder.mainUserIdRest = (TextView) convertView.findViewById(R.id.mainUserIdRest); + holder.keyId = (TextView) convertView.findViewById(R.id.keyId); + holder.fingerprint = (TextView) convertView.findViewById(R.id.fingerprint); + holder.algorithm = (TextView) convertView.findViewById(R.id.algorithm); + holder.status = (TextView) convertView.findViewById(R.id.status); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); @@ -119,36 +118,36 @@ public class ImportKeysAdapter extends ArrayAdapter { // show red user id if it is a secret key if (entry.secretKey) { userIdSplit[0] = mActivity.getString(R.string.secret_key) + " " + userIdSplit[0]; - holder.mMainUserId.setTextColor(Color.RED); + holder.mainUserId.setTextColor(Color.RED); } - holder.mMainUserId.setText(userIdSplit[0]); + holder.mainUserId.setText(userIdSplit[0]); } else { - holder.mMainUserId.setText(R.string.user_id_no_name); + holder.mainUserId.setText(R.string.user_id_no_name); } // email if (userIdSplit[1] != null) { - holder.mMainUserIdRest.setText(userIdSplit[1]); - holder.mMainUserIdRest.setVisibility(View.VISIBLE); + holder.mainUserIdRest.setText(userIdSplit[1]); + holder.mainUserIdRest.setVisibility(View.VISIBLE); } else { - holder.mMainUserIdRest.setVisibility(View.GONE); + holder.mainUserIdRest.setVisibility(View.GONE); } - holder.mKeyId.setText(entry.keyIdHex); + holder.keyId.setText(entry.keyIdHex); if (entry.fingerPrintHex != null) { - holder.mFingerprint.setText(mActivity.getString(R.string.fingerprint) + " " + entry.fingerPrintHex); - holder.mFingerprint.setVisibility(View.VISIBLE); + holder.fingerprint.setText(mActivity.getString(R.string.fingerprint) + " " + entry.fingerPrintHex); + holder.fingerprint.setVisibility(View.VISIBLE); } else { - holder.mFingerprint.setVisibility(View.GONE); + holder.fingerprint.setVisibility(View.GONE); } - holder.mAlgorithm.setText("" + entry.bitStrength + "/" + entry.algorithm); + holder.algorithm.setText("" + entry.bitStrength + "/" + entry.algorithm); if (entry.revoked) { - holder.mStatus.setText(R.string.revoked); + holder.status.setText(R.string.revoked); } else { - holder.mStatus.setVisibility(View.GONE); + holder.status.setVisibility(View.GONE); } LinearLayout ll = (LinearLayout) convertView.findViewById(R.id.list); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java index d7c38213d..2610dfb3e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java @@ -36,11 +36,10 @@ import java.util.Date; public class ImportKeysListEntry implements Serializable, Parcelable { private static final long serialVersionUID = -7797972103284992662L; - public ArrayList userIds; + public ArrayList userIds; public long keyId; public String keyIdHex; - public boolean revoked; public Date date; // TODO: not displayed public String fingerPrintHex; @@ -131,6 +130,74 @@ public class ImportKeysListEntry implements Serializable, Parcelable { this.mSelected = selected; } + public long getKeyId() { + return keyId; + } + + public void setKeyId(long keyId) { + this.keyId = keyId; + } + + public void setKeyIdHex(String keyIdHex) { + this.keyIdHex = keyIdHex; + } + + public boolean isRevoked() { + return revoked; + } + + public void setRevoked(boolean revoked) { + this.revoked = revoked; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public String getFingerPrintHex() { + return fingerPrintHex; + } + + public void setFingerPrintHex(String fingerPrintHex) { + this.fingerPrintHex = fingerPrintHex; + } + + public int getBitStrength() { + return bitStrength; + } + + public void setBitStrength(int bitStrength) { + this.bitStrength = bitStrength; + } + + public String getAlgorithm() { + return algorithm; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public boolean isSecretKey() { + return secretKey; + } + + public void setSecretKey(boolean secretKey) { + this.secretKey = secretKey; + } + + public ArrayList getUserIds() { + return userIds; + } + + public void setUserIds(ArrayList userIds) { + this.userIds = userIds; + } + /** * Constructor for later querying from keyserver */ diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java index 3658ef6c6..a25e3d748 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java @@ -239,32 +239,35 @@ public class HkpKeyServer extends KeyServer { final Matcher matcher = PUB_KEY_LINE.matcher(data); while (matcher.find()) { - final ImportKeysListEntry info = new ImportKeysListEntry(); - info.bitStrength = Integer.parseInt(matcher.group(3)); + final ImportKeysListEntry entry = new ImportKeysListEntry(); + + entry.setBitStrength(Integer.parseInt(matcher.group(3))); + final int algorithmId = Integer.decode(matcher.group(2)); - info.algorithm = getAlgorithmFromId(algorithmId); + entry.setAlgorithm(getAlgorithmFromId(algorithmId)); // group 1 contains the full fingerprint (v4) or the long key id if available // see https://bitbucket.org/skskeyserver/sks-keyserver/pull-request/12/fixes-for-machine-readable-indexes/diff // and https://github.com/openpgp-keychain/openpgp-keychain/issues/259#issuecomment-38168176 String fingerprintOrKeyId = matcher.group(1); if (fingerprintOrKeyId.length() > 16) { - info.fingerPrintHex = "0x" + PgpKeyHelper.splitFingerprintHex(fingerprintOrKeyId); - info.keyIdHex = "0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length() - - 16, fingerprintOrKeyId.length()); + entry.setFingerPrintHex(PgpKeyHelper.splitFingerprintHex( + fingerprintOrKeyId.toLowerCase(Locale.US))); + entry.setKeyIdHex("0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length() + - 16, fingerprintOrKeyId.length())); } else { // set key id only - info.keyIdHex = "0x" + fingerprintOrKeyId; + entry.setKeyIdHex("0x" + fingerprintOrKeyId); } final long creationDate = Long.parseLong(matcher.group(4)); final GregorianCalendar tmpGreg = new GregorianCalendar(TimeZone.getTimeZone("UTC")); tmpGreg.setTimeInMillis(creationDate * 1000); - info.date = tmpGreg.getTime(); + entry.setDate(tmpGreg.getTime()); - info.revoked = matcher.group(6).contains("r"); - info.userIds = new ArrayList(); + entry.setRevoked(matcher.group(6).contains("r")); + ArrayList userIds = new ArrayList(); final String uidLines = matcher.group(7); final Matcher uidMatcher = UID_LINE.matcher(uidLines); while (uidMatcher.find()) { @@ -277,9 +280,11 @@ public class HkpKeyServer extends KeyServer { // will never happen, because "UTF8" is supported } } - info.userIds.add(tmp); + userIds.add(tmp); } - results.add(info); + entry.setUserIds(userIds); + + results.add(entry); } return results; } From 81e0d04230963cdd6db9c64e51316533c0a1d103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 20 Mar 2014 16:09:05 +0100 Subject: [PATCH 078/253] colorize fingerprint in import --- .../keychain/ui/adapter/ImportKeysAdapter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index d174bd0c8..57aaf0733 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -30,7 +30,9 @@ import android.widget.CheckBox; import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import android.widget.TextView; + import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.OtherHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import java.util.ArrayList; @@ -136,7 +138,7 @@ public class ImportKeysAdapter extends ArrayAdapter { holder.keyId.setText(entry.keyIdHex); if (entry.fingerPrintHex != null) { - holder.fingerprint.setText(mActivity.getString(R.string.fingerprint) + " " + entry.fingerPrintHex); + holder.fingerprint.setText(OtherHelper.colorizeFingerprint(entry.fingerPrintHex)); holder.fingerprint.setVisibility(View.VISIBLE); } else { holder.fingerprint.setVisibility(View.GONE); From 77365202e0771abb0a1e8e3a2599ecb9664b3990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 20 Mar 2014 16:10:42 +0100 Subject: [PATCH 079/253] put colorize method into key helper --- .../keychain/helper/OtherHelper.java | 77 ----------------- .../keychain/pgp/PgpKeyHelper.java | 86 +++++++++++++++++++ .../keychain/ui/CertifyKeyActivity.java | 2 +- .../keychain/ui/ViewKeyMainFragment.java | 2 +- .../ui/adapter/ImportKeysAdapter.java | 2 +- 5 files changed, 89 insertions(+), 80 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java index 736bff02d..d0ba20ea6 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java @@ -65,81 +65,4 @@ public class OtherHelper { } } - public static SpannableStringBuilder colorizeFingerprint(String fingerprint) { - SpannableStringBuilder sb = new SpannableStringBuilder(fingerprint); - try { - // for each 4 characters of the fingerprint + 1 space - for (int i = 0; i < fingerprint.length(); i += 5) { - int spanEnd = Math.min(i + 4, fingerprint.length()); - String fourChars = fingerprint.substring(i, spanEnd); - - int raw = Integer.parseInt(fourChars, 16); - byte[] bytes = {(byte) ((raw >> 8) & 0xff - 128), (byte) (raw & 0xff - 128)}; - int[] color = OtherHelper.getRgbForData(bytes); - int r = color[0]; - int g = color[1]; - int b = color[2]; - - // we cannot change black by multiplication, so adjust it to an almost-black grey, - // which will then be brightened to the minimal brightness level - if (r == 0 && g == 0 && b == 0) { - r = 1; - g = 1; - b = 1; - } - - // Convert rgb to brightness - double brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b; - - // If a color is too dark to be seen on black, - // then brighten it up to a minimal brightness. - if (brightness < 80) { - double factor = 80.0 / brightness; - r = Math.min(255, (int) (r * factor)); - g = Math.min(255, (int) (g * factor)); - b = Math.min(255, (int) (b * factor)); - - // If it is too light, then darken it to a respective maximal brightness. - } else if (brightness > 180) { - double factor = 180.0 / brightness; - r = (int) (r * factor); - g = (int) (g * factor); - b = (int) (b * factor); - } - - // Create a foreground color with the 3 digest integers as RGB - // and then converting that int to hex to use as a color - sb.setSpan(new ForegroundColorSpan(Color.rgb(r, g, b)), - i, spanEnd, Spannable.SPAN_INCLUSIVE_INCLUSIVE); - } - } catch (Exception e) { - Log.e(Constants.TAG, "Colorization failed", e); - // if anything goes wrong, then just display the fingerprint without colour, - // instead of partially correct colour or wrong colours - return new SpannableStringBuilder(fingerprint); - } - - return sb; - } - - /** - * Converts the given bytes to a unique RGB color using SHA1 algorithm - * - * @param bytes - * @return an integer array containing 3 numeric color representations (Red, Green, Black) - * @throws NoSuchAlgorithmException - * @throws DigestException - */ - public static int[] getRgbForData(byte[] bytes) throws NoSuchAlgorithmException, DigestException { - MessageDigest md = MessageDigest.getInstance("SHA1"); - - md.update(bytes); - byte[] digest = md.digest(); - - int[] result = {((int) digest[0] + 256) % 256, - ((int) digest[1] + 256) % 256, - ((int) digest[2] + 256) % 256}; - return result; - } - } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index 09c0b2d8f..35412d4b8 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -18,6 +18,11 @@ package org.sufficientlysecure.keychain.pgp; import android.content.Context; +import android.graphics.Color; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.style.ForegroundColorSpan; + import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.openpgp.*; import org.spongycastle.util.encoders.Hex; @@ -27,6 +32,9 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; +import java.security.DigestException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -501,6 +509,84 @@ public class PgpKeyHelper { return hexString; } + + public static SpannableStringBuilder colorizeFingerprint(String fingerprint) { + SpannableStringBuilder sb = new SpannableStringBuilder(fingerprint); + try { + // for each 4 characters of the fingerprint + 1 space + for (int i = 0; i < fingerprint.length(); i += 5) { + int spanEnd = Math.min(i + 4, fingerprint.length()); + String fourChars = fingerprint.substring(i, spanEnd); + + int raw = Integer.parseInt(fourChars, 16); + byte[] bytes = {(byte) ((raw >> 8) & 0xff - 128), (byte) (raw & 0xff - 128)}; + int[] color = getRgbForData(bytes); + int r = color[0]; + int g = color[1]; + int b = color[2]; + + // we cannot change black by multiplication, so adjust it to an almost-black grey, + // which will then be brightened to the minimal brightness level + if (r == 0 && g == 0 && b == 0) { + r = 1; + g = 1; + b = 1; + } + + // Convert rgb to brightness + double brightness = 0.2126 * r + 0.7152 * g + 0.0722 * b; + + // If a color is too dark to be seen on black, + // then brighten it up to a minimal brightness. + if (brightness < 80) { + double factor = 80.0 / brightness; + r = Math.min(255, (int) (r * factor)); + g = Math.min(255, (int) (g * factor)); + b = Math.min(255, (int) (b * factor)); + + // If it is too light, then darken it to a respective maximal brightness. + } else if (brightness > 180) { + double factor = 180.0 / brightness; + r = (int) (r * factor); + g = (int) (g * factor); + b = (int) (b * factor); + } + + // Create a foreground color with the 3 digest integers as RGB + // and then converting that int to hex to use as a color + sb.setSpan(new ForegroundColorSpan(Color.rgb(r, g, b)), + i, spanEnd, Spannable.SPAN_INCLUSIVE_INCLUSIVE); + } + } catch (Exception e) { + Log.e(Constants.TAG, "Colorization failed", e); + // if anything goes wrong, then just display the fingerprint without colour, + // instead of partially correct colour or wrong colours + return new SpannableStringBuilder(fingerprint); + } + + return sb; + } + + /** + * Converts the given bytes to a unique RGB color using SHA1 algorithm + * + * @param bytes + * @return an integer array containing 3 numeric color representations (Red, Green, Black) + * @throws java.security.NoSuchAlgorithmException + * @throws java.security.DigestException + */ + private static int[] getRgbForData(byte[] bytes) throws NoSuchAlgorithmException, DigestException { + MessageDigest md = MessageDigest.getInstance("SHA1"); + + md.update(bytes); + byte[] digest = md.digest(); + + int[] result = {((int) digest[0] + 256) % 256, + ((int) digest[1] + 256) % 256, + ((int) digest[2] + 256) % 256}; + return result; + } + /** * Splits userId string into naming part, email part, and comment part * diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java index bd14369b0..0983d54fb 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java @@ -211,7 +211,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements fingerprintBlob = ProviderHelper.getFingerprint(this, mDataUri); } String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true); - ((TextView) findViewById(R.id.fingerprint)).setText(OtherHelper.colorizeFingerprint(fingerprint)); + ((TextView) findViewById(R.id.fingerprint)).setText(PgpKeyHelper.colorizeFingerprint(fingerprint)); } break; case LOADER_ID_USER_IDS: diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index e140cb21e..7beef4b5e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -322,7 +322,7 @@ public class ViewKeyMainFragment extends Fragment implements } String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true); - mFingerprint.setText(OtherHelper.colorizeFingerprint(fingerprint)); + mFingerprint.setText(PgpKeyHelper.colorizeFingerprint(fingerprint)); } mKeysAdapter.swapCursor(data); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java index 57aaf0733..7d3166af9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysAdapter.java @@ -138,7 +138,7 @@ public class ImportKeysAdapter extends ArrayAdapter { holder.keyId.setText(entry.keyIdHex); if (entry.fingerPrintHex != null) { - holder.fingerprint.setText(OtherHelper.colorizeFingerprint(entry.fingerPrintHex)); + holder.fingerprint.setText(PgpKeyHelper.colorizeFingerprint(entry.fingerPrintHex)); holder.fingerprint.setVisibility(View.VISIBLE); } else { holder.fingerprint.setVisibility(View.GONE); From a17481b80a2d717afdfc981c5c7e7528d1a7abca Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Thu, 20 Mar 2014 18:33:19 +0100 Subject: [PATCH 080/253] Group fingerprint again to make it visually consistent --- .../org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index 35412d4b8..ec0932773 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -511,6 +511,11 @@ public class PgpKeyHelper { public static SpannableStringBuilder colorizeFingerprint(String fingerprint) { + // add line breaks to have a consistent "image" that can be recognized + char[] chars = fingerprint.toCharArray(); + chars[24] = '\n'; + fingerprint = String.valueOf(chars); + SpannableStringBuilder sb = new SpannableStringBuilder(fingerprint); try { // for each 4 characters of the fingerprint + 1 space From 602fb654af521c7a892ea0c8557a74d8e9cffeda Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Thu, 20 Mar 2014 18:33:46 +0100 Subject: [PATCH 081/253] Move fingerprint to the right in key list entry Fits much better there and doesn't separate the user ID. --- .../main/res/layout/import_keys_list_entry.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml b/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml index fb092793b..724f538dd 100644 --- a/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml +++ b/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml @@ -51,13 +51,6 @@ android:text="@string/label_main_user_id" android:textAppearance="?android:attr/textAppearanceMedium" /> - - + + - \ No newline at end of file + From f5ecd2ae466837f24f4a9614240fd9eb4a6fb536 Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Thu, 20 Mar 2014 18:34:40 +0100 Subject: [PATCH 082/253] Move fingerprint up below the algorithm in key view This makes it consistent with the key list entry layout and it also makes more sense, as it belongs to key identification. --- .../res/layout/view_key_main_fragment.xml | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml index 6ef3f3072..adbdb98dd 100644 --- a/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml +++ b/OpenPGP-Keychain/src/main/res/layout/view_key_main_fragment.xml @@ -130,6 +130,25 @@ android:text="" /> + + + + + + + - - - - - - - - \ No newline at end of file + From 0a8b45ee88b9dcdc7a2c6e067fe65f815641b8ee Mon Sep 17 00:00:00 2001 From: Nikhil Peter Raj Date: Thu, 20 Mar 2014 23:37:29 +0530 Subject: [PATCH 083/253] Fix for #451 --- .../ui/SelectSecretKeyLayoutFragment.java | 158 +++++++++++------- 1 file changed, 93 insertions(+), 65 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java index 960b4aafb..5b441f17b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java @@ -19,22 +19,24 @@ package org.sufficientlysecure.keychain.ui; import android.app.Activity; import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; 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.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.TextView; import com.beardedhen.androidbootstrap.BootstrapButton; -import org.spongycastle.openpgp.PGPSecretKey; -import org.spongycastle.openpgp.PGPSecretKeyRing; -import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; -import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; -public class SelectSecretKeyLayoutFragment extends Fragment { +public class SelectSecretKeyLayoutFragment extends Fragment implements LoaderManager.LoaderCallbacks { private TextView mKeyUserId; private TextView mKeyUserIdRest; @@ -43,12 +45,25 @@ public class SelectSecretKeyLayoutFragment extends Fragment { private BootstrapButton mSelectKeyButton; private Boolean mFilterCertify; + private Uri mReceivedUri = null; + private SelectSecretKeyCallback mCallback; private static final int REQUEST_CODE_SELECT_KEY = 8882; + //Loader ID needs to be different from the usual 0 + private static final int LOADER_ID = 2; + + //The Projection we will retrieve, Master Key ID is for convenience sake, + //to avoid having to pass the Key Around + final String[] PROJECTION = new String[]{KeychainContract.UserIds.USER_ID + , KeychainContract.KeyRings.MASTER_KEY_ID}; + final int INDEX_USER_ID = 0; + final int INDEX_MASTER_KEY_ID = 1; + public interface SelectSecretKeyCallback { void onKeySelected(long secretKeyId); + } public void setCallback(SelectSecretKeyCallback callback) { @@ -59,63 +74,25 @@ public class SelectSecretKeyLayoutFragment extends Fragment { mFilterCertify = filterCertify; } - public void selectKey(long secretKeyId) { - if (secretKeyId == Id.key.none) { - mNoKeySelected.setVisibility(View.VISIBLE); - mKeyUserId.setVisibility(View.GONE); - mKeyUserIdRest.setVisibility(View.GONE); - mKeyMasterKeyIdHex.setVisibility(View.GONE); + public void setNoKeySelected() { + mNoKeySelected.setVisibility(View.VISIBLE); + mKeyUserId.setVisibility(View.GONE); + mKeyUserIdRest.setVisibility(View.GONE); + mKeyMasterKeyIdHex.setVisibility(View.GONE); + } - } else { - PGPSecretKeyRing keyRing = ProviderHelper.getPGPSecretKeyRingByMasterKeyId( - getActivity(), secretKeyId); - if (keyRing != null) { - PGPSecretKey key = PgpKeyHelper.getMasterKey(keyRing); - String masterkeyIdHex = PgpKeyHelper.convertKeyIdToHexShort(secretKeyId); + public void setSelectedKeyData(String userName, String email, String masterKeyHex) { - if (key != null) { - String userId = PgpKeyHelper.getMainUserIdSafe(getActivity(), key); + mNoKeySelected.setVisibility(View.GONE); - String[] userIdSplit = PgpKeyHelper.splitUserId(userId); - String userName, userEmail; + mKeyUserId.setText(userName); + mKeyUserIdRest.setText(email); + mKeyMasterKeyIdHex.setText(masterKeyHex); - if (userIdSplit[0] != null) { - userName = userIdSplit[0]; - } else { - userName = getActivity().getResources().getString(R.string.user_id_no_name); - } + mKeyUserId.setVisibility(View.VISIBLE); + mKeyUserIdRest.setVisibility(View.VISIBLE); + mKeyMasterKeyIdHex.setVisibility(View.VISIBLE); - if (userIdSplit[1] != null) { - userEmail = userIdSplit[1]; - } else { - userEmail = getActivity().getResources().getString(R.string.error_user_id_no_email); - } - - mKeyMasterKeyIdHex.setText(masterkeyIdHex); - mKeyUserId.setText(userName); - mKeyUserIdRest.setText(userEmail); - mKeyMasterKeyIdHex.setVisibility(View.VISIBLE); - mKeyUserId.setVisibility(View.VISIBLE); - mKeyUserIdRest.setVisibility(View.VISIBLE); - mNoKeySelected.setVisibility(View.GONE); - } else { - mKeyMasterKeyIdHex.setVisibility(View.GONE); - mKeyUserId.setVisibility(View.GONE); - mKeyUserIdRest.setVisibility(View.GONE); - mNoKeySelected.setVisibility(View.VISIBLE); - } - } else { - mKeyMasterKeyIdHex.setText( - getActivity().getResources() - .getString(R.string.no_keys_added_or_updated) - + " for master id: " + secretKeyId); - mKeyMasterKeyIdHex.setVisibility(View.VISIBLE); - mKeyUserId.setVisibility(View.GONE); - mKeyUserIdRest.setVisibility(View.GONE); - mNoKeySelected.setVisibility(View.GONE); - } - - } } public void setError(String error) { @@ -147,29 +124,80 @@ public class SelectSecretKeyLayoutFragment extends Fragment { return view; } + //For AppSettingsFragment + public void selectKey(long masterKeyId){ + Uri buildUri = KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(String.valueOf(masterKeyId)); + mReceivedUri=buildUri; + getActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this); + } + private void startSelectKeyActivity() { Intent intent = new Intent(getActivity(), SelectSecretKeyActivity.class); intent.putExtra(SelectSecretKeyActivity.EXTRA_FILTER_CERTIFY, mFilterCertify); startActivityForResult(intent, REQUEST_CODE_SELECT_KEY); } + @Override + public Loader onCreateLoader(int id, Bundle args) { + //We don't care about the Loader id + return new CursorLoader(getActivity(), mReceivedUri, PROJECTION, null, null, null); + } + + @Override + public void onLoadFinished(Loader loader, Cursor data) { + if (data.moveToFirst()) { + String userName, email, masterKeyHex; + String userID = data.getString(INDEX_USER_ID); + long masterKeyID = data.getLong(INDEX_MASTER_KEY_ID); + + String splitUserID[] = PgpKeyHelper.splitUserId(userID); + + if (splitUserID[0] != null) { + userName = splitUserID[0]; + } else { + userName = getActivity().getResources().getString(R.string.user_id_no_name); + } + + if (splitUserID[1] != null) { + email = splitUserID[1]; + } else { + email = getActivity().getResources().getString(R.string.error_user_id_no_email); + } + + //TODO Can the cursor return invalid values for the Master Key ? + masterKeyHex = PgpKeyHelper.convertKeyIdToHexShort(masterKeyID); + + //Set the data + setSelectedKeyData(userName, email, masterKeyHex); + + //Give value to the callback + mCallback.onKeySelected(masterKeyID); + } else { + //Set The empty View + setNoKeySelected(); + } + + } + + @Override + public void onLoaderReset(Loader loader) { + return; + } + // Select Secret Key Activity delivers the intent which was sent by it using interface to Select - // Secret Key Fragment.Intent contains Master Key Id, User Email, User Name, Master Key Id Hex. + // Secret Key Fragment.Intent contains the passed Uri @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode & 0xFFFF) { case REQUEST_CODE_SELECT_KEY: { - long secretKeyId; if (resultCode == Activity.RESULT_OK) { - Bundle bundle = data.getExtras(); - secretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID); - selectKey(secretKeyId); + mReceivedUri = data.getData(); + + //Must be restartLoader() or the data will not be updated on selecting a new key + getActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this); - // remove displayed errors mKeyUserId.setError(null); - // give value back to callback - mCallback.onKeySelected(secretKeyId); } break; } From 0510e0e217f5b7bab548dccf7bdd75f2bd5c0463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 20 Mar 2014 19:11:51 +0100 Subject: [PATCH 084/253] verify downloaded key by comparing fingerprints --- .../service/KeychainIntentService.java | 74 +++++++++++-------- 1 file changed, 42 insertions(+), 32 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 5af81d39d..19358adc9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -25,6 +25,7 @@ import android.os.Bundle; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; + import org.spongycastle.openpgp.*; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; @@ -692,11 +693,11 @@ public class KeychainIntentService extends IntentService for (long masterKeyId : masterKeyIds) { if ((keyType == Id.type.public_key || keyType == Id.type.public_secret_key) - && allPublicMasterKeyIds.contains(masterKeyId)) { + && allPublicMasterKeyIds.contains(masterKeyId)) { publicMasterKeyIds.add(masterKeyId); } if ((keyType == Id.type.secret_key || keyType == Id.type.public_secret_key) - && allSecretMasterKeyIds.contains(masterKeyId)) { + && allSecretMasterKeyIds.contains(masterKeyId)) { secretMasterKeyIds.add(masterKeyId); } } @@ -745,49 +746,58 @@ public class KeychainIntentService extends IntentService ArrayList entries = data.getParcelableArrayList(DOWNLOAD_KEY_LIST); String keyServer = data.getString(DOWNLOAD_KEY_SERVER); + // TODO: add extra which requires fingerprint suport and force verification! + // only supported by newer sks keyserver versions + // this downloads the keys and places them into the ImportKeysListEntry entries HkpKeyServer server = new HkpKeyServer(keyServer); for (ImportKeysListEntry entry : entries) { - byte[] downloadedKey = server.get(entry.getKeyIdHex()).getBytes(); + // if available use complete fingerprint for get request + byte[] downloadedKeyBytes; + if (entry.getFingerPrintHex() != null) { + downloadedKeyBytes = server.get(entry.getFingerPrintHex()).getBytes(); + } else { + downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes(); + } - /** - * TODO: copied from ImportKeysListLoader - * - * - * this parses the downloaded key - */ - // need to have access to the bufferedInput, so we can reuse it for the possible - // PGPObject chunks after the first one, e.g. files with several consecutive ASCII - // armor blocks + // create PGPKeyRing object based on downloaded armored key + PGPKeyRing downloadedKey = null; BufferedInputStream bufferedInput = - new BufferedInputStream(new ByteArrayInputStream(downloadedKey)); - try { + new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes)); + if (bufferedInput.available() > 0) { + InputStream in = PGPUtil.getDecoderStream(bufferedInput); + PGPObjectFactory objectFactory = new PGPObjectFactory(in); - // read all available blocks... (asc files can contain many blocks with BEGIN END) - while (bufferedInput.available() > 0) { - InputStream in = PGPUtil.getDecoderStream(bufferedInput); - PGPObjectFactory objectFactory = new PGPObjectFactory(in); + // get first object in block + Object obj; + if ((obj = objectFactory.nextObject()) != null) { + Log.d(Constants.TAG, "Found class: " + obj.getClass()); - // go through all objects in this block - Object obj; - while ((obj = objectFactory.nextObject()) != null) { - Log.d(Constants.TAG, "Found class: " + obj.getClass()); - - if (obj instanceof PGPKeyRing) { - PGPKeyRing newKeyring = (PGPKeyRing) obj; - - entry.setBytes(newKeyring.getEncoded()); - } else { - Log.e(Constants.TAG, "Object not recognized as PGPKeyRing!"); - } + if (obj instanceof PGPKeyRing) { + downloadedKey = (PGPKeyRing) obj; + } else { + throw new PgpGeneralException("Object not recognized as PGPKeyRing!"); } } - } catch (Exception e) { - Log.e(Constants.TAG, "Exception on parsing key file!", e); } + + // verify downloaded key by comparing fingerprints + if (entry.getFingerPrintHex() != null) { + String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex(downloadedKey.getPublicKey().getFingerprint(), false); + if (downloadedKeyFp.equals(entry.getFingerPrintHex())) { + Log.d(Constants.TAG, "fingerprint of downloaded key is the same as the requested fingerprint!"); + } else { + throw new PgpGeneralException("fingerprint of downloaded key is NOT the same as the requested fingerprint!"); + } + } + + // save key bytes in entry object for doing the + // actual import afterwards + entry.setBytes(downloadedKey.getEncoded()); } + Intent importIntent = new Intent(this, KeychainIntentService.class); importIntent.setAction(ACTION_IMPORT_KEYRING); Bundle importData = new Bundle(); From 016641a422ddff652b697a76d7ed59ab9954239e Mon Sep 17 00:00:00 2001 From: Nikhil Peter Raj Date: Thu, 20 Mar 2014 23:46:26 +0530 Subject: [PATCH 085/253] Cleanup for #451 --- .../keychain/ui/SelectSecretKeyActivity.java | 4 ---- .../keychain/ui/SelectSecretKeyLayoutFragment.java | 1 - 2 files changed, 5 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java index 82a3c2e8e..0ff88d97c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyActivity.java @@ -76,10 +76,6 @@ public class SelectSecretKeyActivity extends ActionBarActivity { Intent data = new Intent(); data.setData(selectedUri); - // TODO: deprecate RESULT_EXTRA_MASTER_KEY_ID! - long masterKeyId = Long.valueOf(selectedUri.getLastPathSegment()); - data.putExtra(RESULT_EXTRA_MASTER_KEY_ID, masterKeyId); - setResult(RESULT_OK, data); finish(); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java index 5b441f17b..41e250bb7 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java @@ -63,7 +63,6 @@ public class SelectSecretKeyLayoutFragment extends Fragment implements LoaderMan public interface SelectSecretKeyCallback { void onKeySelected(long secretKeyId); - } public void setCallback(SelectSecretKeyCallback callback) { From 59d51fe68eb6502c313c6a74d4a1e04d0e959b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Thu, 20 Mar 2014 19:48:18 +0100 Subject: [PATCH 086/253] Fix fingerprint handling --- .../keychain/pgp/PgpKeyHelper.java | 14 +++++--------- .../keychain/service/KeychainIntentService.java | 4 ++-- .../keychain/ui/CertifyKeyActivity.java | 4 +++- .../keychain/ui/ViewKeyActivity.java | 2 +- .../keychain/ui/ViewKeyMainFragment.java | 2 +- .../keychain/ui/adapter/ImportKeysListEntry.java | 2 +- .../ui/dialog/ShareQrCodeDialogFragment.java | 2 +- .../keychain/util/HkpKeyServer.java | 3 +-- .../src/main/res/layout/import_keys_list_entry.xml | 7 ++++--- 9 files changed, 19 insertions(+), 21 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java index ec0932773..b7db92b9b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyHelper.java @@ -452,7 +452,7 @@ public class PgpKeyHelper { key = secretKey.getPublicKey(); } - return convertFingerprintToHex(key.getFingerprint(), true); + return convertFingerprintToHex(key.getFingerprint()); } /** @@ -465,19 +465,12 @@ public class PgpKeyHelper { * @param split split into 4 character chunks * @return */ - public static String convertFingerprintToHex(byte[] fingerprint, boolean split) { + public static String convertFingerprintToHex(byte[] fingerprint) { String hexString = Hex.toHexString(fingerprint); - if (split) { - hexString = splitFingerprintHex(hexString); - } return hexString; } - public static String splitFingerprintHex(String hexString) { - return hexString.replaceAll("(.{4})(?!$)", "$1 "); - } - /** * Convert key id from long to 64 bit hex string *

@@ -511,6 +504,9 @@ public class PgpKeyHelper { public static SpannableStringBuilder colorizeFingerprint(String fingerprint) { + // split by 4 characters + fingerprint = fingerprint.replaceAll("(.{4})(?!$)", "$1 "); + // add line breaks to have a consistent "image" that can be recognized char[] chars = fingerprint.toCharArray(); chars[24] = '\n'; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 19358adc9..0751fa33c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -756,7 +756,7 @@ public class KeychainIntentService extends IntentService // if available use complete fingerprint for get request byte[] downloadedKeyBytes; if (entry.getFingerPrintHex() != null) { - downloadedKeyBytes = server.get(entry.getFingerPrintHex()).getBytes(); + downloadedKeyBytes = server.get("0x" + entry.getFingerPrintHex()).getBytes(); } else { downloadedKeyBytes = server.get(entry.getKeyIdHex()).getBytes(); } @@ -784,7 +784,7 @@ public class KeychainIntentService extends IntentService // verify downloaded key by comparing fingerprints if (entry.getFingerPrintHex() != null) { - String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex(downloadedKey.getPublicKey().getFingerprint(), false); + String downloadedKeyFp = PgpKeyHelper.convertFingerprintToHex(downloadedKey.getPublicKey().getFingerprint()); if (downloadedKeyFp.equals(entry.getFingerPrintHex())) { Log.d(Constants.TAG, "fingerprint of downloaded key is the same as the requested fingerprint!"); } else { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java index 0983d54fb..dff4e9d72 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java @@ -198,6 +198,8 @@ public class CertifyKeyActivity extends ActionBarActivity implements case LOADER_ID_KEYRING: // the first key here is our master key if (data.moveToFirst()) { + // TODO: put findViewById in onCreate! + long keyId = data.getLong(INDEX_MASTER_KEY_ID); String keyIdStr = PgpKeyHelper.convertKeyIdToHexShort(keyId); ((TextView) findViewById(R.id.key_id)).setText(keyIdStr); @@ -210,7 +212,7 @@ public class CertifyKeyActivity extends ActionBarActivity implements // FALLBACK for old database entries fingerprintBlob = ProviderHelper.getFingerprint(this, mDataUri); } - String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true); + String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob); ((TextView) findViewById(R.id.fingerprint)).setText(PgpKeyHelper.colorizeFingerprint(fingerprint)); } break; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index c4097403c..41bd95db3 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -182,7 +182,7 @@ public class ViewKeyActivity extends ActionBarActivity { String content; if (fingerprintOnly) { byte[] fingerprintBlob = ProviderHelper.getFingerprint(this, dataUri); - String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false); + String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob); content = Constants.FINGERPRINT_SCHEME + ":" + fingerprint; } else { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index 7beef4b5e..dd4e7fa94 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -320,7 +320,7 @@ public class ViewKeyMainFragment extends Fragment implements // FALLBACK for old database entries fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), mDataUri); } - String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, true); + String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob); mFingerprint.setText(PgpKeyHelper.colorizeFingerprint(fingerprint)); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java index 2610dfb3e..9b20effc2 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ImportKeysListEntry.java @@ -240,7 +240,7 @@ public class ImportKeysListEntry implements Serializable, Parcelable { this.revoked = pgpKeyRing.getPublicKey().isRevoked(); this.fingerPrintHex = PgpKeyHelper.convertFingerprintToHex(pgpKeyRing.getPublicKey() - .getFingerprint(), true); + .getFingerprint()); this.bitStrength = pgpKeyRing.getPublicKey().getBitStrength(); final int algorithm = pgpKeyRing.getPublicKey().getAlgorithm(); this.algorithm = getAlgorithmFromId(algorithm); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java index b501ba230..94586810e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/ShareQrCodeDialogFragment.java @@ -90,7 +90,7 @@ public class ShareQrCodeDialogFragment extends DialogFragment { alert.setPositiveButton(R.string.btn_okay, null); byte[] fingerprintBlob = ProviderHelper.getFingerprint(getActivity(), dataUri); - String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob, false); + String fingerprint = PgpKeyHelper.convertFingerprintToHex(fingerprintBlob); mText.setText(getString(R.string.share_qr_code_dialog_fingerprint_text) + " " + fingerprint); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java index a25e3d748..b987e1533 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/util/HkpKeyServer.java @@ -251,8 +251,7 @@ public class HkpKeyServer extends KeyServer { // and https://github.com/openpgp-keychain/openpgp-keychain/issues/259#issuecomment-38168176 String fingerprintOrKeyId = matcher.group(1); if (fingerprintOrKeyId.length() > 16) { - entry.setFingerPrintHex(PgpKeyHelper.splitFingerprintHex( - fingerprintOrKeyId.toLowerCase(Locale.US))); + entry.setFingerPrintHex(fingerprintOrKeyId.toLowerCase(Locale.US)); entry.setKeyIdHex("0x" + fingerprintOrKeyId.substring(fingerprintOrKeyId.length() - 16, fingerprintOrKeyId.length())); } else { diff --git a/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml b/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml index 724f538dd..f5a39f115 100644 --- a/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml +++ b/OpenPGP-Keychain/src/main/res/layout/import_keys_list_entry.xml @@ -15,7 +15,7 @@ limitations under the License. --> @@ -87,6 +87,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="fingerprint" + android:typeface="monospace" android:textAppearance="?android:attr/textAppearanceSmall" /> From d9b909a24f4b32225027451062ed6304b1cf9a2e Mon Sep 17 00:00:00 2001 From: Nikhil Peter Raj Date: Fri, 21 Mar 2014 00:27:03 +0530 Subject: [PATCH 087/253] Update fix for #451 --- .../keychain/ui/SelectSecretKeyLayoutFragment.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java index 41e250bb7..2fcfc9650 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java @@ -50,9 +50,8 @@ public class SelectSecretKeyLayoutFragment extends Fragment implements LoaderMan private SelectSecretKeyCallback mCallback; private static final int REQUEST_CODE_SELECT_KEY = 8882; - - //Loader ID needs to be different from the usual 0 - private static final int LOADER_ID = 2; + + private static final int LOADER_ID = 0; //The Projection we will retrieve, Master Key ID is for convenience sake, //to avoid having to pass the Key Around From 2816461283c0067f524d94ddf9702e119025c9d3 Mon Sep 17 00:00:00 2001 From: Nikhil Peter Raj Date: Fri, 21 Mar 2014 01:50:20 +0530 Subject: [PATCH 088/253] Fix for #410 --- .../keychain/helper/ExportHelper.java | 4 +- .../keychain/provider/ProviderHelper.java | 23 +- .../keychain/ui/EditKeyActivity.java | 14 +- .../keychain/ui/KeyListFragment.java | 22 +- .../keychain/ui/ViewKeyActivity.java | 19 +- .../ui/dialog/DeleteKeyDialogFragment.java | 203 ++++++++++-------- .../res/layout/view_key_delete_fragment.xml | 38 ++++ .../src/main/res/values/strings.xml | 2 + 8 files changed, 185 insertions(+), 140 deletions(-) create mode 100644 OpenPGP-Keychain/src/main/res/layout/view_key_delete_fragment.xml diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java index 03cf936ee..2bfa796c7 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java @@ -53,14 +53,14 @@ public class ExportHelper { this.mActivity = activity; } - public void deleteKey(Uri dataUri, final int keyType, Handler deleteHandler) { + public void deleteKey(Uri dataUri, Handler deleteHandler) { long keyRingRowId = Long.valueOf(dataUri.getLastPathSegment()); // Create a new Messenger for the communication back Messenger messenger = new Messenger(deleteHandler); DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, - new long[]{keyRingRowId}, keyType); + new long[]{keyRingRowId}); deleteKeyDialog.show(mActivity.getSupportFragmentManager(), "deleteKeyDialog"); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index db2c57207..8b6feeafd 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Dominik Schürmann + * Copyright (C) 2012-2013 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 @@ -230,7 +230,7 @@ public class ProviderHelper { // get current _ID of key long currentRowId = -1; Cursor oldQuery = context.getContentResolver() - .query(deleteUri, new String[]{KeyRings._ID}, null, null, null); + .query(deleteUri, new String[]{KeyRings._ID}, null, null, null); if (oldQuery != null && oldQuery.moveToFirst()) { currentRowId = oldQuery.getLong(0); } else { @@ -288,7 +288,7 @@ public class ProviderHelper { * Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing */ private static ContentProviderOperation buildPublicKeyOperations(Context context, - long keyRingRowId, PGPPublicKey key, int rank) throws IOException { + long keyRingRowId, PGPPublicKey key, int rank) throws IOException { ContentValues values = new ContentValues(); values.put(Keys.KEY_ID, key.getKeyID()); values.put(Keys.IS_MASTER_KEY, key.isMasterKey()); @@ -316,7 +316,7 @@ public class ProviderHelper { * Build ContentProviderOperation to add PublicUserIds to database corresponding to a keyRing */ private static ContentProviderOperation buildPublicUserIdOperations(Context context, - long keyRingRowId, String userId, int rank) { + long keyRingRowId, String userId, int rank) { ContentValues values = new ContentValues(); values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId); values.put(UserIds.USER_ID, userId); @@ -331,7 +331,7 @@ public class ProviderHelper { * Build ContentProviderOperation to add PGPSecretKey to database corresponding to a keyRing */ private static ContentProviderOperation buildSecretKeyOperations(Context context, - long keyRingRowId, PGPSecretKey key, int rank) throws IOException { + long keyRingRowId, PGPSecretKey key, int rank) throws IOException { ContentValues values = new ContentValues(); boolean hasPrivate = true; @@ -368,7 +368,7 @@ public class ProviderHelper { * Build ContentProviderOperation to add SecretUserIds to database corresponding to a keyRing */ private static ContentProviderOperation buildSecretUserIdOperations(Context context, - long keyRingRowId, String userId, int rank) { + long keyRingRowId, String userId, int rank) { ContentValues values = new ContentValues(); values.put(UserIds.KEY_RING_ROW_ID, keyRingRowId); values.put(UserIds.USER_ID, userId); @@ -469,6 +469,15 @@ public class ProviderHelper { cr.delete(KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)), null, null); } + public static void deleteUnifiedKeyRing(Context context,String masterKeyId,boolean isSecretKey){ + ContentResolver cr= context.getContentResolver(); + cr.delete(KeyRings.buildPublicKeyRingsByMasterKeyIdUri(masterKeyId),null,null); + if(isSecretKey){ + cr.delete(KeyRings.buildSecretKeyRingsByMasterKeyIdUri(masterKeyId),null,null); + } + + } + /** * Get master key id of keyring by its row id */ @@ -855,4 +864,4 @@ public class ProviderHelper { return signature; } -} +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 31804719f..654942db7 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Dominik Schürmann + * Copyright (C) 2012-2013 Dominik Schürmann * Copyright (C) 2010 Thialfihar * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -46,6 +46,7 @@ import org.sufficientlysecure.keychain.helper.ExportHelper; import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.service.KeychainIntentService; import org.sufficientlysecure.keychain.service.KeychainIntentServiceHandler; @@ -58,7 +59,6 @@ import org.sufficientlysecure.keychain.ui.widget.SectionView; import org.sufficientlysecure.keychain.ui.widget.UserIdEditor; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; - import java.util.ArrayList; import java.util.GregorianCalendar; import java.util.Vector; @@ -325,9 +325,13 @@ public class EditKeyActivity extends ActionBarActivity { long masterKeyId = ProviderHelper.getMasterKeyId(this, mDataUri); long[] ids = new long[]{masterKeyId}; mExportHelper.showExportKeysDialog(ids, Id.type.secret_key, Constants.Path.APP_DIR_FILE_SEC, - null); + null); return true; case R.id.menu_key_edit_delete: { + //Convert the uri to one based on rowId + long rowId= ProviderHelper.getRowId(this,mDataUri); + Uri convertUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)); + // Message is received after key is deleted Handler returnHandler = new Handler() { @Override @@ -339,7 +343,7 @@ public class EditKeyActivity extends ActionBarActivity { } }; - mExportHelper.deleteKey(mDataUri, Id.type.secret_key, returnHandler); + mExportHelper.deleteKey(convertUri, returnHandler); return true; } } @@ -697,4 +701,4 @@ public class EditKeyActivity extends ActionBarActivity { : getString(R.string.btn_set_passphrase)); } -} +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index cac8b7046..daf455c03 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -355,25 +355,7 @@ public class KeyListFragment extends Fragment @Override public void handleMessage(Message message) { if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) { - Bundle returnData = message.getData(); - if (returnData != null - && returnData.containsKey(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED)) { - ArrayList notDeleted = - returnData.getStringArrayList(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED); - String notDeletedMsg = ""; - for (String userId : notDeleted) { - notDeletedMsg += userId + "\n"; - } - Toast.makeText(getActivity(), - getString(R.string.error_can_not_delete_contacts, notDeletedMsg) - + getResources() - .getQuantityString( - R.plurals.error_can_not_delete_info, - notDeleted.size()), - Toast.LENGTH_LONG).show(); - - mode.finish(); - } + mode.finish(); } } }; @@ -382,7 +364,7 @@ public class KeyListFragment extends Fragment Messenger messenger = new Messenger(returnHandler); DeleteKeyDialogFragment deleteKeyDialog = DeleteKeyDialogFragment.newInstance(messenger, - keyRingRowIds, Id.type.public_key); + keyRingRowIds); deleteKeyDialog.show(getActivity().getSupportFragmentManager(), "deleteKeyDialog"); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java index c4097403c..5e7cabcac 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyActivity.java @@ -237,25 +237,12 @@ public class ViewKeyActivity extends ActionBarActivity { Handler returnHandler = new Handler() { @Override public void handleMessage(Message message) { - if (message.what == DeleteKeyDialogFragment.MESSAGE_OKAY) { - Bundle returnData = message.getData(); - if (returnData != null - && returnData.containsKey(DeleteKeyDialogFragment.MESSAGE_NOT_DELETED)) { - // we delete only this key, so MESSAGE_NOT_DELETED will solely contain this key - Toast.makeText(ViewKeyActivity.this, - getString(R.string.error_can_not_delete_contact) - + getResources() - .getQuantityString(R.plurals.error_can_not_delete_info, 1), - Toast.LENGTH_LONG).show(); - } else { - setResult(RESULT_CANCELED); - finish(); - } - } + setResult(RESULT_CANCELED); + finish(); } }; - mExportHelper.deleteKey(dataUri, Id.type.public_key, returnHandler); + mExportHelper.deleteKey(dataUri, returnHandler); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java index 1bcf5b33c..76a69328a 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Dominik Schürmann + * Copyright (C) 2013-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 @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - package org.sufficientlysecure.keychain.ui.dialog; import android.app.AlertDialog; @@ -28,6 +27,11 @@ import android.os.Messenger; import android.os.RemoteException; import android.support.v4.app.DialogFragment; import android.support.v4.app.FragmentActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CheckBox; +import android.widget.LinearLayout; +import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; @@ -35,148 +39,167 @@ import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.util.Log; - import java.util.ArrayList; public class DeleteKeyDialogFragment extends DialogFragment { private static final String ARG_MESSENGER = "messenger"; private static final String ARG_DELETE_KEY_RING_ROW_IDS = "delete_file"; - private static final String ARG_KEY_TYPE = "key_type"; public static final int MESSAGE_OKAY = 1; + public static final int MESSAGE_ERROR = 0; - public static final String MESSAGE_NOT_DELETED = "not_deleted"; + private boolean isSingleSelection = false; + + private TextView mainMessage; + private CheckBox checkDeleteSecret; + private LinearLayout deleteSecretKeyView; + private View inflateView; private Messenger mMessenger; /** * Creates new instance of this delete file dialog fragment */ - public static DeleteKeyDialogFragment newInstance(Messenger messenger, long[] keyRingRowIds, - int keyType) { + public static DeleteKeyDialogFragment newInstance(Messenger messenger, long[] keyRingRowIds + ) { DeleteKeyDialogFragment frag = new DeleteKeyDialogFragment(); Bundle args = new Bundle(); args.putParcelable(ARG_MESSENGER, messenger); args.putLongArray(ARG_DELETE_KEY_RING_ROW_IDS, keyRingRowIds); - args.putInt(ARG_KEY_TYPE, keyType); + //We don't need the key type frag.setArguments(args); return frag; } - /** - * Creates dialog - */ + @Override public Dialog onCreateDialog(Bundle savedInstanceState) { + final FragmentActivity activity = getActivity(); mMessenger = getArguments().getParcelable(ARG_MESSENGER); final long[] keyRingRowIds = getArguments().getLongArray(ARG_DELETE_KEY_RING_ROW_IDS); - final int keyType = getArguments().getInt(ARG_KEY_TYPE); AlertDialog.Builder builder = new AlertDialog.Builder(activity); + + //Setup custom View to display in AlertDialog + LayoutInflater inflater = activity.getLayoutInflater(); + inflateView = inflater.inflate(R.layout.view_key_delete_fragment, null); + builder.setView(inflateView); + + deleteSecretKeyView = (LinearLayout) inflateView.findViewById(R.id.deleteSecretKeyView); + mainMessage = (TextView) inflateView.findViewById(R.id.mainMessage); + checkDeleteSecret = (CheckBox) inflateView.findViewById(R.id.checkDeleteSecret); + builder.setTitle(R.string.warning); + //If only a single key has been selected if (keyRingRowIds.length == 1) { Uri dataUri; - if (keyType == Id.type.public_key) { - dataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(String.valueOf(keyRingRowIds[0])); - } else { - dataUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(String.valueOf(keyRingRowIds[0])); - } - String userId = ProviderHelper.getUserId(activity, dataUri); + ArrayList publicKeyRings; //Any one will do + isSingleSelection = true; + + long selectedRow = keyRingRowIds[0]; + long keyType; + publicKeyRings = ProviderHelper.getPublicKeyRingsRowIds(activity); + + if (publicKeyRings.contains(selectedRow)) { + //TODO Should be a better method to do this other than getting all the KeyRings + dataUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(String.valueOf(selectedRow)); + keyType = Id.type.public_key; + } else { + dataUri = KeychainContract.KeyRings.buildSecretKeyRingsUri(String.valueOf(selectedRow)); + keyType = Id.type.secret_key; + } + + String userId = ProviderHelper.getUserId(activity, dataUri); + //Hide the Checkbox and TextView since this is a single selection,user will be notified thru message + deleteSecretKeyView.setVisibility(View.GONE); + //Set message depending on which key it is. + mainMessage.setText(getString(keyType == Id.type.secret_key ? R.string.secret_key_deletion_confirmation + : R.string.public_key_deletetion_confirmation, userId)); + - builder.setMessage(getString( - keyType == Id.type.public_key ? R.string.key_deletion_confirmation - : R.string.secret_key_deletion_confirmation, userId)); } else { - builder.setMessage(R.string.key_deletion_confirmation_multi); + deleteSecretKeyView.setVisibility(View.VISIBLE); + mainMessage.setText(R.string.key_deletion_confirmation_multi); } + builder.setIcon(R.drawable.ic_dialog_alert_holo_light); builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Uri queryUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri(); + String[] projection = new String[]{ + KeychainContract.KeyRings.MASTER_KEY_ID, // 0 + KeychainContract.KeyRings.TYPE// 1 + }; - @Override - public void onClick(DialogInterface dialog, int id) { - ArrayList notDeleted = new ArrayList(); - - if (keyType == Id.type.public_key) { - Uri queryUri = KeychainContract.KeyRings.buildPublicKeyRingsUri(); - String[] projection = new String[]{ - KeychainContract.KeyRings._ID, // 0 - KeychainContract.KeyRings.MASTER_KEY_ID, // 1 - KeychainContract.UserIds.USER_ID // 2 - }; - - // make selection with all entries where _ID is one of the given row ids - String selection = KeychainDatabase.Tables.KEY_RINGS + "." + - KeychainContract.KeyRings._ID + " IN("; - String selectionIDs = ""; - for (int i = 0; i < keyRingRowIds.length; i++) { - selectionIDs += "'" + String.valueOf(keyRingRowIds[i]) + "'"; - if (i + 1 < keyRingRowIds.length) { - selectionIDs += ","; + // make selection with all entries where _ID is one of the given row ids + String selection = KeychainDatabase.Tables.KEY_RINGS + "." + + KeychainContract.KeyRings._ID + " IN("; + String selectionIDs = ""; + for (int i = 0; i < keyRingRowIds.length; i++) { + selectionIDs += "'" + String.valueOf(keyRingRowIds[i]) + "'"; + if (i + 1 < keyRingRowIds.length) + selectionIDs += ","; } - } - selection += selectionIDs + ")"; + selection += selectionIDs + ")"; - Cursor cursor = activity.getContentResolver().query(queryUri, projection, - selection, null, null); + Cursor cursor = activity.getContentResolver().query(queryUri, projection, + selection, null, null); - long rowId; - long masterKeyId; - String userId; - try { - while (cursor != null && cursor.moveToNext()) { - rowId = cursor.getLong(0); - masterKeyId = cursor.getLong(1); - userId = cursor.getString(2); - Log.d(Constants.TAG, "rowId: " + rowId + ", masterKeyId: " + masterKeyId - + ", userId: " + userId); + long masterKeyId; + long keyType; + boolean isSuccessfullyDeleted; + try { + isSuccessfullyDeleted = false; + while (cursor != null && cursor.moveToNext()) { + masterKeyId = cursor.getLong(0); + keyType = cursor.getLong(1); - // check if a corresponding secret key exists... - Cursor secretCursor = activity.getContentResolver().query( - KeychainContract.KeyRings - .buildSecretKeyRingsByMasterKeyIdUri( - String.valueOf(masterKeyId)), - null, null, null, null - ); - if (secretCursor != null && secretCursor.getCount() > 0) { - notDeleted.add(userId); - } else { - // it is okay to delete this key, no secret key found! - ProviderHelper.deletePublicKeyRing(activity, rowId); + Log.d(Constants.TAG, "masterKeyId: " + masterKeyId + + ", keyType:" + (keyType == KeychainContract.KeyTypes.PUBLIC ? "Public" : "Private")); + + + if (keyType == KeychainContract.KeyTypes.SECRET) { + if (checkDeleteSecret.isChecked() || isSingleSelection) { + ProviderHelper.deleteUnifiedKeyRing(activity, String.valueOf(masterKeyId), true); + } + } else { + ProviderHelper.deleteUnifiedKeyRing(activity, String.valueOf(masterKeyId), false); + } } - if (secretCursor != null) { - secretCursor.close(); + + //Check if the selected rows have actually been deleted + cursor = activity.getContentResolver().query(queryUri, projection, selection, null, null); + if (cursor == null || cursor.getCount() == 0 || !checkDeleteSecret.isChecked()) { + isSuccessfullyDeleted = true; } + + } finally { + if (cursor != null) { + cursor.close(); + } + } - } finally { - if (cursor != null) { - cursor.close(); + + dismiss(); + + if (isSuccessfullyDeleted) { + sendMessageToHandler(MESSAGE_OKAY, null); + } else { + sendMessageToHandler(MESSAGE_ERROR, null); } } - } else { - for (long keyRowId : keyRingRowIds) { - ProviderHelper.deleteSecretKeyRing(activity, keyRowId); - } - } - dismiss(); - - if (notDeleted.size() > 0) { - Bundle data = new Bundle(); - data.putStringArrayList(MESSAGE_NOT_DELETED, notDeleted); - sendMessageToHandler(MESSAGE_OKAY, data); - } else { - sendMessageToHandler(MESSAGE_OKAY, null); } - } - }); + ); builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { @Override @@ -198,7 +221,6 @@ public class DeleteKeyDialogFragment extends DialogFragment { if (data != null) { msg.setData(data); } - try { mMessenger.send(msg); } catch (RemoteException e) { @@ -207,4 +229,5 @@ public class DeleteKeyDialogFragment extends DialogFragment { Log.w(Constants.TAG, "Messenger is null!", e); } } -} + +} \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_delete_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_delete_fragment.xml new file mode 100644 index 000000000..ef31f7690 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/view_key_delete_fragment.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index 9b4ab0747..6713153bf 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -220,6 +220,8 @@ Do you really want to delete the key \'%s\'?\nYou can\'t undo this! Do you really want to delete all selected keys?\nYou can\'t undo this! Do you really want to delete the SECRET key \'%s\'?\nYou can\'t undo this! + Do you really want to delete the PUBLIC key \'%s\'?\nYou can\'t undo this! + Delete Secret Keys ? Also export secret keys? From 4e6325a14068c173ed4372f60a3fae968b676e51 Mon Sep 17 00:00:00 2001 From: Nikhil Peter Raj Date: Fri, 21 Mar 2014 01:56:33 +0530 Subject: [PATCH 089/253] Fix GPL Headers --- .../sufficientlysecure/keychain/provider/ProviderHelper.java | 2 +- .../org/sufficientlysecure/keychain/ui/EditKeyActivity.java | 2 +- .../keychain/ui/dialog/DeleteKeyDialogFragment.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 8b6feeafd..2fa903fe2 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Dominik Schürmann + * Copyright (C) 2012-2013 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 diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 654942db7..9b64def1e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Dominik Schürmann + * Copyright (C) 2012-2013 Dominik Schürmann * Copyright (C) 2010 Thialfihar * * Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java index 76a69328a..a43debd07 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013-2014 Dominik Schürmann + * Copyright (C) 2013-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 From 2f0075e043daab6d59e38cdb110960895a06a2b4 Mon Sep 17 00:00:00 2001 From: Daniel Hammann Date: Thu, 20 Mar 2014 22:35:05 +0100 Subject: [PATCH 090/253] URI is transported in intents data not extra. --- .../org/sufficientlysecure/keychain/ui/EncryptActivity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java index 1231b6209..28c9c7b28 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -1002,8 +1002,8 @@ public class EncryptActivity extends DrawerActivity { case Id.request.secret_keys: { if (resultCode == RESULT_OK) { - Bundle bundle = data.getExtras(); - mSecretKeyId = bundle.getLong(SelectSecretKeyActivity.RESULT_EXTRA_MASTER_KEY_ID); + Uri uri_master_key = data.getData(); + mSecretKeyId = Long.valueOf(uri_master_key.getLastPathSegment()); } else { mSecretKeyId = Id.key.none; } From e00eeab1bfcda87e82c5ca51468cb4a5d12ae397 Mon Sep 17 00:00:00 2001 From: Daniel Hammann Date: Thu, 20 Mar 2014 22:41:12 +0100 Subject: [PATCH 091/253] UI-Icon-Modifying #429 added hint to text edit, different icons for share and clipboard button --- OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml | 7 ++++--- OpenPGP-Keychain/src/main/res/values/strings.xml | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml b/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml index d6a05f0d4..bb947fb5a 100644 --- a/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml +++ b/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml @@ -215,7 +215,8 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="top" - android:inputType="text|textCapSentences|textMultiLine|textLongMessage"/> + android:inputType="text|textCapSentences|textMultiLine|textLongMessage" + android:hint="@string/encrypt_content_edit_text_hint"/> User IDs to sign Reapplying certificates + + Write message here to encrypt… + From 620c67f4412fa56d7edfbfad3ace13f3e409ac5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 21 Mar 2014 10:05:59 +0100 Subject: [PATCH 092/253] reformat merge --- .../ui/SelectSecretKeyLayoutFragment.java | 8 +- .../ui/dialog/DeleteKeyDialogFragment.java | 115 +++++++++--------- 2 files changed, 64 insertions(+), 59 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java index 2fcfc9650..cbc0f4c5c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/SelectSecretKeyLayoutFragment.java @@ -31,7 +31,9 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.TextView; + import com.beardedhen.androidbootstrap.BootstrapButton; + import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract; @@ -50,7 +52,7 @@ public class SelectSecretKeyLayoutFragment extends Fragment implements LoaderMan private SelectSecretKeyCallback mCallback; private static final int REQUEST_CODE_SELECT_KEY = 8882; - + private static final int LOADER_ID = 0; //The Projection we will retrieve, Master Key ID is for convenience sake, @@ -123,9 +125,9 @@ public class SelectSecretKeyLayoutFragment extends Fragment implements LoaderMan } //For AppSettingsFragment - public void selectKey(long masterKeyId){ + public void selectKey(long masterKeyId) { Uri buildUri = KeychainContract.KeyRings.buildSecretKeyRingsByMasterKeyIdUri(String.valueOf(masterKeyId)); - mReceivedUri=buildUri; + mReceivedUri = buildUri; getActivity().getSupportLoaderManager().restartLoader(LOADER_ID, null, this); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java index a43debd07..3ff88aa2b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteKeyDialogFragment.java @@ -14,6 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package org.sufficientlysecure.keychain.ui.dialog; import android.app.AlertDialog; @@ -32,6 +33,7 @@ import android.view.View; import android.widget.CheckBox; import android.widget.LinearLayout; import android.widget.TextView; + import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; @@ -39,11 +41,12 @@ import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainDatabase; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.util.Log; + import java.util.ArrayList; public class DeleteKeyDialogFragment extends DialogFragment { private static final String ARG_MESSENGER = "messenger"; - private static final String ARG_DELETE_KEY_RING_ROW_IDS = "delete_file"; + private static final String ARG_DELETE_KEY_RING_ROW_IDS = "delete_key_ring_row_ids"; public static final int MESSAGE_OKAY = 1; public static final int MESSAGE_ERROR = 0; @@ -131,74 +134,74 @@ public class DeleteKeyDialogFragment extends DialogFragment { builder.setIcon(R.drawable.ic_dialog_alert_holo_light); builder.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - Uri queryUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri(); - String[] projection = new String[]{ - KeychainContract.KeyRings.MASTER_KEY_ID, // 0 - KeychainContract.KeyRings.TYPE// 1 - }; + @Override + public void onClick(DialogInterface dialog, int which) { + Uri queryUri = KeychainContract.KeyRings.buildUnifiedKeyRingsUri(); + String[] projection = new String[]{ + KeychainContract.KeyRings.MASTER_KEY_ID, // 0 + KeychainContract.KeyRings.TYPE// 1 + }; - // make selection with all entries where _ID is one of the given row ids - String selection = KeychainDatabase.Tables.KEY_RINGS + "." + - KeychainContract.KeyRings._ID + " IN("; - String selectionIDs = ""; - for (int i = 0; i < keyRingRowIds.length; i++) { - selectionIDs += "'" + String.valueOf(keyRingRowIds[i]) + "'"; - if (i + 1 < keyRingRowIds.length) - selectionIDs += ","; - } - selection += selectionIDs + ")"; + // make selection with all entries where _ID is one of the given row ids + String selection = KeychainDatabase.Tables.KEY_RINGS + "." + + KeychainContract.KeyRings._ID + " IN("; + String selectionIDs = ""; + for (int i = 0; i < keyRingRowIds.length; i++) { + selectionIDs += "'" + String.valueOf(keyRingRowIds[i]) + "'"; + if (i + 1 < keyRingRowIds.length) + selectionIDs += ","; + } + selection += selectionIDs + ")"; - Cursor cursor = activity.getContentResolver().query(queryUri, projection, - selection, null, null); + Cursor cursor = activity.getContentResolver().query(queryUri, projection, + selection, null, null); - long masterKeyId; - long keyType; - boolean isSuccessfullyDeleted; - try { - isSuccessfullyDeleted = false; - while (cursor != null && cursor.moveToNext()) { - masterKeyId = cursor.getLong(0); - keyType = cursor.getLong(1); + long masterKeyId; + long keyType; + boolean isSuccessfullyDeleted; + try { + isSuccessfullyDeleted = false; + while (cursor != null && cursor.moveToNext()) { + masterKeyId = cursor.getLong(0); + keyType = cursor.getLong(1); - Log.d(Constants.TAG, "masterKeyId: " + masterKeyId - + ", keyType:" + (keyType == KeychainContract.KeyTypes.PUBLIC ? "Public" : "Private")); + Log.d(Constants.TAG, "masterKeyId: " + masterKeyId + + ", keyType:" + (keyType == KeychainContract.KeyTypes.PUBLIC ? "Public" : "Private")); - if (keyType == KeychainContract.KeyTypes.SECRET) { - if (checkDeleteSecret.isChecked() || isSingleSelection) { - ProviderHelper.deleteUnifiedKeyRing(activity, String.valueOf(masterKeyId), true); - } - } else { - ProviderHelper.deleteUnifiedKeyRing(activity, String.valueOf(masterKeyId), false); - } + if (keyType == KeychainContract.KeyTypes.SECRET) { + if (checkDeleteSecret.isChecked() || isSingleSelection) { + ProviderHelper.deleteUnifiedKeyRing(activity, String.valueOf(masterKeyId), true); } - - //Check if the selected rows have actually been deleted - cursor = activity.getContentResolver().query(queryUri, projection, selection, null, null); - if (cursor == null || cursor.getCount() == 0 || !checkDeleteSecret.isChecked()) { - isSuccessfullyDeleted = true; - } - - } finally { - if (cursor != null) { - cursor.close(); - } - - } - - dismiss(); - - if (isSuccessfullyDeleted) { - sendMessageToHandler(MESSAGE_OKAY, null); } else { - sendMessageToHandler(MESSAGE_ERROR, null); + ProviderHelper.deleteUnifiedKeyRing(activity, String.valueOf(masterKeyId), false); } } + //Check if the selected rows have actually been deleted + cursor = activity.getContentResolver().query(queryUri, projection, selection, null, null); + if (cursor == null || cursor.getCount() == 0 || !checkDeleteSecret.isChecked()) { + isSuccessfullyDeleted = true; + } + + } finally { + if (cursor != null) { + cursor.close(); + } + } + + dismiss(); + + if (isSuccessfullyDeleted) { + sendMessageToHandler(MESSAGE_OKAY, null); + } else { + sendMessageToHandler(MESSAGE_ERROR, null); + } + } + + } ); builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { From 4dac00bb21e01203f3f9ee04cb8d3420784059b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 21 Mar 2014 10:28:26 +0100 Subject: [PATCH 093/253] Pull from transifex --- .../src/main/res/raw-de/help_changelog.html | 12 +- .../src/main/res/raw-de/help_start.html | 4 +- .../src/main/res/raw-zh/help_about.html | 6 +- .../src/main/res/raw-zh/help_nfc_beam.html | 10 +- .../src/main/res/raw-zh/help_start.html | 12 +- .../src/main/res/raw-zh/nfc_beam_share.html | 8 +- .../src/main/res/values-de/strings.xml | 107 +++++++++++++----- .../src/main/res/values-el/strings.xml | 1 + .../src/main/res/values-es-rCO/strings.xml | 1 + .../src/main/res/values-es/strings.xml | 46 +++++++- .../src/main/res/values-fa-rIR/strings.xml | 1 + .../src/main/res/values-fr/strings.xml | 35 +++++- .../src/main/res/values-it-rIT/strings.xml | 35 +++++- .../src/main/res/values-ja/strings.xml | 35 +++++- .../src/main/res/values-nl-rNL/strings.xml | 2 +- .../src/main/res/values-pt-rBR/strings.xml | 1 + .../src/main/res/values-ru/strings.xml | 26 ++++- .../src/main/res/values-sl-rSI/strings.xml | 1 + .../src/main/res/values-tr/strings.xml | 2 +- .../src/main/res/values-uk/strings.xml | 37 +++++- .../src/main/res/values-zh/strings.xml | 4 +- 21 files changed, 294 insertions(+), 92 deletions(-) diff --git a/OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html index 1197869b5..e414324d0 100644 --- a/OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html +++ b/OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html @@ -8,7 +8,7 @@

  • more internal fixes when editing keys (thanks to Ash Hughes)
  • querying keyservers directly from the import screen
  • fix layout and dialog style on Android 2.2-3.0
  • -
  • fix crash on keys with empty user ids
  • +
  • Absturz bei leeren Nutzer IDs behoben
  • fix crash and empty lists when coming back from signing screen
  • Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source
  • fix upload of key from signing screen
  • @@ -38,15 +38,15 @@

    2.0

    • Komlett neu designd
    • -
    • share public keys via qr codes, nfc beam
    • +
    • Öffentliche Schlüssel teilen via QR Code, NFC Beam
    • Schlüssel signieren
    • Schlüssel auf den Server hochladen
    • -
    • fixes import issues
    • +
    • Importprobleme behoben
    • new AIDL API

    1.0.8

      -
    • basic keyserver support
    • +
    • Grundlegende Schlüsselserverunterstützung
    • app2sd
    • mehr Auswahlmöglichkeiten für den Passwortcache: 1, 2, 4, 8, Stunden
    • Übersetzungen: norwegisch (Danke, Sander Danielsen), chinesisch (danke, Zhang Fredrick)
    • @@ -98,8 +98,8 @@

      1.0.0

      • K-9 Mail integration, APG supporting beta build of K-9 Mail
      • -
      • support of more file managers (including ASTRO)
      • -
      • Slovenian translation
      • +
      • Unterstützung von mehr Filemanagern (einschließlich ASTRO)
      • +
      • Slowenische Übersetzung
      • Neue Datenbank, viel schneller, weniger Speicherbedarf
      • defined Intents and content provider for other apps
      • Fehlerbehebungen
      • diff --git a/OpenPGP-Keychain/src/main/res/raw-de/help_start.html b/OpenPGP-Keychain/src/main/res/raw-de/help_start.html index d2735f739..a7949dd64 100644 --- a/OpenPGP-Keychain/src/main/res/raw-de/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-de/help_start.html @@ -1,7 +1,7 @@ -

        Getting started

        +

        Los gehts

        First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

        It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

        @@ -9,7 +9,7 @@

        Ich habe einen Fehler in OpenKeychain gefunden!

        Please report the bug using the issue tracker of OpenKeychain.

        -

        Contribute

        +

        Unterstützen

        If you want to help us developing OpenKeychain by contributing code follow our small guide on Github.

        Übersetzungen

        diff --git a/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html b/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html index 863aeee58..23b904995 100644 --- a/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html @@ -3,14 +3,14 @@

        http://www.openkeychain.org

        OpenKeychain is an OpenPGP implementation for Android.

        -

        License: GPLv3+

        +

        授權:GPLv3+

        Developers OpenKeychain

        • Dominik Schürmann (Lead developer)
        • Ash Hughes (crypto patches)
        • Brian C. Barnes
        • -
        • Bahtiar 'kalkin' Gadimov (UI)
        • +
        • Bahtiar 'kalkin' Gadimov (介面)

        Developers APG 1.x

        @@ -20,7 +20,7 @@
      • Oliver Runge
      • Markus Doits
      -

      Libraries

      +

      函式庫

      • Android Support Library v4 (Apache License v2)
      • diff --git a/OpenPGP-Keychain/src/main/res/raw-zh/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-zh/help_nfc_beam.html index 88492731c..7a90a794b 100644 --- a/OpenPGP-Keychain/src/main/res/raw-zh/help_nfc_beam.html +++ b/OpenPGP-Keychain/src/main/res/raw-zh/help_nfc_beam.html @@ -1,12 +1,12 @@ -

        How to receive keys

        +

        如何接收金要

          -
        1. Go to your partners contacts and open the contact you want to share.
        2. -
        3. Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.
        4. -
        5. After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.
        6. -
        7. Tap the card and the content will then load on the your device.
        8. +
        9. 前往你夥伴裝置上的聯絡人清單,並點選你要分享的聯絡人。
        10. +
        11. 將兩部裝置背對背貼近(幾乎接觸),你會感覺到一股震動。
        12. +
        13. 震動之後你會看見你夥伴的畫面變成卡片狀,並且背景帶有如 Star Trek 般的特效。
        14. +
        15. 輕觸卡片,內容隨即顯示在你的裝置上。
        diff --git a/OpenPGP-Keychain/src/main/res/raw-zh/help_start.html b/OpenPGP-Keychain/src/main/res/raw-zh/help_start.html index 3a6443a2f..104bdd545 100644 --- a/OpenPGP-Keychain/src/main/res/raw-zh/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-zh/help_start.html @@ -1,18 +1,18 @@ -

        Getting started

        -

        First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

        +

        快速上手

        +

        首先你需要屬於你個人的金鑰對,從〝我的金鑰〞選單中建立一個或是使用〝匯入金鑰〞匯入現有的金鑰對。在這之後,你可以下載你朋友的公鑰或者是透過二維條碼或NFC和他交換公鑰。

        It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

        -

        I found a bug in OpenKeychain!

        -

        Please report the bug using the issue tracker of OpenKeychain.

        +

        我在OpenKeychain發現問題!

        +

        請利用 OpenKeychain 項目回報系統回報問題。

        -

        Contribute

        +

        發布

        If you want to help us developing OpenKeychain by contributing code follow our small guide on Github.

        -

        Translations

        +

        翻譯

        Help translating OpenKeychain! Everybody can participate at OpenKeychain on Transifex.

        diff --git a/OpenPGP-Keychain/src/main/res/raw-zh/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-zh/nfc_beam_share.html index 083e055c7..99ffe4c12 100644 --- a/OpenPGP-Keychain/src/main/res/raw-zh/nfc_beam_share.html +++ b/OpenPGP-Keychain/src/main/res/raw-zh/nfc_beam_share.html @@ -2,10 +2,10 @@
          -
        1. Make sure that NFC is turned on in Settings > More > NFC and make sure that Android Beam is also on in the same section.
        2. -
        3. Hold the two devices back to back (they have to be almost touching) and you'll feel a vibration.
        4. -
        5. After it vibrates you'll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.
        6. -
        7. Tap the card and the content will then load on the other person’s device.
        8. +
        9. 確定在 "設定" > "更多內容…" > "NFC" 裡面已經開啟 NFC 和 Android Beam。
        10. +
        11. 將兩部裝置背對背貼近(幾乎接觸),你會感覺到一股震動。
        12. +
        13. 震動之後你會看見你夥伴的畫面變成卡片狀,並且背景帶有如 Star Trek 般的特效。
        14. +
        15. 輕觸卡片,內容隨即顯示在你的裝置上。
        diff --git a/OpenPGP-Keychain/src/main/res/values-de/strings.xml b/OpenPGP-Keychain/src/main/res/values-de/strings.xml index 1e0fda3bc..493b81314 100644 --- a/OpenPGP-Keychain/src/main/res/values-de/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-de/strings.xml @@ -13,8 +13,10 @@ Einstellungen Registrierte Anwendungen Schlüsselserver + Passphrase ändern Passwort setzen E-Mail senden... + Datei senden In eine Datei verschlüsseln In eine Datei entschlüsseln Schlüssel importieren @@ -62,6 +64,8 @@ Zwischenablage Teilen mit… Schlüssel nachschlagen + Erweiterte Einstellungen anzeigen + Erweiterte Einstellungen verbergen Einstellungen Hilfe @@ -70,15 +74,17 @@ Importieren NFC Alle Schlüssel exportieren + Alle geheimen Schlüssel exportieren In Datei exportieren Schlüssel löschen Schlüssel erstellen Schlüssel erstellen (Experte) Suchen - Schlüsselserver + Schlüsselserver + Schlüsselserver… Von einem Schlüsselserver aktualisieren Auf Schlüsselserver hochladen - Teilen + Teilen… Teile Fingerabdruck… Teile gesamten Schlüssel… mit… @@ -91,6 +97,7 @@ Beam-Einstellungen Abbrechen Verschlüsseln nach… + Alles auswählen Signieren Nachricht @@ -103,6 +110,7 @@ Empfänger Nach Verschlüsselung löschen Nach Entschlüsselung löschen + Nach dem Verschlüsseln teilen Verschlüsselungsalgorithmus Hash-Algorithmus Öffentlicher Schlüssel @@ -121,9 +129,12 @@ Name Kommentar E-Mail + Benutzer ID unterschreiben + Email unterschreiben Schlüssel nach Beglaubigung auf ausgewählten Schlüsselserver hochladen Fingerabdruck Auswählen + Ablaufdatum festsetzen %d ausgewählt %d ausgewählt @@ -131,11 +142,17 @@ <kein Name> <keine> <kein Schlüssel> + <Keine E-Mail> kann verschlüsseln kann signieren abgelaufen zurückgezogen + Benutzer ID + + 1 Kontakt + %d Kontakte + %d Schlüsselserver %d Schlüsselserver @@ -191,6 +208,7 @@ Soll der Schlüssel \'%s\' wirklich gelöscht werden?\nDies kann nicht rückgängig gemacht werden! Möchtest du wirklich alle ausgewählten Schlüssel löschen?\nDies kann nicht rückgängig gemacht werden! Soll der PRIVATE Schlüssel \'%s\' wirklich gelöscht werden?\nDies kann nicht rückgängig gemacht werden! + Private Schlüssel auch exportieren %d Schlüssel erfolgreich hinzugefügt %d Schlüssel erfolgreich hinzugefügt @@ -212,6 +230,7 @@ %d Schlüssel erfolgreich exportiert. Keine Schlüssel exportiert. Beachte: nur Unterschlüssel unterstützen ElGamal. Für ElGamal wird die am nächsten liegende Schlüssellänge von 1536, 2048, 3072, 4096 oder 8192 verwendet. + Beachte: RSA-Schlüssel mit einer Schlüssellänge von 1024-Bits oder weniger werden als unsicher angesehen und können daher nicht für neue Schlüssel erstellt werden. Schlüssel %08X konnte nicht gefunden werden. %d Schlüssel gefunden. @@ -243,6 +262,7 @@ Der Hauptschlüssel kann kein ElGamal Schlüssel sein Unbekannte Auswahl für Algorithmus ein Name muss angegeben werden + keine E-Mail gefunden eine E-Mail-Adresse muss angegeben werden Mindestens eine Benutzer-ID wird benötigt Hauptbenutzer-ID darf nicht leer sein @@ -265,48 +285,59 @@ Ablaufdatum muss später sein als das Erstellungsdatum Sie können diesen Kontakt nicht löschen, denn es ist ihr eigener. Sie können folgende Kontakte nicht löschen, denn sie gehören Ihnen selbst:\n%s + Unzureichende Serveranfrage Keyserveranfrage fehlgeschlagen + Zu viele Antworten + Datei ist leer + Ein allgemeiner Fehler trat auf, bitte schreiben Sie einen neuen Bugreport für OpenKeychain. Bitte lösche ihn unter \'Meine Schlüssel\'! Bitte lösche sie unter \'Meine Schlüssel\'! + + Ein Teil der geladenen Datei ist ein gültiges OpenPGP Objekt aber kein OpenPGP Schlüssel + Teile der geladenen Dateien sind gültige OpenPGP Objekte aber keine OpenPGP Schlüssel + - fertig. + Erledigt Abbrechen - speichern... - importieren... - exportieren... - erstelle Schlüssel, dies kann bis zu 3 Minuten dauern... - erstelle Schlüssel... - Hauptschlüssel wird vorbereitet... - Hauptschlüssel wird beglaubigt... - erstelle Hauptring... - füge Unterschlüssel hinzu... - Schlüssel wird gespeichert... + speichern… + importieren… + exportieren… + erstelle Schlüssel… + Hauptschlüssel wird vorbereitet… + Hauptschlüssel wird beglaubigt… + erstelle Hauptring… + füge Unterschlüssel hinzu… + Schlüssel wird gespeichert… Schlüssel wird exportiert… Schlüssel werden exportiert… - extrahiere Signaturschlüssel... - extrahiere Schlüssel... - Datenstrom wird vorbereitet... - Daten werden verschlüsselt... - Daten werden entschlüsselt... - Signatur wird vorbereitet... - Signatur wird erstellt... - Signatur wird verarbeitet... - Signatur wird verifiziert... - signiere... - Daten werden gelesen... - Schlüssel wird gesucht... - Daten werden entpackt... - Integrität wird überprüft... - \'%s\' wird sicher gelöscht... - Anfrage wird gestellt... + + erstelle Schlüssel, das kann bis zu 3 Minuten dauern… + erstelle Schlüssel, das kann bis zu 3 Minuten dauern… + + extrahiere Signaturschlüssel… + extrahiere Schlüssel… + Datenstrom wird vorbereitet… + Daten werden verschlüsselt… + Daten werden entschlüsselt… + Signatur wird vorbereitet… + Signatur wird erstellt… + Signatur wird verarbeitet… + Signatur wird verifiziert… + signiere… + Daten werden gelesen… + Schlüssel wird gesucht… + Daten werden entpackt… + Integrität wird überprüft… + \'%s\' wird sicher gelöscht… + Anfrage wird gestellt… Öffentliche Schlüssel suchen Private Schlüssel suchen - Teile Schlüssel über... + Teile Schlüssel über… 512 1024 @@ -317,6 +348,7 @@ sehr langsam Start + FAQ NFC-Beam Changelog Über @@ -338,6 +370,10 @@ Hilfe Füge den Schlüssel aus der Zwischenablage ein + Datei mit OpenKeychain entschlüsseln + Schlüssel mit OpenKeychain importieren + Mit OpenKeychain verschlüsseln + Mit OpenKeychain entschlüsseln Keine registrierten Anwendungen vorhanden!\n\nAnwendungen von Dritten können Zugriff auf OpenKeychain erbitten. Nachdem Zugriff gewährt wurde, werden diese hier aufgelistet. Erweiterte Einstellungen anzeigen @@ -356,7 +392,7 @@ Für diese Benutzer-IDs wurden keine öffentlichen Schlüssel gefunden: Für diese Benutzer-IDs existieren mehrere öffentliche Schlüssel: Bitte die Liste der Empfänger überprüfen! - Signaturüberprüfung fehlgeschlagen! Haben Sie diese App von einer anderen Quelle installiert? Wenn Sie eine Attacke ausschließen können, sollten Sie die Registrierung der App in OpenKeychain widerrufen und die App erneut registrieren. + Signaturüberprüfung fehlgeschlagen! Haben Sie diese App von einer anderen Quelle installiert? Wenn Sie eine Attacke ausschliessen können, sollten Sie die Registrierung der App in OpenKeychain widerrufen und die App erneut registrieren. Über QR Code teilen Mit \'Weiter\' durch alle QR-Codes gehen und diese nacheinander scannen. @@ -374,6 +410,7 @@ deinen eigenen Schlüssel erstellst existierende Schlüssel importierst. + Diesen Schlüssel bearbeiten Für diesen Kontakt verschlüsseln Schlüssel dieses Kontakts beglaubigen Info @@ -387,4 +424,12 @@ Registrierte Anwendungen Menu öffnen Menu schließen + Bearbeiten + Meine Schlüssel + Geheime Schlüssel + verfügbar + nicht verfügbar + Benutzer-IDs, die beglaubigt werden sollen + Wiederhinzufügen der Zertifikate + diff --git a/OpenPGP-Keychain/src/main/res/values-el/strings.xml b/OpenPGP-Keychain/src/main/res/values-el/strings.xml index 84b39c221..094dfe28a 100644 --- a/OpenPGP-Keychain/src/main/res/values-el/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-el/strings.xml @@ -50,4 +50,5 @@ + diff --git a/OpenPGP-Keychain/src/main/res/values-es-rCO/strings.xml b/OpenPGP-Keychain/src/main/res/values-es-rCO/strings.xml index 41dc629aa..ce365b776 100644 --- a/OpenPGP-Keychain/src/main/res/values-es-rCO/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-es-rCO/strings.xml @@ -97,4 +97,5 @@ + diff --git a/OpenPGP-Keychain/src/main/res/values-es/strings.xml b/OpenPGP-Keychain/src/main/res/values-es/strings.xml index c643a3cd8..85c77b0da 100644 --- a/OpenPGP-Keychain/src/main/res/values-es/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-es/strings.xml @@ -16,6 +16,7 @@ Cambiar frase de contraseña Establecer frase de contraseña Enviar email... + Enviar archivo... Cifrar hacia archivo Descifrar hacia archivo Importar claves @@ -63,6 +64,8 @@ Portapapeles Compartir con... Buscar clave + Mostrar ajustes avanzados + Ocultar ajustes avanzados Ajustes Ayuda @@ -71,15 +74,17 @@ Importar Importar desde NFC Exportar todas las claves + Exportar todas las claves secretas Exportar hacia archivo Borrar clave Crear clave Crear clave (experto) Buscar - Importar desde servidor de claves + Servidor de claves... + Servidor de claves... Actualizar desde servidor de claves Cargar al servidor de claves - Compartir + Compartir... Compartir la huella digital... Compartir la clave completa... con... @@ -105,6 +110,7 @@ Destinatarios Borrar después del cifrado Borrar después del descifrado + Compartir después del cifrado Algoritmo de cifrado Algoritmo de Hash Clave pública @@ -123,6 +129,8 @@ Nombre Comentario Email + Firmar Id de usuario + Firmar correo Cargar clave al servidor de claves seleccionado después de la certificación Huella digital Seleccionar @@ -134,11 +142,17 @@ <sin nombre> <ninguna> <sin clave> + <No hay un email> se puede cifrar se puede firmar caducado revocado + ID de usuario + + 1 contacto + %d contactos + %d servidor de claves %d servidores de claves @@ -194,6 +208,7 @@ ¿Quieres realmente borrar la clave \'%s\'?\n¡No podrás deshacerlo! ¿Quieres realmente borrar todas las claves seleccionadas?\n¡No podrás deshacerlo! ¿Quieres realmente borrar la clave SECRETA \'%s\'?\n¡No podrás deshacerlo! + ¿Exportar también las claves secretas? %d clave añadida satisfactoriamente %d claves añadidas satisfactoriamente @@ -215,6 +230,7 @@ %d claves exportadas satisfactoriamente. No se han exportado claves. Nota: solo las subclaves son compatibles con ElGamal, y para ElGamal debe usarse el tamaño de clave más próximo de 1536, 2048, 3072, 4096, o 8192. + Nota: generar una clave RSA de longitud 1024-bit o menos está considerado inseguro y desactivado para generar nuevas claves. No se puede encontrar la clave %08X. Se ha encontrado %d clave. @@ -246,6 +262,7 @@ la clave maestra no puede ser una clave ElGamal elegido algoritmo desconocido necesitas determinar un nombre + no se ha encontrado un email tienes que determinar una dirección de email necesitas al menos una ID de usuario la ID del usuario principal no puede estar vacía @@ -271,17 +288,22 @@ Consulta al servidor insuficiente La consulta al servidor de claves ha fallado Demasiadas respuestas + El archivo está vacio + Ha ocurrido un error genérico, por favor, informa de este bug a OpenKeychain Por favor, bórralo desde la pantalla \'Mis claves\'! Por favor, bórralos desde la pantalla \'Mis claves\'! + + parte del archivo cargado es un objeto OpenPGP válido pero no una clave OpenPGP + partes del archivo cargado son objetos OpenPGP válidos pero no claves OpenPGP + - hecho. - cancelar + Hecho. + Cancelar guardando... importando... exportando... - generando la clave, esto puede tardar más de 3 minutos... construyendo la clave... preparando la clave maestra... certificando la clave maestra... @@ -292,6 +314,10 @@ exportando clave... exportando claves... + + generando clave, esto puede tardar más de 3 minutos... + generando claves, esto puede tardar más de 3 minutos... + extrayendo la clave de firma... extrayendo la clave... preparando las transmisiones... @@ -322,6 +348,7 @@ muy lento Comenzar + FAQ NFC Beam Registro de cambios A cerca de @@ -383,6 +410,7 @@ crear tu propia clave importar claves + Editar esta clave Cifrar hacia este contacto Certificar la clave de este contacto Información @@ -396,4 +424,12 @@ Aplicaciones registradas Abrir el Navigation Drawer Cerrar el Navigation Drawer + Editar + Mis claves + Claves secretas + disponible + no disponible + IDs de usuario para firmar + Nueva aplicación de certificados + diff --git a/OpenPGP-Keychain/src/main/res/values-fa-rIR/strings.xml b/OpenPGP-Keychain/src/main/res/values-fa-rIR/strings.xml index 6bb115049..3d00a143f 100644 --- a/OpenPGP-Keychain/src/main/res/values-fa-rIR/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-fa-rIR/strings.xml @@ -23,4 +23,5 @@ + diff --git a/OpenPGP-Keychain/src/main/res/values-fr/strings.xml b/OpenPGP-Keychain/src/main/res/values-fr/strings.xml index d99bbcd7c..4f92f7855 100644 --- a/OpenPGP-Keychain/src/main/res/values-fr/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-fr/strings.xml @@ -64,6 +64,8 @@ Presse-papiers Partager avec... Rechercher la clef + Afficher les paramètres avancés + Masquer les paramètres avancés Paramètres Aide @@ -72,15 +74,17 @@ Importer Importer avec NFC Exporter toutes les clefs + Exporter toutes les clefs secrètes Exporter vers un fichier Supprimer la clef Créer une clef Créer une clef (expert) Rechercher - Importer depuis le serveur de clefs + Serveur de clefs + Serveur de clefs... Mettre à jour depuis le serveur de clefs Téléverser vers le serveur de clefs - Partager + Partager... Partager l\'empreinte... Partager la clef entière... avec... @@ -138,12 +142,17 @@ <aucun nom> <aucune> <pas de clef> + <aucun courriel> peut chiffrer peut signer expiré révoquée ID utilisateur + + 1 contact + %d contacts + %d serveur de clefs %d serveurs de clefs @@ -199,6 +208,7 @@ Voulez-vous vraiment supprimer la clef %s ?\nVous ne pourrez pas la restituer ! Voulez-vous vraiment supprimer toutes les clefs choisies ?\nCeci est irréversible ! Voulez-vous vraiment supprimer la clef SECRÈTE %s ?\nVous ne pourrez pas la restituer ! + Exporter aussi les clefs secrètes? %d clef ajoutée avec succès %d clefs ajoutées avec succès @@ -220,6 +230,7 @@ %d clefs exportées avec succès. Aucune clef exportée. Note : seules les sous-clefs prennent en charge ElGamal, et pour ElGamal la taille de clef la plus proche de 1 536, 2 048, 3 072, 4 096 ou 8 192 sera utilisée. + Note : générer des clefs RSA d\'une longueur de 1024 bits ou moins est considéré non sécuritaire et est désactivé pour la génération de nouvelles clefs. Clef %08X introuvable. %d clef trouvée. @@ -251,6 +262,7 @@ la clef maîtresse ne peut être une clef ElGama choix d\'algorhitme inconnu vous devez spécifier un nom + aucun courriel trouvé vous devez spécifier une adresse courriel vous avez besoin d\'au moins un ID utilisateur l\'ID utilisateur principal ne doit pas être vide @@ -287,12 +299,11 @@ certaines parties du fichier chargé sont des objets OpenPGP valides mais pas des clefs OpenPGP - fait. - annuler + Terminé. + Annuler sauvegarde... importation... exportation... - génération de la clef, ceci peut prendre jusqu\'à 3 minutes... assemblage de la clef... préparation de la clef maîtresse... certification de la clef maîtresse... @@ -303,6 +314,10 @@ exportation de la clef... exportation des clefs... + + génération de la clef, ceci peut prendre jusqu\'à 3 min... + génération des clefs, ceci peut prendre jusqu\'à 3 min... + extraction de la clef de signature... extraction de la clef... préparation des flux... @@ -333,6 +348,7 @@ très lent Commencer + FAQ NFC Beam Journal des changements À propos de @@ -394,6 +410,7 @@ créer votre propre clef Importer des clefs. + Modifier cette clef Chiffrer vers ce contact Certifier la clef de ce contact Infos @@ -407,4 +424,12 @@ Applis enregistrées Ouvrir le tiroir de navigation Fermer le tiroir de navigation + Modifier + Mes clefs + Clef secrète + disponible + non disponible + ID utilisateur pour signer + Nouvel application des certificats + diff --git a/OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml b/OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml index f9e7074da..b125de448 100644 --- a/OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml @@ -64,6 +64,8 @@ Appunti Condividi con... Chiave di ricerca + Mostra impostazioni avanzate + Nascondi impostazioni avanzate Impostazioni Aiuto @@ -72,15 +74,17 @@ Importa Importa tramite NFC Esporta tutte le chiavi + Esporta tutte le chiavi segrete Esporta su un file Cancella chiave Crea chiave Crea chiave (esperto) Cerca - Importa dal server delle chiavi + Server delle Chiavi + Server delle Chiavi... Aggiorna dal server delle chiavi Carica chiave nel server - Condividi + Condividi... Condivi impronta... Condividi intera chiave... con.. @@ -138,12 +142,17 @@ <nessun nome> <nessuno> <nessuna chiave> + <Nessuna Email> puo\'; codificare puo\' firmare scaduto revocato ID Utente + + 1 contatto + %d contatti + %d server delle chiavi %d server delle chiavi @@ -199,6 +208,7 @@ Vuoi veramente eliminare la chiave \'%s\'?\nNon potrai annullare! Vuoi veramente eliminare le chiavi selezionate?\nNon potrai annullare! Vuoi veramente eliminare la chiave PRIVATA \'%s\'?\nNon potrai annullare! + Esportare anche le chiavi segrete? %d chiave aggiunta correttamente %d chiavi aggiunte correttamente @@ -220,6 +230,7 @@ %d chiavi esportate correttamente. Nessuna chiave esportata. Nota: solo le sottochiavi supportano ElGamal, e per ElGamal verra\' usata la grandezza chiave piu\' vicina a 1536, 2048, 3072, 4096 o 8192. + Nota: la generazione di chiavi RSA con lunghezza pari a 1024 bit o inferiore è considerata non sicura ed è disabilitata per la generazione di nuove chiavi. Impossibile trovare la chiave %08X. Trovata %d chiave. @@ -251,6 +262,7 @@ La chiave principale non puo\' essere ElGamal opzione algoritmo sconosciuta devi specificare un nome + Nessuna email trovata devi specificare un indirizzo email necessario almeno un id utente id utente principale non puo\' essere vuoto @@ -287,12 +299,11 @@ parti del file caricato sono oggetti OpenPGP validi, ma non chavi OpenPGP - fatto. - cancella + Fatto. + Annulla salvataggio... importazione... esportazione... - generazione chiave, richiede fino a 3 minuti... fabbricazione chiave... preparazione chiave principale... certificazione chiave principale... @@ -303,6 +314,10 @@ esportazione chiave... esportazione chiavi... + + generazione chiave, sono necessari fino a 3 minuti... + generazione chiavi, sono necessari fino a 3 minuti... + estrazione chiavi di firma... estrazione chiave... preparazione flussi... @@ -333,6 +348,7 @@ molto lento Inizia + FAQ NFC Beam Novita\' Info @@ -394,6 +410,7 @@ creazione della tua chiave importazione chiavi. + Modifica chiave Codifica a questo contatto Certifica la chiave di questo contatto Info @@ -407,4 +424,12 @@ App Registrate Apri drawer di navigazione Chiudi drawer di navigazione + Modifica + Le Mie Chiavi + Chiave Segreta + disponibile + non disponibile + ID Utente da firmare + Riapplicazione certificati + diff --git a/OpenPGP-Keychain/src/main/res/values-ja/strings.xml b/OpenPGP-Keychain/src/main/res/values-ja/strings.xml index e5ee5ecc0..97f0c6eed 100644 --- a/OpenPGP-Keychain/src/main/res/values-ja/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-ja/strings.xml @@ -62,8 +62,10 @@ 戻る クリップボード - 共有... + ...で共有 鍵検出 + 拡張設定を表示 + 拡張設定を隠す 設定 ヘルプ @@ -72,15 +74,17 @@ インポート NFCからインポート すべての鍵のエクスポート + すべての秘密鍵のエクスポート ファイルへのエクスポート 鍵の削除 鍵の生成 鍵の生成(上級) 検索 - 鍵サーバからのインポート + 鍵サーバ + 鍵サーバ... 鍵サーバからの更新 鍵サーバへのアップロード - 共有 + 共有... 指紋の共有... すべての鍵の共有... ...(指紋) @@ -137,12 +141,16 @@ <名前なし> <無し> <鍵無し> + <メールなし> 暗号化可能 署名可能 期限切れ 破棄 ユーザーID + + %d個の連絡先 + %d の鍵サーバ @@ -197,6 +205,7 @@ 鍵\'%s\'を本当に削除してもよいですか?\nこれは元に戻せません! 選択したすべての鍵を本当に削除してよいですか?\nこれは元に戻せません。 秘密鍵\'%s\'を本当に削除してもよいですか?\nこれは元に戻せません! + 秘密鍵もエクスポートしますか? %d の鍵を追加しました @@ -214,6 +223,7 @@ %d の鍵をエクスポートしました。 鍵をエクスポートしていません。 備考: 副鍵として ElGamalだけがサポートされ, ElGamal は鍵サイズとして1536, 2048, 3072, 4096, 8192 だけが使えます。 + 付記: 長さ1024bitかそれ以下で生成されたRSA鍵は安全とはみなされず、新な鍵の生成は無効にされています。 鍵 %08X は見付かりませんでした。 %d の鍵を発見。 @@ -243,6 +253,7 @@ 主鍵を ElGamal にすることはできません 未知のアルゴリズムを選択しています 名前を特定する必要があります + メールが見付かりません Eメールアドレスを特定する必要があります 最低でも1つのユーザIDが必要です 主ユーザIDは空にすることはできません @@ -282,7 +293,6 @@ 保存... インポート... エクスポート... - 鍵の生成、3分ほどかかります... 鍵の構築中... 主鍵の準備中... 主鍵の検証中... @@ -292,6 +302,9 @@ 鍵のエクスポート... + + 鍵の生成中、最大3分ほどかかります... + 署名鍵の取り出し中... 鍵の取り出し中... ストリームの準備中... @@ -311,7 +324,7 @@ 公開鍵の検索 秘密鍵の検索 - 鍵の共有... + ...で鍵の共有 512 1024 @@ -322,6 +335,7 @@ とても遅い 開始 + FAQ NFC Beam Changelog これについて @@ -349,7 +363,7 @@ 登録されていないアプリケーション!\n\nサードパーティアプリケーションはOpenKeychainにアクセスを要求できます。アクセスを与えた後、それらはここにリストされます。 拡張設定を表示 - 拡張設定を非表示 + 拡張設定を隠す 鍵が選択されていない 鍵の選択 保存 @@ -381,6 +395,7 @@ あなた所有の鍵を作る 鍵のインポート。 + この鍵の編集 この連絡先を暗号化 この連絡先の鍵を検証 情報 @@ -394,4 +409,12 @@ 登録済みのアプリ ナビゲーションドロワーを開く ナビゲーションドロワーを閉める + 編集 + 自分の鍵 + 秘密鍵 + 存在する + 存在しない + 署名に使うユーザーID + 検証を再適用する + diff --git a/OpenPGP-Keychain/src/main/res/values-nl-rNL/strings.xml b/OpenPGP-Keychain/src/main/res/values-nl-rNL/strings.xml index de6ba554d..a7f674ea0 100644 --- a/OpenPGP-Keychain/src/main/res/values-nl-rNL/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-nl-rNL/strings.xml @@ -170,7 +170,6 @@ Uw apparaat biedt geen ondersteuning voor NFC Niets te importeren - gereed. opslaan... importeren... exporteren... @@ -236,4 +235,5 @@ + diff --git a/OpenPGP-Keychain/src/main/res/values-pt-rBR/strings.xml b/OpenPGP-Keychain/src/main/res/values-pt-rBR/strings.xml index 6bb115049..3d00a143f 100644 --- a/OpenPGP-Keychain/src/main/res/values-pt-rBR/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-pt-rBR/strings.xml @@ -23,4 +23,5 @@ + diff --git a/OpenPGP-Keychain/src/main/res/values-ru/strings.xml b/OpenPGP-Keychain/src/main/res/values-ru/strings.xml index 22f676ccb..55e778bee 100644 --- a/OpenPGP-Keychain/src/main/res/values-ru/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-ru/strings.xml @@ -16,6 +16,7 @@ Изменить пароль Задать пароль Отправить... + Отправить файл Зашифровать в файл Расшифровать в файл Импорт ключей @@ -63,6 +64,8 @@ Буфер обмена Поделиться... Найти ключ + Показать расширенные настройки + Скрыть расширенные настройки Настройки Помощь @@ -76,10 +79,8 @@ Создать ключ Создать ключ (эксперт) Поиск - Импорт с сервера ключей Обновить с сервера ключей Загрузить на сервер ключей - Отправить... Отправить отпечаток... Отправить ключ... Отправить @@ -105,6 +106,7 @@ Получатели Удалить после шифрования Удалить после расшифровки + Отправить после шифрования Алгоритм шифрования Hash-алгоритм Публичный ключ @@ -135,6 +137,7 @@ <нет имени> <нет> <нет ключа> + <нет email> шифрование подпись @@ -254,6 +257,7 @@ ключ ElGamal не может быть основным выбран неизвестный алгоритм необходимо указать имя + email не найден необходимо указать email необходим хотя бы один id пользователя основная запись пользователя не может быть пустой @@ -279,18 +283,24 @@ Ограничение запроса сервера Сбой запроса сервера ключей Слишком много ответов + Файл пуст + Выявлена ошибка. Пожалуйста, сообщите о ней разработчику. Пожалуйста, удалите его в разделе \'Мои ключи\'! Пожалуйста, удалите их в разделе \'Мои ключи\'! Пожалуйста, удалите их в разделе \'Мои ключи\'! + + часть загруженного файла содержит данные OpenPGP, но это не ключ + части загруженного файла содержат данные OpenPGP, но это не ключ + части загруженного файла содержат данные OpenPGP, но это не ключ + - готово. - отмена + Готово. + Отмена сохранение... импорт... экспорт... - создание ключа. это может занять до 3 минут... создание ключа... подготовка основного ключа... сертификация основного ключа... @@ -302,6 +312,11 @@ экспорт ключей... экспорт ключей... + + создание ключа. это может занять до 3 минут... + создание ключей. это может занять до 3 минут... + создание ключей. это может занять до 3 минут... + извлечение подписи ключа... извлечение ключа... подготовка к передаче... @@ -408,4 +423,5 @@ Связанные приложения Открыть панель навигации Закрыть панель навигации + diff --git a/OpenPGP-Keychain/src/main/res/values-sl-rSI/strings.xml b/OpenPGP-Keychain/src/main/res/values-sl-rSI/strings.xml index 6bb115049..3d00a143f 100644 --- a/OpenPGP-Keychain/src/main/res/values-sl-rSI/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-sl-rSI/strings.xml @@ -23,4 +23,5 @@ + diff --git a/OpenPGP-Keychain/src/main/res/values-tr/strings.xml b/OpenPGP-Keychain/src/main/res/values-tr/strings.xml index 5bb5225b5..174d8538f 100644 --- a/OpenPGP-Keychain/src/main/res/values-tr/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-tr/strings.xml @@ -95,7 +95,6 @@ anahtar uzunluğu en az 512bit olmalı bozuk veri - bitti. kaydediliyor... alıyor... veriyor... @@ -135,4 +134,5 @@ + diff --git a/OpenPGP-Keychain/src/main/res/values-uk/strings.xml b/OpenPGP-Keychain/src/main/res/values-uk/strings.xml index 7ccb661d3..58ac643af 100644 --- a/OpenPGP-Keychain/src/main/res/values-uk/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-uk/strings.xml @@ -64,6 +64,8 @@ Буфер обміну Поділитися через… Шукати ключ + Показати додаткові налаштування + Приховати додаткові налаштування Параметри Довідка @@ -72,15 +74,17 @@ Імпорт Імпорт з NFC Експортувати усі ключі + Експортувати усі секретні ключі Експорт до файлу Вилучити ключ Створити ключ Створити ключ (експерт) Пошук - Імпорт з сервера ключів + Сервер ключів + Сервер ключів… Оновити з сервера ключів Завантажити на сервер ключів - Поділитися + Поділитися… Поділитися відбитком… Поділитися цілим ключем… з… @@ -139,12 +143,18 @@ <без імені> <жоден> <без ключа> + <Немає ел. пошти> можна зашифрувати можна підписати закінчився скасовано ІД користувача + + 1 контакт + %d контакти + %d контактів + %d сервер ключів %d сервери ключів @@ -201,6 +211,7 @@ Ви справді хочете вилучити ключ \'%s\'?\nВи не зможете це відмінити! Ви справді хочете вилучити усі вибрані ключі?\nВи не зможете це відмінити! Ви справді хочете вилучити секретний ключ \'%s\'?\nВи не зможете це відмінити! + Також експортувати секретні ключі? Успішно додано %d ключ Успішно додано %d ключі @@ -226,6 +237,7 @@ Успішно експортовано %d ключів. Жодного ключа не експортовано. Примітка: тільки підключі підтримують ElGamal, а для ElGamal буде використаний найближчий розмір ключа з 1536, 2048, 3072, 4096, або 8192. + Примітка: генерація ключа RSA з довжиною 1024 біти і менше вважається небезпечною і вона вимкнена для генерації нових ключів. Не можливо знайти ключ %08X. Знайдено %d ключ. @@ -259,6 +271,7 @@ основний ключ не може бути ключем ElGamal вибір невідомого алгоритму вам потрібно вказати назву + жодного листа не знайдено вам потрібно вказати електронну адресу потрібний хоча б один ІД користувача ІД основного користувача не має бути порожнім @@ -297,12 +310,11 @@ частин завантаженого файлу є вірним об\'єктом OpenPGP, але не ключем OpenPGP - готово. - cкасувати + Готово. + Скасувати збереження… імпортується… експортується… - генерується ключ, вона може тривати до 3 хвилин… будується ключ… підготовка основного ключа… сертифікація основного ключа… @@ -314,6 +326,11 @@ експортуються ключі… експортуються ключі… + + генерується ключ, це може тривати до 3 хвилини + генеруються ключі, це може тривати до 3 хвилини + генеруються ключі, це може тривати до 3 хвилини + видобування ключа підпису… видобувається ключа… підготовка потоків… @@ -344,6 +361,7 @@ дуже повільне Початок + ЧАП NFC промінь Журнал змін Про @@ -407,6 +425,7 @@ створюється ваш власний ключ імпортуюся ключі. + Редагувати цей ключ Зашифрувати у цей контакт Сертифікувати ключ цього контакту Інформація @@ -420,4 +439,12 @@ Зареєстровані програми Відкрити панель навігації Закрити панель навігації + Редагувати + Мої ключі + Секретний ключ + доступний + недоступний + ІД користувача для реєстрації + Перезастосування сертифікатів + diff --git a/OpenPGP-Keychain/src/main/res/values-zh/strings.xml b/OpenPGP-Keychain/src/main/res/values-zh/strings.xml index 80413d589..244e7e57b 100644 --- a/OpenPGP-Keychain/src/main/res/values-zh/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-zh/strings.xml @@ -1,6 +1,7 @@ + 聯絡人 选择公钥 选择私钥 加密 @@ -50,7 +51,6 @@ 创建密钥 创建密钥(专家) 搜索 - 分享 复制到剪贴板 签署密钥 取消 @@ -135,7 +135,6 @@ 损坏的数据 错误的密语 - 完成。 保存... 导入中... 导出中... @@ -187,4 +186,5 @@ 解密 导入密钥 我的密钥 + From c85a0e9cca4af8f42fa2522a8bf808ea6538be96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 21 Mar 2014 10:28:34 +0100 Subject: [PATCH 094/253] Pull from transifex --- .../src/main/res/raw-ach/help_about.html | 45 ++ .../src/main/res/raw-ach/help_changelog.html | 108 +++++ .../src/main/res/raw-ach/help_nfc_beam.html | 12 + .../src/main/res/raw-ach/help_start.html | 19 + .../src/main/res/raw-ach/nfc_beam_share.html | 11 + .../src/main/res/raw-cs-rCZ/help_about.html | 45 ++ .../main/res/raw-cs-rCZ/help_changelog.html | 108 +++++ .../main/res/raw-cs-rCZ/help_nfc_beam.html | 12 + .../src/main/res/raw-cs-rCZ/help_start.html | 19 + .../main/res/raw-cs-rCZ/nfc_beam_share.html | 11 + .../src/main/res/raw-pl/help_about.html | 45 ++ .../src/main/res/raw-pl/help_changelog.html | 108 +++++ .../src/main/res/raw-pl/help_nfc_beam.html | 12 + .../src/main/res/raw-pl/help_start.html | 19 + .../src/main/res/raw-pl/nfc_beam_share.html | 11 + .../src/main/res/raw-zh-rTW/help_about.html | 45 ++ .../main/res/raw-zh-rTW/help_changelog.html | 108 +++++ .../main/res/raw-zh-rTW/help_nfc_beam.html | 12 + .../src/main/res/raw-zh-rTW/help_start.html | 19 + .../main/res/raw-zh-rTW/nfc_beam_share.html | 11 + .../src/main/res/values-ach/strings.xml | 27 ++ .../src/main/res/values-cs-rCZ/strings.xml | 27 ++ .../src/main/res/values-pl/strings.xml | 450 ++++++++++++++++++ .../src/main/res/values-zh-rTW/strings.xml | 27 ++ 24 files changed, 1311 insertions(+) create mode 100644 OpenPGP-Keychain/src/main/res/raw-ach/help_about.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-ach/help_changelog.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-ach/help_nfc_beam.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-ach/help_start.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-ach/nfc_beam_share.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_changelog.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_nfc_beam.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_start.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-cs-rCZ/nfc_beam_share.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-pl/help_about.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-pl/help_changelog.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-pl/help_nfc_beam.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-pl/help_start.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-pl/nfc_beam_share.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_changelog.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_nfc_beam.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_start.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-zh-rTW/nfc_beam_share.html create mode 100644 OpenPGP-Keychain/src/main/res/values-ach/strings.xml create mode 100644 OpenPGP-Keychain/src/main/res/values-cs-rCZ/strings.xml create mode 100644 OpenPGP-Keychain/src/main/res/values-pl/strings.xml create mode 100644 OpenPGP-Keychain/src/main/res/values-zh-rTW/strings.xml diff --git a/OpenPGP-Keychain/src/main/res/raw-ach/help_about.html b/OpenPGP-Keychain/src/main/res/raw-ach/help_about.html new file mode 100644 index 000000000..863aeee58 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-ach/help_about.html @@ -0,0 +1,45 @@ + + + +

        http://www.openkeychain.org

        +

        OpenKeychain is an OpenPGP implementation for Android.

        +

        License: GPLv3+

        + +

        Developers OpenKeychain

        +
          +
        • Dominik Schürmann (Lead developer)
        • +
        • Ash Hughes (crypto patches)
        • +
        • Brian C. Barnes
        • +
        • Bahtiar 'kalkin' Gadimov (UI)
        • + +
        +

        Developers APG 1.x

        +
          +
        • 'Thialfihar' (Lead developer)
        • +
        • 'Senecaso' (QRCode, sign key, upload key)
        • +
        • Oliver Runge
        • +
        • Markus Doits
        • +
        +

        Libraries

        + + + diff --git a/OpenPGP-Keychain/src/main/res/raw-ach/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-ach/help_changelog.html new file mode 100644 index 000000000..abf660ba8 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-ach/help_changelog.html @@ -0,0 +1,108 @@ + + + +

        2.3

        +
          +
        • remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)
        • +
        • fix setting expiry dates on keys (thanks to Ash Hughes)
        • +
        • more internal fixes when editing keys (thanks to Ash Hughes)
        • +
        • querying keyservers directly from the import screen
        • +
        • fix layout and dialog style on Android 2.2-3.0
        • +
        • fix crash on keys with empty user ids
        • +
        • fix crash and empty lists when coming back from signing screen
        • +
        • Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source
        • +
        • fix upload of key from signing screen
        • +
        +

        2.2

        +
          +
        • new design with navigation drawer
        • +
        • new public key list design
        • +
        • new public key view
        • +
        • bug fixes for importing of keys
        • +
        • key cross-certification (thanks to Ash Hughes)
        • +
        • handle UTF-8 passwords properly (thanks to Ash Hughes)
        • +
        • first version with new languages (thanks to the contributors on Transifex)
        • +
        • sharing of keys via QR Codes fixed and improved
        • +
        • package signature verification for API
        • +
        +

        2.1.1

        +
          +
        • API Updates, preparation for K-9 Mail integration
        • +
        +

        2.1

        +
          +
        • lots of bug fixes
        • +
        • new API for developers
        • +
        • PRNG bug fix by Google
        • +
        +

        2.0

        +
          +
        • complete redesign
        • +
        • share public keys via qr codes, nfc beam
        • +
        • sign keys
        • +
        • upload keys to server
        • +
        • fixes import issues
        • +
        • new AIDL API
        • +
        +

        1.0.8

        +
          +
        • basic keyserver support
        • +
        • app2sd
        • +
        • more choices for pass phrase cache: 1, 2, 4, 8, hours
        • +
        • translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
        • +
        • bugfixes
        • +
        • optimizations
        • +
        +

        1.0.7

        +
          +
        • fixed problem with signature verification of texts with trailing newline
        • +
        • more options for pass phrase cache time to live (20, 40, 60 mins)
        • +
        +

        1.0.6

        +
          +
        • account adding crash on Froyo fixed
        • +
        • secure file deletion
        • +
        • option to delete key file after import
        • +
        • stream encryption/decryption (gallery, etc.)
        • +
        • new options (language, force v3 signatures)
        • +
        • interface changes
        • +
        • bugfixes
        • +
        +

        1.0.5

        +
          +
        • German and Italian translation
        • +
        • much smaller package, due to reduced BC sources
        • +
        • new preferences GUI
        • +
        • layout adjustment for localization
        • +
        • signature bugfix
        • +
        +

        1.0.4

        +
          +
        • fixed another crash caused by some SDK bug with query builder
        • +
        +

        1.0.3

        +
          +
        • fixed crashes during encryption/signing and possibly key export
        • +
        +

        1.0.2

        +
          +
        • filterable key lists
        • +
        • smarter pre-selection of encryption keys
        • +
        • new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
        • +
        • fixes and additional features (key preselection) for K-9 Mail, new beta build available
        • +
        +

        1.0.1

        +
          +
        • GMail account listing was broken in 1.0.0, fixed again
        • +
        +

        1.0.0

        +
          +
        • K-9 Mail integration, APG supporting beta build of K-9 Mail
        • +
        • support of more file managers (including ASTRO)
        • +
        • Slovenian translation
        • +
        • new database, much faster, less memory usage
        • +
        • defined Intents and content provider for other apps
        • +
        • bugfixes
        • +
        + + diff --git a/OpenPGP-Keychain/src/main/res/raw-ach/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-ach/help_nfc_beam.html new file mode 100644 index 000000000..88492731c --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-ach/help_nfc_beam.html @@ -0,0 +1,12 @@ + + + +

        How to receive keys

        +
          +
        1. Go to your partners contacts and open the contact you want to share.
        2. +
        3. Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.
        4. +
        5. After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.
        6. +
        7. Tap the card and the content will then load on the your device.
        8. +
        + + diff --git a/OpenPGP-Keychain/src/main/res/raw-ach/help_start.html b/OpenPGP-Keychain/src/main/res/raw-ach/help_start.html new file mode 100644 index 000000000..3a6443a2f --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-ach/help_start.html @@ -0,0 +1,19 @@ + + + +

        Getting started

        +

        First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

        + +

        It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

        + +

        I found a bug in OpenKeychain!

        +

        Please report the bug using the issue tracker of OpenKeychain.

        + +

        Contribute

        +

        If you want to help us developing OpenKeychain by contributing code follow our small guide on Github.

        + +

        Translations

        +

        Help translating OpenKeychain! Everybody can participate at OpenKeychain on Transifex.

        + + + diff --git a/OpenPGP-Keychain/src/main/res/raw-ach/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-ach/nfc_beam_share.html new file mode 100644 index 000000000..083e055c7 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-ach/nfc_beam_share.html @@ -0,0 +1,11 @@ + + + +
          +
        1. Make sure that NFC is turned on in Settings > More > NFC and make sure that Android Beam is also on in the same section.
        2. +
        3. Hold the two devices back to back (they have to be almost touching) and you'll feel a vibration.
        4. +
        5. After it vibrates you'll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.
        6. +
        7. Tap the card and the content will then load on the other person’s device.
        8. +
        + + diff --git a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html new file mode 100644 index 000000000..863aeee58 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html @@ -0,0 +1,45 @@ + + + +

        http://www.openkeychain.org

        +

        OpenKeychain is an OpenPGP implementation for Android.

        +

        License: GPLv3+

        + +

        Developers OpenKeychain

        +
          +
        • Dominik Schürmann (Lead developer)
        • +
        • Ash Hughes (crypto patches)
        • +
        • Brian C. Barnes
        • +
        • Bahtiar 'kalkin' Gadimov (UI)
        • + +
        +

        Developers APG 1.x

        +
          +
        • 'Thialfihar' (Lead developer)
        • +
        • 'Senecaso' (QRCode, sign key, upload key)
        • +
        • Oliver Runge
        • +
        • Markus Doits
        • +
        +

        Libraries

        + + + diff --git a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_changelog.html new file mode 100644 index 000000000..abf660ba8 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_changelog.html @@ -0,0 +1,108 @@ + + + +

        2.3

        +
          +
        • remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)
        • +
        • fix setting expiry dates on keys (thanks to Ash Hughes)
        • +
        • more internal fixes when editing keys (thanks to Ash Hughes)
        • +
        • querying keyservers directly from the import screen
        • +
        • fix layout and dialog style on Android 2.2-3.0
        • +
        • fix crash on keys with empty user ids
        • +
        • fix crash and empty lists when coming back from signing screen
        • +
        • Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source
        • +
        • fix upload of key from signing screen
        • +
        +

        2.2

        +
          +
        • new design with navigation drawer
        • +
        • new public key list design
        • +
        • new public key view
        • +
        • bug fixes for importing of keys
        • +
        • key cross-certification (thanks to Ash Hughes)
        • +
        • handle UTF-8 passwords properly (thanks to Ash Hughes)
        • +
        • first version with new languages (thanks to the contributors on Transifex)
        • +
        • sharing of keys via QR Codes fixed and improved
        • +
        • package signature verification for API
        • +
        +

        2.1.1

        +
          +
        • API Updates, preparation for K-9 Mail integration
        • +
        +

        2.1

        +
          +
        • lots of bug fixes
        • +
        • new API for developers
        • +
        • PRNG bug fix by Google
        • +
        +

        2.0

        +
          +
        • complete redesign
        • +
        • share public keys via qr codes, nfc beam
        • +
        • sign keys
        • +
        • upload keys to server
        • +
        • fixes import issues
        • +
        • new AIDL API
        • +
        +

        1.0.8

        +
          +
        • basic keyserver support
        • +
        • app2sd
        • +
        • more choices for pass phrase cache: 1, 2, 4, 8, hours
        • +
        • translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
        • +
        • bugfixes
        • +
        • optimizations
        • +
        +

        1.0.7

        +
          +
        • fixed problem with signature verification of texts with trailing newline
        • +
        • more options for pass phrase cache time to live (20, 40, 60 mins)
        • +
        +

        1.0.6

        +
          +
        • account adding crash on Froyo fixed
        • +
        • secure file deletion
        • +
        • option to delete key file after import
        • +
        • stream encryption/decryption (gallery, etc.)
        • +
        • new options (language, force v3 signatures)
        • +
        • interface changes
        • +
        • bugfixes
        • +
        +

        1.0.5

        +
          +
        • German and Italian translation
        • +
        • much smaller package, due to reduced BC sources
        • +
        • new preferences GUI
        • +
        • layout adjustment for localization
        • +
        • signature bugfix
        • +
        +

        1.0.4

        +
          +
        • fixed another crash caused by some SDK bug with query builder
        • +
        +

        1.0.3

        +
          +
        • fixed crashes during encryption/signing and possibly key export
        • +
        +

        1.0.2

        +
          +
        • filterable key lists
        • +
        • smarter pre-selection of encryption keys
        • +
        • new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
        • +
        • fixes and additional features (key preselection) for K-9 Mail, new beta build available
        • +
        +

        1.0.1

        +
          +
        • GMail account listing was broken in 1.0.0, fixed again
        • +
        +

        1.0.0

        +
          +
        • K-9 Mail integration, APG supporting beta build of K-9 Mail
        • +
        • support of more file managers (including ASTRO)
        • +
        • Slovenian translation
        • +
        • new database, much faster, less memory usage
        • +
        • defined Intents and content provider for other apps
        • +
        • bugfixes
        • +
        + + diff --git a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_nfc_beam.html new file mode 100644 index 000000000..88492731c --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_nfc_beam.html @@ -0,0 +1,12 @@ + + + +

        How to receive keys

        +
          +
        1. Go to your partners contacts and open the contact you want to share.
        2. +
        3. Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.
        4. +
        5. After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.
        6. +
        7. Tap the card and the content will then load on the your device.
        8. +
        + + diff --git a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_start.html b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_start.html new file mode 100644 index 000000000..3a6443a2f --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_start.html @@ -0,0 +1,19 @@ + + + +

        Getting started

        +

        First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

        + +

        It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

        + +

        I found a bug in OpenKeychain!

        +

        Please report the bug using the issue tracker of OpenKeychain.

        + +

        Contribute

        +

        If you want to help us developing OpenKeychain by contributing code follow our small guide on Github.

        + +

        Translations

        +

        Help translating OpenKeychain! Everybody can participate at OpenKeychain on Transifex.

        + + + diff --git a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/nfc_beam_share.html new file mode 100644 index 000000000..083e055c7 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/nfc_beam_share.html @@ -0,0 +1,11 @@ + + + +
          +
        1. Make sure that NFC is turned on in Settings > More > NFC and make sure that Android Beam is also on in the same section.
        2. +
        3. Hold the two devices back to back (they have to be almost touching) and you'll feel a vibration.
        4. +
        5. After it vibrates you'll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.
        6. +
        7. Tap the card and the content will then load on the other person’s device.
        8. +
        + + diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html b/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html new file mode 100644 index 000000000..de16d333f --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html @@ -0,0 +1,45 @@ + + + +

        http://www.openkeychain.org

        +

        OpenKeychain to implementacja OpenPGP na platformę Android.

        +

        Licencja: GPLv3+

        + +

        Deweloperzy OpenKeychain

        +
          +
        • Dominik Schürmann (Wiodący developer)
        • +
        • Ash Hughes (łatki crypto)
        • +
        • Brian C. Barnes
        • +
        • Bahtiar 'kalkin' Gadimov (Interfejs Użytkownika)
        • + +
        +

        Deweloperzy APG 1.x

        +
          +
        • 'Thialfihar' (Wiodacy deweloper)
        • +
        • 'Senecaso' (kody QR, podpisy kluczy, wysyłanie kluczy)
        • +
        • Oliver Runge
        • +
        • Markus Doits
        • +
        +

        Biblioteki

        + + + diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-pl/help_changelog.html new file mode 100644 index 000000000..cdf124d3a --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-pl/help_changelog.html @@ -0,0 +1,108 @@ + + + +

        2.3

        +
          +
        • usunięto zbędne eksportowanie kluczy publicznych przy eksportowaniu kluczy prywatnych (podziękowania dla Ash Hughes)
        • +
        • naprawiono błąd z ustawianiem daty wygaśnięcia kluczy (podziękowania dla Ash Hugens)
        • +
        • więcej wewnętrznych poprawek przy edytowaniu kluczy (podziękowania dla Ash Hughes)
        • +
        • wysyłanie zapytań do serwera kluczy bezpośrednio z ekranu importu
        • +
        • poprawiony wygląd interfejsu i okienek na Androidzie 2.2-3.0
        • +
        • naprawiono awarię programu dla kluczy z pustym identyfikatorem użytkownika
        • +
        • naprawiono awarię aplikacji przy powrocie z ekranu podpisywania
        • +
        • Bouncy Castle (biblioteka kryptograficzna) zaktualizowana z wersji 1.47 do 1.50 i kompilowana ze źródeł
        • +
        • naprawiony błąd przy wysyłaniu klucza z ekranu podpisywania
        • +
        +

        2.2

        +
          +
        • nowy wygląd z panelem nawigacji
        • +
        • nowy wygląd listy kluczy publicznych
        • +
        • nowy widok klucza publicznego
        • +
        • naprawiono błędy związane z importowaniem kluczy
        • +
        • krzyżowa certyfikacja kluczy (podziękowania dla Ash Hughes)
        • +
        • hasła zapisane w UTF-8 są teraz prawidłowo obsługiwane (podziękowania dla Ash Hughes)
        • +
        • pierwsza wersja z nowymi językami (podziękowania dla tłumaczy-wolontariuszy z Transifex)
        • +
        • udostępnianie kluczy przez kody QR zostało poprawione i ulepszone
        • +
        • weryfikacja podpisu paczki dla API
        • +
        +

        2.1.1

        +
          +
        • aktualizacje API, przygotowanie do integracji z K-9 Mail
        • +
        +

        2.1

        +
          +
        • wiele poprawek błędów
        • +
        • nowe API dla programistów
        • +
        • Naprawiono błąd generatora liczb losowych (PRNG), Google.
        • +
        +

        2.0

        +
          +
        • kompletna przebudowa
        • +
        • udostępnianie kluczy publicznych przez kody QR oraz NFC
        • +
        • możliwość podpisywania kluczem
        • +
        • wysyłanie kluczy na serwer
        • +
        • naprawiono problemy związane z importowaniem
        • +
        • nowy AIDL API
        • +
        +

        1.0.8

        +
          +
        • podstawowa obsługa serwerów kluczy
        • +
        • app2sd
        • +
        • dodano więcej przedziałów czasowych zapamiętywania hasła: 1, 2, 4, 8 godzin
        • +
        • tłumaczenia: norweski (podziękowania dla Sander Danielsen), chiński (podziękowania dla Zhang Fredrick)
        • +
        • naprawione błędy
        • +
        • usprawnienia
        • +
        +

        1.0.7

        +
          +
        • naprawiono problem z weryfikowaniem podpisu tekstów kończących się znakiem nowej linii
        • +
        • dodano więcej przedziałów czasowych zapamiętywania hasła (20, 40, 60 minut)
        • +
        +

        1.0.6

        +
          +
        • naprawiono błąd powodujący awarię aplikacji przy dodawaniu nowego konta na Androidzie 2.2 Froyo
        • +
        • dodano bezpieczne usuwanie plików
        • +
        • Dodano możliwość usuwania plików kluczy po zaimportowaniu
        • +
        • możliwość strumieniowego szyfrowania/deszyfrowania (galeria i inne)
        • +
        • nowe opcje (języki, wymuszanie podpisów v3)
        • +
        • zmiany w interfejsie
        • +
        • naprawione błędy
        • +
        +

        1.0.5

        +
          +
        • tłumaczenie na niemiecki i włoski
        • +
        • znaczne zmniejszenie rozmiaru paczki, z powodu zredukowania źródeł BC
        • +
        • nowy interfejs graficzny Właściwości
        • +
        • usprawnienia wyglądu dla lokalizacji
        • +
        • naprawa błędu z podpisami
        • +
        +

        1.0.4

        +
          +
        • naprawiono kolejny błąd powodujący awarię aplikacji, spowodowany przez jakąś usterkę w SDK przy budowaniu zapytań
        • +
        +

        1.0.3

        +
          +
        • naprawiono błąd w trakcie szyfrowania/podpisywania i prawdopodobnie eksportowania klucza
        • +
        +

        1.0.2

        +
          +
        • dodano możliwość filtrowania listy kluczy
        • +
        • sprytniejsze automatyczne wybieranie kluczy szyfrujących
        • +
        • dodano nowy sposób obsługi intencji "wyświetl" i "wyślij", umożliwia szyfrowanie/deszyfrowanie plików wprost z menadżera plików.
        • +
        • poprawki i dodatkowe funkcje (podpowiedź wyboru klucza) dla K-9 Mail, nowe wydanie beta dostępne
        • +
        +

        1.0.1

        +
          +
        • wyświetlanie kont w GMailu było zepsute w 1.0.0, naprawiono je ponownie
        • +
        +

        1.0.0

        +
          +
        • integracja z K-9 Mail, APG obsługuje wersję beta K-9 Mail
        • +
        • dodano wsparcie dla większej liczby menadżerów plików (włącznie z ASTRO)
        • +
        • tłumaczenie na słoweński
        • +
        • Wykorzystanie nowej bazy danych, która jest znacznie szybsza i mniej pamięciożerna
        • +
        • zdefiniowano intecję i dostawców treści dla pozostałych aplikacji
        • +
        • naprawione błędy
        • +
        + + diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-pl/help_nfc_beam.html new file mode 100644 index 000000000..53db5e80c --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-pl/help_nfc_beam.html @@ -0,0 +1,12 @@ + + + +

        Jak odbierać klucze

        +
          +
        1. Wejdź do listy kontaktów Twojego partnera i otwórz kontakt, który chcesz przesłać.
        2. +
        3. Przytrzymaj oba urządzenia plecami do siebie (powinny się niemal dotykać) i poczujesz wibrację.
        4. +
        5. Po zakończeniu wibracji zobaczysz, że zawartość urządzenia partnera zamienia się w obiekt zbliżony do wizytówki, z animacją rodem ze Star Treka w tle.
        6. +
        7. Dotknij wizytówkę, a jej zawartość zostanie wysłana na Twoje urządzenie.
        8. +
        + + diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/help_start.html b/OpenPGP-Keychain/src/main/res/raw-pl/help_start.html new file mode 100644 index 000000000..539388789 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-pl/help_start.html @@ -0,0 +1,19 @@ + + + +

        Pierwsze kroki

        +

        Po pierwsze potrzebujesz swoją osobistą parę kluczy. Stwórz ją, korzystając z odpowiedniej opcji w sekcji "Moje klucze" lub też zaimportuj istniejącą parę korzystając z sekcji "Importuj klucze". Następnie możesz pobrać klucze Twoich znajomych lub wymieniać się z nimi za pośrednictwem kodów QR lub technologii NFC.

        + +

        Zalecana jest instalacja menadżera plików OI File Manager w celu zapewnienia wygodniejszego wyboru plików oraz programu Barcode Scanner, który jest w stanie skanować wygenerowane kody QR. Kliknięcie na powyższe linki przekieruje Cię do sklepu Google Play / F-Droid.

        + +

        Znalazłem błąd w OpenKeychain!

        +

        Zgłoś błąd korzystając z systemu śledzenia błędów OpenKeychain.

        + +

        Wkład

        +

        Jeżeli chcesz pomóc nam rozwijać OpenKeychain jako programista, zapoznaj się z naszym małym poradnikiem na Githubie.

        + +

        Tłumaczenia

        +

        Pomóż przetłumaczyć OpenKeychain! Każdy może wziąć udział przez stronę OpenKeychain w serwisie Transifex.

        + + + diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-pl/nfc_beam_share.html new file mode 100644 index 000000000..f17e44079 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-pl/nfc_beam_share.html @@ -0,0 +1,11 @@ + + + +
          +
        1. Upewnij się, że NFC (Near Field Communication, pol.: komunikacja bliskiego zasięgu) jest włączone. W tym celu wejdź w Ustawienia > Inne > NFC. Upewnij się również, że włączona jest funkcja Android Beam (znajduje się w tym samym miejscu).
        2. +
        3. Przytrzymaj oba urządzenia plecami do siebie (powinny się niemal dotykać) i poczujesz wibrację.
        4. +
        5. Po zakończeniu wibracji zobaczysz, że zawartość urządzenia partnera zamienia się w obiekt zbliżony do wizytówki, z animacją rodem ze Star Treka w tle.
        6. +
        7. Dotknij wizytówkę, a jej zawartość zostanie wysłana na urządzenie drugiej osoby.
        8. +
        + + diff --git a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html new file mode 100644 index 000000000..863aeee58 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html @@ -0,0 +1,45 @@ + + + +

        http://www.openkeychain.org

        +

        OpenKeychain is an OpenPGP implementation for Android.

        +

        License: GPLv3+

        + +

        Developers OpenKeychain

        +
          +
        • Dominik Schürmann (Lead developer)
        • +
        • Ash Hughes (crypto patches)
        • +
        • Brian C. Barnes
        • +
        • Bahtiar 'kalkin' Gadimov (UI)
        • + +
        +

        Developers APG 1.x

        +
          +
        • 'Thialfihar' (Lead developer)
        • +
        • 'Senecaso' (QRCode, sign key, upload key)
        • +
        • Oliver Runge
        • +
        • Markus Doits
        • +
        +

        Libraries

        + + + diff --git a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_changelog.html new file mode 100644 index 000000000..abf660ba8 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_changelog.html @@ -0,0 +1,108 @@ + + + +

        2.3

        +
          +
        • remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)
        • +
        • fix setting expiry dates on keys (thanks to Ash Hughes)
        • +
        • more internal fixes when editing keys (thanks to Ash Hughes)
        • +
        • querying keyservers directly from the import screen
        • +
        • fix layout and dialog style on Android 2.2-3.0
        • +
        • fix crash on keys with empty user ids
        • +
        • fix crash and empty lists when coming back from signing screen
        • +
        • Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source
        • +
        • fix upload of key from signing screen
        • +
        +

        2.2

        +
          +
        • new design with navigation drawer
        • +
        • new public key list design
        • +
        • new public key view
        • +
        • bug fixes for importing of keys
        • +
        • key cross-certification (thanks to Ash Hughes)
        • +
        • handle UTF-8 passwords properly (thanks to Ash Hughes)
        • +
        • first version with new languages (thanks to the contributors on Transifex)
        • +
        • sharing of keys via QR Codes fixed and improved
        • +
        • package signature verification for API
        • +
        +

        2.1.1

        +
          +
        • API Updates, preparation for K-9 Mail integration
        • +
        +

        2.1

        +
          +
        • lots of bug fixes
        • +
        • new API for developers
        • +
        • PRNG bug fix by Google
        • +
        +

        2.0

        +
          +
        • complete redesign
        • +
        • share public keys via qr codes, nfc beam
        • +
        • sign keys
        • +
        • upload keys to server
        • +
        • fixes import issues
        • +
        • new AIDL API
        • +
        +

        1.0.8

        +
          +
        • basic keyserver support
        • +
        • app2sd
        • +
        • more choices for pass phrase cache: 1, 2, 4, 8, hours
        • +
        • translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
        • +
        • bugfixes
        • +
        • optimizations
        • +
        +

        1.0.7

        +
          +
        • fixed problem with signature verification of texts with trailing newline
        • +
        • more options for pass phrase cache time to live (20, 40, 60 mins)
        • +
        +

        1.0.6

        +
          +
        • account adding crash on Froyo fixed
        • +
        • secure file deletion
        • +
        • option to delete key file after import
        • +
        • stream encryption/decryption (gallery, etc.)
        • +
        • new options (language, force v3 signatures)
        • +
        • interface changes
        • +
        • bugfixes
        • +
        +

        1.0.5

        +
          +
        • German and Italian translation
        • +
        • much smaller package, due to reduced BC sources
        • +
        • new preferences GUI
        • +
        • layout adjustment for localization
        • +
        • signature bugfix
        • +
        +

        1.0.4

        +
          +
        • fixed another crash caused by some SDK bug with query builder
        • +
        +

        1.0.3

        +
          +
        • fixed crashes during encryption/signing and possibly key export
        • +
        +

        1.0.2

        +
          +
        • filterable key lists
        • +
        • smarter pre-selection of encryption keys
        • +
        • new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
        • +
        • fixes and additional features (key preselection) for K-9 Mail, new beta build available
        • +
        +

        1.0.1

        +
          +
        • GMail account listing was broken in 1.0.0, fixed again
        • +
        +

        1.0.0

        +
          +
        • K-9 Mail integration, APG supporting beta build of K-9 Mail
        • +
        • support of more file managers (including ASTRO)
        • +
        • Slovenian translation
        • +
        • new database, much faster, less memory usage
        • +
        • defined Intents and content provider for other apps
        • +
        • bugfixes
        • +
        + + diff --git a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_nfc_beam.html new file mode 100644 index 000000000..88492731c --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_nfc_beam.html @@ -0,0 +1,12 @@ + + + +

        How to receive keys

        +
          +
        1. Go to your partners contacts and open the contact you want to share.
        2. +
        3. Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.
        4. +
        5. After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.
        6. +
        7. Tap the card and the content will then load on the your device.
        8. +
        + + diff --git a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_start.html b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_start.html new file mode 100644 index 000000000..3a6443a2f --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_start.html @@ -0,0 +1,19 @@ + + + +

        Getting started

        +

        First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

        + +

        It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

        + +

        I found a bug in OpenKeychain!

        +

        Please report the bug using the issue tracker of OpenKeychain.

        + +

        Contribute

        +

        If you want to help us developing OpenKeychain by contributing code follow our small guide on Github.

        + +

        Translations

        +

        Help translating OpenKeychain! Everybody can participate at OpenKeychain on Transifex.

        + + + diff --git a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/nfc_beam_share.html new file mode 100644 index 000000000..083e055c7 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/nfc_beam_share.html @@ -0,0 +1,11 @@ + + + +
          +
        1. Make sure that NFC is turned on in Settings > More > NFC and make sure that Android Beam is also on in the same section.
        2. +
        3. Hold the two devices back to back (they have to be almost touching) and you'll feel a vibration.
        4. +
        5. After it vibrates you'll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.
        6. +
        7. Tap the card and the content will then load on the other person’s device.
        8. +
        + + diff --git a/OpenPGP-Keychain/src/main/res/values-ach/strings.xml b/OpenPGP-Keychain/src/main/res/values-ach/strings.xml new file mode 100644 index 000000000..3d00a143f --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/values-ach/strings.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OpenPGP-Keychain/src/main/res/values-cs-rCZ/strings.xml b/OpenPGP-Keychain/src/main/res/values-cs-rCZ/strings.xml new file mode 100644 index 000000000..3d00a143f --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/values-cs-rCZ/strings.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OpenPGP-Keychain/src/main/res/values-pl/strings.xml b/OpenPGP-Keychain/src/main/res/values-pl/strings.xml new file mode 100644 index 000000000..afdf72223 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/values-pl/strings.xml @@ -0,0 +1,450 @@ + + + + Kontakty + Klucze prywatne + Wybierz Klucz Publiczny + Wybierz Klucz Prywatny + Zaszyfruj + Odszyfruj + Hasło + Utwórz Klucz + Edytuj Klucz + Właściwości + Zarejestrowane Aplikacje + Właściwości serwera kluczy + Zmień hasło + Ustaw hasło + Wyślij maila... + Wyślij plik... + Zaszyfruj do pliku + Odszyfruj do pliku + Importuj klucze + Eksportuj klucz + Eksportuj klucze + Nie znaleziono klucza + Wyślij zapytanie do serwera kluczy + Wyślij do serwera kluczy + Nieznany klucz podpisu + Certyfikuj klucz + Szczegóły klucza + Pomoc + + Identyfikator użytkownika + Klucze + Ogólne + Domyślne + Zaawansowane + Klucz główny + Główny identyfikator użytkownika + Działania + Twój klucz użyty do certyfikacji + Wyślij klucz + Serwer kluczy + Zaszyfruj i/lub podpisz + Deszyfruj i weryfikuj + + Podpisz + Certyfikuj + Odszyfruj + Deszyfruj i weryfikuj + Wybierz odbiorców + Zaszyfruj plik + Zapisz + Anuluj + Usuń + Żaden + Ok + Zmień hasło + Ustaw hasło + Wyszukaj + Wyślij do serwera kluczy + Dalej + Wstecz + Schowek + Podziel się z... + Klucz wyszukiwania + Pokaż zaawanowane ustawienia + Ukryj zaawansowane ustawienia + + Ustawienia + Pomoc + Zaimportuj z pliku + Zaimportuj z kodu QR + Import + Zaimportuj przy użyciu NFC + Eksportuj wszystkie klucze + Eksportuj wszystkie klucze prywatne + Eksportuj do pliku + Usuń klucz + Stwórz klucz + Stwórz klucz (tryb zaawansowany) + Znajdź + Serwer kluczy + Serwer kluczy... + Aktualizuj z serwera kluczy + Wyślij do serwera kluczy + Udostepnij... + Udostepnij odcisk... + Udostępnij cały klucz... + z... + z... + za pomocą kodu QR + za pomocą kodu QR + za pomocą NFC + Kopiuj do schowka + Klucz podpisu + Ustawienia Beam + Anuluj + Zaszyfruj do... + Wybierz wszystko + + Podpis + Wiadomość + Plik + Brak hasła + Hasło + Ponów + Algorytm + ASCII Armor + Odbiorcy + Usuń po zaszyfrowaniu + Usuń po odszyfrowaniu + Udostępnij po zaszyfrowaniu + Algorytm szyfrujący + Algorytm funkcji skrótu + Klucz publiczny + Hasło + Bufor haseł + Kompresja wiadomości + Kompresja plików + Wymuś stare podpisy OpenPGPv3 + Serwery kluczy + Identyfikator klucza + Utworzenia + Wygaśnięcia + Wykorzystanie + Rozmiar klucza + Identyfikator głównego użytkownika + Imię + Komentarz + Adres email + Identyfikator użytkownika podpisu (sign user id) + Podpisz e-mail + Wyślij klucz do serwera kluczy po certyfikacji + Odcisk + Wybierz + Ustaw datę wygaśnięcia + + wybrano %d + wybrano %d + wybrano %d + + <bez nazwy> + <żaden> + <brak klucza> + <Brak adresu email> + + może szyfrować + może podpisywać + wygasły + unieważniony + Identyfikator użytkownika + + 1 kontakt + %d kontakty + %d kontaktów + + + %d serwer kluczy + %d serwerów kluczy + %d serwerów kluczy + + Odcisk: + Klucz prywatny: + + Żaden + Tylko podpisz + Tylko zaszyfruj + Podpisz i zaszyfruj + 15 sekund + 1 minuta + 3 minuty + 5 minut + 10 minut + 20 minut + 40 minut + 1 godzina + 2 godziny + 4 godziny + 8 godzin + na zawsze + DSA + ElGamal + RSA + Otwórz... + Ostrzeżenie + Błąd + Błąd: %s + + Nieprawidłowe hasło. + Użycie zawartości schowka. + Najpierw ustaw hasło. + Nie zainstalowano żadnego kompatybilnego menadżera plików. + Hasła nie pasują do siebie + Puste hasła nie są dozwolone. + Szyfrowanie symetryczne. + Podaj hasło dla \'%s\' + Czy jesteś pewien że chcesz usunąć\n%s? + Usunięto pomyślnie. + Najpierw wskaż plik. + Odszyfrowano pomyślnie. + Zaszyfrowano pomyślnie. + Pomyślnie zaszyfrowano do schowka. + Podaj hasło dwukrotnie. + Wybierz co najmniej jeden klucz szyfrujący. + Wybierz co najmniej jeden klucz szyfrujący lub klucz podpisujący. + Wskaż, do którego pliku zapisać zaszyfrowane dane.\nOSTRZEŻENIE: Plik zostanie nadpisany, jeżeli istnieje. + Wskaż, do którego pliku zapisać odszyfrowane dane.\nOSTRZEŻENIE: Plik zostanie nadpisany, jeżeli istnieje. + Wskaż, do którego pliku wyeksportować dane.\nOSTRZEŻENIE: Plik zostanie nadpisany, jeżeli istnieje. + Wskaż, do którego pliku zapisać eksportowane dane.\nOSTRZEŻENIE: Masz zamiar zapisać klucze PRYWATNE (tajne)\nOSTRZEŻENIE: Plik zostanie nadpisany, jeżeli istnieje. + Czy na pewno chcesz usunąć klucz \'%s\'?\nNie można cofnąć tej operacji! + Czy na pewno chcesz usunąć wszystkie zaznaczone klucze?\nTej operacji nie można cofnąć! + Czy na pewno chcesz usunąć klucz prywatny \'%s\'?\nNie można cofnąć tej operacji! + Czy wyeksportować również klucze prywatne? + + Pomyślnie dodano %d klucz + Pomyślnie dodano %d kluczy + Pomyślnie dodano %d kluczy + + + i zaktualizowano %d klucz. + i zaktualizowano %d kluczy. + i zaktualizowano %d kluczy. + + + Pomyślnie dodano %d klucz. + Pomyślnie dodano %d kluczy. + Pomyślnie dodano %d kluczy. + + + Pomyślnie zaktualizowano %d klucz. + Pomyślnie zaktualizowano %d kluczy. + Pomyślnie zaktualizowano %d kluczy. + + Nie dodano ani zaktualizowano żadnych kluczy. + Pomyślnie wyeksportowano 1 klucz. + Pomyślnie wyeksportowano %d kluczy. + Nie wyeksportowano żadnych kluczy. + Uwaga: algorytm EnGamal jest obsługiwany tylko przez podklucze i użyty zostanie najbliższy rozmiar klucza z podanych: 1536, 2048, 3072, 4096, 8192. + Uwaga: generowanie klucza RSA o długości 1024 bity i mniejszej jest uważane za niebezpieczne i wyłączone dla tworzenia nowych kluczy. + Nie można znaleźć klucza %08X. + + Znaleziono %d klucz. + Znaleziono %d kluczy. + Znaleziono %d kluczy. + + Nieznany podpis, naciśnij przycisk, aby wyszukać brakujący klucz. + + Zignorowano %d niepoprawny klucz prywatny. Prawdopodobnie został wyeksportowany przy uzyciu opcji\n --export-secret-subkeys\nUpewnij się że eksportujesz go z opcją\n --export-secret-keys\nktóra jest poprawna. + Zignorowano %d niepoprawnych kluczy prywatnych. Prawdopodobnie zostały wyeksportowane przy uzyciu opcji\n --export-secret-subkeys\nUpewnij się że eksportujesz je z opcją\n --export-secret-keys\nktóra jest poprawna. + zignorowano %d niepoprawnych kluczy prywatnych. Prawdopodobnie zostały wyeksportowane przy uzyciu opcji\n --export-secret-subkeys\nUpewnij się że eksportujesz je z opcją\n --export-secret-keys\nktóra jest poprawna. + + Pomyślnie wysłano klucz na serwer + Pomyślnie podpisano klucz + Lista jest pusta! + Pomyślnie wysłano klucz przez NFC! + Klucz został skopiowany do schowka! + Klucz został już wcześniej podpisany! + Wybierz klucz, który zostanie użyty do podpisania! + Klucz ma za duży rozmiar by być udostępniony w ten sposób! + + usuwanie \'%s\' zakończone niepowodzeniem + plik nie znaleziony + nie znaleziono pasującego klucza prywatnego + napotkano nieznany rodzaj szyfrowania + zewnętrzne urządzenie jest niegotowe + nieprawidłowy adres email \'%s\' + klucz musi mieć rozmiar co najmniej 512 bitów + klucz EnGamal nie może być kluczem głównym + wybrano nieznany algorytm + musisz wskazać imię + nie znaleziono adresu email + musisz wskazać adres email + potrzeba co najmniej jednego identyfikatora użytkownika + główny identyfikator użytkownika nie może być pusty + potrzeba co najmniej klucza głównego + nie podano hasła ani klucza szyfrującego + podpisywanie nie powiodło się + nie podano hasła + nie podano klucza podpisu + nieprawidłowe dane + uszkodzone dane + Sprawdzanie spójności zakończone niepowodzeniem! Dane były modyfikowane! + nie znaleziono pakietu z szyfrowaniem symatrycznym + nieprawidłowe hasło + błąd przy zapisywaniu kluczy + nie można wyodrębnić klucza prywatnego + Dane binarne pozbawione pliku nie są obsługiwane. To jest wspierane tylko dla akcji ACTION_ENCRYPT_STREAM_AND_RETURN. + Potrzebujesz Androida 4.1 Jelly Bean, aby korzystać z Android NFC Beam! + NCF jest niedostępne na twoim urządzeniu + Nie ma nic do zaimportowania! + data wygaśnięcia musi być późniejsza niż data stworzenia + nie możesz usunąć tego kontaktu, ponieważ należy do ciebie. + nie możesz usunąć tych kontaktów, ponieważ należą do ciebie:\n%s + Niewystarczające zapytanie do serwera + Odpytywanie serwera zakończone niepowodzeniem + Za dużo odpowiedzi + Plik jest pusty + Wystąpił błąd ogólny, proszę zgłoś go autorom OpenKeychain. + + Usuń go z ekranu \'Moje klucze\'! + Usuń je z ekranu \'Moje klucze\'! + Usuń je z ekranu \'Moje klucze\'! + + + Część wczytanego pliku jest poprawnym obiektem OpenPGP, ale nie jest kluczem OpenPGP + Część wczytanego pliku to poprawne obiekty OpenPGP, ale nie są kluczami OpenPGP + Część wczytanego pliku to poprawne obiekty OpenPGP, ale nie są kluczami OpenPGP + + + Gotowe. + Anuluj + zapisywanie... + importowanie... + eksportowanie... + budowanie klucza... + przygotowywanie klucza glównego... + podpisywanie klucza głównego... + budowanie głównego zbioru kluczy... + dodawanie podkluczy... + zapisywanie klucza... + + eksportowanie klucza... + eksportowanie kluczy... + eksportowanie kluczy... + + + generowanie klucza, może to potrwać do 3 minut... + generowanie kluczy, może to potrwać do 3 minut... + generowanie kluczy, może to potrwać do 3 minut... + + wyodrębnianie klucza podpisu... + wyodrębnianie klucza... + przygotowywanie strumieni... + szyfrowanie danych... + deszyfrowywanie danych... + przygotowywanie podpisu... + generowanie podpisu... + przetwarzanie podpisu... + weryfikowanie podpisu... + podpisywanie... + czytanie danych... + szukanie klucza... + dekompresja danych... + weryfikacja spójności... + usuwanie \'%s\' bezpiecznie… + odpytywanie... + + Wyszukaj klucze publiczne + Wyszukaj klucze prywatne + Udostępnij klucz... + + 512 + 1024 + 2048 + 4096 + + szybka + bardzo wolna + + Początek + FAQ + NFC Beam + Dziennik zmian + O programie + Wersja: + + Zaimportuj wybrane klucze + Importuj, podpisz i wyślij wybrane klucze + Importuj ze schowka + + Brakuje kodu QR o identyfikatorze %s + Brakuje kodów QR o identyfikatorach %s + Brakuje kodów QR o identyfikatorach %s + + Zacznij od kodu QR o identyfikatorze 1 + Kod QR zniekształcony! Spróbuj jeszcze raz! + Skanowanie kodu QR zakończone! + Odcisk klucza zawarty w tym kodzie QR jest za krótki (< 16 znaków) + Odczytaj kod QR przy pomocy \'Barcode Scanner\' + Aby odbierać klucze przez NFC, urządzenie musi być odblokowane. + Pomoc + Odczytaj klucz ze schowka + + Deszyfruj plik korzystając z OpenKeychain + Importuj klucz korzystając z OpenKeychain + Zaszyfruj korzystając z OpenKeychain + Deszyfruj korzystając z OpenKeychain + + Brak zarejestrowanych aplikacji!\n\nZewnętrzne aplikacje mogą żądać dostępu do OpenKeychain. Po przyznaniu dostępu, będa wyświetlone tutaj. + Pokaż zaawanowane ustawienia + Ukryj zaawansowane ustawienia + Nie wybrano klucza + Wybierz klucz + Zapisz + Anuluj + Odwołaj dostęp + Nazwa paczki + Skrót SHA-256 podpisu paczki + Wyświetlona aplikacja prosi o dostęp do OpenKeychain.\nZezwolić?\n\nOSTRZEZENIE: Jeżeli nie wiesz, czemu wyświetlił się ten komunikat, nie zezwalaj na dostęp! Możesz to również zrobić później, korzystając z ekranu \'Zarejestrowane aplikacje\'. + Zezwól na dostęp + Odmów dostępu + Wybierz klucz! + Nie znaleziono kluczy publiczych dla tych identyfikatorów użytkownika: + Więcej niż jeden klucz publiczny istnieje dla tych identyfikatorów użytkownika: + Proszę przejrzeć listę adresatów! + Sprawdzanie podpisu zakończone niepowodzeniem! Czy zainstalowałeś tę aplikację z innego źródła? Jeżeli jesteś pewien, że nie jest to atak, odwołaj rejestrację teg aplikacji w OpenKeychain, a następnie zarejestruj ją ponownie. + + Udostępnij przez kod QR + Przejdź przez wszystkiego kody QR korzystając z przycisku \'Nastepny\' i skanuj je pojedynczo. + Odcisk: + Kod QR o identyfikatorze %1$d z %2$d + Udostępnij przez NFC + + + 1 klucz wybrany. + %d kluczy wybranych. + %d kluczy wybranych. + + Żadne klucze nie są jeszcze dostępne... + Możesz zacząć od + lub + tworzenie własnego klucza + importowanie kluczy. + + Edytuj ten klucz + Zaszyfruj do tego kontaktu + Certyfikuj klucz tego kontaktu + Informacje + Certyfikaty + + Kontakty + Zaszyfruj + Deszyfruj + importuj klucze + Moje klucze + Zarejestrowane aplikacje + Otwórz panel nawigacji + Zamknij panel nawigacji + Edytuj + Moje klucze + Klucz prywatny + dostępny + niedostepny + Identyfikator użytkownika do podpisu + Ponowne stosowanie certyfikatów + + diff --git a/OpenPGP-Keychain/src/main/res/values-zh-rTW/strings.xml b/OpenPGP-Keychain/src/main/res/values-zh-rTW/strings.xml new file mode 100644 index 000000000..3d00a143f --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/values-zh-rTW/strings.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + From c6189c52186fbbc47ef476f75c66137da1c027eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 21 Mar 2014 10:29:29 +0100 Subject: [PATCH 095/253] remove ach language, not supported by android --- .../src/main/res/raw-ach/help_about.html | 45 -------- .../src/main/res/raw-ach/help_changelog.html | 108 ------------------ .../src/main/res/raw-ach/help_nfc_beam.html | 12 -- .../src/main/res/raw-ach/help_start.html | 19 --- .../src/main/res/raw-ach/nfc_beam_share.html | 11 -- .../src/main/res/values-ach/strings.xml | 27 ----- 6 files changed, 222 deletions(-) delete mode 100644 OpenPGP-Keychain/src/main/res/raw-ach/help_about.html delete mode 100644 OpenPGP-Keychain/src/main/res/raw-ach/help_changelog.html delete mode 100644 OpenPGP-Keychain/src/main/res/raw-ach/help_nfc_beam.html delete mode 100644 OpenPGP-Keychain/src/main/res/raw-ach/help_start.html delete mode 100644 OpenPGP-Keychain/src/main/res/raw-ach/nfc_beam_share.html delete mode 100644 OpenPGP-Keychain/src/main/res/values-ach/strings.xml diff --git a/OpenPGP-Keychain/src/main/res/raw-ach/help_about.html b/OpenPGP-Keychain/src/main/res/raw-ach/help_about.html deleted file mode 100644 index 863aeee58..000000000 --- a/OpenPGP-Keychain/src/main/res/raw-ach/help_about.html +++ /dev/null @@ -1,45 +0,0 @@ - - - -

        http://www.openkeychain.org

        -

        OpenKeychain is an OpenPGP implementation for Android.

        -

        License: GPLv3+

        - -

        Developers OpenKeychain

        -
          -
        • Dominik Schürmann (Lead developer)
        • -
        • Ash Hughes (crypto patches)
        • -
        • Brian C. Barnes
        • -
        • Bahtiar 'kalkin' Gadimov (UI)
        • - -
        -

        Developers APG 1.x

        -
          -
        • 'Thialfihar' (Lead developer)
        • -
        • 'Senecaso' (QRCode, sign key, upload key)
        • -
        • Oliver Runge
        • -
        • Markus Doits
        • -
        -

        Libraries

        - - - diff --git a/OpenPGP-Keychain/src/main/res/raw-ach/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-ach/help_changelog.html deleted file mode 100644 index abf660ba8..000000000 --- a/OpenPGP-Keychain/src/main/res/raw-ach/help_changelog.html +++ /dev/null @@ -1,108 +0,0 @@ - - - -

        2.3

        -
          -
        • remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)
        • -
        • fix setting expiry dates on keys (thanks to Ash Hughes)
        • -
        • more internal fixes when editing keys (thanks to Ash Hughes)
        • -
        • querying keyservers directly from the import screen
        • -
        • fix layout and dialog style on Android 2.2-3.0
        • -
        • fix crash on keys with empty user ids
        • -
        • fix crash and empty lists when coming back from signing screen
        • -
        • Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source
        • -
        • fix upload of key from signing screen
        • -
        -

        2.2

        -
          -
        • new design with navigation drawer
        • -
        • new public key list design
        • -
        • new public key view
        • -
        • bug fixes for importing of keys
        • -
        • key cross-certification (thanks to Ash Hughes)
        • -
        • handle UTF-8 passwords properly (thanks to Ash Hughes)
        • -
        • first version with new languages (thanks to the contributors on Transifex)
        • -
        • sharing of keys via QR Codes fixed and improved
        • -
        • package signature verification for API
        • -
        -

        2.1.1

        -
          -
        • API Updates, preparation for K-9 Mail integration
        • -
        -

        2.1

        -
          -
        • lots of bug fixes
        • -
        • new API for developers
        • -
        • PRNG bug fix by Google
        • -
        -

        2.0

        -
          -
        • complete redesign
        • -
        • share public keys via qr codes, nfc beam
        • -
        • sign keys
        • -
        • upload keys to server
        • -
        • fixes import issues
        • -
        • new AIDL API
        • -
        -

        1.0.8

        -
          -
        • basic keyserver support
        • -
        • app2sd
        • -
        • more choices for pass phrase cache: 1, 2, 4, 8, hours
        • -
        • translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
        • -
        • bugfixes
        • -
        • optimizations
        • -
        -

        1.0.7

        -
          -
        • fixed problem with signature verification of texts with trailing newline
        • -
        • more options for pass phrase cache time to live (20, 40, 60 mins)
        • -
        -

        1.0.6

        -
          -
        • account adding crash on Froyo fixed
        • -
        • secure file deletion
        • -
        • option to delete key file after import
        • -
        • stream encryption/decryption (gallery, etc.)
        • -
        • new options (language, force v3 signatures)
        • -
        • interface changes
        • -
        • bugfixes
        • -
        -

        1.0.5

        -
          -
        • German and Italian translation
        • -
        • much smaller package, due to reduced BC sources
        • -
        • new preferences GUI
        • -
        • layout adjustment for localization
        • -
        • signature bugfix
        • -
        -

        1.0.4

        -
          -
        • fixed another crash caused by some SDK bug with query builder
        • -
        -

        1.0.3

        -
          -
        • fixed crashes during encryption/signing and possibly key export
        • -
        -

        1.0.2

        -
          -
        • filterable key lists
        • -
        • smarter pre-selection of encryption keys
        • -
        • new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
        • -
        • fixes and additional features (key preselection) for K-9 Mail, new beta build available
        • -
        -

        1.0.1

        -
          -
        • GMail account listing was broken in 1.0.0, fixed again
        • -
        -

        1.0.0

        -
          -
        • K-9 Mail integration, APG supporting beta build of K-9 Mail
        • -
        • support of more file managers (including ASTRO)
        • -
        • Slovenian translation
        • -
        • new database, much faster, less memory usage
        • -
        • defined Intents and content provider for other apps
        • -
        • bugfixes
        • -
        - - diff --git a/OpenPGP-Keychain/src/main/res/raw-ach/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-ach/help_nfc_beam.html deleted file mode 100644 index 88492731c..000000000 --- a/OpenPGP-Keychain/src/main/res/raw-ach/help_nfc_beam.html +++ /dev/null @@ -1,12 +0,0 @@ - - - -

        How to receive keys

        -
          -
        1. Go to your partners contacts and open the contact you want to share.
        2. -
        3. Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.
        4. -
        5. After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.
        6. -
        7. Tap the card and the content will then load on the your device.
        8. -
        - - diff --git a/OpenPGP-Keychain/src/main/res/raw-ach/help_start.html b/OpenPGP-Keychain/src/main/res/raw-ach/help_start.html deleted file mode 100644 index 3a6443a2f..000000000 --- a/OpenPGP-Keychain/src/main/res/raw-ach/help_start.html +++ /dev/null @@ -1,19 +0,0 @@ - - - -

        Getting started

        -

        First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

        - -

        It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

        - -

        I found a bug in OpenKeychain!

        -

        Please report the bug using the issue tracker of OpenKeychain.

        - -

        Contribute

        -

        If you want to help us developing OpenKeychain by contributing code follow our small guide on Github.

        - -

        Translations

        -

        Help translating OpenKeychain! Everybody can participate at OpenKeychain on Transifex.

        - - - diff --git a/OpenPGP-Keychain/src/main/res/raw-ach/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-ach/nfc_beam_share.html deleted file mode 100644 index 083e055c7..000000000 --- a/OpenPGP-Keychain/src/main/res/raw-ach/nfc_beam_share.html +++ /dev/null @@ -1,11 +0,0 @@ - - - -
          -
        1. Make sure that NFC is turned on in Settings > More > NFC and make sure that Android Beam is also on in the same section.
        2. -
        3. Hold the two devices back to back (they have to be almost touching) and you'll feel a vibration.
        4. -
        5. After it vibrates you'll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.
        6. -
        7. Tap the card and the content will then load on the other person’s device.
        8. -
        - - diff --git a/OpenPGP-Keychain/src/main/res/values-ach/strings.xml b/OpenPGP-Keychain/src/main/res/values-ach/strings.xml deleted file mode 100644 index 3d00a143f..000000000 --- a/OpenPGP-Keychain/src/main/res/values-ach/strings.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - From 1b57a10bb6a65a1900c79c68efe4c320177485da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 21 Mar 2014 11:26:18 +0100 Subject: [PATCH 096/253] Fix nfc help button --- .../sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java index 44b5848d8..110647284 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysNFCFragment.java @@ -57,7 +57,7 @@ public class ImportKeysNFCFragment extends Fragment { public void onClick(View v) { // show nfc help Intent intent = new Intent(getActivity(), HelpActivity.class); - intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, 1); + intent.putExtra(HelpActivity.EXTRA_SELECTED_TAB, 2); startActivityForResult(intent, 0); } }); From b7bc8faee6e9500287baf86a6bb4fe42be80ef0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Fri, 21 Mar 2014 11:43:30 +0100 Subject: [PATCH 097/253] raname to mKeychainDatabase --- .../keychain/provider/KeychainProvider.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index b963ceb39..746449f7e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -247,7 +247,7 @@ public class KeychainProvider extends ContentProvider { return matcher; } - private KeychainDatabase mApgDatabase; + private KeychainDatabase mKeychainDatabase; /** * {@inheritDoc} @@ -255,7 +255,7 @@ public class KeychainProvider extends ContentProvider { @Override public boolean onCreate() { mUriMatcher = buildUriMatcher(); - mApgDatabase = new KeychainDatabase(getContext()); + mKeychainDatabase = new KeychainDatabase(getContext()); return true; } @@ -486,7 +486,7 @@ public class KeychainProvider extends ContentProvider { Log.v(Constants.TAG, "query(uri=" + uri + ", proj=" + Arrays.toString(projection) + ")"); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - SQLiteDatabase db = mApgDatabase.getReadableDatabase(); + SQLiteDatabase db = mKeychainDatabase.getReadableDatabase(); int match = mUriMatcher.match(uri); @@ -715,7 +715,7 @@ public class KeychainProvider extends ContentProvider { public Uri insert(Uri uri, ContentValues values) { Log.d(Constants.TAG, "insert(uri=" + uri + ", values=" + values.toString() + ")"); - final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); + final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase(); Uri rowUri = null; long rowId = -1; @@ -792,7 +792,7 @@ public class KeychainProvider extends ContentProvider { public int delete(Uri uri, String selection, String[] selectionArgs) { Log.v(Constants.TAG, "delete(uri=" + uri + ")"); - final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); + final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase(); int count; final int match = mUriMatcher.match(uri); @@ -853,7 +853,7 @@ public class KeychainProvider extends ContentProvider { public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { Log.v(Constants.TAG, "update(uri=" + uri + ", values=" + values.toString() + ")"); - final SQLiteDatabase db = mApgDatabase.getWritableDatabase(); + final SQLiteDatabase db = mKeychainDatabase.getWritableDatabase(); String defaultSelection = null; int count = 0; From 8f37569967c44f0693ba63541e4e6ecfe42aa73f Mon Sep 17 00:00:00 2001 From: uberspot Date: Fri, 21 Mar 2014 18:43:11 +0200 Subject: [PATCH 098/253] Make foldable settings in EncryptActivity a separate widget --- .../keychain/ui/EncryptActivity.java | 47 ---- .../ui/widget/FoldableLinearLayout.java | 203 ++++++++++++++++++ .../src/main/res/layout/encrypt_content.xml | 104 +-------- .../layout/encrypt_content_adv_settings.xml | 63 ++++++ .../main/res/layout/foldable_linearlayout.xml | 41 ++++ OpenPGP-Keychain/src/main/res/values/attr.xml | 11 + 6 files changed, 329 insertions(+), 140 deletions(-) create mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java create mode 100644 OpenPGP-Keychain/src/main/res/layout/encrypt_content_adv_settings.xml create mode 100644 OpenPGP-Keychain/src/main/res/layout/foldable_linearlayout.xml create mode 100644 OpenPGP-Keychain/src/main/res/values/attr.xml diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java index 28c9c7b28..4ccfd393f 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -108,11 +108,6 @@ public class EncryptActivity extends DrawerActivity { private String mInputFilename = null; private String mOutputFilename = null; - private Integer mShortAnimationDuration = null; - private boolean mFileAdvancedSettingsVisible = false; - private TextView mFileAdvancedSettings = null; - private LinearLayout mFileAdvancedSettingsContainer = null; - private FontAwesomeText mAdvancedSettingsIcon; private boolean mAsciiArmorDemand = false; private boolean mOverrideAsciiArmor = false; @@ -147,9 +142,6 @@ public class EncryptActivity extends DrawerActivity { updateMode(); updateActionBarButtons(); - - // retrieve and cache the system's short animation time - mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime); } /** @@ -795,49 +787,10 @@ public class EncryptActivity extends DrawerActivity { } }); - mAdvancedSettingsIcon = (FontAwesomeText) findViewById(R.id.advancedSettingsIcon); - mFileAdvancedSettingsContainer = (LinearLayout) findViewById(R.id.fileAdvancedSettingsContainer); - mFileAdvancedSettings = (TextView) findViewById(R.id.advancedSettings); - LinearLayout advancedSettingsControl = (LinearLayout) findViewById(R.id.advancedSettingsControl); - advancedSettingsControl.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - mFileAdvancedSettingsVisible = !mFileAdvancedSettingsVisible; - if (mFileAdvancedSettingsVisible) { - mAdvancedSettingsIcon.setIcon("fa-chevron-down"); - mFileAdvancedSettingsContainer.setVisibility(View.VISIBLE); - AlphaAnimation animation = new AlphaAnimation(0f, 1f); - animation.setDuration(mShortAnimationDuration); - mFileAdvancedSettingsContainer.startAnimation(animation); - mFileAdvancedSettings.setText(R.string.btn_encryption_advanced_settings_hide); - } else { - mAdvancedSettingsIcon.setIcon("fa-chevron-right"); - AlphaAnimation animation = new AlphaAnimation(1f, 0f); - animation.setDuration(mShortAnimationDuration); - animation.setAnimationListener(new Animation.AnimationListener() { - @Override - public void onAnimationStart(Animation animation) { - // do nothing - } - @Override - public void onAnimationEnd(Animation animation) { - // making sure that at the end the container is completely removed from view - mFileAdvancedSettingsContainer.setVisibility(View.GONE); - } - @Override - public void onAnimationRepeat(Animation animation) { - // do nothing - } - }); - mFileAdvancedSettingsContainer.startAnimation(animation); - mFileAdvancedSettings.setText(R.string.btn_encryption_advanced_settings_show); - } - } - }); mFileCompression = (Spinner) findViewById(R.id.fileCompression); Choice[] choices = new Choice[]{ diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java new file mode 100644 index 000000000..f9a5b92f3 --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/FoldableLinearLayout.java @@ -0,0 +1,203 @@ +/* + * 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.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.widget.LinearLayout; +import android.widget.TextView; +import com.beardedhen.androidbootstrap.FontAwesomeText; +import org.sufficientlysecure.keychain.R; + +/** + * Class representing a LinearLayout that can fold and hide it's content when pressed + * To use just add the following to your xml layout + + + + + + + + */ +public class FoldableLinearLayout extends LinearLayout { + + private FontAwesomeText mFoldableIcon; + private boolean mFolded; + private boolean mHasMigrated = false; + private Integer mShortAnimationDuration = null; + private TextView mFoldableTextView = null; + private LinearLayout mFoldableContainer = null; + private View mFoldableLayout = null; + + private String mFoldedIconName; + private String mUnFoldedIconName; + private String mFoldedLabel; + private String mUnFoldedLabel; + + public FoldableLinearLayout(Context context) { + super(context); + processAttributes(context, null); + } + + public FoldableLinearLayout(Context context, AttributeSet attrs) { + super(context, attrs); + processAttributes(context, attrs); + } + + public FoldableLinearLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs); + processAttributes(context, attrs); + } + + /** + * Load given attributes to inner variables, + * @param context + * @param attrs + */ + private void processAttributes(Context context, AttributeSet attrs) { + if(attrs != null) { + TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.FoldableLinearLayout, 0, 0); + mFoldedIconName = a.getString(R.styleable.FoldableLinearLayout_foldedIcon); + mUnFoldedIconName = a.getString(R.styleable.FoldableLinearLayout_unFoldedIcon); + mFoldedLabel = a.getString(R.styleable.FoldableLinearLayout_foldedLabel); + mUnFoldedLabel = a.getString(R.styleable.FoldableLinearLayout_unFoldedLabel); + a.recycle(); + } + // If any attribute isn't found then set a default one + mFoldedIconName = (mFoldedIconName == null) ? "fa-chevron-right" : mFoldedIconName; + mUnFoldedIconName = (mUnFoldedIconName == null) ? "fa-chevron-down" : mUnFoldedIconName; + mFoldedLabel = (mFoldedLabel == null) ? context.getString(R.id.none) : mFoldedLabel; + mUnFoldedLabel = (mUnFoldedLabel == null) ? context.getString(R.id.none) : mUnFoldedLabel; + } + + @Override + protected void onFinishInflate() { + // if the migration has already happened + // there is no need to move any children + if(!mHasMigrated) { + migrateChildrenToContainer(); + mHasMigrated = true; + } + + initialiseInnerViews(); + + super.onFinishInflate(); + } + + /** + * Migrates Child views as declared in xml to the inner foldableContainer + */ + private void migrateChildrenToContainer() { + // Collect children of FoldableLinearLayout as declared in XML + int childNum = getChildCount(); + View[] children = new View[childNum]; + + for(int i = 0; i < childNum; i++) { + children[i] = getChildAt(i); + } + if(children[0].getId() == R.id.foldableControl) { + + } + + // remove all of them from FoldableLinearLayout + detachAllViewsFromParent(); + + // Inflate the inner foldable_linearlayout.xml + LayoutInflater inflator = (LayoutInflater)getContext().getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + + mFoldableLayout = inflator.inflate(R.layout.foldable_linearlayout, this, true); + mFoldableContainer = (LinearLayout) mFoldableLayout.findViewById(R.id.foldableContainer); + + // Push previously collected children into foldableContainer. + for(int i = 0; i < childNum; i++) { + addView(children[i]); + } + } + + private void initialiseInnerViews() { + mFoldableIcon = (FontAwesomeText) mFoldableLayout.findViewById(R.id.foldableIcon); + mFoldableIcon.setIcon(mFoldedIconName); + mFoldableTextView = (TextView) mFoldableLayout.findViewById(R.id.foldableText); + mFoldableTextView.setText(mFoldedLabel); + + // retrieve and cache the system's short animation time + mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime); + + LinearLayout foldableControl = (LinearLayout) mFoldableLayout.findViewById(R.id.foldableControl); + foldableControl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + mFolded = !mFolded; + if (mFolded) { + mFoldableIcon.setIcon(mUnFoldedIconName); + mFoldableContainer.setVisibility(View.VISIBLE); + AlphaAnimation animation = new AlphaAnimation(0f, 1f); + animation.setDuration(mShortAnimationDuration); + mFoldableContainer.startAnimation(animation); + mFoldableTextView.setText(mUnFoldedLabel); + + } else { + mFoldableIcon.setIcon(mFoldedIconName); + AlphaAnimation animation = new AlphaAnimation(1f, 0f); + animation.setDuration(mShortAnimationDuration); + animation.setAnimationListener(new Animation.AnimationListener() { + @Override + public void onAnimationStart(Animation animation) { } + + @Override + public void onAnimationEnd(Animation animation) { + // making sure that at the end the container is completely removed from view + mFoldableContainer.setVisibility(View.GONE); + } + + @Override + public void onAnimationRepeat(Animation animation) { } + }); + mFoldableContainer.startAnimation(animation); + mFoldableTextView.setText(mFoldedLabel); + } + } + }); + + } + + /** + * Adds provided child view to foldableContainer View + * @param child + */ + @Override + public void addView(View child) { + if(mFoldableContainer != null) { + mFoldableContainer.addView(child); + } + } +} diff --git a/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml b/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml index bb947fb5a..03fe496c6 100644 --- a/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml +++ b/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml @@ -1,8 +1,9 @@ - + android:layout_height="match_parent" + custom:foldedLabel="@string/btn_encryption_advanced_settings_show" + custom:unFoldedLabel="@string/btn_encryption_advanced_settings_hide" + custom:foldedIcon="fa-chevron-right" + custom:unFoldedIcon="fa-chevron-down"> - + - - + - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/OpenPGP-Keychain/src/main/res/layout/encrypt_content_adv_settings.xml b/OpenPGP-Keychain/src/main/res/layout/encrypt_content_adv_settings.xml new file mode 100644 index 000000000..2281759d1 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/encrypt_content_adv_settings.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/layout/foldable_linearlayout.xml b/OpenPGP-Keychain/src/main/res/layout/foldable_linearlayout.xml new file mode 100644 index 000000000..2b863d52b --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/foldable_linearlayout.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/values/attr.xml b/OpenPGP-Keychain/src/main/res/values/attr.xml new file mode 100644 index 000000000..86622b3e0 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/values/attr.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file From 0f6dc253a40ecada0c0c2a467c56ee45b7b9cc72 Mon Sep 17 00:00:00 2001 From: Daniel Hammann Date: Fri, 21 Mar 2014 23:01:24 +0100 Subject: [PATCH 099/253] Because of an unknown reason toast messages were commented out. --- .../keychain/ui/EditKeyActivity.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 9b64def1e..092a502e1 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -495,11 +495,10 @@ public class EditKeyActivity extends ActionBarActivity { private void saveClicked() { long masterKeyId = getMasterKeyId(); - try { - if (!isPassphraseSet()) { - throw new PgpGeneralException(this.getString(R.string.set_a_passphrase)); - } - + if (!isPassphraseSet()) { + Log.e(Constants.TAG, "No passphrase has been set"); + Toast.makeText(this, R.string.set_a_passphrase, Toast.LENGTH_LONG).show(); + } else { String passphrase = null; if (mIsPassPhraseSet) { passphrase = PassphraseCacheService.getCachedPassphrase(this, masterKeyId); @@ -512,9 +511,6 @@ public class EditKeyActivity extends ActionBarActivity { mCurrentPassphrase = passphrase; finallySaveClicked(); } - } catch (PgpGeneralException e) { - //Toast.makeText(this, getString(R.string.error_message, e.getMessage()), - // Toast.LENGTH_SHORT).show(); } } @@ -576,8 +572,9 @@ public class EditKeyActivity extends ActionBarActivity { // start service with intent startService(intent); } catch (PgpGeneralException e) { - //Toast.makeText(this, getString(R.string.error_message, e.getMessage()), - // Toast.LENGTH_SHORT).show(); + Log.e(Constants.TAG, getString(R.string.error_message, e.getMessage())); + Toast.makeText(this, getString(R.string.error_message, e.getMessage()), + Toast.LENGTH_SHORT).show(); } } From 4e285d9254f98381bd59a9a683d406ad1b6020d6 Mon Sep 17 00:00:00 2001 From: Daniel Hammann Date: Sat, 22 Mar 2014 15:31:18 +0100 Subject: [PATCH 100/253] The current implementation of saving new generated key doesn't save the key due to the public key of master key id can not be resolved by ProviderHelper.getPGPPublicKeyByKeyId (via URI/Cursor). Workaround uses local keys-arraylist for resoving the pubkey of master key id. --- .../keychain/service/KeychainIntentService.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 0751fa33c..5ef41cced 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -543,7 +543,14 @@ public class KeychainIntentService extends IntentService ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId), oldPassPhrase, newPassPhrase); } else { - PGPPublicKey pubkey = ProviderHelper.getPGPPublicKeyByKeyId(this, masterKeyId); + PGPPublicKey pubkey = null; + for(PGPSecretKey key : keys) { + PGPPublicKey tempKey = key.getPublicKey(); + if (tempKey.getKeyID() == masterKeyId) { + pubkey = tempKey; + } + } + //PGPPublicKey pubkey = ProviderHelper.getPGPPublicKeyByKeyId(this, masterKeyId); keyOperations.buildSecretKey(userIds, keys, keysUsages, keysExpiryDates, pubkey, oldPassPhrase, newPassPhrase); } From 9c180629510d6b07aa296088f61593234b52a14a Mon Sep 17 00:00:00 2001 From: Daniel Hammann Date: Sat, 22 Mar 2014 15:31:18 +0100 Subject: [PATCH 101/253] The current implementation of saving new generated key doesn't save the key due to the public key of master key id can not be resolved by ProviderHelper.getPGPPublicKeyByKeyId (via URI/Cursor). Workaround uses local keys-arraylist for resoving the pubkey of master key id. --- .../keychain/service/KeychainIntentService.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index 0751fa33c..e26ee3c76 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -543,7 +543,15 @@ public class KeychainIntentService extends IntentService ProviderHelper.getPGPSecretKeyRingByKeyId(this, masterKeyId), oldPassPhrase, newPassPhrase); } else { - PGPPublicKey pubkey = ProviderHelper.getPGPPublicKeyByKeyId(this, masterKeyId); + //TODO: Workaround due to ProviderHelper.getPGPPublicKeyByKeyId can not resolve public key of master-key id with uri/cursor + PGPPublicKey pubkey = null; + for(PGPSecretKey key : keys) { + PGPPublicKey tempKey = key.getPublicKey(); + if (tempKey.getKeyID() == masterKeyId) { + pubkey = tempKey; + } + } + //PGPPublicKey pubkey = ProviderHelper.getPGPPublicKeyByKeyId(this, masterKeyId); keyOperations.buildSecretKey(userIds, keys, keysUsages, keysExpiryDates, pubkey, oldPassPhrase, newPassPhrase); } From 153b99a186f514ff08f4b2a659202049f237c9fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sun, 23 Mar 2014 00:03:49 +0100 Subject: [PATCH 102/253] Use new FoldableLinearLayout in API settings --- .../service/remote/AppSettingsFragment.java | 46 ++----------------- .../res/layout/api_app_settings_fragment.xml | 28 ++++------- 2 files changed, 15 insertions(+), 59 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java index 837295018..52b06a2ae 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java @@ -25,13 +25,13 @@ 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.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.widget.*; +import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; -import com.beardedhen.androidbootstrap.BootstrapButton; +import android.widget.ImageView; +import android.widget.Spinner; +import android.widget.TextView; + import org.spongycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; @@ -50,8 +50,6 @@ public class AppSettingsFragment extends Fragment implements private AppSettings mAppSettings; // view - private LinearLayout mAdvancedSettingsContainer; - private BootstrapButton mAdvancedSettingsButton; private TextView mAppNameView; private ImageView mAppIconView; private Spinner mEncryptionAlgorithm; @@ -117,11 +115,6 @@ public class AppSettingsFragment extends Fragment implements R.id.api_app_settings_select_key_fragment); mSelectKeyFragment.setCallback(this); - mAdvancedSettingsButton = (BootstrapButton) view - .findViewById(R.id.api_app_settings_advanced_button); - mAdvancedSettingsContainer = (LinearLayout) view - .findViewById(R.id.api_app_settings_advanced); - mAppNameView = (TextView) view.findViewById(R.id.api_app_settings_app_name); mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon); mEncryptionAlgorithm = (Spinner) view @@ -176,35 +169,6 @@ public class AppSettingsFragment extends Fragment implements public void onNothingSelected(AdapterView parent) { } }); - - final Animation visibleAnimation = new AlphaAnimation(0.0f, 1.0f); - visibleAnimation.setDuration(250); - final Animation invisibleAnimation = new AlphaAnimation(1.0f, 0.0f); - invisibleAnimation.setDuration(250); - - // TODO: Better: collapse/expand animation - // final Animation animation2 = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0.0f, - // Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, -1.0f, - // Animation.RELATIVE_TO_SELF, 0.0f);u - // animation2.setDuration(150); - - mAdvancedSettingsButton.setOnClickListener(new OnClickListener() { - - @Override - public void onClick(View v) { - if (mAdvancedSettingsContainer.getVisibility() == View.VISIBLE) { - mAdvancedSettingsContainer.startAnimation(invisibleAnimation); - mAdvancedSettingsContainer.setVisibility(View.GONE); - mAdvancedSettingsButton.setText(getString(R.string.api_settings_show_advanced)); - mAdvancedSettingsButton.setLeftIcon("fa-caret-up"); - } else { - mAdvancedSettingsContainer.startAnimation(visibleAnimation); - mAdvancedSettingsContainer.setVisibility(View.VISIBLE); - mAdvancedSettingsButton.setText(getString(R.string.api_settings_hide_advanced)); - mAdvancedSettingsButton.setLeftIcon("fa-caret-down"); - } - } - }); } private void setPackage(String packageName) { diff --git a/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml index a8b68859b..a7917ad4e 100644 --- a/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml +++ b/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml @@ -2,6 +2,7 @@ @@ -42,24 +43,13 @@ android:layout_height="wrap_content" tools:layout="@layout/select_secret_key_layout_fragment" /> - - - + android:layout_height="match_parent" + custom:foldedLabel="@string/btn_encryption_advanced_settings_show" + custom:unFoldedLabel="@string/btn_encryption_advanced_settings_hide" + custom:foldedIcon="fa-chevron-right" + custom:unFoldedIcon="fa-chevron-down"> - + + + \ No newline at end of file From 12402d9a513e83755aa657acd29dd7382f355d5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sun, 23 Mar 2014 01:16:56 +0100 Subject: [PATCH 103/253] New db table for api accounts --- .../keychain/provider/KeychainContract.java | 4 +++ .../keychain/provider/KeychainDatabase.java | 27 ++++++++++++++----- 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 5f2354c24..de51cebbb 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -56,10 +56,14 @@ public class KeychainContract { interface ApiAppsColumns { String PACKAGE_NAME = "package_name"; String PACKAGE_SIGNATURE = "package_signature"; + } + + interface ApiAppsAccountsColumns { String KEY_ID = "key_id"; // not a database id String ENCRYPTION_ALGORITHM = "encryption_algorithm"; String HASH_ALORITHM = "hash_algorithm"; String COMPRESSION = "compression"; + String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name } public static final class KeyTypes { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 031a7d5ae..f7c9fe79d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -23,6 +23,7 @@ import android.database.sqlite.SQLiteOpenHelper; import android.provider.BaseColumns; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns; @@ -30,13 +31,14 @@ import org.sufficientlysecure.keychain.util.Log; public class KeychainDatabase extends SQLiteOpenHelper { private static final String DATABASE_NAME = "apg.db"; - private static final int DATABASE_VERSION = 7; + private static final int DATABASE_VERSION = 8; public interface Tables { String KEY_RINGS = "key_rings"; String KEYS = "keys"; String USER_IDS = "user_ids"; String API_APPS = "api_apps"; + String API_APPS_ACCOUNTS = "api_apps_accounts"; } private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS @@ -76,11 +78,17 @@ public class KeychainDatabase extends SQLiteOpenHelper { private static final String CREATE_API_APPS = "CREATE TABLE IF NOT EXISTS " + Tables.API_APPS + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + ApiAppsColumns.PACKAGE_NAME + " TEXT UNIQUE, " - + ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB, " - + ApiAppsColumns.KEY_ID + " INT64, " - + ApiAppsColumns.ENCRYPTION_ALGORITHM + " INTEGER, " - + ApiAppsColumns.HASH_ALORITHM + " INTEGER, " - + ApiAppsColumns.COMPRESSION + " INTEGER)"; + + ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB)"; + + private static final String CREATE_API_APPS_ACCOUNTS = "CREATE TABLE IF NOT EXISTS " + Tables.API_APPS_ACCOUNTS + + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + ApiAppsAccountsColumns.KEY_ID + " INT64, " + + ApiAppsAccountsColumns.ENCRYPTION_ALGORITHM + " INTEGER, " + + ApiAppsAccountsColumns.HASH_ALORITHM + " INTEGER, " + + ApiAppsAccountsColumns.COMPRESSION + " INTEGER" + + ApiAppsAccountsColumns.PACKAGE_NAME + " TEXT NOT NULL, FOREIGN KEY(" + + ApiAppsAccountsColumns.PACKAGE_NAME + ") REFERENCES " + Tables.API_APPS + "(" + + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE)"; KeychainDatabase(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); @@ -94,6 +102,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { db.execSQL(CREATE_KEYS); db.execSQL(CREATE_USER_IDS); db.execSQL(CREATE_API_APPS); + db.execSQL(CREATE_API_APPS_ACCOUNTS); } @Override @@ -133,6 +142,12 @@ public class KeychainDatabase extends SQLiteOpenHelper { db.execSQL("ALTER TABLE " + Tables.KEYS + " ADD COLUMN " + KeysColumns.FINGERPRINT + " BLOB;"); break; + case 7: + // new db layout for api apps + db.execSQL("DROP TABLE IF EXISTS " + Tables.API_APPS); + db.execSQL(CREATE_API_APPS); + db.execSQL(CREATE_API_APPS_ACCOUNTS); + break; default: break; From 9555ae3bc1287daa27151456a1f266bdc1640e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sun, 23 Mar 2014 01:26:49 +0100 Subject: [PATCH 104/253] Move classes for OpenPGP API into remote package --- OpenPGP-Keychain/src/main/AndroidManifest.xml | 8 ++++---- .../keychain/provider/ProviderHelper.java | 10 +++++----- .../keychain/{service => }/remote/AppSettings.java | 2 +- .../keychain/{service => }/remote/OpenPgpService.java | 3 ++- .../keychain/{service => }/remote/RemoteService.java | 3 ++- .../remote/WrongPackageSignatureException.java | 2 +- .../remote => remote/ui}/AppSettingsActivity.java | 3 ++- .../remote => remote/ui}/AppSettingsFragment.java | 3 ++- .../remote => remote/ui}/RegisteredAppsAdapter.java | 2 +- .../ui}/RegisteredAppsListActivity.java | 2 +- .../ui}/RegisteredAppsListFragment.java | 2 +- .../remote => remote/ui}/RemoteServiceActivity.java | 3 ++- 12 files changed, 24 insertions(+), 19 deletions(-) rename OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/{service => }/remote/AppSettings.java (97%) rename OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/{service => }/remote/OpenPgpService.java (99%) rename OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/{service => }/remote/RemoteService.java (98%) rename OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/{service => }/remote/WrongPackageSignatureException.java (94%) rename OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/{service/remote => remote/ui}/AppSettingsActivity.java (96%) rename OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/{service/remote => remote/ui}/AppSettingsFragment.java (98%) rename OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/{service/remote => remote/ui}/RegisteredAppsAdapter.java (97%) rename OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/{service/remote => remote/ui}/RegisteredAppsListActivity.java (95%) rename OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/{service/remote => remote/ui}/RegisteredAppsListFragment.java (98%) rename OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/{service/remote => remote/ui}/RemoteServiceActivity.java (99%) diff --git a/OpenPGP-Keychain/src/main/AndroidManifest.xml b/OpenPGP-Keychain/src/main/AndroidManifest.xml index b1fbcf555..e2ed8c5e4 100644 --- a/OpenPGP-Keychain/src/main/AndroidManifest.xml +++ b/OpenPGP-Keychain/src/main/AndroidManifest.xml @@ -384,24 +384,24 @@ diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 2fa903fe2..e0d007de7 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -33,7 +33,7 @@ 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.KeychainDatabase.Tables; -import org.sufficientlysecure.keychain.service.remote.AppSettings; +import org.sufficientlysecure.keychain.remote.AppSettings; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -801,10 +801,10 @@ public class ProviderHelper { ContentValues values = new ContentValues(); values.put(ApiApps.PACKAGE_NAME, appSettings.getPackageName()); values.put(ApiApps.PACKAGE_SIGNATURE, appSettings.getPackageSignature()); - values.put(ApiApps.KEY_ID, appSettings.getKeyId()); - values.put(ApiApps.COMPRESSION, appSettings.getCompression()); - values.put(ApiApps.ENCRYPTION_ALGORITHM, appSettings.getEncryptionAlgorithm()); - values.put(ApiApps.HASH_ALORITHM, appSettings.getHashAlgorithm()); +// values.put(ApiApps.KEY_ID, appSettings.getKeyId()); +// values.put(ApiApps.COMPRESSION, appSettings.getCompression()); +// values.put(ApiApps.ENCRYPTION_ALGORITHM, appSettings.getEncryptionAlgorithm()); +// values.put(ApiApps.HASH_ALORITHM, appSettings.getHashAlgorithm()); return values; } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettings.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java similarity index 97% rename from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettings.java rename to OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java index 6f2d67efb..ad48a402b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettings.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package org.sufficientlysecure.keychain.service.remote; +package org.sufficientlysecure.keychain.remote; import org.spongycastle.bcpg.HashAlgorithmTags; import org.spongycastle.openpgp.PGPEncryptedData; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java similarity index 99% rename from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java rename to OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index 95dc897f0..9b56a491a 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/OpenPgpService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package org.sufficientlysecure.keychain.service.remote; +package org.sufficientlysecure.keychain.remote; import android.app.PendingIntent; import android.content.Intent; @@ -36,6 +36,7 @@ import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity; import org.sufficientlysecure.keychain.service.PassphraseCacheService; import org.sufficientlysecure.keychain.util.InputData; import org.sufficientlysecure.keychain.util.Log; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java similarity index 98% rename from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java rename to OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java index 6a883316a..434fe84c1 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package org.sufficientlysecure.keychain.service.remote; +package org.sufficientlysecure.keychain.remote; import android.app.PendingIntent; import android.app.Service; @@ -33,6 +33,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.remote.ui.RemoteServiceActivity; import org.sufficientlysecure.keychain.util.Log; import java.util.ArrayList; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/WrongPackageSignatureException.java similarity index 94% rename from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java rename to OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/WrongPackageSignatureException.java index 0b642086a..6f44a65e9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/WrongPackageSignatureException.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/WrongPackageSignatureException.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package org.sufficientlysecure.keychain.service.remote; +package org.sufficientlysecure.keychain.remote; public class WrongPackageSignatureException extends Exception { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java similarity index 96% rename from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsActivity.java rename to OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java index 2ef170dec..d544455ec 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package org.sufficientlysecure.keychain.service.remote; +package org.sufficientlysecure.keychain.remote.ui; import android.content.Intent; import android.net.Uri; @@ -28,6 +28,7 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.ActionBarHelper; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.remote.AppSettings; import org.sufficientlysecure.keychain.util.Log; public class AppSettingsActivity extends ActionBarActivity { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java similarity index 98% rename from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java rename to OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java index 52b06a2ae..8e8f3827b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/AppSettingsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package org.sufficientlysecure.keychain.service.remote; +package org.sufficientlysecure.keychain.remote.ui; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -35,6 +35,7 @@ import android.widget.TextView; import org.spongycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.remote.AppSettings; import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment; import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter; import org.sufficientlysecure.keychain.util.AlgorithmNames; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsAdapter.java similarity index 97% rename from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsAdapter.java rename to OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsAdapter.java index e0dc4162f..c846343ab 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsAdapter.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package org.sufficientlysecure.keychain.service.remote; +package org.sufficientlysecure.keychain.remote.ui; import android.content.Context; import android.content.pm.ApplicationInfo; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsListActivity.java similarity index 95% rename from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java rename to OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsListActivity.java index f6f216efd..79b3c8abc 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsListActivity.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package org.sufficientlysecure.keychain.service.remote; +package org.sufficientlysecure.keychain.remote.ui; import android.os.Bundle; import org.sufficientlysecure.keychain.R; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsListFragment.java similarity index 98% rename from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java rename to OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsListFragment.java index 25d0c7593..99e73e510 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RegisteredAppsListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsListFragment.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package org.sufficientlysecure.keychain.service.remote; +package org.sufficientlysecure.keychain.remote.ui; import android.content.ContentUris; import android.content.Intent; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java similarity index 99% rename from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java rename to OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java index e20114853..3e19a8ef6 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/remote/RemoteServiceActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package org.sufficientlysecure.keychain.service.remote; +package org.sufficientlysecure.keychain.remote.ui; import android.content.Intent; import android.os.Bundle; @@ -32,6 +32,7 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.ActionBarHelper; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.remote.AppSettings; import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; import org.sufficientlysecure.keychain.util.Log; From f082b53118e7a174b8c9a79964abaeef4a34fa29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sun, 23 Mar 2014 02:17:39 +0100 Subject: [PATCH 105/253] Extend provider for new api tables --- .../keychain/provider/KeychainContract.java | 66 +++++++- .../keychain/provider/KeychainDatabase.java | 8 +- .../keychain/provider/KeychainProvider.java | 149 +++++++++++++++--- .../keychain/provider/ProviderHelper.java | 42 +++-- 4 files changed, 223 insertions(+), 42 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index de51cebbb..aa4c1f131 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -63,7 +63,7 @@ public class KeychainContract { String ENCRYPTION_ALGORITHM = "encryption_algorithm"; String HASH_ALORITHM = "hash_algorithm"; String COMPRESSION = "compression"; - String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name + String PACKAGE_NAME_FK = "package_name"; // foreign key to api_apps.package_name } public static final class KeyTypes { @@ -90,7 +90,10 @@ public class KeychainContract { public static final String PATH_USER_IDS = "user_ids"; public static final String PATH_KEYS = "keys"; - public static final String BASE_API_APPS = "api_apps"; + public static final String BASE_API = "api"; + public static final String PATH_APPS = "apps"; + public static final String PATH_ACCOUNTS = "accounts"; + public static final String PATH_BY_PACKAGE_NAME = "package_name"; public static class KeyRings implements KeyRingsColumns, BaseColumns { @@ -254,19 +257,70 @@ public class KeychainContract { } } - public static class ApiApps implements ApiAppsColumns, BaseColumns { + /** + * Join over ApiApps with ApiAppsAccounts + */ + public static class Api implements ApiAppsColumns, ApiAppsAccountsColumns, BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() - .appendPath(BASE_API_APPS).build(); + .appendPath(BASE_API).build(); /** * Use if multiple items get returned */ - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api_apps"; + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.apis"; /** * Use if a single item is returned */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_apps"; + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api"; + + public static Uri buildIdUri(String rowId) { + return CONTENT_URI.buildUpon().appendPath(rowId).build(); + } + + public static Uri buildByPackageNameUri(String packageName) { + return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName) + .build(); + } + } + + public static class ApiApps implements ApiAppsColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_API).appendPath(PATH_APPS).build(); + + /** + * Use if multiple items get returned + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api.app"; + + /** + * Use if a single item is returned + */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api.apps"; + + public static Uri buildIdUri(String rowId) { + return CONTENT_URI.buildUpon().appendPath(rowId).build(); + } + + public static Uri buildByPackageNameUri(String packageName) { + return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName) + .build(); + } + } + + public static class ApiAccounts implements ApiAppsAccountsColumns, BaseColumns { + public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() + .appendPath(BASE_API).appendPath(PATH_ACCOUNTS).build(); + + /** + * Use if multiple items get returned + */ + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api.acoounts"; + + /** + * Use if a single item is returned + */ + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api.account"; public static Uri buildIdUri(String rowId) { return CONTENT_URI.buildUpon().appendPath(rowId).build(); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index f7c9fe79d..e56fa2f8f 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -38,7 +38,7 @@ public class KeychainDatabase extends SQLiteOpenHelper { String KEYS = "keys"; String USER_IDS = "user_ids"; String API_APPS = "api_apps"; - String API_APPS_ACCOUNTS = "api_apps_accounts"; + String API_ACCOUNTS = "api_accounts"; } private static final String CREATE_KEY_RINGS = "CREATE TABLE IF NOT EXISTS " + Tables.KEY_RINGS @@ -80,14 +80,14 @@ public class KeychainDatabase extends SQLiteOpenHelper { + ApiAppsColumns.PACKAGE_NAME + " TEXT UNIQUE, " + ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB)"; - private static final String CREATE_API_APPS_ACCOUNTS = "CREATE TABLE IF NOT EXISTS " + Tables.API_APPS_ACCOUNTS + private static final String CREATE_API_APPS_ACCOUNTS = "CREATE TABLE IF NOT EXISTS " + Tables.API_ACCOUNTS + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + ApiAppsAccountsColumns.KEY_ID + " INT64, " + ApiAppsAccountsColumns.ENCRYPTION_ALGORITHM + " INTEGER, " + ApiAppsAccountsColumns.HASH_ALORITHM + " INTEGER, " + ApiAppsAccountsColumns.COMPRESSION + " INTEGER" - + ApiAppsAccountsColumns.PACKAGE_NAME + " TEXT NOT NULL, FOREIGN KEY(" - + ApiAppsAccountsColumns.PACKAGE_NAME + ") REFERENCES " + Tables.API_APPS + "(" + + ApiAppsAccountsColumns.PACKAGE_NAME_FK + " TEXT NOT NULL, FOREIGN KEY(" + + ApiAppsAccountsColumns.PACKAGE_NAME_FK + ") REFERENCES " + Tables.API_APPS + "(" + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE)"; KeychainDatabase(Context context) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 746449f7e..a094b13de 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012-2013 Dominik Schürmann + * Copyright (C) 2012-2014 Dominik Schürmann * Copyright (C) 2010 Thialfihar * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,8 +28,19 @@ import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.provider.BaseColumns; import android.text.TextUtils; + import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.provider.KeychainContract.*; +import org.sufficientlysecure.keychain.provider.KeychainContract.Api; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes; +import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; +import org.sufficientlysecure.keychain.provider.KeychainContract.KeysColumns; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIds; +import org.sufficientlysecure.keychain.provider.KeychainContract.UserIdsColumns; import org.sufficientlysecure.keychain.provider.KeychainDatabase.Tables; import org.sufficientlysecure.keychain.util.Log; @@ -70,9 +81,15 @@ public class KeychainProvider extends ContentProvider { private static final int SECRET_KEY_RING_USER_ID = 221; private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222; - private static final int API_APPS = 301; - private static final int API_APPS_BY_ROW_ID = 302; - private static final int API_APPS_BY_PACKAGE_NAME = 303; + private static final int API = 301; + private static final int API_BY_ROW_ID = 302; + private static final int API_BY_PACKAGE_NAME = 303; + private static final int API_APPS = 304; + private static final int API_APPS_BY_ROW_ID = 305; + private static final int API_APPS_BY_PACKAGE_NAME = 306; + private static final int API_ACCOUNTS = 307; + private static final int API_ACCOUNTS_BY_ROW_ID = 308; + private static final int API_ACCOUNTS_BY_PACKAGE_NAME = 309; private static final int UNIFIED_KEY_RING = 401; @@ -230,11 +247,27 @@ public class KeychainProvider extends ContentProvider { /** * API apps */ - matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS); - matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/#", API_APPS_BY_ROW_ID); - matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/" + matcher.addURI(authority, KeychainContract.BASE_API, API); + matcher.addURI(authority, KeychainContract.BASE_API + "/#", API_BY_ROW_ID); + matcher.addURI(authority, KeychainContract.BASE_API + "/" + + KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_BY_PACKAGE_NAME); + + matcher.addURI(authority, KeychainContract.BASE_API + "/" + + KeychainContract.PATH_APPS, API_APPS); + matcher.addURI(authority, KeychainContract.BASE_API + "/" + + KeychainContract.PATH_APPS + "/#", API_APPS_BY_ROW_ID); + matcher.addURI(authority, KeychainContract.BASE_API + "/" + + KeychainContract.PATH_APPS + "/" + KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_APPS_BY_PACKAGE_NAME); + matcher.addURI(authority, KeychainContract.BASE_API + "/" + + KeychainContract.PATH_ACCOUNTS, API_ACCOUNTS); + matcher.addURI(authority, KeychainContract.BASE_API + "/" + + KeychainContract.PATH_ACCOUNTS + "/#", API_ACCOUNTS_BY_ROW_ID); + matcher.addURI(authority, KeychainContract.BASE_API + "/" + + KeychainContract.PATH_ACCOUNTS + "/" + + KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_ACCOUNTS_BY_PACKAGE_NAME); + /** * data stream * @@ -299,6 +332,13 @@ public class KeychainProvider extends ContentProvider { case SECRET_KEY_RING_USER_ID_BY_ROW_ID: return UserIds.CONTENT_ITEM_TYPE; + case API: + return Api.CONTENT_TYPE; + + case API_BY_ROW_ID: + case API_BY_PACKAGE_NAME: + return Api.CONTENT_ITEM_TYPE; + case API_APPS: return ApiApps.CONTENT_TYPE; @@ -306,6 +346,13 @@ public class KeychainProvider extends ContentProvider { case API_APPS_BY_PACKAGE_NAME: return ApiApps.CONTENT_ITEM_TYPE; + case API_ACCOUNTS: + return ApiAccounts.CONTENT_TYPE; + + case API_ACCOUNTS_BY_ROW_ID: + case API_ACCOUNTS_BY_PACKAGE_NAME: + return ApiAccounts.CONTENT_ITEM_TYPE; + default: throw new UnsupportedOperationException("Unknown uri: " + uri); } @@ -506,7 +553,6 @@ public class KeychainProvider extends ContentProvider { } break; - case PUBLIC_KEY_RING: case SECRET_KEY_RING: qb = buildKeyRingQuery(qb, match); @@ -516,7 +562,6 @@ public class KeychainProvider extends ContentProvider { } break; - case PUBLIC_KEY_RING_BY_ROW_ID: case SECRET_KEY_RING_BY_ROW_ID: qb = buildKeyRingQuery(qb, match); @@ -529,7 +574,6 @@ public class KeychainProvider extends ContentProvider { } break; - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID: case SECRET_KEY_RING_BY_MASTER_KEY_ID: qb = buildKeyRingQuery(qb, match); @@ -542,7 +586,6 @@ public class KeychainProvider extends ContentProvider { } break; - case SECRET_KEY_RING_BY_KEY_ID: case PUBLIC_KEY_RING_BY_KEY_ID: qb = buildKeyRingQueryWithSpecificKey(qb, match); @@ -555,7 +598,6 @@ public class KeychainProvider extends ContentProvider { } break; - case SECRET_KEY_RING_BY_EMAILS: case PUBLIC_KEY_RING_BY_EMAILS: qb = buildKeyRingQuery(qb, match); @@ -585,7 +627,6 @@ public class KeychainProvider extends ContentProvider { } break; - case SECRET_KEY_RING_BY_LIKE_EMAIL: case PUBLIC_KEY_RING_BY_LIKE_EMAIL: qb = buildKeyRingQuery(qb, match); @@ -601,7 +642,6 @@ public class KeychainProvider extends ContentProvider { + "))"); break; - case PUBLIC_KEY_RING_KEY: case SECRET_KEY_RING_KEY: qb.setTables(Tables.KEYS); @@ -614,7 +654,6 @@ public class KeychainProvider extends ContentProvider { qb.setProjectionMap(getProjectionMapForKeys()); break; - case PUBLIC_KEY_RING_KEY_BY_ROW_ID: case SECRET_KEY_RING_KEY_BY_ROW_ID: qb.setTables(Tables.KEYS); @@ -630,7 +669,6 @@ public class KeychainProvider extends ContentProvider { qb.setProjectionMap(getProjectionMapForKeys()); break; - case PUBLIC_KEY_RING_BY_MASTER_KEY_ID_USER_ID: qb.setTables(Tables.USER_IDS + " INNER JOIN " + Tables.KEY_RINGS + " ON " + "(" + Tables.KEY_RINGS + "." + BaseColumns._ID + " = " + Tables.USER_IDS + "." @@ -641,7 +679,6 @@ public class KeychainProvider extends ContentProvider { qb.setProjectionMap(getProjectionMapForUserIds()); break; - case PUBLIC_KEY_RING_USER_ID: case SECRET_KEY_RING_USER_ID: qb.setTables(Tables.USER_IDS); @@ -649,7 +686,6 @@ public class KeychainProvider extends ContentProvider { qb.appendWhereEscapeString(uri.getPathSegments().get(2)); break; - case PUBLIC_KEY_RING_USER_ID_BY_ROW_ID: case SECRET_KEY_RING_USER_ID_BY_ROW_ID: qb.setTables(Tables.USER_IDS); @@ -660,7 +696,28 @@ public class KeychainProvider extends ContentProvider { qb.appendWhereEscapeString(uri.getLastPathSegment()); break; + case API: + qb.setTables(Tables.API_ACCOUNTS + " INNER JOIN " + Tables.API_APPS + " ON " + "(" + + Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = " + Tables.API_ACCOUNTS + "." + + ApiAccounts.PACKAGE_NAME_FK + " )"); + break; + case API_BY_ROW_ID: + qb.setTables(Tables.API_ACCOUNTS + " INNER JOIN " + Tables.API_APPS + " ON " + "(" + + Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = " + Tables.API_ACCOUNTS + "." + + ApiAccounts.PACKAGE_NAME_FK + " )"); + qb.appendWhere(Tables.API_APPS + "." + BaseColumns._ID + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + + break; + case API_BY_PACKAGE_NAME: + qb.setTables(Tables.API_ACCOUNTS + " INNER JOIN " + Tables.API_APPS + " ON " + "(" + + Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = " + Tables.API_ACCOUNTS + "." + + ApiAccounts.PACKAGE_NAME_FK + " )"); + qb.appendWhere(Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + + break; case API_APPS: qb.setTables(Tables.API_APPS); @@ -675,10 +732,26 @@ public class KeychainProvider extends ContentProvider { case API_APPS_BY_PACKAGE_NAME: qb.setTables(Tables.API_APPS); qb.appendWhere(ApiApps.PACKAGE_NAME + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + qb.appendWhereEscapeString(uri.getLastPathSegment()); break; + case API_ACCOUNTS: + qb.setTables(Tables.API_ACCOUNTS); + break; + case API_ACCOUNTS_BY_ROW_ID: + qb.setTables(Tables.API_ACCOUNTS); + + qb.appendWhere(BaseColumns._ID + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + + break; + case API_ACCOUNTS_BY_PACKAGE_NAME: + qb.setTables(Tables.API_ACCOUNTS); + qb.appendWhere(ApiAppsAccountsColumns.PACKAGE_NAME_FK + " = "); + qb.appendWhereEscapeString(uri.getLastPathSegment()); + + break; default: throw new IllegalArgumentException("Unknown URI " + uri); @@ -770,6 +843,11 @@ public class KeychainProvider extends ContentProvider { rowId = db.insertOrThrow(Tables.API_APPS, null, values); rowUri = ApiApps.buildIdUri(Long.toString(rowId)); + break; + case API_ACCOUNTS: + rowId = db.insertOrThrow(Tables.API_ACCOUNTS, null, values); + rowUri = ApiAccounts.buildIdUri(Long.toString(rowId)); + break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); @@ -836,6 +914,14 @@ public class KeychainProvider extends ContentProvider { count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, true, selection), selectionArgs); break; + case API_ACCOUNTS_BY_ROW_ID: + count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, false, selection), + selectionArgs); + break; + case API_ACCOUNTS_BY_PACKAGE_NAME: + count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, true, selection), + selectionArgs); + break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); } @@ -906,6 +992,14 @@ public class KeychainProvider extends ContentProvider { count = db.update(Tables.API_APPS, values, buildDefaultApiAppsSelection(uri, true, selection), selectionArgs); break; + case API_ACCOUNTS_BY_ROW_ID: + count = db.update(Tables.API_ACCOUNTS, values, + buildDefaultApiAccountsSelection(uri, false, selection), selectionArgs); + break; + case API_ACCOUNTS_BY_PACKAGE_NAME: + count = db.update(Tables.API_ACCOUNTS, values, + buildDefaultApiAccountsSelection(uri, true, selection), selectionArgs); + break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); } @@ -1018,6 +1112,21 @@ public class KeychainProvider extends ContentProvider { } } + private String buildDefaultApiAccountsSelection(Uri uri, boolean packageSelection, String selection) { + String lastPathSegment = uri.getLastPathSegment(); + + String andSelection = ""; + if (!TextUtils.isEmpty(selection)) { + andSelection = " AND (" + selection + ")"; + } + + if (packageSelection) { + return ApiAccounts.PACKAGE_NAME_FK + "=" + lastPathSegment + andSelection; + } else { + return BaseColumns._ID + "=" + lastPathSegment + andSelection; + } + } + // @Override // public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { // int match = mUriMatcher.match(uri); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index e0d007de7..7e8bfbc7b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -809,44 +809,62 @@ public class ProviderHelper { return values; } - public static void insertApiApp(Context context, AppSettings appSettings) { - context.getContentResolver().insert(ApiApps.CONTENT_URI, + private static ContentValues contentValueForApiAccounts(AppSettings appSettings) { + ContentValues values = new ContentValues(); + values.put(KeychainContract.ApiAccounts.PACKAGE_NAME_FK, appSettings.getPackageName()); + values.put(KeychainContract.ApiAccounts.KEY_ID, appSettings.getKeyId()); + values.put(KeychainContract.ApiAccounts.COMPRESSION, appSettings.getCompression()); + values.put(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM, appSettings.getEncryptionAlgorithm()); + values.put(KeychainContract.ApiAccounts.HASH_ALORITHM, appSettings.getHashAlgorithm()); + + return values; + } + + public static void insertApi(Context context, AppSettings appSettings) { + context.getContentResolver().insert(KeychainContract.ApiApps.CONTENT_URI, + contentValueForApiApps(appSettings)); + context.getContentResolver().insert(KeychainContract.ApiAccounts.CONTENT_URI, contentValueForApiApps(appSettings)); } + // TODO: uri not working because it is used for both tables public static void updateApiApp(Context context, AppSettings appSettings, Uri uri) { if (context.getContentResolver().update(uri, contentValueForApiApps(appSettings), null, null) <= 0) { throw new RuntimeException(); } + if (context.getContentResolver().update(uri, contentValueForApiAccounts(appSettings), null, + null) <= 0) { + throw new RuntimeException(); + } } - public static AppSettings getApiAppSettings(Context context, Uri uri) { + public static AppSettings getApiSettings(Context context, Uri uri) { AppSettings settings = null; Cursor cur = context.getContentResolver().query(uri, null, null, null, null); if (cur != null && cur.moveToFirst()) { settings = new AppSettings(); settings.setPackageName(cur.getString(cur - .getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME))); + .getColumnIndex(KeychainContract.Api.PACKAGE_NAME))); settings.setPackageSignature(cur.getBlob(cur - .getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE))); - settings.setKeyId(cur.getLong(cur.getColumnIndex(KeychainContract.ApiApps.KEY_ID))); + .getColumnIndex(KeychainContract.Api.PACKAGE_SIGNATURE))); + settings.setKeyId(cur.getLong(cur.getColumnIndex(KeychainContract.Api.KEY_ID))); settings.setCompression(cur.getInt(cur - .getColumnIndexOrThrow(KeychainContract.ApiApps.COMPRESSION))); + .getColumnIndexOrThrow(KeychainContract.Api.COMPRESSION))); settings.setHashAlgorithm(cur.getInt(cur - .getColumnIndexOrThrow(KeychainContract.ApiApps.HASH_ALORITHM))); + .getColumnIndexOrThrow(KeychainContract.Api.HASH_ALORITHM))); settings.setEncryptionAlgorithm(cur.getInt(cur - .getColumnIndexOrThrow(KeychainContract.ApiApps.ENCRYPTION_ALGORITHM))); + .getColumnIndexOrThrow(KeychainContract.Api.ENCRYPTION_ALGORITHM))); } return settings; } - public static byte[] getApiAppSignature(Context context, String packageName) { - Uri queryUri = KeychainContract.ApiApps.buildByPackageNameUri(packageName); + public static byte[] getApiSignature(Context context, String packageName) { + Uri queryUri = KeychainContract.Api.buildByPackageNameUri(packageName); - String[] projection = new String[]{ApiApps.PACKAGE_SIGNATURE}; + String[] projection = new String[]{KeychainContract.Api.PACKAGE_SIGNATURE}; ContentResolver cr = context.getContentResolver(); Cursor cursor = cr.query(queryUri, projection, null, null, null); From 57f9b55677fd0829280a082f739e7d49fbf61db2 Mon Sep 17 00:00:00 2001 From: Sreeram Boyapati Date: Sun, 23 Mar 2014 14:35:48 +0530 Subject: [PATCH 106/253] Added can_encrypt check --- .../keychain/ui/ViewKeyMainFragment.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index e140cb21e..e4f707f3c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -324,7 +324,17 @@ public class ViewKeyMainFragment extends Fragment implements mFingerprint.setText(OtherHelper.colorizeFingerprint(fingerprint)); } - + int valid_keys = 0; + data.moveToFirst(); + do{ + if(data.getInt(KEYS_INDEX_CAN_ENCRYPT) == 1){ + valid_keys++; + } + }while(data.moveToNext()); + if(valid_keys == 0){ + mActionEncrypt.setVisibility(View.GONE); + } + Log.i("Valid Encryption keys", Integer.toString(valid_keys)); mKeysAdapter.swapCursor(data); break; From 900e8ce990862534370df84e6dea53716b7b594c Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Sun, 23 Mar 2014 12:32:13 +0100 Subject: [PATCH 107/253] Adjust credits --- OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-de/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-el/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-es/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-fr/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-ja/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-pl/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-ru/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-tr/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-uk/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw-zh/help_about.html | 3 +-- OpenPGP-Keychain/src/main/res/raw/help_about.html | 3 +-- 19 files changed, 19 insertions(+), 38 deletions(-) diff --git a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html index 863aeee58..58ce75258 100644 --- a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html @@ -15,9 +15,8 @@

      Developers APG 1.x

        -
      • 'Thialfihar' (Lead developer)
      • +
      • Thialfihar (Lead developer)
      • 'Senecaso' (QRCode, sign key, upload key)
      • -
      • Oliver Runge
      • Markus Doits

      Libraries

      diff --git a/OpenPGP-Keychain/src/main/res/raw-de/help_about.html b/OpenPGP-Keychain/src/main/res/raw-de/help_about.html index 37d4193f7..683c8e859 100644 --- a/OpenPGP-Keychain/src/main/res/raw-de/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-de/help_about.html @@ -15,9 +15,8 @@

    Entwickler APG 1.x

      -
    • 'Thialfihar' (Leitender Entwickler)
    • +
    • Thialfihar (Leitender Entwickler)
    • 'Senecaso' (QR-Code, Schlüssel signtieren, Schlüssel hochladen)
    • -
    • Oliver Runge
    • Markus Doits

    Bibliotheken

    diff --git a/OpenPGP-Keychain/src/main/res/raw-el/help_about.html b/OpenPGP-Keychain/src/main/res/raw-el/help_about.html index 863aeee58..58ce75258 100644 --- a/OpenPGP-Keychain/src/main/res/raw-el/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-el/help_about.html @@ -15,9 +15,8 @@

    Developers APG 1.x

      -
    • 'Thialfihar' (Lead developer)
    • +
    • Thialfihar (Lead developer)
    • 'Senecaso' (QRCode, sign key, upload key)
    • -
    • Oliver Runge
    • Markus Doits

    Libraries

    diff --git a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html index 863aeee58..58ce75258 100644 --- a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html @@ -15,9 +15,8 @@

    Developers APG 1.x

      -
    • 'Thialfihar' (Lead developer)
    • +
    • Thialfihar (Lead developer)
    • 'Senecaso' (QRCode, sign key, upload key)
    • -
    • Oliver Runge
    • Markus Doits

    Libraries

    diff --git a/OpenPGP-Keychain/src/main/res/raw-es/help_about.html b/OpenPGP-Keychain/src/main/res/raw-es/help_about.html index 95189425d..7281e1007 100644 --- a/OpenPGP-Keychain/src/main/res/raw-es/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-es/help_about.html @@ -15,9 +15,8 @@

    Desarrolladores de APG 1.x

      -
    • 'Thialfihar' (Desarrollador principal)
    • +
    • Thialfihar (Desarrollador principal)
    • 'Senecaso' (Código QR, clave de firma, carga de clave)
    • -
    • Oliver Runge
    • Markus Doits

    Librerías

    diff --git a/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html b/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html index 863aeee58..58ce75258 100644 --- a/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html @@ -15,9 +15,8 @@

    Developers APG 1.x

      -
    • 'Thialfihar' (Lead developer)
    • +
    • Thialfihar (Lead developer)
    • 'Senecaso' (QRCode, sign key, upload key)
    • -
    • Oliver Runge
    • Markus Doits

    Libraries

    diff --git a/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html b/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html index 3cbbce4d5..7606292ac 100644 --- a/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html @@ -15,9 +15,8 @@

    Les développeurs d'APG 1.x

      -
    • « Thialfihar (développeur principal)
    • +
    • Thialfihar (développeur principal)
    • « Senecaso » (Code QR, signer/téléverser la clef)
    • -
    • Oliver Runge
    • Markus Doits

    Bibliothèques

    diff --git a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html index ba0676f3e..499e33238 100644 --- a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html @@ -15,9 +15,8 @@

    Sviluppatori APG 1.x

      -
    • 'Thialfihar' (Capo Sviluppatore)
    • +
    • Thialfihar (Capo Sviluppatore)
    • 'Senecaso' (QRCode, firma chiavi, caricamento chiavi)
    • -
    • Oliver Runge
    • Markus Doits

    Librerie

    diff --git a/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html b/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html index 206fc9f8d..206334bcd 100644 --- a/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html @@ -15,9 +15,8 @@

    APG 1.xの開発者達

      -
    • 'Thialfihar' (主任開発者)
    • +
    • Thialfihar (主任開発者)
    • 'Senecaso' (QRコード, 鍵署名, 鍵アップロード関係)
    • -
    • Oliver Runge
    • Markus Doits

    ライブラリ

    diff --git a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html index 863aeee58..58ce75258 100644 --- a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html @@ -15,9 +15,8 @@

    Developers APG 1.x

      -
    • 'Thialfihar' (Lead developer)
    • +
    • Thialfihar (Lead developer)
    • 'Senecaso' (QRCode, sign key, upload key)
    • -
    • Oliver Runge
    • Markus Doits

    Libraries

    diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html b/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html index de16d333f..aff9a6de6 100644 --- a/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html @@ -15,9 +15,8 @@

    Deweloperzy APG 1.x

      -
    • 'Thialfihar' (Wiodacy deweloper)
    • +
    • Thialfihar (Wiodacy deweloper)
    • 'Senecaso' (kody QR, podpisy kluczy, wysyłanie kluczy)
    • -
    • Oliver Runge
    • Markus Doits

    Biblioteki

    diff --git a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html index 863aeee58..58ce75258 100644 --- a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html @@ -15,9 +15,8 @@

    Developers APG 1.x

      -
    • 'Thialfihar' (Lead developer)
    • +
    • Thialfihar (Lead developer)
    • 'Senecaso' (QRCode, sign key, upload key)
    • -
    • Oliver Runge
    • Markus Doits

    Libraries

    diff --git a/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html b/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html index 655e98758..ad1fd62b6 100644 --- a/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html @@ -15,9 +15,8 @@

    Разработчики APG 1.x

      -
    • 'Thialfihar' (главный разработчик)
    • +
    • Thialfihar (главный разработчик)
    • 'Senecaso' (QR коды, подписание и загрузка ключей)
    • -
    • Oliver Runge
    • Markus Doits

    Компоненты

    diff --git a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html index 863aeee58..58ce75258 100644 --- a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html @@ -15,9 +15,8 @@

    Developers APG 1.x

      -
    • 'Thialfihar' (Lead developer)
    • +
    • Thialfihar (Lead developer)
    • 'Senecaso' (QRCode, sign key, upload key)
    • -
    • Oliver Runge
    • Markus Doits

    Libraries

    diff --git a/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html b/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html index eb262b242..6faa367dd 100644 --- a/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html @@ -15,9 +15,8 @@

    Geliştiriciler APG 1.x

      -
    • 'Thialfihar' (Baş geliştirici)
    • +
    • Thialfihar (Baş geliştirici)
    • 'Senecaso' (QR Kodu, anahtar imzalama, anahtar yükleme)
    • -
    • Oliver Runge
    • Markus Doits

    Kütüphaneler

    diff --git a/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html b/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html index c8a5a82c5..f801dd335 100644 --- a/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html @@ -15,9 +15,8 @@

    Розробники APG 1.x

      -
    • 'Thialfihar' (основний розробник)
    • +
    • Thialfihar (основний розробник)
    • 'Senecaso' (штрих-код, підпис і завантаження ключів)
    • -
    • Олівер Ранж
    • Маркус Дойтс

    Бібліотеки

    diff --git a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html index 863aeee58..58ce75258 100644 --- a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html @@ -15,9 +15,8 @@

    Developers APG 1.x

      -
    • 'Thialfihar' (Lead developer)
    • +
    • Thialfihar (Lead developer)
    • 'Senecaso' (QRCode, sign key, upload key)
    • -
    • Oliver Runge
    • Markus Doits

    Libraries

    diff --git a/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html b/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html index 23b904995..9038dc3f4 100644 --- a/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html @@ -15,9 +15,8 @@

    Developers APG 1.x

      -
    • 'Thialfihar' (Lead developer)
    • +
    • Thialfihar (Lead developer)
    • 'Senecaso' (QRCode, sign key, upload key)
    • -
    • Oliver Runge
    • Markus Doits

    函式庫

    diff --git a/OpenPGP-Keychain/src/main/res/raw/help_about.html b/OpenPGP-Keychain/src/main/res/raw/help_about.html index 51e3f1325..0fa14efc4 100644 --- a/OpenPGP-Keychain/src/main/res/raw/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw/help_about.html @@ -19,9 +19,8 @@ And don't add newlines before or after p tags because of transifex -->

    Developers APG 1.x

      -
    • 'Thialfihar' (Lead developer)
    • +
    • Thialfihar (Lead developer)
    • 'Senecaso' (QRCode, sign key, upload key)
    • -
    • Oliver Runge
    • Markus Doits
    From 2ea0cd0e8a02072150230dda7dd24a63ff682603 Mon Sep 17 00:00:00 2001 From: Daniel Hammann Date: Sun, 23 Mar 2014 19:35:12 +0100 Subject: [PATCH 108/253] No "empty-list" layout flashing up on start of OpenKeychain --- OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml index 77bd6f4e9..f2430f213 100644 --- a/OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml +++ b/OpenPGP-Keychain/src/main/res/layout/key_list_fragment.xml @@ -11,7 +11,7 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - android:visibility="gone" + android:visibility="visible" android:gravity="center"> + android:orientation="vertical" + android:visibility="gone"> Date: Sun, 23 Mar 2014 19:21:45 +0100 Subject: [PATCH 109/253] Adjust encrypt layout, align signature with button --- OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml b/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml index 03fe496c6..1dba66cfa 100644 --- a/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml +++ b/OpenPGP-Keychain/src/main/res/layout/encrypt_content.xml @@ -72,7 +72,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:paddingLeft="16dp"> + android:paddingLeft="16dp" + android:paddingRight="4dip"> - \ No newline at end of file + From a2048773ee194168594c52c4ea6472b211ef0d5e Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Sun, 23 Mar 2014 19:37:41 +0100 Subject: [PATCH 110/253] Fix file decryption interface Actually use mAssumeSymmetricEncryption and set it to false as default. Don't close the file input stream, so we can actually look for PGP encrypted data in the next step. --- .../keychain/ui/DecryptActivity.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java index 3e389c034..ed9735d8c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java @@ -443,8 +443,7 @@ public class DecryptActivity extends DrawerActivity { getDecryptionKeyFromInputStream(); // if we need a symmetric passphrase or a passphrase to use a secret key ask for it - if (mSecretKeyId == Id.key.symmetric - || PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) { + if (mAssumeSymmetricEncryption || PassphraseCacheService.getCachedPassphrase(this, mSecretKeyId) == null) { showPassphraseDialog(); } else { if (mDecryptTarget == Id.target.file) { @@ -494,6 +493,7 @@ public class DecryptActivity extends DrawerActivity { * TODO: Rework function, remove global variables */ private void getDecryptionKeyFromInputStream() { + mAssumeSymmetricEncryption = false; InputStream inStream = null; if (mContentUri != null) { try { @@ -517,13 +517,6 @@ public class DecryptActivity extends DrawerActivity { Log.e(Constants.TAG, "File not found!", e); AppMsg.makeText(this, getString(R.string.error_file_not_found, e.getMessage()), AppMsg.STYLE_ALERT).show(); - } finally { - try { - if (inStream != null) { - inStream.close(); - } - } catch (Exception e) { - } } } else { inStream = new ByteArrayInputStream(mMessage.getText().toString().getBytes()); @@ -540,7 +533,6 @@ public class DecryptActivity extends DrawerActivity { if (mSecretKeyId == Id.key.none) { throw new PgpGeneralException(getString(R.string.error_no_secret_key_found)); } - mAssumeSymmetricEncryption = false; } catch (NoAsymmetricEncryptionException e) { if (inStream.markSupported()) { inStream.reset(); @@ -553,6 +545,7 @@ public class DecryptActivity extends DrawerActivity { mAssumeSymmetricEncryption = true; } } catch (Exception e) { + Log.e(Constants.TAG, "error while reading decryption key from input stream", e); AppMsg.makeText(this, getString(R.string.error_message, e.getMessage()), AppMsg.STYLE_ALERT).show(); } From b4b709b1e0ff9f520ebd63d934cee7b4fcfc274f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Sun, 23 Mar 2014 19:42:17 +0100 Subject: [PATCH 111/253] put original license of launcher icon into repo --- Resources/graphics/icon/AUTHORS | 21 ++++++++ Resources/graphics/icon/COPYING | 48 ++++++++++++++++++ .../graphics/{ => icon}/kgpg_key2_kopete.svgz | Bin 3 files changed, 69 insertions(+) create mode 100644 Resources/graphics/icon/AUTHORS create mode 100644 Resources/graphics/icon/COPYING rename Resources/graphics/{ => icon}/kgpg_key2_kopete.svgz (100%) diff --git a/Resources/graphics/icon/AUTHORS b/Resources/graphics/icon/AUTHORS new file mode 100644 index 000000000..dbfcfb4fc --- /dev/null +++ b/Resources/graphics/icon/AUTHORS @@ -0,0 +1,21 @@ +Oxygen for KDE3 +Oxygen Icon Theme has been developed by The Oxygen Team. + +Art Director: +David Vignoni + +Designers: +David J. Miller +David Vignoni +Johann Ollivier Lapeyre +Kenneth Wimer +Nuno F. Pinheiro +Riccardo Iaconelli + +Thanks to: +Lee Olson: Contributed drawing used in application-x-bittorent icon. +Marco Aurélio "Coré": Improved audio-input-microphone icon. +Matthias Kretz: Contributed "audio-input-line" device icon. +Mauricio Piacentini : game icons mashup + +Repackaged by Luke Channings \ No newline at end of file diff --git a/Resources/graphics/icon/COPYING b/Resources/graphics/icon/COPYING new file mode 100644 index 000000000..2faa27568 --- /dev/null +++ b/Resources/graphics/icon/COPYING @@ -0,0 +1,48 @@ +The Oxygen Icon Theme for KDE3 + Copyright (C) 2007 David Vignoni + Copyright (C) 2007 Johann Ollivier Lapeyre + Copyright (C) 2007 Kenneth Wimer + Copyright (C) 2007 Nuno Fernades Pinheiro + Copyright (C) 2007 Riccardo Iaconelli + Copyright (C) 2007 David Miller + +and others + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library. If not, see . + +Clarification: + + The GNU Lesser General Public License or LGPL is written for + software libraries in the first place. We expressly want the LGPL to + be valid for this artwork library too. + + KDE Oxygen theme icons is a special kind of software library, it is an + artwork library, it's elements can be used in a Graphical User Interface, or + GUI. + + Source code, for this library means: + - where they exist, SVG; + - otherwise, if applicable, the multi-layered formats xcf or psd, or + otherwise png. + + The LGPL in some sections obliges you to make the files carry + notices. With images this is in some cases impossible or hardly useful. + + With this library a notice is placed at a prominent place in the directory + containing the elements. You may follow this practice. + + The exception in section 5 of the GNU Lesser General Public License covers + the use of elements of this art library in a GUI. + + kde-artists [at] kde.org diff --git a/Resources/graphics/kgpg_key2_kopete.svgz b/Resources/graphics/icon/kgpg_key2_kopete.svgz similarity index 100% rename from Resources/graphics/kgpg_key2_kopete.svgz rename to Resources/graphics/icon/kgpg_key2_kopete.svgz From 3dfcb037dcf5ae18ab34eb8a42a220d695febfe7 Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Sun, 23 Mar 2014 12:57:06 +0100 Subject: [PATCH 112/253] Remove icon set credits --- OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-de/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-el/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-es/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-fr/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-ja/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-pl/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-ru/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-tr/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-uk/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw-zh/help_about.html | 2 -- OpenPGP-Keychain/src/main/res/raw/help_about.html | 2 -- 19 files changed, 38 deletions(-) diff --git a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html index 58ce75258..5a8f7bde7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Apache License v2)
  • Android AppMsg Library (Apache License v2)
  • -
  • Icons from RRZE Icon Set (Creative Commons Attribution Share-Alike licence 3.0)
  • -
  • Icons from Tango Icon Set (Public Domain)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-de/help_about.html b/OpenPGP-Keychain/src/main/res/raw-de/help_about.html index 683c8e859..4199f9b5e 100644 --- a/OpenPGP-Keychain/src/main/res/raw-de/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-de/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Apache Lizenz v2)
  • Android AppMsg Bibliothek (Apache Lizenz v2)
  • -
  • Icons von RRZE Icon Set (Creative Commons Attribution Share-Alike Lizenz 3.0)
  • -
  • Icons von Tango Icon Set (Public Domain)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-el/help_about.html b/OpenPGP-Keychain/src/main/res/raw-el/help_about.html index 58ce75258..5a8f7bde7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-el/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-el/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Apache License v2)
  • Android AppMsg Library (Apache License v2)
  • -
  • Icons from RRZE Icon Set (Creative Commons Attribution Share-Alike licence 3.0)
  • -
  • Icons from Tango Icon Set (Public Domain)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html index 58ce75258..5a8f7bde7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Apache License v2)
  • Android AppMsg Library (Apache License v2)
  • -
  • Icons from RRZE Icon Set (Creative Commons Attribution Share-Alike licence 3.0)
  • -
  • Icons from Tango Icon Set (Public Domain)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-es/help_about.html b/OpenPGP-Keychain/src/main/res/raw-es/help_about.html index 7281e1007..64d80ab74 100644 --- a/OpenPGP-Keychain/src/main/res/raw-es/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-es/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Licencia Apache v2)
  • Librería Android AppMsg (Licencia Apache v2)
  • -
  • Icons de RRZE Icon Set (Creative Commons Attribution Compartir-Igual licencia 3.0)
  • -
  • Iconos de Tango Icon Set (Dominio Público)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html b/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html index 58ce75258..5a8f7bde7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Apache License v2)
  • Android AppMsg Library (Apache License v2)
  • -
  • Icons from RRZE Icon Set (Creative Commons Attribution Share-Alike licence 3.0)
  • -
  • Icons from Tango Icon Set (Public Domain)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html b/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html index 7606292ac..93e5a5df7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Licence Apache v2)
  • Bibliothèque Android AppMsg (Licence Apache v2)
  • -
  • Icônes du jeu d'icônes RRZE (Licence Creative Commons Paternité - Partage des Conditions Initiales à l'Identique 3.0)
  • -
  • Icônes du jeu d'icônes Tango (domaine public)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html index 499e33238..5e4a53ed6 100644 --- a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Licenza Apache v2)
  • Android AppMsg Library (Licenza Apache v2)
  • -
  • Icone da RRZE Icon Set (Licenza Creative Commons Attribution Share-Alike 3.0)
  • -
  • Icone da Tango Icon Set (Pubblico Dominio)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html b/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html index 206334bcd..72c7616a3 100644 --- a/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Apache License v2)
  • Android AppMsg Library (Apache License v2)
  • -
  • Icons from RRZE Icon Set (Creative Commons Attribution Share-Alike licence 3.0)
  • -
  • Icons from Tango Icon Set (パブリックドメイン)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html index 58ce75258..5a8f7bde7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Apache License v2)
  • Android AppMsg Library (Apache License v2)
  • -
  • Icons from RRZE Icon Set (Creative Commons Attribution Share-Alike licence 3.0)
  • -
  • Icons from Tango Icon Set (Public Domain)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html b/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html index aff9a6de6..c07fc503f 100644 --- a/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Licencja Apache v2)
  • Android AppMsg Library (Licencja Apache v2)
  • -
  • Icons from RRZE Icon Set (Licencja Uznanie autorstwa - Na tych samych warunkach 3.0 CC-BY-SA)
  • -
  • Icons from Tango Icon Set (Domena Publiczna)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html index 58ce75258..5a8f7bde7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Apache License v2)
  • Android AppMsg Library (Apache License v2)
  • -
  • Icons from RRZE Icon Set (Creative Commons Attribution Share-Alike licence 3.0)
  • -
  • Icons from Tango Icon Set (Public Domain)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html b/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html index ad1fd62b6..8379bf8de 100644 --- a/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Apache License v2)
  • Библиотека Android AppMsg (Apache License v2)
  • -
  • Иконки RRZE Icon Set (Creative Commons Attribution Share-Alike licence 3.0)
  • -
  • Иконки Tango Icon Set (Public Domain)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html index 58ce75258..5a8f7bde7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Apache License v2)
  • Android AppMsg Library (Apache License v2)
  • -
  • Icons from RRZE Icon Set (Creative Commons Attribution Share-Alike licence 3.0)
  • -
  • Icons from Tango Icon Set (Public Domain)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html b/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html index 6faa367dd..454d16151 100644 --- a/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Apache License v2)
  • Android AppMsg Library (Apache License v2)
  • -
  • İkonlar RRZE Icon Set (Creative Commons Attribution Share-Alike licence 3.0)
  • -
  • İkonlar Tango Icon Set (Public Domain)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html b/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html index f801dd335..9526dc0c6 100644 --- a/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (ліцензія Apache в.2)
  • Бібліотека Android AppMsg Library (Ліцензія Apache в. 2)
  • -
  • Піктограми із набору піктограм RRZE (ліцензія Creative Commons - Із зазначенням авторства - Розповсюдження на тих самих умовах - версія 3.0)
  • -
  • Піктограми із набору піктограм Tango (відкритий домен)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html index 58ce75258..5a8f7bde7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Apache License v2)
  • Android AppMsg Library (Apache License v2)
  • -
  • Icons from RRZE Icon Set (Creative Commons Attribution Share-Alike licence 3.0)
  • -
  • Icons from Tango Icon Set (Public Domain)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html b/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html index 9038dc3f4..6d00fc6a1 100644 --- a/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html @@ -37,8 +37,6 @@ HtmlTextView (Apache License v2)
  • Android AppMsg Library (Apache License v2)
  • -
  • Icons from RRZE Icon Set (Creative Commons Attribution Share-Alike licence 3.0)
  • -
  • Icons from Tango Icon Set (Public Domain)
  • diff --git a/OpenPGP-Keychain/src/main/res/raw/help_about.html b/OpenPGP-Keychain/src/main/res/raw/help_about.html index 0fa14efc4..8d7714365 100644 --- a/OpenPGP-Keychain/src/main/res/raw/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw/help_about.html @@ -34,8 +34,6 @@ And don't add newlines before or after p tags because of transifex -->
  • SpongyCastle (MIT X11 License)
  • HtmlTextView (Apache License v2)
  • Android AppMsg Library (Apache License v2)
  • -
  • Icons from RRZE Icon Set (Creative Commons Attribution Share-Alike licence 3.0)
  • -
  • Icons from Tango Icon Set (Public Domain)
  • From 9d4582f9689f53589bbf7cc3e224f9fd061f1d51 Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Sun, 23 Mar 2014 19:52:25 +0100 Subject: [PATCH 113/253] Update README.md, icon set not used anymore --- README.md | 66 ++++++++++++++++++++++++++----------------------------- 1 file changed, 31 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index 8954bdcda..958a445be 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # OpenKeychain (for Android) -OpenKeychain is an OpenPGP implementation for Android. +OpenKeychain is an OpenPGP implementation for Android. For a more detailed description and installation instructions go to http://www.openkeychain.org . ### Travis CI Build Status @@ -31,9 +31,9 @@ Development mailinglist at http://groups.google.com/d/forum/openpgp-keychain-dev ### Build with Gradle 1. Have Android SDK "tools", "platform-tools", and "build-tools" directories in your PATH (http://developer.android.com/sdk/index.html) -2. Open the Android SDK Manager (shell command: ``android``). -Expand the Tools directory and select "Android SDK Build-tools (Version 19.0.3)". -Expand the Extras directory and install "Android Support Repository" +2. Open the Android SDK Manager (shell command: ``android``). +Expand the Tools directory and select "Android SDK Build-tools (Version 19.0.3)". +Expand the Extras directory and install "Android Support Repository" Select everything for the newest SDK (API-Level 19) 3. Export ANDROID_HOME pointing to your Android SDK 4. Execute ``./gradlew build`` @@ -59,11 +59,11 @@ I am using the newest [Android Studio](http://developer.android.com/sdk/installi OpenKeychain provides two APIs, namely the Intent API and the Remote OpenPGP API. The Intent API can be used without permissions to start OpenKeychain's activities for cryptographic operations, import of keys, etc. -However, it always requires user input, so that no malicious application can use this API without user intervention. +However, it always requires user input, so that no malicious application can use this API without user intervention. The Remote OpenPGP API is more sophisticated and allows to to operations without user interaction in the background. When utilizing this API, OpenKeychain asks the user on first use to grant access for the calling client application. -More technical information and examples about these APIs can be found in the project's wiki: +More technical information and examples about these APIs can be found in the project's wiki: * [Intent API](https://github.com/openpgp-keychain/openpgp-keychain/wiki/Intent-API) * [Remote OpenPGP API](https://github.com/openpgp-keychain/openpgp-keychain/wiki/OpenPGP-API) @@ -110,7 +110,7 @@ see ### Gradle Build System -We try to make our builds as [reproducible/deterministic](https://blog.torproject.org/blog/deterministic-builds-part-one-cyberwar-and-global-compromise) as possible. +We try to make our builds as [reproducible/deterministic](https://blog.torproject.org/blog/deterministic-builds-part-one-cyberwar-and-global-compromise) as possible. When changing build files or dependencies, respect the following requirements: * No precompiled libraries. All libraries should be provided as sourcecode in "libraries" folder (you never know what pre-compiled jar files really contain! The library files are currently directly commited, because git submodules/git subtree are too much of a hassle for new contributors. This could change in the future!) * No dependencies from Maven (also a soft requirement for inclusion in [F-Droid](https://f-droid.org)) @@ -180,59 +180,55 @@ Some parts (older parts and some libraries are Apache License v2, MIT X11 Licens > 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 . ### Libraries -* SpongyCastle - https://github.com/rtyley/spongycastle +* SpongyCastle + https://github.com/rtyley/spongycastle MIT X11 License -* Android Support Library v4 - http://developer.android.com/tools/support-library/index.html - Apache License v2 - -* Android Support Library v7 'appcompat' - http://developer.android.com/tools/support-library/index.html +* Android Support Library v4 + http://developer.android.com/tools/support-library/index.html Apache License v2 -* HtmlTextView - https://github.com/dschuermann/html-textview +* Android Support Library v7 'appcompat' + http://developer.android.com/tools/support-library/index.html Apache License v2 -* ZXing - https://github.com/zxing/zxing +* HtmlTextView + https://github.com/dschuermann/html-textview Apache License v2 - -* StickyListHeaders - https://github.com/emilsjolander/StickyListHeaders + +* ZXing + https://github.com/zxing/zxing Apache License v2 - -* Android-Bootstrap - https://github.com/Bearded-Hen/Android-Bootstrap + +* StickyListHeaders + https://github.com/emilsjolander/StickyListHeaders + Apache License v2 + +* Android-Bootstrap + https://github.com/Bearded-Hen/Android-Bootstrap MIT License -* Android AppMsg - https://github.com/johnkil/Android-AppMsg +* Android AppMsg + https://github.com/johnkil/Android-AppMsg Apache License v2 ### Images -* icon.svg +* icon.svg modified version of kgpg_key2_kopete.svgz -* key.svg - http://rrze-icon-set.berlios.de/ - Creative Commons Attribution Share-Alike licence 3.0 - -* Menu icons +* Menu icons http://developer.android.com/design/downloads/index.html#action-bar-icon-pack From c9053b97a880ffcc7e7f8a58b9150fa546f87db0 Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Sun, 23 Mar 2014 21:56:18 +0100 Subject: [PATCH 114/253] Rename old mentionings of APG-named things Mostly in comments, most importantly: STORE_PATH now ends in /KeychainBlobs instead of /ApgBlobs --- .../sufficientlysecure/keychain/helper/ExportHelper.java | 4 ++-- .../keychain/provider/KeychainServiceBlobProvider.java | 2 +- .../keychain/service/KeychainIntentService.java | 2 +- .../keychain/ui/CertifyKeyActivity.java | 8 ++++---- .../sufficientlysecure/keychain/ui/DecryptActivity.java | 4 ++-- .../sufficientlysecure/keychain/ui/EditKeyActivity.java | 8 ++++---- .../sufficientlysecure/keychain/ui/EncryptActivity.java | 4 ++-- .../keychain/ui/ImportKeysActivity.java | 2 +- .../sufficientlysecure/keychain/ui/UploadKeyActivity.java | 4 ++-- .../keychain/ui/dialog/DeleteFileDialogFragment.java | 4 ++-- .../keychain/ui/widget/SectionView.java | 4 ++-- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java index 2bfa796c7..cc240a67b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/ExportHelper.java @@ -139,7 +139,7 @@ public class ExportHelper { intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after exporting is done in ApgService + // Message is received after exporting is done in KeychainIntentService KeychainIntentServiceHandler exportHandler = new KeychainIntentServiceHandler(mActivity, mActivity.getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL, @@ -151,7 +151,7 @@ public class ExportHelper { } }) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java index 6ac61e157..aa30e845d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainServiceBlobProvider.java @@ -38,7 +38,7 @@ import java.util.List; import java.util.UUID; public class KeychainServiceBlobProvider extends ContentProvider { - private static final String STORE_PATH = Constants.Path.APP_DIR + "/ApgBlobs"; + private static final String STORE_PATH = Constants.Path.APP_DIR + "/KeychainBlobs"; private KeychainServiceBlobDatabase mBlobDatabase = null; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java index e26ee3c76..bd3a0421b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/KeychainIntentService.java @@ -855,7 +855,7 @@ public class KeychainIntentService extends IntentService if (this.mIsCanceled) { return; } - Log.e(Constants.TAG, "ApgService Exception: ", e); + Log.e(Constants.TAG, "KeychainIntentService Exception: ", e); e.printStackTrace(); Bundle data = new Bundle(); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java index dff4e9d72..5dc06c16d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/CertifyKeyActivity.java @@ -327,11 +327,11 @@ public class CertifyKeyActivity extends ActionBarActivity implements intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after signing is done in ApgService + // Message is received after signing is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, getString(R.string.progress_signing), ProgressDialog.STYLE_SPINNER) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { @@ -380,11 +380,11 @@ public class CertifyKeyActivity extends ActionBarActivity implements intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after uploading is done in ApgService + // Message is received after uploading is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java index ed9735d8c..9b3b00c19 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java @@ -631,11 +631,11 @@ public class DecryptActivity extends DrawerActivity { intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after encrypting is done in ApgService + // Message is received after encrypting is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, getString(R.string.progress_decrypting), ProgressDialog.STYLE_HORIZONTAL) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java index 092a502e1..6eb5b9d2d 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EditKeyActivity.java @@ -180,7 +180,7 @@ public class EditKeyActivity extends ActionBarActivity { serviceIntent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after generating is done in ApgService + // Message is received after generating is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( this, getResources().getQuantityString(R.plurals.progress_generating, 1), ProgressDialog.STYLE_HORIZONTAL, true, @@ -197,7 +197,7 @@ public class EditKeyActivity extends ActionBarActivity { @Override public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { @@ -540,11 +540,11 @@ public class EditKeyActivity extends ActionBarActivity { intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after saving is done in ApgService + // Message is received after saving is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, getString(R.string.progress_saving), ProgressDialog.STYLE_HORIZONTAL) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java index 4ccfd393f..4f18f69d7 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/EncryptActivity.java @@ -597,11 +597,11 @@ public class EncryptActivity extends DrawerActivity { intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after encrypting is done in ApgService + // Message is received after encrypting is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, getString(R.string.progress_encrypting), ProgressDialog.STYLE_HORIZONTAL) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java index 05bfc613e..834509f53 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ImportKeysActivity.java @@ -363,7 +363,7 @@ public class ImportKeysActivity extends DrawerActivity implements ActionBar.OnNa * Import keys with mImportData */ public void importKeys() { - // Message is received after importing is done in ApgService + // Message is received after importing is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler( this, getString(R.string.progress_importing), diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java index 2c8f66488..0e231e6a8 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/UploadKeyActivity.java @@ -98,11 +98,11 @@ public class UploadKeyActivity extends ActionBarActivity { intent.putExtra(KeychainIntentService.EXTRA_DATA, data); - // Message is received after uploading is done in ApgService + // Message is received after uploading is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(this, getString(R.string.progress_exporting), ProgressDialog.STYLE_HORIZONTAL) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java index b067010df..b4c38184c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/dialog/DeleteFileDialogFragment.java @@ -87,11 +87,11 @@ public class DeleteFileDialogFragment extends DialogFragment { false, null); - // Message is received after deleting is done in ApgService + // Message is received after deleting is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(activity, deletingDialog) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java index e5b6003b1..1ef178f15 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/widget/SectionView.java @@ -256,11 +256,11 @@ public class SectionView extends LinearLayout implements OnClickListener, Editor } }); - // Message is received after generating is done in ApgService + // Message is received after generating is done in KeychainIntentService KeychainIntentServiceHandler saveHandler = new KeychainIntentServiceHandler(mActivity, mGeneratingDialog) { public void handleMessage(Message message) { - // handle messages by standard ApgHandler first + // handle messages by standard KeychainIntentServiceHandler first super.handleMessage(message); if (message.arg1 == KeychainIntentServiceHandler.MESSAGE_OKAY) { From 7d5896b107643803aa7d32c0ef4c33c40246a1e2 Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Sun, 23 Mar 2014 21:28:29 +0100 Subject: [PATCH 115/253] Add recent contributors to credits --- OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-de/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-el/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-es/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-fr/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-ja/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-pl/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-ru/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-tr/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-uk/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw-zh/help_about.html | 6 ++++++ OpenPGP-Keychain/src/main/res/raw/help_about.html | 6 ++++++ 19 files changed, 114 insertions(+) diff --git a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html index 5a8f7bde7..d9150a5e8 100644 --- a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (crypto patches)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (UI)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Developers APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-de/help_about.html b/OpenPGP-Keychain/src/main/res/raw-de/help_about.html index 4199f9b5e..676c2bdd2 100644 --- a/OpenPGP-Keychain/src/main/res/raw-de/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-de/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (crypto patches)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (UI)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Entwickler APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-el/help_about.html b/OpenPGP-Keychain/src/main/res/raw-el/help_about.html index 5a8f7bde7..d9150a5e8 100644 --- a/OpenPGP-Keychain/src/main/res/raw-el/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-el/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (crypto patches)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (UI)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Developers APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html index 5a8f7bde7..d9150a5e8 100644 --- a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (crypto patches)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (UI)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Developers APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-es/help_about.html b/OpenPGP-Keychain/src/main/res/raw-es/help_about.html index 64d80ab74..552155d68 100644 --- a/OpenPGP-Keychain/src/main/res/raw-es/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-es/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (Parches cryptográficos)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (UI)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Desarrolladores de APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html b/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html index 5a8f7bde7..d9150a5e8 100644 --- a/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (crypto patches)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (UI)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Developers APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html b/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html index 93e5a5df7..43094ce9d 100644 --- a/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (correctif crypto)
  • Brian C. Barnes
  • Bahtiar « kalkin » Gadimov (interface utilisateur)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Les développeurs d'APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html index 5e4a53ed6..2d17e1882 100644 --- a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (Patch crittografia)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (Interfaccia Utente)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Sviluppatori APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html b/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html index 72c7616a3..3f630264c 100644 --- a/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-ja/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (暗号関係パッチ提供)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (UI)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • APG 1.xの開発者達

    diff --git a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html index 5a8f7bde7..d9150a5e8 100644 --- a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (crypto patches)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (UI)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Developers APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html b/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html index c07fc503f..c41859b60 100644 --- a/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (łatki crypto)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (Interfejs Użytkownika)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Deweloperzy APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html index 5a8f7bde7..d9150a5e8 100644 --- a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (crypto patches)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (UI)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Developers APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html b/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html index 8379bf8de..63335110e 100644 --- a/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (патчи криптографии)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (UI)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Разработчики APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html index 5a8f7bde7..d9150a5e8 100644 --- a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (crypto patches)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (UI)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Developers APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html b/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html index 454d16151..d50154765 100644 --- a/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (kripto yamaları)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (Arayüz)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Geliştiriciler APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html b/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html index 9526dc0c6..4e6bb02dd 100644 --- a/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html @@ -11,6 +11,12 @@
  • Аш Гюдж (латки шифрування)
  • Браян С. Барнс
  • Бахтіяр 'kalkin' Ґадімов (інтерфейс)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Розробники APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html index 5a8f7bde7..d9150a5e8 100644 --- a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (crypto patches)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (UI)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Developers APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html b/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html index 6d00fc6a1..1eec8bdbf 100644 --- a/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-zh/help_about.html @@ -11,6 +11,12 @@
  • Ash Hughes (crypto patches)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (介面)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Developers APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw/help_about.html b/OpenPGP-Keychain/src/main/res/raw/help_about.html index 8d7714365..2ffbb47a6 100644 --- a/OpenPGP-Keychain/src/main/res/raw/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw/help_about.html @@ -15,6 +15,12 @@ And don't add newlines before or after p tags because of transifex -->
  • Ash Hughes (crypto patches)
  • Brian C. Barnes
  • Bahtiar 'kalkin' Gadimov (UI)
  • +
  • Daniel Hammann
  • +
  • Daniel Haß
  • +
  • Greg Witczak
  • +
  • Miroojin Bakshi
  • +
  • Paul Sarbinowski
  • +
  • Vincent Breitmoser
  • Developers APG 1.x

    From 0867d1f0788660bd62aa50735ff3b7b385da0f23 Mon Sep 17 00:00:00 2001 From: Thialfihar Date: Mon, 24 Mar 2014 00:23:18 +0100 Subject: [PATCH 116/253] Change the getting started text "My Keys" is now wrapped into "Contacts". --- OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw-de/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw-el/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw-es-rCO/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw-es/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw-fr/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw-it-rIT/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw-ja/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw-pl/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw-ru/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw-tr/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw-uk/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_start.html | 2 +- OpenPGP-Keychain/src/main/res/raw/help_start.html | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_start.html b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_start.html index 3a6443a2f..0e60c17a7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_start.html @@ -2,7 +2,7 @@

    Getting started

    -

    First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-de/help_start.html b/OpenPGP-Keychain/src/main/res/raw-de/help_start.html index a7949dd64..7a652682e 100644 --- a/OpenPGP-Keychain/src/main/res/raw-de/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-de/help_start.html @@ -2,7 +2,7 @@

    Los gehts

    -

    First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-el/help_start.html b/OpenPGP-Keychain/src/main/res/raw-el/help_start.html index 3a6443a2f..0e60c17a7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-el/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-el/help_start.html @@ -2,7 +2,7 @@

    Getting started

    -

    First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_start.html b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_start.html index 3a6443a2f..0e60c17a7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-es-rCO/help_start.html @@ -2,7 +2,7 @@

    Getting started

    -

    First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-es/help_start.html b/OpenPGP-Keychain/src/main/res/raw-es/help_start.html index 2907bbc99..895b52469 100644 --- a/OpenPGP-Keychain/src/main/res/raw-es/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-es/help_start.html @@ -2,7 +2,7 @@

    Primeros pasos

    -

    Primero necesitas un par de claves personales. Crea una a través del menú "Mis claves" o importa un par de claves ya existentes a través de "Importar claves". Después, puedes descargar las claves de tus amigos o intercambiarlas a través de códigos QR o NFC.

    +

    Primero necesitas un par de claves personales. Crea una a través del menú "Contactos" o importa un par de claves ya existentes a través de "Importar claves". Después, puedes descargar las claves de tus amigos o intercambiarlas a través de códigos QR o NFC.

    Es recomendable que instales OI File Manager para una mejor selección de archivos y Barcode Scanner para escanear los códigos QR generados. Pulsando en los enlaces se abrirá Google Play o F-Droid.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-fr/help_start.html b/OpenPGP-Keychain/src/main/res/raw-fr/help_start.html index 0c1610f0c..962c33d86 100644 --- a/OpenPGP-Keychain/src/main/res/raw-fr/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-fr/help_start.html @@ -2,7 +2,7 @@

    Commencer

    -

    Vous avez d'abord besoin d'une paire de clefs personelles. Créez-en une avec l'option du menu « Mes clefs » ou importez des paires de clefs existantes avec « Importer des clefs ». Ensuite vous pouvez télécharger les clefs de vos amis, ou les échanger par codes QR ou NFC.

    +

    Vous avez d'abord besoin d'une paire de clefs personelles. Créez-en une avec l'option du menu « Contacts » ou importez des paires de clefs existantes avec « Importer des clefs ». Ensuite vous pouvez télécharger les clefs de vos amis, ou les échanger par codes QR ou NFC.

    Il vous est recommendé d'installer le gestionnaire de fichiers OI pour sa fonction améliorée de séléction des fichiers et le lecteur de codes à barres pour balayer les codes QR générés. Cliquer sur les liens ouvrira Google Play Store ou F-Droid pour l'installation.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_start.html b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_start.html index 4eadd82fc..1a4a7303e 100644 --- a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_start.html @@ -2,7 +2,7 @@

    Per iniziare

    -

    Per prima cosa hai bisogno di un paio di chiavi personali. Creane una tramite i menu di opzione sotto 'Mie Chiavi' o importane di esistenti attraverso "Importa Chiavi". Dopodiche' puoi scaricare le chiavi dei tuoi amici o scambiarle tramite Codici QR o NFC.

    +

    Per prima cosa hai bisogno di un paio di chiavi personali. Creane una tramite i menu di opzione sotto 'Contatti' o importane di esistenti attraverso "Importa Chiavi". Dopodiche' puoi scaricare le chiavi dei tuoi amici o scambiarle tramite Codici QR o NFC.

    Si raccomanda di installare OI File Manager per una migliore selezione dei file e Barcode Scanner per scansionare i codici QR. I collegamenti verranno aperti in Google Play Store o F-Droid per l'installazione.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-ja/help_start.html b/OpenPGP-Keychain/src/main/res/raw-ja/help_start.html index 9764e876a..04ad31352 100644 --- a/OpenPGP-Keychain/src/main/res/raw-ja/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-ja/help_start.html @@ -2,7 +2,7 @@

    入門

    -

    最初にあなたの個人用鍵ペアが必要になります。オプションメニューの"自分の鍵"で生成するか、"鍵のインポート"から既存の鍵ペアをインポートします。その後、あなたの友人の鍵をダウンロード、もしくはQRコードやNFCで交換します。

    +

    最初にあなたの個人用鍵ペアが必要になります。オプションメニューの"連絡先"で生成するか、"鍵のインポート"から既存の鍵ペアをインポートします。その後、あなたの友人の鍵をダウンロード、もしくはQRコードやNFCで交換します。

    ファイルの選択を拡張するにはOI File ManagerBarcode Scannerを生成したQRコードのスキャンのため、それぞれのインストールを必要とします。 リンクをクリックして、Google Play Store上かF-Droidからインストールしてください。

    diff --git a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_start.html b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_start.html index 3a6443a2f..0e60c17a7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-nl-rNL/help_start.html @@ -2,7 +2,7 @@

    Getting started

    -

    First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/help_start.html b/OpenPGP-Keychain/src/main/res/raw-pl/help_start.html index 539388789..d2faa2db9 100644 --- a/OpenPGP-Keychain/src/main/res/raw-pl/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-pl/help_start.html @@ -2,7 +2,7 @@

    Pierwsze kroki

    -

    Po pierwsze potrzebujesz swoją osobistą parę kluczy. Stwórz ją, korzystając z odpowiedniej opcji w sekcji "Moje klucze" lub też zaimportuj istniejącą parę korzystając z sekcji "Importuj klucze". Następnie możesz pobrać klucze Twoich znajomych lub wymieniać się z nimi za pośrednictwem kodów QR lub technologii NFC.

    +

    Po pierwsze potrzebujesz swoją osobistą parę kluczy. Stwórz ją, korzystając z odpowiedniej opcji w sekcji "Kontakty" lub też zaimportuj istniejącą parę korzystając z sekcji "Importuj klucze". Następnie możesz pobrać klucze Twoich znajomych lub wymieniać się z nimi za pośrednictwem kodów QR lub technologii NFC.

    Zalecana jest instalacja menadżera plików OI File Manager w celu zapewnienia wygodniejszego wyboru plików oraz programu Barcode Scanner, który jest w stanie skanować wygenerowane kody QR. Kliknięcie na powyższe linki przekieruje Cię do sklepu Google Play / F-Droid.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_start.html b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_start.html index 3a6443a2f..0e60c17a7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-pt-rBR/help_start.html @@ -2,7 +2,7 @@

    Getting started

    -

    First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-ru/help_start.html b/OpenPGP-Keychain/src/main/res/raw-ru/help_start.html index 9b2b99e96..bc9dade67 100644 --- a/OpenPGP-Keychain/src/main/res/raw-ru/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-ru/help_start.html @@ -2,7 +2,7 @@

    Приступая

    -

    Для начала вам понадобится своя пара ключей. Воспользуйтесь меню в разделе "Мои ключи", что бы создать новую, или добавьте ранее созданную пару в разделе "Импорт ключей". После этого вы сможете скачать ключи ваших друзей или обменяться ключами посредством QR кодов или NFC.

    +

    Для начала вам понадобится своя пара ключей. Воспользуйтесь меню в разделе "Контакты", что бы создать новую, или добавьте ранее созданную пару в разделе "Импорт ключей". После этого вы сможете скачать ключи ваших друзей или обменяться ключами посредством QR кодов или NFC.

    Рекомендуется установить OI File Manager для удобного выбора файлов и Barcode Scanner для распознавания QR кодов. Перейдите по ссылкам на соответствующие страницы Google Play или F-Droid для дальнейшей установки.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_start.html b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_start.html index 3a6443a2f..0e60c17a7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-sl-rSI/help_start.html @@ -2,7 +2,7 @@

    Getting started

    -

    First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-tr/help_start.html b/OpenPGP-Keychain/src/main/res/raw-tr/help_start.html index 3a6443a2f..0e60c17a7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-tr/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-tr/help_start.html @@ -2,7 +2,7 @@

    Getting started

    -

    First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-uk/help_start.html b/OpenPGP-Keychain/src/main/res/raw-uk/help_start.html index 3bfb40f8a..78443070d 100644 --- a/OpenPGP-Keychain/src/main/res/raw-uk/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-uk/help_start.html @@ -2,7 +2,7 @@

    Приступаючи до роботи

    -

    Спершу вам потрібна персональна в'язка ключів. Створіть одну через меню параметрів у "Мої Ключі" або імпортуйте наявні в'язки ключів через "Імпорт ключів". Після цього ви зможете завантажувати ключі ваших друзів чи обміняти їх через штрих-коди або NFC.

    +

    Спершу вам потрібна персональна в'язка ключів. Створіть одну через меню параметрів у "Контакти" або імпортуйте наявні в'язки ключів через "Імпорт ключів". Після цього ви зможете завантажувати ключі ваших друзів чи обміняти їх через штрих-коди або NFC.

    Рекомендуємо вам встановити OI File Manager для поліпшеного виділення файлів та Barcode Scanner для сканування згенерованих штрих-кодів. Натискання посилань відкриє Google Play або F-Droid для встановлення.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_start.html b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_start.html index 3a6443a2f..0e60c17a7 100644 --- a/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-zh-rTW/help_start.html @@ -2,7 +2,7 @@

    Getting started

    -

    First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

    diff --git a/OpenPGP-Keychain/src/main/res/raw/help_start.html b/OpenPGP-Keychain/src/main/res/raw/help_start.html index 7afac0f08..56c02b1fd 100644 --- a/OpenPGP-Keychain/src/main/res/raw/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw/help_start.html @@ -6,7 +6,7 @@ And don't add newlines before or after p tags because of transifex -->

    Getting started

    -

    First you need a personal key pair. Create one via the option menus in "My Keys" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

    From fcc7117770be50b1fdaf204bf725a69a111c26d2 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Sun, 23 Mar 2014 23:41:08 +0000 Subject: [PATCH 117/253] move stuff around, save work --- .../keychain/pgp/PgpKeyOperation.java | 120 +++++++++--------- 1 file changed, 61 insertions(+), 59 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 8ef2a7452..e64c11852 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -497,12 +497,69 @@ public class PgpKeyOperation { for (int i = 0; i < saveParcel.keys.size(); ++i) { updateProgress(40 + 50 * (i - 1) / (saveParcel.keys.size() - 1), 100); if (saveParcel.moddedKeys[i]) { + //to make public key, use a keygen and temp.publickeyring? + + PGPSecretKey subKey = saveParcel.keys.get(i); + PGPPublicKey subPublicKey = subKey.getPublicKey(); + + PBESecretKeyDecryptor keyDecryptor2; + if (saveParcel.newKeys[i]) { + keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( + "".toCharArray()); + } else { + keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( + saveParcel.oldPassPhrase.toCharArray()); + } + PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2); + + // TODO: now used without algorithm and creation time?! (APG 1) + PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey); + + hashedPacketsGen = new PGPSignatureSubpacketGenerator(); + unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); + + usageId = saveParcel.keysUsages.get(i); + canSign = (usageId & KeyFlags.SIGN_DATA) > 0; //todo - separate function for this + if (canSign) { + Date todayDate = new Date(); //both sig times the same + // cross-certify signing keys + hashedPacketsGen.setSignatureCreationTime(false, todayDate); //set outer creation time + PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); + subHashedPacketsGen.setSignatureCreationTime(false, todayDate); //set inner creation time + PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( + subPublicKey.getAlgorithm(), PGPUtil.SHA1) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); + sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey); + sGen.setHashedSubpackets(subHashedPacketsGen.generate()); + PGPSignature certification = sGen.generateCertification(masterPublicKey, + subPublicKey); + unhashedPacketsGen.setEmbeddedSignature(false, certification); + } + hashedPacketsGen.setKeyFlags(false, usageId); + + if (saveParcel.keysExpiryDates.get(i) != null) { + GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC")); + creationDate.setTime(subPublicKey.getCreationTime()); + GregorianCalendar expiryDate = saveParcel.keysExpiryDates.get(i); + //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c + //here we purposefully ignore partial days in each date - long type has no fractional part! + long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000); + if (numDays <= 0) + throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation)); + hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400); + } else { + hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding, + //this happens anyway + } + + keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate()); + //discard only certain certs //secretkey.replacepublickey with updated public key -//secretkeyring.insertsecretkey with newly signed secret key - } else { -//else nothing, right? } - if (saveParcel.newKeys[i]) { + if (saveParcel.newKeys[i]) { //might not be necessary //set the passphrase to the old one, so we can update the whole keyring passphrase later PBESecretKeyEncryptor keyEncryptorOld = new JcePBESecretKeyEncryptorBuilder( PGPEncryptedData.CAST5, sha1Calc) @@ -520,61 +577,6 @@ public class PgpKeyOperation { } updateProgress(R.string.progress_adding_sub_keys, 40, 100); - for (int i = 1; i < saveParcel.keys.size(); ++i) { - updateProgress(40 + 50 * (i - 1) / (saveParcel.keys.size() - 1), 100); - - PGPSecretKey subKey = saveParcel.keys.get(i); - PGPPublicKey subPublicKey = subKey.getPublicKey(); - - PBESecretKeyDecryptor keyDecryptor2 = new JcePBESecretKeyDecryptorBuilder() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.oldPassPhrase.toCharArray()); - PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2); - - // TODO: now used without algorithm and creation time?! (APG 1) - PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey); - - hashedPacketsGen = new PGPSignatureSubpacketGenerator(); - unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); - - usageId = saveParcel.keysUsages.get(i); - canSign = (usageId & KeyFlags.SIGN_DATA) > 0; //todo - separate function for this - if (canSign) { - Date todayDate = new Date(); //both sig times the same - // cross-certify signing keys - hashedPacketsGen.setSignatureCreationTime(false, todayDate); //set outer creation time - PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator(); - subHashedPacketsGen.setSignatureCreationTime(false, todayDate); //set inner creation time - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - subPublicKey.getAlgorithm(), PGPUtil.SHA1) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey); - sGen.setHashedSubpackets(subHashedPacketsGen.generate()); - PGPSignature certification = sGen.generateCertification(masterPublicKey, - subPublicKey); - unhashedPacketsGen.setEmbeddedSignature(false, certification); - } - hashedPacketsGen.setKeyFlags(false, usageId); - - if (saveParcel.keysExpiryDates.get(i) != null) { - GregorianCalendar creationDate = new GregorianCalendar(TimeZone.getTimeZone("UTC")); - creationDate.setTime(subPublicKey.getCreationTime()); - GregorianCalendar expiryDate = saveParcel.keysExpiryDates.get(i); - //note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c - //here we purposefully ignore partial days in each date - long type has no fractional part! - long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000); - if (numDays <= 0) - throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation)); - hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400); - } else { - hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding, - //this happens anyway - } - - keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate()); - } - //update the passphrase mKR = PGPSecretKeyRing.copyWithNewPassword(mKR, keyDecryptor, keyEncryptor); updateProgress(R.string.progress_saving_key_ring, 90, 100); From 974514e470d6835ee7a2f6cc34863dae23f16a3e Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Mon, 24 Mar 2014 16:15:28 +0000 Subject: [PATCH 118/253] work on saving --- .../keychain/pgp/PgpKeyOperation.java | 47 ++++++++++--------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index e64c11852..3d7826553 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -382,6 +382,8 @@ public class PgpKeyOperation { do we need to remove and add in? */ + //todo: flag changes of master key if IDs changed maybe? + for (PGPSecretKey dKey : saveParcel.deletedKeys) { mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey); } @@ -484,11 +486,11 @@ public class PgpKeyOperation { PGPContentSignerBuilder certificationSignerBuilder = new JcaPGPContentSignerBuilder( masterKeyPair.getPublicKey().getAlgorithm(), HashAlgorithmTags.SHA1); - // Build key encrypter based on passphrase + // Build key encryptor based on old passphrase, as some keys may be unchanged PBESecretKeyEncryptor keyEncryptor = new JcePBESecretKeyEncryptorBuilder( PGPEncryptedData.CAST5, sha1Calc) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.newPassPhrase.toCharArray()); + saveParcel.oldPassPhrase.toCharArray()); PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), @@ -497,8 +499,6 @@ public class PgpKeyOperation { for (int i = 0; i < saveParcel.keys.size(); ++i) { updateProgress(40 + 50 * (i - 1) / (saveParcel.keys.size() - 1), 100); if (saveParcel.moddedKeys[i]) { - //to make public key, use a keygen and temp.publickeyring? - PGPSecretKey subKey = saveParcel.keys.get(i); PGPPublicKey subPublicKey = subKey.getPublicKey(); @@ -513,8 +513,6 @@ public class PgpKeyOperation { saveParcel.oldPassPhrase.toCharArray()); } PGPPrivateKey subPrivateKey = subKey.extractPrivateKey(keyDecryptor2); - - // TODO: now used without algorithm and creation time?! (APG 1) PGPKeyPair subKeyPair = new PGPKeyPair(subPublicKey, subPrivateKey); hashedPacketsGen = new PGPSignatureSubpacketGenerator(); @@ -557,28 +555,31 @@ public class PgpKeyOperation { keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate()); //discard only certain certs -//secretkey.replacepublickey with updated public key } - if (saveParcel.newKeys[i]) { //might not be necessary - //set the passphrase to the old one, so we can update the whole keyring passphrase later - PBESecretKeyEncryptor keyEncryptorOld = new JcePBESecretKeyEncryptorBuilder( - PGPEncryptedData.CAST5, sha1Calc) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.oldPassPhrase.toCharArray()); - PBESecretKeyDecryptor keyDecryptorBlank = new JcePBESecretKeyDecryptorBuilder() - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( - saveParcel.oldPassPhrase.toCharArray()); - saveParcel.keys.set(i, PGPSecretKey.copyWithNewPassword(saveParcel.keys.get(i), - keyDecryptorBlank, keyEncryptorOld)); - } - //finally, update the keyrings - mKR = PGPSecretKeyRing.insertSecretKey(mKR, saveParcel.keys.get(i)); - pKR = PGPPublicKeyRing.insertPublicKey(pKR, saveParcel.keys.get(i).getPublicKey()); } + + PGPSecretKeyRing updatedSecretKeyRing = keyGen.generateSecretKeyRing(); + //finally, update the keyrings + Iterator itr = updatedSecretKeyRing.getSecretKeys(); + while (itr.hasNext()) { + PGPSecretKey theNextKey = itr.next(); + if ((theNextKey.isMasterKey() && saveParcel.moddedKeys[0]) || !theNextKey.isMasterKey()) { + mKR = PGPSecretKeyRing.insertSecretKey(mKR, theNextKey); + pKR = PGPPublicKeyRing.insertPublicKey(pKR, theNextKey.getPublicKey()); + } + } + + updateProgress(R.string.progress_adding_sub_keys, 40, 100); + // Build key encryptor based on new passphrase + PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( + PGPEncryptedData.CAST5, sha1Calc) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( + saveParcel.newPassPhrase.toCharArray()); + //update the passphrase - mKR = PGPSecretKeyRing.copyWithNewPassword(mKR, keyDecryptor, keyEncryptor); + mKR = PGPSecretKeyRing.copyWithNewPassword(mKR, keyDecryptor, keyEncryptorNew); updateProgress(R.string.progress_saving_key_ring, 90, 100); ProviderHelper.saveKeyRing(mContext, mKR); From 300b90bcac6fbf5b5e416943e538a2c2bc49342c Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Mon, 24 Mar 2014 16:47:02 +0000 Subject: [PATCH 119/253] make sure ID info is saved --- .../keychain/pgp/PgpKeyOperation.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 3d7826553..5f97aa2e5 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -368,7 +368,7 @@ public class PgpKeyOperation { } /* - IDs - + IDs - NB This might not need to happen later, if we change the way the primary ID is chosen remove deleted ids if the primary ID changed we need to: remove all of the IDs from the keyring, saving their certifications @@ -382,8 +382,6 @@ public class PgpKeyOperation { do we need to remove and add in? */ - //todo: flag changes of master key if IDs changed maybe? - for (PGPSecretKey dKey : saveParcel.deletedKeys) { mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey); } @@ -406,7 +404,9 @@ public class PgpKeyOperation { } int user_id_index = 0; + boolean anyIDChanged = false; if (saveParcel.primaryIDChanged) { + anyIDChanged = true; ArrayList> sigList = new ArrayList>(); for (String userId : saveParcel.userIDs) { String orig_id = saveParcel.originalIDs.get(user_id_index); @@ -437,6 +437,7 @@ public class PgpKeyOperation { for (String userId : saveParcel.userIDs) { String orig_id = saveParcel.originalIDs.get(user_id_index); if (!orig_id.equals(userId)) { + anyIDChanged = true; PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); @@ -452,6 +453,12 @@ public class PgpKeyOperation { } } + //update the keyring with the new ID information + if (anyIDChanged) { + pKR = PGPPublicKeyRing.insertPublicKey(pKR, masterPublicKey); + mKR = PGPSecretKeyRing.replacePublicKeys(mKR, pKR); + } + PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); From 02688f6ebfb07b70656cbe9fe6d723d36f3ffc35 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Mon, 24 Mar 2014 17:41:45 +0000 Subject: [PATCH 120/253] comments, slight change to progress bar --- .../keychain/pgp/PgpKeyOperation.java | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 5f97aa2e5..adccc5ba3 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -380,6 +380,10 @@ public class PgpKeyOperation { remove deleted keys if a key is modified, re-sign it do we need to remove and add in? + + Todo + identify more things which need to be preserved - e.g. trust levels? + user attributes */ for (PGPSecretKey dKey : saveParcel.deletedKeys) { @@ -504,7 +508,7 @@ public class PgpKeyOperation { unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); for (int i = 0; i < saveParcel.keys.size(); ++i) { - updateProgress(40 + 50 * (i - 1) / (saveParcel.keys.size() - 1), 100); + updateProgress(40 + 50 * i/ saveParcel.keys.size(), 100); if (saveParcel.moddedKeys[i]) { PGPSecretKey subKey = saveParcel.keys.get(i); PGPPublicKey subPublicKey = subKey.getPublicKey(); @@ -561,7 +565,11 @@ public class PgpKeyOperation { } keyGen.addSubKey(subKeyPair, hashedPacketsGen.generate(), unhashedPacketsGen.generate()); - //discard only certain certs + //certifications will be discarded if the key is changed, because I think, for a start, + //they will be invalid. Binding certs are regenerated anyway, and other certs which + //need to be kept are on IDs and attributes + //TODO: don't let revoked keys be edited, other than removed - changing one would result in the + //revocation being wrong? } } @@ -575,10 +583,6 @@ public class PgpKeyOperation { pKR = PGPPublicKeyRing.insertPublicKey(pKR, theNextKey.getPublicKey()); } } - - - updateProgress(R.string.progress_adding_sub_keys, 40, 100); - // Build key encryptor based on new passphrase PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder( PGPEncryptedData.CAST5, sha1Calc) From e7b3dd64f4f0dbf5841ade521fe654ed8e3e0b26 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Tue, 25 Mar 2014 00:20:35 +0000 Subject: [PATCH 121/253] results of debugging, fix several bugs - more needed though... --- .../keychain/pgp/PgpKeyOperation.java | 132 ++++++++++-------- 1 file changed, 73 insertions(+), 59 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index adccc5ba3..fab456bf8 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -386,8 +386,10 @@ public class PgpKeyOperation { user attributes */ - for (PGPSecretKey dKey : saveParcel.deletedKeys) { - mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey); + if (saveParcel.deletedKeys != null) { + for (PGPSecretKey dKey : saveParcel.deletedKeys) { + mKR = PGPSecretKeyRing.removeSecretKey(mKR, dKey); + } } masterKey = mKR.getSecretKey(); @@ -409,61 +411,6 @@ public class PgpKeyOperation { int user_id_index = 0; boolean anyIDChanged = false; - if (saveParcel.primaryIDChanged) { - anyIDChanged = true; - ArrayList> sigList = new ArrayList>(); - for (String userId : saveParcel.userIDs) { - String orig_id = saveParcel.originalIDs.get(user_id_index); - if (orig_id.equals(userId)) { - Iterator orig_sigs = masterPublicKey.getSignaturesForID(orig_id); //TODO: make sure this iterator only has signatures we are interested in - while (orig_sigs.hasNext()) { - PGPSignature orig_sig = orig_sigs.next(); - sigList.add(new Pair(orig_id, orig_sig)); - } - } else { - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - - sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); - - PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); - sigList.add(new Pair(userId, certification)); - } - masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, orig_id); - user_id_index++; - } - for (Pair to_add : sigList) { - masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, to_add.first, to_add.second); - } - } else { - for (String userId : saveParcel.userIDs) { - String orig_id = saveParcel.originalIDs.get(user_id_index); - if (!orig_id.equals(userId)) { - anyIDChanged = true; - PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( - masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) - .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); - PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); - - sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); - - PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); - masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, orig_id); - masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification); - } - user_id_index++; - } - } - - //update the keyring with the new ID information - if (anyIDChanged) { - pKR = PGPPublicKeyRing.insertPublicKey(pKR, masterPublicKey); - mKR = PGPSecretKeyRing.replacePublicKeys(mKR, pKR); - } - - PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); @@ -486,9 +433,75 @@ public class PgpKeyOperation { hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400); } else { hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding, - //this happens anyway + //this happens anyway } + if (saveParcel.primaryIDChanged) { + anyIDChanged = true; + ArrayList> sigList = new ArrayList>(); + for (String userId : saveParcel.userIDs) { + String orig_id = saveParcel.originalIDs.get(user_id_index); + if (orig_id.equals(userId)) { + Iterator orig_sigs = masterPublicKey.getSignaturesForID(orig_id); //TODO: make sure this iterator only has signatures we are interested in + while (orig_sigs.hasNext()) { + PGPSignature orig_sig = orig_sigs.next(); + sigList.add(new Pair(orig_id, orig_sig)); + } + } else { + PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( + masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); + + sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); + if (user_id_index == 0) { + sGen.setHashedSubpackets(hashedPacketsGen.generate()); + sGen.setUnhashedSubpackets(unhashedPacketsGen.generate()); + } + PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); + sigList.add(new Pair(userId, certification)); + } + if (!orig_id.equals("")) { + masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, orig_id); + } + user_id_index++; + } + for (Pair to_add : sigList) { + masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, to_add.first, to_add.second); + } + } else { + for (String userId : saveParcel.userIDs) { + String orig_id = saveParcel.originalIDs.get(user_id_index); + if (!orig_id.equals(userId)) { + anyIDChanged = true; + PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder( + masterPublicKey.getAlgorithm(), HashAlgorithmTags.SHA1) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder); + + sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey); + if (user_id_index == 0) { + sGen.setHashedSubpackets(hashedPacketsGen.generate()); + sGen.setUnhashedSubpackets(unhashedPacketsGen.generate()); + } + PGPSignature certification = sGen.generateCertification(userId, masterPublicKey); + if (!orig_id.equals("")) { + masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, orig_id); + } + masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, certification); + } + user_id_index++; + } + } + + //update the keyring with the new ID information + if (anyIDChanged) { + pKR = PGPPublicKeyRing.insertPublicKey(pKR, masterPublicKey); + mKR = PGPSecretKeyRing.replacePublicKeys(mKR, pKR); + } + + PGPKeyPair masterKeyPair = new PGPKeyPair(masterPublicKey, masterPrivateKey); + updateProgress(R.string.progress_building_master_key, 30, 100); // define hashing and signing algos @@ -503,11 +516,12 @@ public class PgpKeyOperation { .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build( saveParcel.oldPassPhrase.toCharArray()); + //this generates one more signature than necessary... PGPKeyRingGenerator keyGen = new PGPKeyRingGenerator(PGPSignature.POSITIVE_CERTIFICATION, masterKeyPair, mainUserId, sha1Calc, hashedPacketsGen.generate(), unhashedPacketsGen.generate(), certificationSignerBuilder, keyEncryptor); - for (int i = 0; i < saveParcel.keys.size(); ++i) { + for (int i = 1; i < saveParcel.keys.size(); ++i) { updateProgress(40 + 50 * i/ saveParcel.keys.size(), 100); if (saveParcel.moddedKeys[i]) { PGPSecretKey subKey = saveParcel.keys.get(i); From 4e481512c31c60177d3c1b404c97fa8d7a004124 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Tue, 25 Mar 2014 05:26:35 +0100 Subject: [PATCH 122/253] Display expiry of subkeys in single key view. --- .../ui/adapter/ViewKeyKeysAdapter.java | 22 +++++++++++++++++++ .../main/res/layout/view_key_keys_item.xml | 10 ++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java index 068d6e6e9..d925480e9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java @@ -22,6 +22,7 @@ import android.content.res.ColorStateList; import android.database.Cursor; import android.graphics.Color; import android.support.v4.widget.CursorAdapter; +import android.text.format.DateFormat; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -31,6 +32,8 @@ import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; +import java.util.Date; + public class ViewKeyKeysAdapter extends CursorAdapter { private LayoutInflater mInflater; @@ -42,6 +45,7 @@ public class ViewKeyKeysAdapter extends CursorAdapter { private int mIndexCanEncrypt; private int mIndexCanSign; private int mIndexRevokedKey; + private int mIndexExpiry; private ColorStateList mDefaultTextColor; @@ -76,6 +80,7 @@ public class ViewKeyKeysAdapter extends CursorAdapter { mIndexCanEncrypt = cursor.getColumnIndexOrThrow(Keys.CAN_ENCRYPT); mIndexCanSign = cursor.getColumnIndexOrThrow(Keys.CAN_SIGN); mIndexRevokedKey = cursor.getColumnIndexOrThrow(Keys.IS_REVOKED); + mIndexExpiry = cursor.getColumnIndexOrThrow(Keys.EXPIRY); } } @@ -83,6 +88,7 @@ public class ViewKeyKeysAdapter extends CursorAdapter { public void bindView(View view, Context context, Cursor cursor) { TextView keyId = (TextView) view.findViewById(R.id.keyId); TextView keyDetails = (TextView) view.findViewById(R.id.keyDetails); + TextView keyExpiry = (TextView) view.findViewById(R.id.keyExpiry); ImageView masterKeyIcon = (ImageView) view.findViewById(R.id.ic_masterKey); ImageView certifyIcon = (ImageView) view.findViewById(R.id.ic_certifyKey); ImageView encryptIcon = (ImageView) view.findViewById(R.id.ic_encryptKey); @@ -129,6 +135,22 @@ public class ViewKeyKeysAdapter extends CursorAdapter { keyDetails.setTextColor(mDefaultTextColor); revokedKeyIcon.setVisibility(View.GONE); } + + boolean valid = true; + if (!cursor.isNull(mIndexExpiry)) { + Date expiryDate = new Date(cursor.getLong(mIndexExpiry) * 1000); + valid = expiryDate.after(new Date()); + keyExpiry.setText("(" + + context.getString(R.string.label_expiry) + ": " + + DateFormat.getDateFormat(context).format(expiryDate) + ")"); + keyExpiry.setVisibility(View.VISIBLE); + } + else { + keyExpiry.setVisibility(View.GONE); + } + keyId.setEnabled(valid); + keyDetails.setEnabled(valid); + keyExpiry.setEnabled(valid); } @Override diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml index 235949ce2..43f06e246 100644 --- a/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml +++ b/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml @@ -28,9 +28,17 @@ android:id="@+id/keyDetails" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:paddingRight="5dip" android:text="(RSA, 1024bit)" android:textAppearance="?android:attr/textAppearanceSmall" /> + + - \ No newline at end of file + From aa35b1f4b5a5198482c9c5a659d357b3ac9a101b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 25 Mar 2014 19:11:20 +0100 Subject: [PATCH 123/253] More experimental work on API accounts --- .../openintents/openpgp/util/OpenPgpApi.java | 2 + OpenPGP-Keychain/src/main/AndroidManifest.xml | 2 +- .../keychain/Constants.java | 4 +- .../keychain/provider/KeychainContract.java | 61 ++---- .../keychain/provider/KeychainDatabase.java | 3 +- .../keychain/provider/KeychainProvider.java | 108 ++++------ .../keychain/provider/ProviderHelper.java | 99 +++++---- .../keychain/remote/AccountSettings.java | 85 ++++++++ .../keychain/remote/AppSettings.java | 40 ---- .../keychain/remote/OpenPgpService.java | 48 +++-- .../keychain/remote/RemoteService.java | 8 +- .../remote/ui/AccountSettingsFragment.java | 203 ++++++++++++++++++ .../remote/ui/AccountsListFragment.java | 174 +++++++++++++++ .../remote/ui/AppSettingsFragment.java | 87 +------- ...istActivity.java => AppsListActivity.java} | 2 +- ...istFragment.java => AppsListFragment.java} | 53 ++++- .../remote/ui/RegisteredAppsAdapter.java | 75 ------- .../remote/ui/RemoteServiceActivity.java | 84 ++++++-- .../layout/api_account_create_activity.xml | 21 ++ .../layout/api_account_settings_fragment.xml | 115 ++++++++++ .../res/layout/api_app_register_activity.xml | 2 +- .../res/layout/api_app_settings_fragment.xml | 40 ---- .../main/res/layout/api_apps_list_content.xml | 2 +- 23 files changed, 884 insertions(+), 434 deletions(-) create mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java create mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java create mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java rename OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/{RegisteredAppsListActivity.java => AppsListActivity.java} (94%) rename OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/{RegisteredAppsListFragment.java => AppsListFragment.java} (67%) delete mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsAdapter.java create mode 100644 OpenPGP-Keychain/src/main/res/layout/api_account_create_activity.xml create mode 100644 OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml diff --git a/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpApi.java b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpApi.java index f768a1685..c59590fec 100644 --- a/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpApi.java +++ b/OpenPGP-Keychain-API/libraries/openpgp-api-library/src/org/openintents/openpgp/util/OpenPgpApi.java @@ -126,6 +126,8 @@ public class OpenPgpApi { /* Intent extras */ public static final String EXTRA_API_VERSION = "api_version"; + public static final String EXTRA_ACCOUNT_NAME = "account_name"; + // SIGN, ENCRYPT, SIGN_AND_ENCRYPT, DECRYPT_VERIFY // request ASCII Armor for output // OpenPGP Radix-64, 33 percent overhead compared to binary, see http://tools.ietf.org/html/rfc4880#page-53) diff --git a/OpenPGP-Keychain/src/main/AndroidManifest.xml b/OpenPGP-Keychain/src/main/AndroidManifest.xml index e2ed8c5e4..3ab39280e 100644 --- a/OpenPGP-Keychain/src/main/AndroidManifest.xml +++ b/OpenPGP-Keychain/src/main/AndroidManifest.xml @@ -390,7 +390,7 @@ diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java index ff4abe56a..f9a7962ec 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/Constants.java @@ -19,7 +19,7 @@ package org.sufficientlysecure.keychain; import android.os.Environment; import org.spongycastle.jce.provider.BouncyCastleProvider; -import org.sufficientlysecure.keychain.service.remote.RegisteredAppsListActivity; +import org.sufficientlysecure.keychain.remote.ui.AppsListActivity; import org.sufficientlysecure.keychain.ui.DecryptActivity; import org.sufficientlysecure.keychain.ui.EncryptActivity; import org.sufficientlysecure.keychain.ui.ImportKeysActivity; @@ -73,7 +73,7 @@ public final class Constants { public static final Class ENCRYPT = EncryptActivity.class; public static final Class DECRYPT = DecryptActivity.class; public static final Class IMPORT_KEYS = ImportKeysActivity.class; - public static final Class REGISTERED_APPS_LIST = RegisteredAppsListActivity.class; + public static final Class REGISTERED_APPS_LIST = AppsListActivity.class; public static final Class[] ARRAY = new Class[]{KEY_LIST, ENCRYPT, DECRYPT, IMPORT_KEYS, REGISTERED_APPS_LIST}; } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index aa4c1f131..6e7b76fbe 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -19,6 +19,7 @@ package org.sufficientlysecure.keychain.provider; import android.net.Uri; import android.provider.BaseColumns; + import org.sufficientlysecure.keychain.Constants; public class KeychainContract { @@ -59,6 +60,7 @@ public class KeychainContract { } interface ApiAppsAccountsColumns { + String ACCOUNT_NAME = "account_name"; String KEY_ID = "key_id"; // not a database id String ENCRYPTION_ALGORITHM = "encryption_algorithm"; String HASH_ALORITHM = "hash_algorithm"; @@ -90,8 +92,7 @@ public class KeychainContract { public static final String PATH_USER_IDS = "user_ids"; public static final String PATH_KEYS = "keys"; - public static final String BASE_API = "api"; - public static final String PATH_APPS = "apps"; + public static final String BASE_API_APPS = "api_apps"; public static final String PATH_ACCOUNTS = "accounts"; public static final String PATH_BY_PACKAGE_NAME = "package_name"; @@ -257,36 +258,9 @@ public class KeychainContract { } } - /** - * Join over ApiApps with ApiAppsAccounts - */ - public static class Api implements ApiAppsColumns, ApiAppsAccountsColumns, BaseColumns { - public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() - .appendPath(BASE_API).build(); - - /** - * Use if multiple items get returned - */ - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.apis"; - - /** - * Use if a single item is returned - */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api"; - - public static Uri buildIdUri(String rowId) { - return CONTENT_URI.buildUpon().appendPath(rowId).build(); - } - - public static Uri buildByPackageNameUri(String packageName) { - return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName) - .build(); - } - } - public static class ApiApps implements ApiAppsColumns, BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() - .appendPath(BASE_API).appendPath(PATH_APPS).build(); + .appendPath(BASE_API_APPS).build(); /** * Use if multiple items get returned @@ -303,14 +277,14 @@ public class KeychainContract { } public static Uri buildByPackageNameUri(String packageName) { - return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName) - .build(); + return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME) + .appendEncodedPath(packageName).build(); } } public static class ApiAccounts implements ApiAppsAccountsColumns, BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() - .appendPath(BASE_API).appendPath(PATH_ACCOUNTS).build(); + .appendPath(BASE_API_APPS).build(); /** * Use if multiple items get returned @@ -322,13 +296,24 @@ public class KeychainContract { */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api.account"; - public static Uri buildIdUri(String rowId) { - return CONTENT_URI.buildUpon().appendPath(rowId).build(); +// public static Uri buildUri(String rowIdApp) { +// return CONTENT_URI.buildUpon().appendPath(rowIdApp).appendPath(PATH_ACCOUNTS) +// .build(); +// } +// +// public static Uri buildIdUri(String rowIdApp, String rowId) { +// return CONTENT_URI.buildUpon().appendPath(rowIdApp).appendPath(PATH_ACCOUNTS) +// .appendPath(rowId).build(); +// } + + public static Uri buildBaseUri(String packageName) { + return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS) + .build(); } - public static Uri buildByPackageNameUri(String packageName) { - return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME).appendPath(packageName) - .build(); + public static Uri buildByPackageAndAccountUri(String packageName, String accountName) { + return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS) + .appendEncodedPath(accountName).build(); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index e56fa2f8f..ca1a47f0c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -82,10 +82,11 @@ public class KeychainDatabase extends SQLiteOpenHelper { private static final String CREATE_API_APPS_ACCOUNTS = "CREATE TABLE IF NOT EXISTS " + Tables.API_ACCOUNTS + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + ApiAppsAccountsColumns.ACCOUNT_NAME + " TEXT UNIQUE, " + ApiAppsAccountsColumns.KEY_ID + " INT64, " + ApiAppsAccountsColumns.ENCRYPTION_ALGORITHM + " INTEGER, " + ApiAppsAccountsColumns.HASH_ALORITHM + " INTEGER, " - + ApiAppsAccountsColumns.COMPRESSION + " INTEGER" + + ApiAppsAccountsColumns.COMPRESSION + " INTEGER, " + ApiAppsAccountsColumns.PACKAGE_NAME_FK + " TEXT NOT NULL, FOREIGN KEY(" + ApiAppsAccountsColumns.PACKAGE_NAME_FK + ") REFERENCES " + Tables.API_APPS + "(" + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE)"; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index a094b13de..cae76003c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -30,10 +30,8 @@ import android.provider.BaseColumns; import android.text.TextUtils; import org.sufficientlysecure.keychain.Constants; -import org.sufficientlysecure.keychain.provider.KeychainContract.Api; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAccounts; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; -import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyTypes; @@ -81,15 +79,12 @@ public class KeychainProvider extends ContentProvider { private static final int SECRET_KEY_RING_USER_ID = 221; private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222; - private static final int API = 301; - private static final int API_BY_ROW_ID = 302; - private static final int API_BY_PACKAGE_NAME = 303; - private static final int API_APPS = 304; - private static final int API_APPS_BY_ROW_ID = 305; - private static final int API_APPS_BY_PACKAGE_NAME = 306; - private static final int API_ACCOUNTS = 307; - private static final int API_ACCOUNTS_BY_ROW_ID = 308; - private static final int API_ACCOUNTS_BY_PACKAGE_NAME = 309; + private static final int API_APPS = 301; + private static final int API_APPS_BY_ROW_ID = 302; + private static final int API_APPS_BY_PACKAGE_NAME = 303; + private static final int API_ACCOUNTS = 304; + private static final int API_ACCOUNTS_BY_ROW_ID = 305; + private static final int API_ACCOUNTS_BY_ACCOUNT_NAME = 306; private static final int UNIFIED_KEY_RING = 401; @@ -247,26 +242,18 @@ public class KeychainProvider extends ContentProvider { /** * API apps */ - matcher.addURI(authority, KeychainContract.BASE_API, API); - matcher.addURI(authority, KeychainContract.BASE_API + "/#", API_BY_ROW_ID); - matcher.addURI(authority, KeychainContract.BASE_API + "/" - + KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_BY_PACKAGE_NAME); - - matcher.addURI(authority, KeychainContract.BASE_API + "/" - + KeychainContract.PATH_APPS, API_APPS); - matcher.addURI(authority, KeychainContract.BASE_API + "/" - + KeychainContract.PATH_APPS + "/#", API_APPS_BY_ROW_ID); - matcher.addURI(authority, KeychainContract.BASE_API + "/" - + KeychainContract.PATH_APPS + "/" + matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS); + matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/#", API_APPS_BY_ROW_ID); + matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/" + KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_APPS_BY_PACKAGE_NAME); - matcher.addURI(authority, KeychainContract.BASE_API + "/" + matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/" + KeychainContract.PATH_ACCOUNTS, API_ACCOUNTS); - matcher.addURI(authority, KeychainContract.BASE_API + "/" + matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/" + KeychainContract.PATH_ACCOUNTS + "/#", API_ACCOUNTS_BY_ROW_ID); - matcher.addURI(authority, KeychainContract.BASE_API + "/" + matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/" + KeychainContract.PATH_ACCOUNTS + "/" - + KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_ACCOUNTS_BY_PACKAGE_NAME); + + KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_ACCOUNTS_BY_ACCOUNT_NAME); /** * data stream @@ -332,13 +319,6 @@ public class KeychainProvider extends ContentProvider { case SECRET_KEY_RING_USER_ID_BY_ROW_ID: return UserIds.CONTENT_ITEM_TYPE; - case API: - return Api.CONTENT_TYPE; - - case API_BY_ROW_ID: - case API_BY_PACKAGE_NAME: - return Api.CONTENT_ITEM_TYPE; - case API_APPS: return ApiApps.CONTENT_TYPE; @@ -350,7 +330,7 @@ public class KeychainProvider extends ContentProvider { return ApiAccounts.CONTENT_TYPE; case API_ACCOUNTS_BY_ROW_ID: - case API_ACCOUNTS_BY_PACKAGE_NAME: + case API_ACCOUNTS_BY_ACCOUNT_NAME: return ApiAccounts.CONTENT_ITEM_TYPE; default: @@ -695,28 +675,6 @@ public class KeychainProvider extends ContentProvider { qb.appendWhere(" AND " + BaseColumns._ID + " = "); qb.appendWhereEscapeString(uri.getLastPathSegment()); - break; - case API: - qb.setTables(Tables.API_ACCOUNTS + " INNER JOIN " + Tables.API_APPS + " ON " + "(" - + Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = " + Tables.API_ACCOUNTS + "." - + ApiAccounts.PACKAGE_NAME_FK + " )"); - - break; - case API_BY_ROW_ID: - qb.setTables(Tables.API_ACCOUNTS + " INNER JOIN " + Tables.API_APPS + " ON " + "(" - + Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = " + Tables.API_ACCOUNTS + "." - + ApiAccounts.PACKAGE_NAME_FK + " )"); - qb.appendWhere(Tables.API_APPS + "." + BaseColumns._ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - - break; - case API_BY_PACKAGE_NAME: - qb.setTables(Tables.API_ACCOUNTS + " INNER JOIN " + Tables.API_APPS + " ON " + "(" - + Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = " + Tables.API_ACCOUNTS + "." - + ApiAccounts.PACKAGE_NAME_FK + " )"); - qb.appendWhere(Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - break; case API_APPS: qb.setTables(Tables.API_APPS); @@ -740,15 +698,24 @@ public class KeychainProvider extends ContentProvider { break; case API_ACCOUNTS_BY_ROW_ID: - qb.setTables(Tables.API_ACCOUNTS); + qb.setTables(Tables.API_ACCOUNTS + " INNER JOIN " + Tables.API_APPS + " ON " + "(" + + Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = " + Tables.API_ACCOUNTS + "." + + ApiAccounts.PACKAGE_NAME_FK + " )"); + qb.appendWhere(Tables.API_APPS + "." + BaseColumns._ID + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(2)); - qb.appendWhere(BaseColumns._ID + " = "); + qb.appendWhere(" AND " + Tables.API_ACCOUNTS + "." + BaseColumns._ID + " = "); qb.appendWhereEscapeString(uri.getLastPathSegment()); break; - case API_ACCOUNTS_BY_PACKAGE_NAME: - qb.setTables(Tables.API_ACCOUNTS); - qb.appendWhere(ApiAppsAccountsColumns.PACKAGE_NAME_FK + " = "); + case API_ACCOUNTS_BY_ACCOUNT_NAME: + qb.setTables(Tables.API_ACCOUNTS + " INNER JOIN " + Tables.API_APPS + " ON " + "(" + + Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = " + Tables.API_ACCOUNTS + "." + + ApiAccounts.PACKAGE_NAME_FK + " )"); + qb.appendWhere(Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + + qb.appendWhere(" AND " + Tables.API_ACCOUNTS + "." + ApiAccounts.ACCOUNT_NAME + " = "); qb.appendWhereEscapeString(uri.getLastPathSegment()); break; @@ -808,13 +775,15 @@ public class KeychainProvider extends ContentProvider { values.put(Keys.TYPE, KeyTypes.PUBLIC); rowId = db.insertOrThrow(Tables.KEYS, null, values); - rowUri = Keys.buildPublicKeysUri(Long.toString(rowId)); + // TODO: this is wrong: +// rowUri = Keys.buildPublicKeysUri(Long.toString(rowId)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; case PUBLIC_KEY_RING_USER_ID: rowId = db.insertOrThrow(Tables.USER_IDS, null, values); - rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId)); + // TODO: this is wrong: +// rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; @@ -830,13 +799,15 @@ public class KeychainProvider extends ContentProvider { values.put(Keys.TYPE, KeyTypes.SECRET); rowId = db.insertOrThrow(Tables.KEYS, null, values); - rowUri = Keys.buildSecretKeysUri(Long.toString(rowId)); + // TODO: this is wrong: +// rowUri = Keys.buildSecretKeysUri(Long.toString(rowId)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; case SECRET_KEY_RING_USER_ID: rowId = db.insertOrThrow(Tables.USER_IDS, null, values); - rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId)); + // TODO: this is wrong: +// rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId)); break; case API_APPS: @@ -846,7 +817,8 @@ public class KeychainProvider extends ContentProvider { break; case API_ACCOUNTS: rowId = db.insertOrThrow(Tables.API_ACCOUNTS, null, values); - rowUri = ApiAccounts.buildIdUri(Long.toString(rowId)); + // TODO: this is wrong: +// rowUri = ApiAccounts.buildIdUri(Long.toString(rowId)); break; default: @@ -918,7 +890,7 @@ public class KeychainProvider extends ContentProvider { count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, false, selection), selectionArgs); break; - case API_ACCOUNTS_BY_PACKAGE_NAME: + case API_ACCOUNTS_BY_ACCOUNT_NAME: count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, true, selection), selectionArgs); break; @@ -996,7 +968,7 @@ public class KeychainProvider extends ContentProvider { count = db.update(Tables.API_ACCOUNTS, values, buildDefaultApiAccountsSelection(uri, false, selection), selectionArgs); break; - case API_ACCOUNTS_BY_PACKAGE_NAME: + case API_ACCOUNTS_BY_ACCOUNT_NAME: count = db.update(Tables.API_ACCOUNTS, values, buildDefaultApiAccountsSelection(uri, true, selection), selectionArgs); break; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index 7e8bfbc7b..e3727b2f8 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -22,6 +22,7 @@ import android.database.Cursor; import android.database.DatabaseUtils; import android.net.Uri; import android.os.RemoteException; + import org.spongycastle.bcpg.ArmoredOutputStream; import org.spongycastle.openpgp.*; import org.sufficientlysecure.keychain.Constants; @@ -33,6 +34,7 @@ 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.KeychainDatabase.Tables; +import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AppSettings; import org.sufficientlysecure.keychain.util.IterableIterator; import org.sufficientlysecure.keychain.util.Log; @@ -469,11 +471,11 @@ public class ProviderHelper { cr.delete(KeyRings.buildSecretKeyRingsUri(Long.toString(rowId)), null, null); } - public static void deleteUnifiedKeyRing(Context context,String masterKeyId,boolean isSecretKey){ - ContentResolver cr= context.getContentResolver(); - cr.delete(KeyRings.buildPublicKeyRingsByMasterKeyIdUri(masterKeyId),null,null); - if(isSecretKey){ - cr.delete(KeyRings.buildSecretKeyRingsByMasterKeyIdUri(masterKeyId),null,null); + public static void deleteUnifiedKeyRing(Context context, String masterKeyId, boolean isSecretKey) { + ContentResolver cr = context.getContentResolver(); + cr.delete(KeyRings.buildPublicKeyRingsByMasterKeyIdUri(masterKeyId), null, null); + if (isSecretKey) { + cr.delete(KeyRings.buildSecretKeyRingsByMasterKeyIdUri(masterKeyId), null, null); } } @@ -504,7 +506,7 @@ public class ProviderHelper { + " AS sign_keys WHERE sign_keys." + Keys.KEY_RING_ROW_ID + " = " + KeychainDatabase.Tables.KEY_RINGS + "." + KeyRings._ID + " AND sign_keys." + Keys.CAN_SIGN + " = '1' AND " + Keys.IS_MASTER_KEY - + " = 1) AS sign", }; + + " = 1) AS sign",}; ContentResolver cr = context.getContentResolver(); Cursor cursor = cr.query(queryUri, projection, null, null, null); @@ -801,70 +803,93 @@ public class ProviderHelper { ContentValues values = new ContentValues(); values.put(ApiApps.PACKAGE_NAME, appSettings.getPackageName()); values.put(ApiApps.PACKAGE_SIGNATURE, appSettings.getPackageSignature()); -// values.put(ApiApps.KEY_ID, appSettings.getKeyId()); -// values.put(ApiApps.COMPRESSION, appSettings.getCompression()); -// values.put(ApiApps.ENCRYPTION_ALGORITHM, appSettings.getEncryptionAlgorithm()); -// values.put(ApiApps.HASH_ALORITHM, appSettings.getHashAlgorithm()); - return values; } - private static ContentValues contentValueForApiAccounts(AppSettings appSettings) { + private static ContentValues contentValueForApiAccounts(AccountSettings accSettings) { ContentValues values = new ContentValues(); - values.put(KeychainContract.ApiAccounts.PACKAGE_NAME_FK, appSettings.getPackageName()); - values.put(KeychainContract.ApiAccounts.KEY_ID, appSettings.getKeyId()); - values.put(KeychainContract.ApiAccounts.COMPRESSION, appSettings.getCompression()); - values.put(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM, appSettings.getEncryptionAlgorithm()); - values.put(KeychainContract.ApiAccounts.HASH_ALORITHM, appSettings.getHashAlgorithm()); - + values.put(KeychainContract.ApiAccounts.ACCOUNT_NAME, accSettings.getAccountName()); + values.put(KeychainContract.ApiAccounts.KEY_ID, accSettings.getKeyId()); + values.put(KeychainContract.ApiAccounts.COMPRESSION, accSettings.getCompression()); + values.put(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM, accSettings.getEncryptionAlgorithm()); + values.put(KeychainContract.ApiAccounts.HASH_ALORITHM, accSettings.getHashAlgorithm()); +// values.put(KeychainContract.ApiAccounts.PACKAGE_NAME_FK, accSettings.getPackageName()); return values; } - public static void insertApi(Context context, AppSettings appSettings) { + public static void insertApiApp(Context context, AppSettings appSettings) { context.getContentResolver().insert(KeychainContract.ApiApps.CONTENT_URI, contentValueForApiApps(appSettings)); - context.getContentResolver().insert(KeychainContract.ApiAccounts.CONTENT_URI, - contentValueForApiApps(appSettings)); } - // TODO: uri not working because it is used for both tables + public static void insertApiAccount(Context context, Uri uri, AccountSettings accSettings) { + context.getContentResolver().insert(uri, + contentValueForApiAccounts(accSettings)); + } + public static void updateApiApp(Context context, AppSettings appSettings, Uri uri) { if (context.getContentResolver().update(uri, contentValueForApiApps(appSettings), null, null) <= 0) { throw new RuntimeException(); } - if (context.getContentResolver().update(uri, contentValueForApiAccounts(appSettings), null, + } + + public static void updateApiAccount(Context context, AccountSettings accSettings, Uri uri) { + if (context.getContentResolver().update(uri, contentValueForApiAccounts(accSettings), null, null) <= 0) { throw new RuntimeException(); } } - public static AppSettings getApiSettings(Context context, Uri uri) { + + /** + * Must be an uri pointing to an account + * + * @param context + * @param uri + * @return + */ + public static AppSettings getApiAppSettings(Context context, Uri uri) { AppSettings settings = null; Cursor cur = context.getContentResolver().query(uri, null, null, null, null); if (cur != null && cur.moveToFirst()) { settings = new AppSettings(); - settings.setPackageName(cur.getString(cur - .getColumnIndex(KeychainContract.Api.PACKAGE_NAME))); - settings.setPackageSignature(cur.getBlob(cur - .getColumnIndex(KeychainContract.Api.PACKAGE_SIGNATURE))); - settings.setKeyId(cur.getLong(cur.getColumnIndex(KeychainContract.Api.KEY_ID))); - settings.setCompression(cur.getInt(cur - .getColumnIndexOrThrow(KeychainContract.Api.COMPRESSION))); - settings.setHashAlgorithm(cur.getInt(cur - .getColumnIndexOrThrow(KeychainContract.Api.HASH_ALORITHM))); - settings.setEncryptionAlgorithm(cur.getInt(cur - .getColumnIndexOrThrow(KeychainContract.Api.ENCRYPTION_ALGORITHM))); + settings.setPackageName(cur.getString( + cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_NAME))); + settings.setPackageSignature(cur.getBlob( + cur.getColumnIndex(KeychainContract.ApiApps.PACKAGE_SIGNATURE))); + } + + return settings; + } + + public static AccountSettings getApiAccountSettings(Context context, Uri uri) { + AccountSettings settings = null; + + Cursor cur = context.getContentResolver().query(uri, null, null, null, null); + if (cur != null && cur.moveToFirst()) { + settings = new AccountSettings(); + + settings.setAccountName(cur.getString( + cur.getColumnIndex(KeychainContract.ApiAccounts.ACCOUNT_NAME))); + settings.setKeyId(cur.getLong( + cur.getColumnIndex(KeychainContract.ApiAccounts.KEY_ID))); + settings.setCompression(cur.getInt( + cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.COMPRESSION))); + settings.setHashAlgorithm(cur.getInt( + cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.HASH_ALORITHM))); + settings.setEncryptionAlgorithm(cur.getInt( + cur.getColumnIndexOrThrow(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM))); } return settings; } public static byte[] getApiSignature(Context context, String packageName) { - Uri queryUri = KeychainContract.Api.buildByPackageNameUri(packageName); + Uri queryUri = ApiApps.buildByPackageNameUri(packageName); - String[] projection = new String[]{KeychainContract.Api.PACKAGE_SIGNATURE}; + String[] projection = new String[]{ApiApps.PACKAGE_SIGNATURE}; ContentResolver cr = context.getContentResolver(); Cursor cursor = cr.query(queryUri, projection, null, null, null); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java new file mode 100644 index 000000000..832cbc752 --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AccountSettings.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2013 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 org.spongycastle.bcpg.HashAlgorithmTags; +import org.spongycastle.openpgp.PGPEncryptedData; +import org.sufficientlysecure.keychain.Id; + +public class AccountSettings { + private String mAccountName; + private long mKeyId = Id.key.none; + private int mEncryptionAlgorithm; + private int mHashAlgorithm; + private int mCompression; + + public AccountSettings() { + + } + + public AccountSettings(String accountName) { + super(); + this.mAccountName = accountName; + + // defaults: + this.mEncryptionAlgorithm = PGPEncryptedData.AES_256; + this.mHashAlgorithm = HashAlgorithmTags.SHA512; + this.mCompression = Id.choice.compression.zlib; + } + + public String getAccountName() { + return mAccountName; + } + + public void setAccountName(String mAccountName) { + this.mAccountName = mAccountName; + } + + public long getKeyId() { + return mKeyId; + } + + public void setKeyId(long scretKeyId) { + this.mKeyId = scretKeyId; + } + + public int getEncryptionAlgorithm() { + return mEncryptionAlgorithm; + } + + public void setEncryptionAlgorithm(int encryptionAlgorithm) { + this.mEncryptionAlgorithm = encryptionAlgorithm; + } + + public int getHashAlgorithm() { + return mHashAlgorithm; + } + + public void setHashAlgorithm(int hashAlgorithm) { + this.mHashAlgorithm = hashAlgorithm; + } + + public int getCompression() { + return mCompression; + } + + public void setCompression(int compression) { + this.mCompression = compression; + } + +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java index ad48a402b..6c7e51bf0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/AppSettings.java @@ -24,10 +24,6 @@ import org.sufficientlysecure.keychain.Id; public class AppSettings { private String mPackageName; private byte[] mPackageSignature; - private long mKeyId = Id.key.none; - private int mEncryptionAlgorithm; - private int mHashAlgorithm; - private int mCompression; public AppSettings() { @@ -37,10 +33,6 @@ public class AppSettings { super(); this.mPackageName = packageName; this.mPackageSignature = packageSignature; - // defaults: - this.mEncryptionAlgorithm = PGPEncryptedData.AES_256; - this.mHashAlgorithm = HashAlgorithmTags.SHA512; - this.mCompression = Id.choice.compression.zlib; } public String getPackageName() { @@ -59,36 +51,4 @@ public class AppSettings { this.mPackageSignature = packageSignature; } - public long getKeyId() { - return mKeyId; - } - - public void setKeyId(long scretKeyId) { - this.mKeyId = scretKeyId; - } - - public int getEncryptionAlgorithm() { - return mEncryptionAlgorithm; - } - - public void setEncryptionAlgorithm(int encryptionAlgorithm) { - this.mEncryptionAlgorithm = encryptionAlgorithm; - } - - public int getHashAlgorithm() { - return mHashAlgorithm; - } - - public void setHashAlgorithm(int hashAlgorithm) { - this.mHashAlgorithm = hashAlgorithm; - } - - public int getCompression() { - return mCompression; - } - - public void setCompression(int compression) { - this.mCompression = compression; - } - } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index 9b56a491a..b04e76cbd 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -138,7 +138,7 @@ public class OpenPgpService extends RemoteService { } private Intent signImpl(Intent data, ParcelFileDescriptor input, - ParcelFileDescriptor output, AppSettings appSettings) { + ParcelFileDescriptor output, AccountSettings accSettings) { try { boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); @@ -147,11 +147,11 @@ public class OpenPgpService extends RemoteService { if (data.hasExtra(OpenPgpApi.EXTRA_PASSPHRASE)) { passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); } else { - passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), appSettings.getKeyId()); + passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), accSettings.getKeyId()); } if (passphrase == null) { // get PendingIntent for passphrase input, add it to given params and return to client - Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId()); + Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId()); return passphraseBundle; } @@ -165,9 +165,9 @@ public class OpenPgpService extends RemoteService { // sign-only PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os); builder.enableAsciiArmorOutput(asciiArmor) - .signatureHashAlgorithm(appSettings.getHashAlgorithm()) + .signatureHashAlgorithm(accSettings.getHashAlgorithm()) .signatureForceV3(false) - .signatureKeyId(appSettings.getKeyId()) + .signatureKeyId(accSettings.getKeyId()) .signaturePassphrase(passphrase); builder.build().execute(); } finally { @@ -188,7 +188,7 @@ public class OpenPgpService extends RemoteService { } private Intent encryptAndSignImpl(Intent data, ParcelFileDescriptor input, - ParcelFileDescriptor output, AppSettings appSettings, boolean sign) { + ParcelFileDescriptor output, AccountSettings accSettings, boolean sign) { try { boolean asciiArmor = data.getBooleanExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); @@ -218,7 +218,7 @@ public class OpenPgpService extends RemoteService { // add own key for encryption keyIds = Arrays.copyOf(keyIds, keyIds.length + 1); - keyIds[keyIds.length - 1] = appSettings.getKeyId(); + keyIds[keyIds.length - 1] = accSettings.getKeyId(); // build InputData and write into OutputStream // Get Input- and OutputStream from ParcelFileDescriptor @@ -230,8 +230,8 @@ public class OpenPgpService extends RemoteService { PgpSignEncrypt.Builder builder = new PgpSignEncrypt.Builder(getContext(), inputData, os); builder.enableAsciiArmorOutput(asciiArmor) - .compressionId(appSettings.getCompression()) - .symmetricEncryptionAlgorithm(appSettings.getEncryptionAlgorithm()) + .compressionId(accSettings.getCompression()) + .symmetricEncryptionAlgorithm(accSettings.getEncryptionAlgorithm()) .encryptionKeyIds(keyIds); if (sign) { @@ -240,18 +240,18 @@ public class OpenPgpService extends RemoteService { passphrase = data.getStringExtra(OpenPgpApi.EXTRA_PASSPHRASE); } else { passphrase = PassphraseCacheService.getCachedPassphrase(getContext(), - appSettings.getKeyId()); + accSettings.getKeyId()); } if (passphrase == null) { // get PendingIntent for passphrase input, add it to given params and return to client - Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId()); + Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId()); return passphraseBundle; } // sign and encrypt - builder.signatureHashAlgorithm(appSettings.getHashAlgorithm()) + builder.signatureHashAlgorithm(accSettings.getHashAlgorithm()) .signatureForceV3(false) - .signatureKeyId(appSettings.getKeyId()) + .signatureKeyId(accSettings.getKeyId()) .signaturePassphrase(passphrase); } else { // encrypt only @@ -277,7 +277,7 @@ public class OpenPgpService extends RemoteService { } private Intent decryptAndVerifyImpl(Intent data, ParcelFileDescriptor input, - ParcelFileDescriptor output, AppSettings appSettings) { + ParcelFileDescriptor output, AccountSettings accSettings) { try { // Get Input- and OutputStream from ParcelFileDescriptor InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(input); @@ -293,7 +293,7 @@ public class OpenPgpService extends RemoteService { PgpDecryptVerify.Builder builder = new PgpDecryptVerify.Builder(this, inputData, os); builder.assumeSymmetric(false) // no support for symmetric encryption // allow only the private key for this app for decryption - .enforcedKeyId(appSettings.getKeyId()) + .enforcedKeyId(accSettings.getKeyId()) .passphrase(passphrase); // TODO: currently does not support binary signed-only content @@ -301,7 +301,7 @@ public class OpenPgpService extends RemoteService { if (decryptVerifyResult.isKeyPassphraseNeeded()) { // get PendingIntent for passphrase input, add it to given params and return to client - Intent passphraseBundle = getPassphraseBundleIntent(data, appSettings.getKeyId()); + Intent passphraseBundle = getPassphraseBundleIntent(data, accSettings.getKeyId()); return passphraseBundle; } else if (decryptVerifyResult.isSymmetricPassphraseNeeded()) { throw new PgpGeneralException("Decryption of symmetric content not supported by API!"); @@ -433,17 +433,23 @@ public class OpenPgpService extends RemoteService { return errorResult; } - final AppSettings appSettings = getAppSettings(); + String accName; + if (data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME) != null) { + accName = data.getStringExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME); + } else { + accName = "default"; + } + final AccountSettings accSettings = getAccSettings(accName); String action = data.getAction(); if (OpenPgpApi.ACTION_SIGN.equals(action)) { - return signImpl(data, input, output, appSettings); + return signImpl(data, input, output, accSettings); } else if (OpenPgpApi.ACTION_ENCRYPT.equals(action)) { - return encryptAndSignImpl(data, input, output, appSettings, false); + return encryptAndSignImpl(data, input, output, accSettings, false); } else if (OpenPgpApi.ACTION_SIGN_AND_ENCRYPT.equals(action)) { - return encryptAndSignImpl(data, input, output, appSettings, true); + return encryptAndSignImpl(data, input, output, accSettings, true); } else if (OpenPgpApi.ACTION_DECRYPT_VERIFY.equals(action)) { - return decryptAndVerifyImpl(data, input, output, appSettings); + return decryptAndVerifyImpl(data, input, output, accSettings); } else if (OpenPgpApi.ACTION_GET_KEY.equals(action)) { return getKeyImpl(data); } else if (OpenPgpApi.ACTION_GET_KEY_IDS.equals(action)) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java index 434fe84c1..7b66a0b5c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java @@ -130,16 +130,16 @@ public abstract class RemoteService extends Service { * * @return */ - protected AppSettings getAppSettings() { + protected AccountSettings getAccSettings(String accountName) { String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); // get app settings for this package for (int i = 0; i < callingPackages.length; i++) { String currentPkg = callingPackages[i]; - Uri uri = KeychainContract.ApiApps.buildByPackageNameUri(currentPkg); + Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(currentPkg, accountName); - AppSettings settings = ProviderHelper.getApiAppSettings(this, uri); + AccountSettings settings = ProviderHelper.getApiAccountSettings(this, uri); if (settings != null) { return settings; @@ -210,7 +210,7 @@ public abstract class RemoteService extends Service { throw new WrongPackageSignatureException(e.getMessage()); } - byte[] storedSig = ProviderHelper.getApiAppSignature(this, packageName); + byte[] storedSig = ProviderHelper.getApiSignature(this, packageName); if (Arrays.equals(currentSig, storedSig)) { Log.d(Constants.TAG, "Package signature is correct! (equals signature from database)"); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java new file mode 100644 index 000000000..3d88d216e --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2013-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.remote.ui; + +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.ImageView; +import android.widget.Spinner; +import android.widget.TextView; + +import org.spongycastle.util.encoders.Hex; +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.remote.AccountSettings; +import org.sufficientlysecure.keychain.remote.AppSettings; +import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment; +import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter; +import org.sufficientlysecure.keychain.util.AlgorithmNames; +import org.sufficientlysecure.keychain.util.Log; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class AccountSettingsFragment extends Fragment implements + SelectSecretKeyLayoutFragment.SelectSecretKeyCallback { + + // model + private AccountSettings mAccSettings; + + // view + private TextView mAppNameView; + private ImageView mAppIconView; + private Spinner mEncryptionAlgorithm; + private Spinner mHashAlgorithm; + private Spinner mCompression; + private TextView mPackageName; + private TextView mPackageSignature; + + private SelectSecretKeyLayoutFragment mSelectKeyFragment; + + KeyValueSpinnerAdapter mEncryptionAdapter; + KeyValueSpinnerAdapter mHashAdapter; + KeyValueSpinnerAdapter mCompressionAdapter; + + public AccountSettings getAccSettings() { + return mAccSettings; + } + + public void setAccSettings(AccountSettings appSettings) { + this.mAccSettings = appSettings; +// setPackage(appSettings.getPackageName()); +// mPackageName.setText(appSettings.getPackageName()); + +// try { +// MessageDigest md = MessageDigest.getInstance("SHA-256"); +// md.update(appSettings.getPackageSignature()); +// byte[] digest = md.digest(); +// String signature = new String(Hex.encode(digest)); +// +// mPackageSignature.setText(signature); +// } catch (NoSuchAlgorithmException e) { +// Log.e(Constants.TAG, "Should not happen!", e); +// } + + mSelectKeyFragment.selectKey(appSettings.getKeyId()); + mEncryptionAlgorithm.setSelection(mEncryptionAdapter.getPosition(appSettings + .getEncryptionAlgorithm())); + mHashAlgorithm.setSelection(mHashAdapter.getPosition(appSettings.getHashAlgorithm())); + mCompression.setSelection(mCompressionAdapter.getPosition(appSettings.getCompression())); + } + + /** + * Inflate the layout for this fragment + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.api_account_settings_fragment, container, false); + initView(view); + return view; + } + + /** + * Set error String on key selection + * + * @param error + */ + public void setErrorOnSelectKeyFragment(String error) { + mSelectKeyFragment.setError(error); + } + + private void initView(View view) { + mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getFragmentManager().findFragmentById( + R.id.api_account_settings_select_key_fragment); + mSelectKeyFragment.setCallback(this); + + mAppNameView = (TextView) view.findViewById(R.id.api_account_settings_app_name); + mAppIconView = (ImageView) view.findViewById(R.id.api_account_settings_app_icon); + mEncryptionAlgorithm = (Spinner) view + .findViewById(R.id.api_account_settings_encryption_algorithm); + mHashAlgorithm = (Spinner) view.findViewById(R.id.api_account_settings_hash_algorithm); + mCompression = (Spinner) view.findViewById(R.id.api_account_settings_compression); + mPackageName = (TextView) view.findViewById(R.id.api_account_settings_package_name); + mPackageSignature = (TextView) view.findViewById(R.id.api_account_settings_package_signature); + + AlgorithmNames algorithmNames = new AlgorithmNames(getActivity()); + + mEncryptionAdapter = new KeyValueSpinnerAdapter(getActivity(), + algorithmNames.getEncryptionNames()); + mEncryptionAlgorithm.setAdapter(mEncryptionAdapter); + mEncryptionAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() { + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + mAccSettings.setEncryptionAlgorithm((int) id); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + + mHashAdapter = new KeyValueSpinnerAdapter(getActivity(), algorithmNames.getHashNames()); + mHashAlgorithm.setAdapter(mHashAdapter); + mHashAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() { + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + mAccSettings.setHashAlgorithm((int) id); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + + mCompressionAdapter = new KeyValueSpinnerAdapter(getActivity(), + algorithmNames.getCompressionNames()); + mCompression.setAdapter(mCompressionAdapter); + mCompression.setOnItemSelectedListener(new OnItemSelectedListener() { + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + mAccSettings.setCompression((int) id); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + } +// +// private void setPackage(String packageName) { +// PackageManager pm = getActivity().getApplicationContext().getPackageManager(); +// +// // get application name and icon from package manager +// String appName = null; +// Drawable appIcon = null; +// try { +// ApplicationInfo ai = pm.getApplicationInfo(packageName, 0); +// +// appName = (String) pm.getApplicationLabel(ai); +// appIcon = pm.getApplicationIcon(ai); +// } catch (final NameNotFoundException e) { +// // fallback +// appName = packageName; +// } +// mAppNameView.setText(appName); +// mAppIconView.setImageDrawable(appIcon); +// } + + /** + * callback from select secret key fragment + */ + @Override + public void onKeySelected(long secretKeyId) { + mAccSettings.setKeyId(secretKeyId); + } + +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java new file mode 100644 index 000000000..0cec319a5 --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2013 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.ui; + +import android.annotation.TargetApi; +import android.content.ContentUris; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.support.v4.app.ListFragment; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +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.AdapterView.OnItemClickListener; +import android.widget.ImageView; +import android.widget.SimpleCursorAdapter; +import android.widget.TextView; + +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; + +// TODO: make compat with < 11 +@TargetApi(Build.VERSION_CODES.HONEYCOMB) +public class AccountsListFragment extends ListFragment implements + LoaderManager.LoaderCallbacks { + + // This is the Adapter being used to display the list's data. + SimpleCursorAdapter mAdapter; + + private String mPackageName; + + public String getPackageName() { + return mPackageName; + } + + public void setPackageName(String packageName) { + this.mPackageName = packageName; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + + getListView().setOnItemClickListener(new OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, int position, long id) { + // edit app settings + Intent intent = new Intent(getActivity(), AppSettingsActivity.class); + intent.setData(ContentUris.withAppendedId(ApiApps.CONTENT_URI, id)); + startActivity(intent); + } + }); + + // Give some text to display if there is no data. In a real + // application this would come from a resource. + setEmptyText(getString(R.string.api_no_apps)); + + // We have a menu item to show in action bar. + setHasOptionsMenu(true); + + // Create an empty adapter we will use to display the loaded data. + mAdapter = new SimpleCursorAdapter(getActivity(), + android.R.layout.simple_list_item_1, + null, + new String[]{KeychainContract.ApiAccounts.ACCOUNT_NAME}, + new int[]{android.R.id.text1}, + 0); + setListAdapter(mAdapter); + + // Prepare the loader. Either re-connect with an existing one, + // or start a new one. + getLoaderManager().initLoader(0, null, this); + } + + // These are the Contacts rows that we will retrieve. + static final String[] PROJECTION = new String[]{ + KeychainContract.ApiAccounts._ID, + KeychainContract.ApiAccounts.ACCOUNT_NAME}; + + public Loader onCreateLoader(int id, Bundle args) { + // This is called when a new Loader needs to be created. This + // sample only has one Loader, so we don't care about the ID. + // First, pick the base URI to use depending on whether we are + // currently filtering. + Uri baseUri = KeychainContract.ApiAccounts.buildBaseUri(mPackageName); + + // 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, null, null, + KeychainContract.ApiAccounts.ACCOUNT_NAME + " COLLATE LOCALIZED ASC"); + } + + public void onLoadFinished(Loader loader, Cursor data) { + // Swap the new cursor in. (The framework will take care of closing the + // old cursor once we return.) + mAdapter.swapCursor(data); + } + + public void onLoaderReset(Loader loader) { + // 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. + mAdapter.swapCursor(null); + } + +// private class RegisteredAppsAdapter extends CursorAdapter { +// +// private LayoutInflater mInflater; +// private PackageManager mPM; +// +// public RegisteredAppsAdapter(Context context, Cursor c, int flags) { +// super(context, c, flags); +// +// mInflater = LayoutInflater.from(context); +// mPM = context.getApplicationContext().getPackageManager(); +// } +// +// @Override +// public void bindView(View view, Context context, Cursor cursor) { +// TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name); +// ImageView icon = (ImageView) view.findViewById(R.id.api_apps_adapter_item_icon); +// +// String packageName = cursor.getString(cursor.getColumnIndex(ApiApps.PACKAGE_NAME)); +// if (packageName != null) { +// // get application name +// try { +// ApplicationInfo ai = mPM.getApplicationInfo(packageName, 0); +// +// text.setText(mPM.getApplicationLabel(ai)); +// icon.setImageDrawable(mPM.getApplicationIcon(ai)); +// } catch (final PackageManager.NameNotFoundException e) { +// // fallback +// text.setText(packageName); +// } +// } else { +// // fallback +// text.setText(packageName); +// } +// +// } +// +// @Override +// public View newView(Context context, Cursor cursor, ViewGroup parent) { +// return mInflater.inflate(R.layout.api_apps_adapter_list_item, null); +// } +// } + +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java index 8e8f3827b..8bcd83fc7 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java @@ -44,8 +44,7 @@ import org.sufficientlysecure.keychain.util.Log; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -public class AppSettingsFragment extends Fragment implements - SelectSecretKeyLayoutFragment.SelectSecretKeyCallback { +public class AppSettingsFragment extends Fragment { // model private AppSettings mAppSettings; @@ -53,18 +52,9 @@ public class AppSettingsFragment extends Fragment implements // view private TextView mAppNameView; private ImageView mAppIconView; - private Spinner mEncryptionAlgorithm; - private Spinner mHashAlgorithm; - private Spinner mCompression; private TextView mPackageName; private TextView mPackageSignature; - private SelectSecretKeyLayoutFragment mSelectKeyFragment; - - KeyValueSpinnerAdapter mEncryptionAdapter; - KeyValueSpinnerAdapter mHashAdapter; - KeyValueSpinnerAdapter mCompressionAdapter; - public AppSettings getAppSettings() { return mAppSettings; } @@ -85,11 +75,6 @@ public class AppSettingsFragment extends Fragment implements Log.e(Constants.TAG, "Should not happen!", e); } - mSelectKeyFragment.selectKey(appSettings.getKeyId()); - mEncryptionAlgorithm.setSelection(mEncryptionAdapter.getPosition(appSettings - .getEncryptionAlgorithm())); - mHashAlgorithm.setSelection(mHashAdapter.getPosition(appSettings.getHashAlgorithm())); - mCompression.setSelection(mCompressionAdapter.getPosition(appSettings.getCompression())); } /** @@ -102,74 +87,13 @@ public class AppSettingsFragment extends Fragment implements return view; } - /** - * Set error String on key selection - * - * @param error - */ - public void setErrorOnSelectKeyFragment(String error) { - mSelectKeyFragment.setError(error); - } private void initView(View view) { - mSelectKeyFragment = (SelectSecretKeyLayoutFragment) getFragmentManager().findFragmentById( - R.id.api_app_settings_select_key_fragment); - mSelectKeyFragment.setCallback(this); - mAppNameView = (TextView) view.findViewById(R.id.api_app_settings_app_name); mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon); - mEncryptionAlgorithm = (Spinner) view - .findViewById(R.id.api_app_settings_encryption_algorithm); - mHashAlgorithm = (Spinner) view.findViewById(R.id.api_app_settings_hash_algorithm); - mCompression = (Spinner) view.findViewById(R.id.api_app_settings_compression); + mPackageName = (TextView) view.findViewById(R.id.api_app_settings_package_name); mPackageSignature = (TextView) view.findViewById(R.id.api_app_settings_package_signature); - - AlgorithmNames algorithmNames = new AlgorithmNames(getActivity()); - - mEncryptionAdapter = new KeyValueSpinnerAdapter(getActivity(), - algorithmNames.getEncryptionNames()); - mEncryptionAlgorithm.setAdapter(mEncryptionAdapter); - mEncryptionAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() { - - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - mAppSettings.setEncryptionAlgorithm((int) id); - } - - @Override - public void onNothingSelected(AdapterView parent) { - } - }); - - mHashAdapter = new KeyValueSpinnerAdapter(getActivity(), algorithmNames.getHashNames()); - mHashAlgorithm.setAdapter(mHashAdapter); - mHashAlgorithm.setOnItemSelectedListener(new OnItemSelectedListener() { - - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - mAppSettings.setHashAlgorithm((int) id); - } - - @Override - public void onNothingSelected(AdapterView parent) { - } - }); - - mCompressionAdapter = new KeyValueSpinnerAdapter(getActivity(), - algorithmNames.getCompressionNames()); - mCompression.setAdapter(mCompressionAdapter); - mCompression.setOnItemSelectedListener(new OnItemSelectedListener() { - - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - mAppSettings.setCompression((int) id); - } - - @Override - public void onNothingSelected(AdapterView parent) { - } - }); } private void setPackage(String packageName) { @@ -191,12 +115,5 @@ public class AppSettingsFragment extends Fragment implements mAppIconView.setImageDrawable(appIcon); } - /** - * callback from select secret key fragment - */ - @Override - public void onKeySelected(long secretKeyId) { - mAppSettings.setKeyId(secretKeyId); - } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsListActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java similarity index 94% rename from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsListActivity.java rename to OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java index 79b3c8abc..f86d279f0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsListActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListActivity.java @@ -21,7 +21,7 @@ import android.os.Bundle; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.ui.DrawerActivity; -public class RegisteredAppsListActivity extends DrawerActivity { +public class AppsListActivity extends DrawerActivity { @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java similarity index 67% rename from OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsListFragment.java rename to OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java index 99e73e510..7054a2195 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java @@ -18,7 +18,10 @@ package org.sufficientlysecure.keychain.remote.ui; import android.content.ContentUris; +import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; @@ -26,14 +29,20 @@ import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; +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.AdapterView.OnItemClickListener; +import android.widget.ImageView; +import android.widget.TextView; + import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; -public class RegisteredAppsListFragment extends ListFragment implements +public class AppsListFragment extends ListFragment implements LoaderManager.LoaderCallbacks { // This is the Adapter being used to display the list's data. @@ -98,4 +107,46 @@ public class RegisteredAppsListFragment extends ListFragment implements mAdapter.swapCursor(null); } + private class RegisteredAppsAdapter extends CursorAdapter { + + private LayoutInflater mInflater; + private PackageManager mPM; + + public RegisteredAppsAdapter(Context context, Cursor c, int flags) { + super(context, c, flags); + + mInflater = LayoutInflater.from(context); + mPM = context.getApplicationContext().getPackageManager(); + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name); + ImageView icon = (ImageView) view.findViewById(R.id.api_apps_adapter_item_icon); + + String packageName = cursor.getString(cursor.getColumnIndex(ApiApps.PACKAGE_NAME)); + if (packageName != null) { + // get application name + try { + ApplicationInfo ai = mPM.getApplicationInfo(packageName, 0); + + text.setText(mPM.getApplicationLabel(ai)); + icon.setImageDrawable(mPM.getApplicationIcon(ai)); + } catch (final PackageManager.NameNotFoundException e) { + // fallback + text.setText(packageName); + } + } else { + // fallback + text.setText(packageName); + } + + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return mInflater.inflate(R.layout.api_apps_adapter_list_item, null); + } + } + } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsAdapter.java deleted file mode 100644 index c846343ab..000000000 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RegisteredAppsAdapter.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 2013 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.ui; - -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.database.Cursor; -import android.support.v4.widget.CursorAdapter; -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.provider.KeychainContract.ApiApps; - -public class RegisteredAppsAdapter extends CursorAdapter { - - private LayoutInflater mInflater; - private PackageManager mPM; - - public RegisteredAppsAdapter(Context context, Cursor c, int flags) { - super(context, c, flags); - - mInflater = LayoutInflater.from(context); - mPM = context.getApplicationContext().getPackageManager(); - } - - @Override - public void bindView(View view, Context context, Cursor cursor) { - TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name); - ImageView icon = (ImageView) view.findViewById(R.id.api_apps_adapter_item_icon); - - String packageName = cursor.getString(cursor.getColumnIndex(ApiApps.PACKAGE_NAME)); - if (packageName != null) { - // get application name - try { - ApplicationInfo ai = mPM.getApplicationInfo(packageName, 0); - - text.setText(mPM.getApplicationLabel(ai)); - icon.setImageDrawable(mPM.getApplicationIcon(ai)); - } catch (final NameNotFoundException e) { - // fallback - text.setText(packageName); - } - } else { - // fallback - text.setText(packageName); - } - - } - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return mInflater.inflate(R.layout.api_apps_adapter_list_item, null); - } - -} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java index 3e19a8ef6..a088ad4b0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java @@ -24,6 +24,7 @@ import android.os.Message; import android.os.Messenger; import android.support.v7.app.ActionBarActivity; import android.view.View; + import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.htmltextview.HtmlTextView; import org.sufficientlysecure.keychain.Constants; @@ -31,7 +32,9 @@ import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.ActionBarHelper; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; +import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AppSettings; import org.sufficientlysecure.keychain.ui.SelectPublicKeyFragment; import org.sufficientlysecure.keychain.ui.dialog.PassphraseDialogFragment; @@ -42,6 +45,8 @@ import java.util.ArrayList; public class RemoteServiceActivity extends ActionBarActivity { public static final String ACTION_REGISTER = Constants.INTENT_PREFIX + "API_ACTIVITY_REGISTER"; + public static final String ACTION_REGISTER_ACCOUNT = Constants.INTENT_PREFIX + + "API_ACTIVITY_REGISTER_ACCOUNT"; public static final String ACTION_CACHE_PASSPHRASE = Constants.INTENT_PREFIX + "API_ACTIVITY_CACHE_PASSPHRASE"; public static final String ACTION_SELECT_PUB_KEYS = Constants.INTENT_PREFIX @@ -58,6 +63,8 @@ public class RemoteServiceActivity extends ActionBarActivity { // register action public static final String EXTRA_PACKAGE_NAME = "package_name"; public static final String EXTRA_PACKAGE_SIGNATURE = "package_signature"; + // create acc action + public static final String EXTRA_ACC_NAME = "acc_name"; // select pub keys action public static final String EXTRA_SELECTED_MASTER_KEY_IDS = "master_key_ids"; public static final String EXTRA_MISSING_USER_IDS = "missing_user_ids"; @@ -66,7 +73,9 @@ public class RemoteServiceActivity extends ActionBarActivity { public static final String EXTRA_ERROR_MESSAGE = "error_message"; // register view - private AppSettingsFragment mSettingsFragment; + private AppSettingsFragment mAppSettingsFragment; + // create acc view + private AccountSettingsFragment mAccSettingsFragment; // select pub keys view private SelectPublicKeyFragment mSelectFragment; @@ -95,13 +104,52 @@ public class RemoteServiceActivity extends ActionBarActivity { public void onClick(View v) { // Allow + ProviderHelper.insertApiApp(RemoteServiceActivity.this, + mAppSettingsFragment.getAppSettings()); + + // give data through for new service call + Intent resultData = extras.getParcelable(EXTRA_DATA); + RemoteServiceActivity.this.setResult(RESULT_OK, resultData); + RemoteServiceActivity.this.finish(); + } + }, R.string.api_register_disallow, R.drawable.ic_action_cancel, + new View.OnClickListener() { + @Override + public void onClick(View v) { + // Disallow + RemoteServiceActivity.this.setResult(RESULT_CANCELED); + RemoteServiceActivity.this.finish(); + } + } + ); + + setContentView(R.layout.api_app_register_activity); + + mAppSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById( + R.id.api_app_settings_fragment); + + AppSettings settings = new AppSettings(packageName, packageSignature); + mAppSettingsFragment.setAppSettings(settings); + } else if (ACTION_REGISTER_ACCOUNT.equals(action)) { + final String packageName = extras.getString(EXTRA_PACKAGE_NAME); + final String accName = extras.getString(EXTRA_ACC_NAME); + + // Inflate a "Done"/"Cancel" custom action bar view + ActionBarHelper.setTwoButtonView(getSupportActionBar(), + R.string.api_settings_save, R.drawable.ic_action_done, + new View.OnClickListener() { + @Override + public void onClick(View v) { + // Save + // user needs to select a key! - if (mSettingsFragment.getAppSettings().getKeyId() == Id.key.none) { - mSettingsFragment.setErrorOnSelectKeyFragment( + if (mAccSettingsFragment.getAccSettings().getKeyId() == Id.key.none) { + mAccSettingsFragment.setErrorOnSelectKeyFragment( getString(R.string.api_register_error_select_key)); } else { - ProviderHelper.insertApiApp(RemoteServiceActivity.this, - mSettingsFragment.getAppSettings()); + ProviderHelper.insertApiAccount(RemoteServiceActivity.this, + KeychainContract.ApiAccounts.buildBaseUri(packageName), + mAccSettingsFragment.getAccSettings()); // give data through for new service call Intent resultData = extras.getParcelable(EXTRA_DATA); @@ -109,24 +157,24 @@ public class RemoteServiceActivity extends ActionBarActivity { RemoteServiceActivity.this.finish(); } } - }, R.string.api_register_disallow, R.drawable.ic_action_cancel, - new View.OnClickListener() { - @Override - public void onClick(View v) { - // Disallow - RemoteServiceActivity.this.setResult(RESULT_CANCELED); - RemoteServiceActivity.this.finish(); - } + }, R.string.api_settings_cancel, R.drawable.ic_action_cancel, + new View.OnClickListener() { + @Override + public void onClick(View v) { + // Cancel + RemoteServiceActivity.this.setResult(RESULT_CANCELED); + RemoteServiceActivity.this.finish(); + } } ); - setContentView(R.layout.api_app_register_activity); + setContentView(R.layout.api_account_create_activity); - mSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById( - R.id.api_app_settings_fragment); + mAccSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById( + R.id.api_account_settings_fragment); - AppSettings settings = new AppSettings(packageName, packageSignature); - mSettingsFragment.setAppSettings(settings); + AccountSettings settings = new AccountSettings(accName); + mAccSettingsFragment.setAccSettings(settings); } else if (ACTION_CACHE_PASSPHRASE.equals(action)) { long secretKeyId = extras.getLong(EXTRA_SECRET_KEY_ID); Intent resultData = extras.getParcelable(EXTRA_DATA); diff --git a/OpenPGP-Keychain/src/main/res/layout/api_account_create_activity.xml b/OpenPGP-Keychain/src/main/res/layout/api_account_create_activity.xml new file mode 100644 index 000000000..ead336dbb --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/api_account_create_activity.xml @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml new file mode 100644 index 000000000..ff560b657 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml b/OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml index aa9d59004..f85f3b8f7 100644 --- a/OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml +++ b/OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml @@ -20,7 +20,7 @@ diff --git a/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml index a7917ad4e..b91025474 100644 --- a/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml +++ b/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml @@ -36,13 +36,6 @@ android:textAppearance="?android:attr/textAppearanceMedium" /> - - - - - - - - - - - - - - \ No newline at end of file From cff35ca84283202d1a0b824b6eb9eaf4231c061a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 25 Mar 2014 21:23:59 +0100 Subject: [PATCH 124/253] More experimental work on api accounts --- .../keychain/provider/KeychainContract.java | 11 ++-- .../keychain/provider/KeychainProvider.java | 61 ++++--------------- .../remote/ui/AccountsListFragment.java | 44 ++++++------- .../remote/ui/AppSettingsActivity.java | 32 +++++++++- .../keychain/remote/ui/AppsListFragment.java | 30 ++++++++- .../layout/api_account_settings_fragment.xml | 4 +- .../res/layout/api_app_settings_activity.xml | 16 ++++- .../res/layout/api_app_settings_fragment.xml | 4 +- .../src/main/res/values/strings.xml | 4 ++ 9 files changed, 120 insertions(+), 86 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 6e7b76fbe..98ce98495 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -95,8 +95,6 @@ public class KeychainContract { public static final String BASE_API_APPS = "api_apps"; public static final String PATH_ACCOUNTS = "accounts"; - public static final String PATH_BY_PACKAGE_NAME = "package_name"; - public static class KeyRings implements KeyRingsColumns, BaseColumns { public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon() .appendPath(BASE_KEY_RINGS).build(); @@ -272,13 +270,12 @@ public class KeychainContract { */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api.apps"; - public static Uri buildIdUri(String rowId) { - return CONTENT_URI.buildUpon().appendPath(rowId).build(); - } +// public static Uri buildIdUri(String rowId) { +// return CONTENT_URI.buildUpon().appendPath(rowId).build(); +// } public static Uri buildByPackageNameUri(String packageName) { - return CONTENT_URI.buildUpon().appendPath(PATH_BY_PACKAGE_NAME) - .appendEncodedPath(packageName).build(); + return CONTENT_URI.buildUpon().appendEncodedPath(packageName).build(); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index cae76003c..005be2fd6 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -80,10 +80,8 @@ public class KeychainProvider extends ContentProvider { private static final int SECRET_KEY_RING_USER_ID_BY_ROW_ID = 222; private static final int API_APPS = 301; - private static final int API_APPS_BY_ROW_ID = 302; private static final int API_APPS_BY_PACKAGE_NAME = 303; private static final int API_ACCOUNTS = 304; - private static final int API_ACCOUNTS_BY_ROW_ID = 305; private static final int API_ACCOUNTS_BY_ACCOUNT_NAME = 306; private static final int UNIFIED_KEY_RING = 401; @@ -241,19 +239,22 @@ public class KeychainProvider extends ContentProvider { /** * API apps + * + *
    +         * api_apps
    +         * api_apps/_
    +         *
    +         * api_apps/_/accounts
    +         * api_apps/_/accounts/_
    +         * 
    */ matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS); - matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/#", API_APPS_BY_ROW_ID); - matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/" - + KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_APPS_BY_PACKAGE_NAME); + matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*", API_APPS_BY_PACKAGE_NAME); - matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/" + matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/" + KeychainContract.PATH_ACCOUNTS, API_ACCOUNTS); - matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/" - + KeychainContract.PATH_ACCOUNTS + "/#", API_ACCOUNTS_BY_ROW_ID); - matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/" - + KeychainContract.PATH_ACCOUNTS + "/" - + KeychainContract.PATH_BY_PACKAGE_NAME + "/*", API_ACCOUNTS_BY_ACCOUNT_NAME); + matcher.addURI(authority, KeychainContract.BASE_API_APPS + "/*/" + + KeychainContract.PATH_ACCOUNTS + "/*", API_ACCOUNTS_BY_ACCOUNT_NAME); /** * data stream @@ -322,14 +323,12 @@ public class KeychainProvider extends ContentProvider { case API_APPS: return ApiApps.CONTENT_TYPE; - case API_APPS_BY_ROW_ID: case API_APPS_BY_PACKAGE_NAME: return ApiApps.CONTENT_ITEM_TYPE; case API_ACCOUNTS: return ApiAccounts.CONTENT_TYPE; - case API_ACCOUNTS_BY_ROW_ID: case API_ACCOUNTS_BY_ACCOUNT_NAME: return ApiAccounts.CONTENT_ITEM_TYPE; @@ -679,13 +678,6 @@ public class KeychainProvider extends ContentProvider { case API_APPS: qb.setTables(Tables.API_APPS); - break; - case API_APPS_BY_ROW_ID: - qb.setTables(Tables.API_APPS); - - qb.appendWhere(BaseColumns._ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - break; case API_APPS_BY_PACKAGE_NAME: qb.setTables(Tables.API_APPS); @@ -696,17 +688,6 @@ public class KeychainProvider extends ContentProvider { case API_ACCOUNTS: qb.setTables(Tables.API_ACCOUNTS); - break; - case API_ACCOUNTS_BY_ROW_ID: - qb.setTables(Tables.API_ACCOUNTS + " INNER JOIN " + Tables.API_APPS + " ON " + "(" - + Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = " + Tables.API_ACCOUNTS + "." - + ApiAccounts.PACKAGE_NAME_FK + " )"); - qb.appendWhere(Tables.API_APPS + "." + BaseColumns._ID + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); - - qb.appendWhere(" AND " + Tables.API_ACCOUNTS + "." + BaseColumns._ID + " = "); - qb.appendWhereEscapeString(uri.getLastPathSegment()); - break; case API_ACCOUNTS_BY_ACCOUNT_NAME: qb.setTables(Tables.API_ACCOUNTS + " INNER JOIN " + Tables.API_APPS + " ON " + "(" @@ -812,7 +793,7 @@ public class KeychainProvider extends ContentProvider { break; case API_APPS: rowId = db.insertOrThrow(Tables.API_APPS, null, values); - rowUri = ApiApps.buildIdUri(Long.toString(rowId)); +// rowUri = ApiApps.buildIdUri(Long.toString(rowId)); break; case API_ACCOUNTS: @@ -878,18 +859,10 @@ public class KeychainProvider extends ContentProvider { count = db.delete(Tables.KEYS, buildDefaultUserIdsSelection(uri, selection), selectionArgs); break; - case API_APPS_BY_ROW_ID: - count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, false, selection), - selectionArgs); - break; case API_APPS_BY_PACKAGE_NAME: count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, true, selection), selectionArgs); break; - case API_ACCOUNTS_BY_ROW_ID: - count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, false, selection), - selectionArgs); - break; case API_ACCOUNTS_BY_ACCOUNT_NAME: count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, true, selection), selectionArgs); @@ -956,18 +929,10 @@ public class KeychainProvider extends ContentProvider { count = db.update(Tables.USER_IDS, values, buildDefaultUserIdsSelection(uri, selection), selectionArgs); break; - case API_APPS_BY_ROW_ID: - count = db.update(Tables.API_APPS, values, - buildDefaultApiAppsSelection(uri, false, selection), selectionArgs); - break; case API_APPS_BY_PACKAGE_NAME: count = db.update(Tables.API_APPS, values, buildDefaultApiAppsSelection(uri, true, selection), selectionArgs); break; - case API_ACCOUNTS_BY_ROW_ID: - count = db.update(Tables.API_ACCOUNTS, values, - buildDefaultApiAccountsSelection(uri, false, selection), selectionArgs); - break; case API_ACCOUNTS_BY_ACCOUNT_NAME: count = db.update(Tables.API_ACCOUNTS, values, buildDefaultApiAccountsSelection(uri, true, selection), selectionArgs); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java index 0cec319a5..853dc2d3c 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java @@ -19,10 +19,7 @@ package org.sufficientlysecure.keychain.remote.ui; import android.annotation.TargetApi; import android.content.ContentUris; -import android.content.Context; import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Build; @@ -31,15 +28,10 @@ import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; -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.AdapterView.OnItemClickListener; -import android.widget.ImageView; import android.widget.SimpleCursorAdapter; -import android.widget.TextView; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.provider.KeychainContract; @@ -50,36 +42,46 @@ import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; public class AccountsListFragment extends ListFragment implements LoaderManager.LoaderCallbacks { + private static final String ARG_DATA_URI = "uri"; + // This is the Adapter being used to display the list's data. SimpleCursorAdapter mAdapter; - private String mPackageName; + private Uri mDataUri; - public String getPackageName() { - return mPackageName; - } + /** + * Creates new instance of this fragment + */ + public static AccountsListFragment newInstance(Uri dataUri) { + AccountsListFragment frag = new AccountsListFragment(); - public void setPackageName(String packageName) { - this.mPackageName = packageName; + Bundle args = new Bundle(); + args.putParcelable(ARG_DATA_URI, dataUri); + + frag.setArguments(args); + + return frag; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); + mDataUri = getArguments().getParcelable(ARG_DATA_URI); + getListView().setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int position, long id) { - // edit app settings - Intent intent = new Intent(getActivity(), AppSettingsActivity.class); - intent.setData(ContentUris.withAppendedId(ApiApps.CONTENT_URI, id)); - startActivity(intent); +// // edit app settings +// Intent intent = new Intent(getActivity(), AppSettingsActivity.class); +// intent.setData(ContentUris.withAppendedId(ApiApps.CONTENT_URI, id)); +// startActivity(intent); } }); // Give some text to display if there is no data. In a real // application this would come from a resource. - setEmptyText(getString(R.string.api_no_apps)); + setEmptyText(getString(R.string.api_settings_accounts_empty)); // We have a menu item to show in action bar. setHasOptionsMenu(true); @@ -108,11 +110,11 @@ public class AccountsListFragment extends ListFragment implements // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. - Uri baseUri = KeychainContract.ApiAccounts.buildBaseUri(mPackageName); +// Uri baseUri = KeychainContract.ApiAccounts.buildBaseUri(mPackageName); // 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, null, null, + return new CursorLoader(getActivity(), mDataUri, PROJECTION, null, null, KeychainContract.ApiAccounts.ACCOUNT_NAME + " COLLATE LOCALIZED ASC"); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java index d544455ec..33cde49ba 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java @@ -24,9 +24,11 @@ import android.support.v7.app.ActionBarActivity; import android.view.Menu; import android.view.MenuItem; import android.view.View; + import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.ActionBarHelper; +import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.remote.AppSettings; import org.sufficientlysecure.keychain.util.Log; @@ -35,6 +37,7 @@ public class AppSettingsActivity extends ActionBarActivity { private Uri mAppUri; private AppSettingsFragment mSettingsFragment; + private AccountsListFragment mAccountsListFragment; @Override protected void onCreate(Bundle savedInstanceState) { @@ -64,7 +67,7 @@ public class AppSettingsActivity extends ActionBarActivity { return; } else { Log.d(Constants.TAG, "uri: " + mAppUri); - loadData(mAppUri); + loadData(savedInstanceState, mAppUri); } } @@ -88,9 +91,34 @@ public class AppSettingsActivity extends ActionBarActivity { return super.onOptionsItemSelected(item); } - private void loadData(Uri appUri) { + private void loadData(Bundle savedInstanceState, Uri appUri) { + // TODO: load this also like other fragment with newInstance arguments? AppSettings settings = ProviderHelper.getApiAppSettings(this, appUri); mSettingsFragment.setAppSettings(settings); + + Uri accountsUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ACCOUNTS).build(); + Log.d(Constants.TAG, "accountsUri: " + accountsUri); + startListFragment(savedInstanceState, accountsUri); + } + + private void startListFragment(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 + mAccountsListFragment = AccountsListFragment.newInstance(dataUri); + + // Add the fragment to the 'fragment_container' FrameLayout + // NOTE: We use commitAllowingStateLoss() to prevent weird crashes! + getSupportFragmentManager().beginTransaction() + .replace(R.id.api_accounts_list_fragment, mAccountsListFragment) + .commitAllowingStateLoss(); + // do it immediately! + getSupportFragmentManager().executePendingTransactions(); } private void revokeAccess() { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java index 7054a2195..f3fa6e7c6 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppsListFragment.java @@ -17,7 +17,6 @@ package org.sufficientlysecure.keychain.remote.ui; -import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -38,9 +37,11 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.ImageView; import android.widget.TextView; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; +import org.sufficientlysecure.keychain.util.Log; public class AppsListFragment extends ListFragment implements LoaderManager.LoaderCallbacks { @@ -55,9 +56,10 @@ public class AppsListFragment extends ListFragment implements getListView().setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int position, long id) { + String selectedPackageName = mAdapter.getItemPackageName(position); // edit app settings Intent intent = new Intent(getActivity(), AppSettingsActivity.class); - intent.setData(ContentUris.withAppendedId(KeychainContract.ApiApps.CONTENT_URI, id)); + intent.setData(KeychainContract.ApiApps.buildByPackageNameUri(selectedPackageName)); startActivity(intent); } }); @@ -79,7 +81,10 @@ public class AppsListFragment extends ListFragment implements } // These are the Contacts rows that we will retrieve. - static final String[] PROJECTION = new String[]{ApiApps._ID, ApiApps.PACKAGE_NAME}; + static final String[] PROJECTION = new String[]{ + ApiApps._ID, // 0 + ApiApps.PACKAGE_NAME // 1 + }; public Loader onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This @@ -119,6 +124,25 @@ public class AppsListFragment extends ListFragment implements mPM = context.getApplicationContext().getPackageManager(); } + /** + * Similar to CursorAdapter.getItemId(). + * Required to build Uris for api app view, which is not based on row ids + * + * @param position + * @return + */ + public String getItemPackageName(int position) { + if (mDataValid && mCursor != null) { + if (mCursor.moveToPosition(position)) { + return mCursor.getString(1); + } else { + return null; + } + } else { + return null; + } + } + @Override public void bindView(View view, Context context, Cursor cursor) { TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name); diff --git a/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml index ff560b657..3284b5b0a 100644 --- a/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml +++ b/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml @@ -46,8 +46,8 @@ diff --git a/OpenPGP-Keychain/src/main/res/layout/api_app_settings_activity.xml b/OpenPGP-Keychain/src/main/res/layout/api_app_settings_activity.xml index d83c8e87d..d38567497 100644 --- a/OpenPGP-Keychain/src/main/res/layout/api_app_settings_activity.xml +++ b/OpenPGP-Keychain/src/main/res/layout/api_app_settings_activity.xml @@ -12,10 +12,24 @@ + + + + diff --git a/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml index b91025474..96271d418 100644 --- a/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml +++ b/OpenPGP-Keychain/src/main/res/layout/api_app_settings_fragment.xml @@ -39,8 +39,8 @@ diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index 11e3028f3..5bf7ca8ca 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -412,6 +412,8 @@ No registered applications!\n\nThird-party applications can request access to OpenKeychain. After granting access, they will be listed here. + Show advanced information + Hide advanced information Show advanced settings Hide advanced settings No key selected @@ -421,6 +423,8 @@ Revoke access Package Name SHA-256 of Package Signature + Accounts + No accounts attached to this application. The displayed application requests access to OpenKeychain.\nAllow access?\n\nWARNING: If you do not know why this screen appeared, disallow access! You can revoke access later using the \'Registered Applications\' screen. Allow access Disallow access From 8c496d3393188559c4ce86d5ac4e3a9b1b2bdff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 25 Mar 2014 21:52:06 +0100 Subject: [PATCH 125/253] Fix update and insert for new api content provider --- .../keychain/provider/KeychainProvider.java | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 005be2fd6..1f725266e 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -27,6 +27,7 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.provider.BaseColumns; +import android.support.v4.database.DatabaseUtilsCompat; import android.text.TextUtils; import org.sufficientlysecure.keychain.Constants; @@ -860,11 +861,11 @@ public class KeychainProvider extends ContentProvider { selectionArgs); break; case API_APPS_BY_PACKAGE_NAME: - count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, true, selection), + count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, selection), selectionArgs); break; case API_ACCOUNTS_BY_ACCOUNT_NAME: - count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, true, selection), + count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, selection), selectionArgs); break; default: @@ -931,11 +932,11 @@ public class KeychainProvider extends ContentProvider { break; case API_APPS_BY_PACKAGE_NAME: count = db.update(Tables.API_APPS, values, - buildDefaultApiAppsSelection(uri, true, selection), selectionArgs); + buildDefaultApiAppsSelection(uri, selection), selectionArgs); break; case API_ACCOUNTS_BY_ACCOUNT_NAME: count = db.update(Tables.API_ACCOUNTS, values, - buildDefaultApiAccountsSelection(uri, true, selection), selectionArgs); + buildDefaultApiAccountsSelection(uri, selection), selectionArgs); break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); @@ -1034,34 +1035,29 @@ public class KeychainProvider extends ContentProvider { * @param selection * @return */ - private String buildDefaultApiAppsSelection(Uri uri, boolean packageSelection, String selection) { - String lastPathSegment = uri.getLastPathSegment(); + private String buildDefaultApiAppsSelection(Uri uri, String selection) { + String packageName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment()); String andSelection = ""; if (!TextUtils.isEmpty(selection)) { andSelection = " AND (" + selection + ")"; } - if (packageSelection) { - return ApiApps.PACKAGE_NAME + "=" + lastPathSegment + andSelection; - } else { - return BaseColumns._ID + "=" + lastPathSegment + andSelection; - } + return ApiApps.PACKAGE_NAME + "=" + packageName + andSelection; } - private String buildDefaultApiAccountsSelection(Uri uri, boolean packageSelection, String selection) { - String lastPathSegment = uri.getLastPathSegment(); + private String buildDefaultApiAccountsSelection(Uri uri, String selection) { + String packageName = DatabaseUtils.sqlEscapeString(uri.getPathSegments().get(2)); + String accountName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment()); String andSelection = ""; if (!TextUtils.isEmpty(selection)) { andSelection = " AND (" + selection + ")"; } - if (packageSelection) { - return ApiAccounts.PACKAGE_NAME_FK + "=" + lastPathSegment + andSelection; - } else { - return BaseColumns._ID + "=" + lastPathSegment + andSelection; - } + return ApiAccounts.PACKAGE_NAME_FK + "=" + packageName + " AND " + + ApiAccounts.ACCOUNT_NAME + "=" + accountName + + andSelection; } // @Override From dc9fd1221387a4f31ec2ceba615b376b0796ff10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 25 Mar 2014 23:09:57 +0100 Subject: [PATCH 126/253] Return account creation intent if account does not exists in db --- .../keychain/provider/KeychainContract.java | 23 ++----- .../keychain/provider/KeychainProvider.java | 5 +- .../keychain/remote/OpenPgpService.java | 3 + .../keychain/remote/RemoteService.java | 69 +++++++++++++------ .../remote/ui/RemoteServiceActivity.java | 6 +- 5 files changed, 60 insertions(+), 46 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 98ce98495..6e4899fc2 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -160,6 +160,7 @@ public class KeychainContract { } public static Uri buildSecretKeyRingsByEmailsUri(String emails) { + // TODO: encoded? return CONTENT_URI.buildUpon().appendPath(PATH_SECRET).appendPath(PATH_BY_EMAILS) .appendPath(emails).build(); } @@ -263,16 +264,12 @@ public class KeychainContract { /** * Use if multiple items get returned */ - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api.app"; + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api_apps"; /** * Use if a single item is returned */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api.apps"; - -// public static Uri buildIdUri(String rowId) { -// return CONTENT_URI.buildUpon().appendPath(rowId).build(); -// } + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_app"; public static Uri buildByPackageNameUri(String packageName) { return CONTENT_URI.buildUpon().appendEncodedPath(packageName).build(); @@ -286,22 +283,12 @@ public class KeychainContract { /** * Use if multiple items get returned */ - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api.acoounts"; + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.thialfihar.apg.api_app.accounts"; /** * Use if a single item is returned */ - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api.account"; - -// public static Uri buildUri(String rowIdApp) { -// return CONTENT_URI.buildUpon().appendPath(rowIdApp).appendPath(PATH_ACCOUNTS) -// .build(); -// } -// -// public static Uri buildIdUri(String rowIdApp, String rowId) { -// return CONTENT_URI.buildUpon().appendPath(rowIdApp).appendPath(PATH_ACCOUNTS) -// .appendPath(rowId).build(); -// } + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.thialfihar.apg.api_app.account"; public static Uri buildBaseUri(String packageName) { return CONTENT_URI.buildUpon().appendEncodedPath(packageName).appendPath(PATH_ACCOUNTS) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 1f725266e..6e5515cab 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -27,7 +27,6 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.provider.BaseColumns; -import android.support.v4.database.DatabaseUtilsCompat; import android.text.TextUtils; import org.sufficientlysecure.keychain.Constants; @@ -243,10 +242,10 @@ public class KeychainProvider extends ContentProvider { * *
              * api_apps
    -         * api_apps/_
    +         * api_apps/_ (package name)
              *
              * api_apps/_/accounts
    -         * api_apps/_/accounts/_
    +         * api_apps/_/accounts/_ (account name)
              * 
    */ matcher.addURI(authority, KeychainContract.BASE_API_APPS, API_APPS); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java index b04e76cbd..fa6ccf63b 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/OpenPgpService.java @@ -440,6 +440,9 @@ public class OpenPgpService extends RemoteService { accName = "default"; } final AccountSettings accSettings = getAccSettings(accName); + if (accSettings == null) { + return getCreateAccountIntent(data, accName); + } String action = data.getAction(); if (OpenPgpApi.ACTION_SIGN.equals(action)) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java index 7b66a0b5c..0fd9ee7df 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java @@ -27,6 +27,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import android.net.Uri; import android.os.Binder; + import org.openintents.openpgp.OpenPgpError; import org.openintents.openpgp.util.OpenPgpApi; import org.sufficientlysecure.keychain.Constants; @@ -48,7 +49,6 @@ public abstract class RemoteService extends Service { private static final int PRIVATE_REQUEST_CODE_REGISTER = 651; private static final int PRIVATE_REQUEST_CODE_ERROR = 652; - public Context getContext() { return mContext; } @@ -56,13 +56,9 @@ public abstract class RemoteService extends Service { protected Intent isAllowed(Intent data) { try { if (isCallerAllowed(false)) { - return null; } else { - String[] callingPackages = getPackageManager().getPackagesForUid( - Binder.getCallingUid()); - // TODO: currently simply uses first entry - String packageName = callingPackages[0]; + String packageName = getCurrentCallingPackage(); byte[] packageSignature; try { @@ -85,7 +81,7 @@ public abstract class RemoteService extends Service { intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); PendingIntent pi = PendingIntent.getActivity(getBaseContext(), - PRIVATE_REQUEST_CODE_REGISTER, intent, 0); + PRIVATE_REQUEST_CODE_REGISTER, intent, 0); // return PendingIntent to be executed by client Intent result = new Intent(); @@ -100,11 +96,11 @@ public abstract class RemoteService extends Service { Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class); intent.setAction(RemoteServiceActivity.ACTION_ERROR_MESSAGE); intent.putExtra(RemoteServiceActivity.EXTRA_ERROR_MESSAGE, - getString(R.string.api_error_wrong_signature)); + getString(R.string.api_error_wrong_signature)); intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); PendingIntent pi = PendingIntent.getActivity(getBaseContext(), - PRIVATE_REQUEST_CODE_ERROR, intent, 0); + PRIVATE_REQUEST_CODE_ERROR, intent, 0); // return PendingIntent to be executed by client Intent result = new Intent(); @@ -126,27 +122,56 @@ public abstract class RemoteService extends Service { } /** - * Retrieves AppSettings from database for the application calling this remote service + * Returns package name associated with the UID, which is assigned to the process that sent you the + * current transaction that is being processed :) + * + * @return package name + */ + private String getCurrentCallingPackage() { + // TODO: + // callingPackages contains more than one entry when sharedUserId has been used... + String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); + String currentPkg = callingPackages[0]; + Log.d(Constants.TAG, "currentPkg: " + currentPkg); + + return currentPkg; + } + + /** + * Retrieves AccountSettings from database for the application calling this remote service * * @return */ protected AccountSettings getAccSettings(String accountName) { - String[] callingPackages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); + String currentPkg = getCurrentCallingPackage(); + Log.d(Constants.TAG, "accountName: " + accountName); - // get app settings for this package - for (int i = 0; i < callingPackages.length; i++) { - String currentPkg = callingPackages[i]; + Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(currentPkg, accountName); - Uri uri = KeychainContract.ApiAccounts.buildByPackageAndAccountUri(currentPkg, accountName); + AccountSettings settings = ProviderHelper.getApiAccountSettings(this, uri); - AccountSettings settings = ProviderHelper.getApiAccountSettings(this, uri); + return settings; // can be null! + } - if (settings != null) { - return settings; - } - } + protected Intent getCreateAccountIntent(Intent data, String accountName) { + String packageName = getCurrentCallingPackage(); + Log.d(Constants.TAG, "accountName: " + accountName); - return null; + Intent intent = new Intent(getBaseContext(), RemoteServiceActivity.class); + intent.setAction(RemoteServiceActivity.ACTION_CREATE_ACCOUNT); + intent.putExtra(RemoteServiceActivity.EXTRA_PACKAGE_NAME, packageName); + intent.putExtra(RemoteServiceActivity.EXTRA_ACC_NAME, accountName); + intent.putExtra(RemoteServiceActivity.EXTRA_DATA, data); + + PendingIntent pi = PendingIntent.getActivity(getBaseContext(), + PRIVATE_REQUEST_CODE_REGISTER, intent, 0); + + // 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; } /** @@ -217,7 +242,7 @@ public abstract class RemoteService extends Service { return true; } else { throw new WrongPackageSignatureException( - "PACKAGE NOT ALLOWED! Signature wrong! (Signature not equals signature from database)"); + "PACKAGE NOT ALLOWED! Signature wrong! (Signature not equals signature from database)"); } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java index a088ad4b0..d3ac5cade 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java @@ -45,8 +45,8 @@ import java.util.ArrayList; public class RemoteServiceActivity extends ActionBarActivity { public static final String ACTION_REGISTER = Constants.INTENT_PREFIX + "API_ACTIVITY_REGISTER"; - public static final String ACTION_REGISTER_ACCOUNT = Constants.INTENT_PREFIX - + "API_ACTIVITY_REGISTER_ACCOUNT"; + public static final String ACTION_CREATE_ACCOUNT = Constants.INTENT_PREFIX + + "API_ACTIVITY_CREATE_ACCOUNT"; public static final String ACTION_CACHE_PASSPHRASE = Constants.INTENT_PREFIX + "API_ACTIVITY_CACHE_PASSPHRASE"; public static final String ACTION_SELECT_PUB_KEYS = Constants.INTENT_PREFIX @@ -130,7 +130,7 @@ public class RemoteServiceActivity extends ActionBarActivity { AppSettings settings = new AppSettings(packageName, packageSignature); mAppSettingsFragment.setAppSettings(settings); - } else if (ACTION_REGISTER_ACCOUNT.equals(action)) { + } else if (ACTION_CREATE_ACCOUNT.equals(action)) { final String packageName = extras.getString(EXTRA_PACKAGE_NAME); final String accName = extras.getString(EXTRA_ACC_NAME); From 0edfd7ed48943607ebaaa1dd2c9b1018ace0b024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Tue, 25 Mar 2014 23:25:03 +0100 Subject: [PATCH 127/253] Temporary fix for inserting by content provider --- .../keychain/provider/KeychainProvider.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 6e5515cab..a5ee723f0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -757,14 +757,14 @@ public class KeychainProvider extends ContentProvider { rowId = db.insertOrThrow(Tables.KEYS, null, values); // TODO: this is wrong: -// rowUri = Keys.buildPublicKeysUri(Long.toString(rowId)); + rowUri = Keys.buildPublicKeysUri(Long.toString(rowId)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; case PUBLIC_KEY_RING_USER_ID: rowId = db.insertOrThrow(Tables.USER_IDS, null, values); // TODO: this is wrong: -// rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId)); + rowUri = UserIds.buildPublicUserIdsUri(Long.toString(rowId)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; @@ -781,14 +781,14 @@ public class KeychainProvider extends ContentProvider { rowId = db.insertOrThrow(Tables.KEYS, null, values); // TODO: this is wrong: -// rowUri = Keys.buildSecretKeysUri(Long.toString(rowId)); + rowUri = Keys.buildSecretKeysUri(Long.toString(rowId)); sendBroadcastDatabaseChange(getKeyType(match), getType(uri)); break; case SECRET_KEY_RING_USER_ID: rowId = db.insertOrThrow(Tables.USER_IDS, null, values); // TODO: this is wrong: -// rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId)); + rowUri = UserIds.buildSecretUserIdsUri(Long.toString(rowId)); break; case API_APPS: From 68f035ff88131928cdd0681ba3b1fb4980d9a574 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Tue, 25 Mar 2014 22:28:32 +0000 Subject: [PATCH 128/253] allow lists to be subkeys or keyrings --- .../keychain/pgp/PgpConversionHelper.java | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java index 20d446824..c7dd7d647 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpConversionHelper.java @@ -59,13 +59,30 @@ public class PgpConversionHelper { * @return */ public static ArrayList BytesToPGPSecretKeyList(byte[] keysBytes) { - PGPSecretKeyRing keyRing = (PGPSecretKeyRing) BytesToPGPKeyRing(keysBytes); + PGPObjectFactory factory = new PGPObjectFactory(keysBytes); + Object obj = null; ArrayList keys = new ArrayList(); - - @SuppressWarnings("unchecked") - Iterator itr = keyRing.getSecretKeys(); - while (itr.hasNext()) { - keys.add(itr.next()); + try { + while ((obj = factory.nextObject()) != null) { + PGPSecretKey secKey = null; + if(obj instanceof PGPSecretKey) { + if ((secKey = (PGPSecretKey)obj ) == null) { + Log.e(Constants.TAG, "No keys given!"); + } + keys.add(secKey); + } else if(obj instanceof PGPSecretKeyRing) { //master keys are sent as keyrings + PGPSecretKeyRing keyRing = null; + if ((keyRing = (PGPSecretKeyRing)obj) == null) { + Log.e(Constants.TAG, "No keys given!"); + } + @SuppressWarnings("unchecked") + Iterator itr = keyRing.getSecretKeys(); + while (itr.hasNext()) { + keys.add(itr.next()); + } + } + } + } catch (IOException e) { } return keys; From 259a8a63a2efa587c6cbe23ae929d7f8c9478664 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Tue, 25 Mar 2014 22:40:05 +0000 Subject: [PATCH 129/253] delete IDs needs to update keyring, flag it --- .../org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index fab456bf8..370f66388 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -405,12 +405,13 @@ public class PgpKeyOperation { updateProgress(R.string.progress_certifying_master_key, 20, 100); + boolean anyIDChanged = false; for (String delID : saveParcel.deletedIDs) { + anyIDChanged = true; masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, delID); } int user_id_index = 0; - boolean anyIDChanged = false; PGPSignatureSubpacketGenerator hashedPacketsGen = new PGPSignatureSubpacketGenerator(); PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator(); From b77e0504aa1308da36df63038d4c05ca9aaebd71 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Tue, 25 Mar 2014 23:01:17 +0000 Subject: [PATCH 130/253] allow master key updates by removing old primary ID cert --- .../sufficientlysecure/keychain/pgp/PgpKeyOperation.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java index 370f66388..9bf9ecc73 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java @@ -376,6 +376,9 @@ public class PgpKeyOperation { else remove changed IDs and add in with new certs + if the master key changed, we need to remove the primary ID certification, so we can add + the new one when it is generated, and they don't conflict + Keys remove deleted keys if a key is modified, re-sign it @@ -495,6 +498,11 @@ public class PgpKeyOperation { } } + if (saveParcel.moddedKeys[0]) { + masterPublicKey = PGPPublicKey.removeCertification(masterPublicKey, saveParcel.originalIDs.get(0)); + anyIDChanged = true; + } + //update the keyring with the new ID information if (anyIDChanged) { pKR = PGPPublicKeyRing.insertPublicKey(pKR, masterPublicKey); From 028af0c1195b2d6a747874af8cae07dde542dc64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 00:11:49 +0100 Subject: [PATCH 131/253] Fix constraints and insert --- .../keychain/provider/KeychainDatabase.java | 25 +++++++++++-------- .../keychain/provider/KeychainProvider.java | 11 ++++++-- .../keychain/provider/ProviderHelper.java | 7 ++---- .../keychain/remote/RemoteService.java | 2 +- 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index ca1a47f0c..4abcec435 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -21,6 +21,7 @@ import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.provider.BaseColumns; + import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns; import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns; @@ -63,33 +64,35 @@ public class KeychainDatabase extends SQLiteOpenHelper { + KeysColumns.KEY_DATA + " BLOB," + KeysColumns.RANK + " INTEGER, " + KeysColumns.FINGERPRINT + " BLOB, " - + KeysColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY(" - + KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "(" - + BaseColumns._ID + ") ON DELETE CASCADE)"; + + KeysColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, " + + "FOREIGN KEY(" + KeysColumns.KEY_RING_ROW_ID + ") REFERENCES " + + Tables.KEY_RINGS + "(" + BaseColumns._ID + ") ON DELETE CASCADE)"; private static final String CREATE_USER_IDS = "CREATE TABLE IF NOT EXISTS " + Tables.USER_IDS + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + UserIdsColumns.USER_ID + " TEXT, " + UserIdsColumns.RANK + " INTEGER, " - + UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, FOREIGN KEY(" - + UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + Tables.KEY_RINGS + "(" - + BaseColumns._ID + ") ON DELETE CASCADE)"; + + UserIdsColumns.KEY_RING_ROW_ID + " INTEGER NOT NULL, " + + "FOREIGN KEY(" + UserIdsColumns.KEY_RING_ROW_ID + ") REFERENCES " + + Tables.KEY_RINGS + "(" + BaseColumns._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, " - + ApiAppsColumns.PACKAGE_NAME + " TEXT UNIQUE, " + + ApiAppsColumns.PACKAGE_NAME + " TEXT NOT NULL UNIQUE, " + ApiAppsColumns.PACKAGE_SIGNATURE + " BLOB)"; private static final String CREATE_API_APPS_ACCOUNTS = "CREATE TABLE IF NOT EXISTS " + Tables.API_ACCOUNTS + " (" + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " - + ApiAppsAccountsColumns.ACCOUNT_NAME + " TEXT UNIQUE, " + + ApiAppsAccountsColumns.ACCOUNT_NAME + " TEXT NOT NULL, " + ApiAppsAccountsColumns.KEY_ID + " INT64, " + ApiAppsAccountsColumns.ENCRYPTION_ALGORITHM + " INTEGER, " + ApiAppsAccountsColumns.HASH_ALORITHM + " INTEGER, " + ApiAppsAccountsColumns.COMPRESSION + " INTEGER, " - + ApiAppsAccountsColumns.PACKAGE_NAME_FK + " TEXT NOT NULL, FOREIGN KEY(" - + ApiAppsAccountsColumns.PACKAGE_NAME_FK + ") REFERENCES " + Tables.API_APPS + "(" - + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE)"; + + ApiAppsAccountsColumns.PACKAGE_NAME_FK + " TEXT NOT NULL, " + + "UNIQUE(" + ApiAppsAccountsColumns.ACCOUNT_NAME + ", " + + ApiAppsAccountsColumns.PACKAGE_NAME_FK + "), " + + "FOREIGN KEY(" + ApiAppsAccountsColumns.PACKAGE_NAME_FK + ") REFERENCES " + + Tables.API_APPS + "(" + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE)"; KeychainDatabase(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index a5ee723f0..6469da978 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -694,7 +694,7 @@ public class KeychainProvider extends ContentProvider { + Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = " + Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME_FK + " )"); qb.appendWhere(Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = "); - qb.appendWhereEscapeString(uri.getPathSegments().get(2)); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); qb.appendWhere(" AND " + Tables.API_ACCOUNTS + "." + ApiAccounts.ACCOUNT_NAME + " = "); qb.appendWhereEscapeString(uri.getLastPathSegment()); @@ -797,6 +797,13 @@ public class KeychainProvider extends ContentProvider { break; case API_ACCOUNTS: + // set foreign key automatically based on given uri + // e.g., api_apps/com.example.app/accounts/ + String packageName = uri.getPathSegments().get(1); + values.put(ApiAccounts.PACKAGE_NAME_FK, packageName); + + Log.d(Constants.TAG, "provider packageName: " + packageName); + rowId = db.insertOrThrow(Tables.API_ACCOUNTS, null, values); // TODO: this is wrong: // rowUri = ApiAccounts.buildIdUri(Long.toString(rowId)); @@ -1046,7 +1053,7 @@ public class KeychainProvider extends ContentProvider { } private String buildDefaultApiAccountsSelection(Uri uri, String selection) { - String packageName = DatabaseUtils.sqlEscapeString(uri.getPathSegments().get(2)); + String packageName = DatabaseUtils.sqlEscapeString(uri.getPathSegments().get(1)); String accountName = DatabaseUtils.sqlEscapeString(uri.getLastPathSegment()); String andSelection = ""; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java index e3727b2f8..71f74b8d8 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/ProviderHelper.java @@ -813,7 +813,6 @@ public class ProviderHelper { values.put(KeychainContract.ApiAccounts.COMPRESSION, accSettings.getCompression()); values.put(KeychainContract.ApiAccounts.ENCRYPTION_ALGORITHM, accSettings.getEncryptionAlgorithm()); values.put(KeychainContract.ApiAccounts.HASH_ALORITHM, accSettings.getHashAlgorithm()); -// values.put(KeychainContract.ApiAccounts.PACKAGE_NAME_FK, accSettings.getPackageName()); return values; } @@ -823,8 +822,7 @@ public class ProviderHelper { } public static void insertApiAccount(Context context, Uri uri, AccountSettings accSettings) { - context.getContentResolver().insert(uri, - contentValueForApiAccounts(accSettings)); + context.getContentResolver().insert(uri, contentValueForApiAccounts(accSettings)); } public static void updateApiApp(Context context, AppSettings appSettings, Uri uri) { @@ -841,7 +839,6 @@ public class ProviderHelper { } } - /** * Must be an uri pointing to an account * @@ -886,7 +883,7 @@ public class ProviderHelper { return settings; } - public static byte[] getApiSignature(Context context, String packageName) { + public static byte[] getApiAppSignature(Context context, String packageName) { Uri queryUri = ApiApps.buildByPackageNameUri(packageName); String[] projection = new String[]{ApiApps.PACKAGE_SIGNATURE}; diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java index 0fd9ee7df..7e935d317 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/RemoteService.java @@ -235,7 +235,7 @@ public abstract class RemoteService extends Service { throw new WrongPackageSignatureException(e.getMessage()); } - byte[] storedSig = ProviderHelper.getApiSignature(this, packageName); + byte[] storedSig = ProviderHelper.getApiAppSignature(this, packageName); if (Arrays.equals(currentSig, storedSig)) { Log.d(Constants.TAG, "Package signature is correct! (equals signature from database)"); From 930d722013c36104300bfe4773798ae3d5089b6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 00:40:13 +0100 Subject: [PATCH 132/253] Simplify can encrypt check --- .../keychain/ui/ViewKeyMainFragment.java | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java index 8a4f2758a..691be5fa9 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/ViewKeyMainFragment.java @@ -32,7 +32,9 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ListView; import android.widget.TextView; + import com.beardedhen.androidbootstrap.BootstrapButton; + import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.helper.OtherHelper; @@ -143,7 +145,7 @@ public class ViewKeyMainFragment extends Fragment implements editIntent.setData( KeychainContract .KeyRings.buildSecretKeyRingsByMasterKeyIdUri( - Long.toString(masterKeyId))); + Long.toString(masterKeyId))); editIntent.setAction(EditKeyActivity.ACTION_EDIT_KEY); startActivityForResult(editIntent, 0); } @@ -192,27 +194,27 @@ public class ViewKeyMainFragment extends Fragment implements static final String[] KEYRING_PROJECTION = new String[]{KeychainContract.KeyRings._ID, KeychainContract.KeyRings.MASTER_KEY_ID, - KeychainContract.UserIds.USER_ID}; + KeychainContract.UserIds.USER_ID}; static final int KEYRING_INDEX_ID = 0; static final int KEYRING_INDEX_MASTER_KEY_ID = 1; static final int KEYRING_INDEX_USER_ID = 2; static final String[] USER_IDS_PROJECTION = new String[]{ - KeychainContract.UserIds._ID, - KeychainContract.UserIds.USER_ID, - KeychainContract.UserIds.RANK, + KeychainContract.UserIds._ID, + KeychainContract.UserIds.USER_ID, + KeychainContract.UserIds.RANK, }; static final String USER_IDS_SORT_ORDER = KeychainContract.UserIds.RANK + " COLLATE LOCALIZED ASC"; static final String[] KEYS_PROJECTION = new String[]{KeychainContract.Keys._ID, KeychainContract.Keys.KEY_ID, - KeychainContract.Keys.IS_MASTER_KEY, KeychainContract.Keys.ALGORITHM, - KeychainContract.Keys.KEY_SIZE, KeychainContract.Keys.CAN_CERTIFY, - KeychainContract.Keys.CAN_SIGN, KeychainContract.Keys.CAN_ENCRYPT, - KeychainContract.Keys.IS_REVOKED, KeychainContract.Keys.CREATION, - KeychainContract.Keys.EXPIRY, KeychainContract.Keys.FINGERPRINT}; + KeychainContract.Keys.IS_MASTER_KEY, KeychainContract.Keys.ALGORITHM, + KeychainContract.Keys.KEY_SIZE, KeychainContract.Keys.CAN_CERTIFY, + KeychainContract.Keys.CAN_SIGN, KeychainContract.Keys.CAN_ENCRYPT, + KeychainContract.Keys.IS_REVOKED, KeychainContract.Keys.CREATION, + KeychainContract.Keys.EXPIRY, KeychainContract.Keys.FINGERPRINT}; static final String KEYS_SORT_ORDER = KeychainContract.Keys.RANK + " ASC"; static final int KEYS_INDEX_ID = 0; static final int KEYS_INDEX_KEY_ID = 1; @@ -297,7 +299,7 @@ public class ViewKeyMainFragment extends Fragment implements mCreation.setText( DateFormat.getDateFormat(getActivity().getApplicationContext()).format( - creationDate)); + creationDate)); } // get expiry date from EXPIRY @@ -308,7 +310,7 @@ public class ViewKeyMainFragment extends Fragment implements mExpiry.setText( DateFormat.getDateFormat(getActivity().getApplicationContext()).format( - expiryDate)); + expiryDate)); } String algorithmStr = PgpKeyHelper.getAlgorithmInfo( @@ -324,17 +326,20 @@ public class ViewKeyMainFragment extends Fragment implements mFingerprint.setText(PgpKeyHelper.colorizeFingerprint(fingerprint)); } - int valid_keys = 0; + + // hide encrypt button if no encryption key is available + boolean canEncrypt = false; data.moveToFirst(); - do{ - if(data.getInt(KEYS_INDEX_CAN_ENCRYPT) == 1){ - valid_keys++; + do { + if (data.getInt(KEYS_INDEX_CAN_ENCRYPT) == 1) { + canEncrypt = true; + break; } - }while(data.moveToNext()); - if(valid_keys == 0){ + } while (data.moveToNext()); + if (!canEncrypt) { mActionEncrypt.setVisibility(View.GONE); } - Log.i("Valid Encryption keys", Integer.toString(valid_keys)); + mKeysAdapter.swapCursor(data); break; From 4923c9b8e3e78c888cde438a5f9aac7155ed30f5 Mon Sep 17 00:00:00 2001 From: Ashley Hughes Date: Wed, 26 Mar 2014 00:29:39 +0000 Subject: [PATCH 133/253] add original primary ID field to parcel --- .../sufficientlysecure/keychain/service/SaveKeyringParcel.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java index ae481aa80..c99284847 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/service/SaveKeyringParcel.java @@ -41,6 +41,7 @@ public class SaveKeyringParcel implements Parcelable { public String oldPassPhrase; public boolean[] newKeys; public ArrayList keys; + public String originalPrimaryID; public SaveKeyringParcel() {} @@ -62,6 +63,7 @@ public class SaveKeyringParcel implements Parcelable { oldPassPhrase = source.readString(); newKeys = source.createBooleanArray(); keys = PgpConversionHelper.BytesToPGPSecretKeyList(source.createByteArray()); + originalPrimaryID = source.readString(); } @Override @@ -82,6 +84,7 @@ public class SaveKeyringParcel implements Parcelable { destination.writeString(oldPassPhrase); destination.writeBooleanArray(newKeys); destination.writeByteArray(PgpConversionHelper.PGPSecretKeyArrayListToBytes(keys)); + destination.writeString(originalPrimaryID); } public static final Creator CREATOR = new Creator() { From 9542dc0e12a61535c2866a70cdd9266aff6cd9d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 13:07:32 +0100 Subject: [PATCH 134/253] Accounts API, user interface --- OpenPGP-Keychain/src/main/AndroidManifest.xml | 53 ++++---- .../remote/ui/AccountSettingsActivity.java | 110 +++++++++++++++ .../remote/ui/AccountsListFragment.java | 127 +++++++++--------- .../remote/ui/AppSettingsActivity.java | 38 +++--- .../remote/ui/AppSettingsFragment.java | 10 +- .../layout/api_account_settings_activity.xml | 20 +++ .../layout/api_accounts_adapter_list_item.xml | 16 +++ 7 files changed, 258 insertions(+), 116 deletions(-) create mode 100644 OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java create mode 100644 OpenPGP-Keychain/src/main/res/layout/api_account_settings_activity.xml create mode 100644 OpenPGP-Keychain/src/main/res/layout/api_accounts_adapter_list_item.xml diff --git a/OpenPGP-Keychain/src/main/AndroidManifest.xml b/OpenPGP-Keychain/src/main/AndroidManifest.xml index 3ab39280e..142174749 100644 --- a/OpenPGP-Keychain/src/main/AndroidManifest.xml +++ b/OpenPGP-Keychain/src/main/AndroidManifest.xml @@ -50,7 +50,7 @@ - + -
    + android:launchMode="singleTop"> - + android:launchMode="singleTop"> - + - - + + - - + + - + - - + + - - + + @@ -236,7 +234,7 @@ + android:label="@string/title_preferences"> @@ -374,17 +372,17 @@ android:exported="false" android:process=":passphrase_cache" /> @@ -395,23 +393,28 @@ android:exported="false" android:label="@string/title_api_registered_apps" /> + + + + - - diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java new file mode 100644 index 000000000..1092c7dc8 --- /dev/null +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2013 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.ui; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; +import android.view.MenuItem; +import android.view.View; + +import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.ActionBarHelper; +import org.sufficientlysecure.keychain.provider.ProviderHelper; +import org.sufficientlysecure.keychain.remote.AccountSettings; +import org.sufficientlysecure.keychain.util.Log; + +public class AccountSettingsActivity extends ActionBarActivity { + private Uri mAccountUri; + + private AccountSettingsFragment mAccountSettingsFragment; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Inflate a "Done" custom action bar + ActionBarHelper.setOneButtonView(getSupportActionBar(), + R.string.api_settings_save, R.drawable.ic_action_done, + new View.OnClickListener() { + @Override + public void onClick(View v) { + // "Done" + save(); + } + }); + + setContentView(R.layout.api_account_settings_activity); + + mAccountSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById( + R.id.api_account_settings_fragment); + + Intent intent = getIntent(); + mAccountUri = intent.getData(); + if (mAccountUri == null) { + Log.e(Constants.TAG, "Intent data missing. Should be Uri of app!"); + finish(); + return; + } else { + Log.d(Constants.TAG, "uri: " + mAccountUri); + loadData(savedInstanceState, mAccountUri); + } + } + +// @Override +// public boolean onCreateOptionsMenu(Menu menu) { +// super.onCreateOptionsMenu(menu); +// getMenuInflater().inflate(R.menu.api_app_settings, menu); +// return true; +// } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.menu_api_settings_revoke: + deleteAccount(); + return true; + case R.id.menu_api_settings_cancel: + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private void loadData(Bundle savedInstanceState, Uri accountUri) { + // TODO: load this also like other fragment with newInstance arguments? + AccountSettings settings = ProviderHelper.getApiAccountSettings(this, accountUri); + mAccountSettingsFragment.setAccSettings(settings); + } + + private void deleteAccount() { + if (getContentResolver().delete(mAccountUri, null, null) <= 0) { + throw new RuntimeException(); + } + finish(); + } + + private void save() { + ProviderHelper.updateApiAccount(this, mAccountSettingsFragment.getAccSettings(), mAccountUri); + + finish(); + } + +} diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java index 853dc2d3c..22ee7db76 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Dominik Schürmann + * 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 @@ -17,35 +17,35 @@ package org.sufficientlysecure.keychain.remote.ui; -import android.annotation.TargetApi; -import android.content.ContentUris; +import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; +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.AdapterView.OnItemClickListener; -import android.widget.SimpleCursorAdapter; +import android.widget.TextView; +import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.provider.KeychainContract; -import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps; +import org.sufficientlysecure.keychain.util.Log; -// TODO: make compat with < 11 -@TargetApi(Build.VERSION_CODES.HONEYCOMB) public class AccountsListFragment extends ListFragment implements LoaderManager.LoaderCallbacks { private static final String ARG_DATA_URI = "uri"; // This is the Adapter being used to display the list's data. - SimpleCursorAdapter mAdapter; + AccountsAdapter mAdapter; private Uri mDataUri; @@ -72,10 +72,14 @@ public class AccountsListFragment extends ListFragment implements getListView().setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView adapterView, View view, int position, long id) { -// // edit app settings -// Intent intent = new Intent(getActivity(), AppSettingsActivity.class); -// intent.setData(ContentUris.withAppendedId(ApiApps.CONTENT_URI, id)); -// startActivity(intent); + String selectedAccountName = mAdapter.getItemAccountName(position); + Uri accountUri = mDataUri.buildUpon().appendEncodedPath(selectedAccountName).build(); + Log.d(Constants.TAG, "accountUri: " + accountUri); + + // edit account settings + Intent intent = new Intent(getActivity(), AccountSettingsActivity.class); + intent.setData(accountUri); + startActivity(intent); } }); @@ -87,12 +91,7 @@ public class AccountsListFragment extends ListFragment implements setHasOptionsMenu(true); // Create an empty adapter we will use to display the loaded data. - mAdapter = new SimpleCursorAdapter(getActivity(), - android.R.layout.simple_list_item_1, - null, - new String[]{KeychainContract.ApiAccounts.ACCOUNT_NAME}, - new int[]{android.R.id.text1}, - 0); + mAdapter = new AccountsAdapter(getActivity(), null, 0); setListAdapter(mAdapter); // Prepare the loader. Either re-connect with an existing one, @@ -102,15 +101,13 @@ public class AccountsListFragment extends ListFragment implements // These are the Contacts rows that we will retrieve. static final String[] PROJECTION = new String[]{ - KeychainContract.ApiAccounts._ID, - KeychainContract.ApiAccounts.ACCOUNT_NAME}; + KeychainContract.ApiAccounts._ID, // 0 + KeychainContract.ApiAccounts.ACCOUNT_NAME // 1 + }; public Loader onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. - // First, pick the base URI to use depending on whether we are - // currently filtering. -// Uri baseUri = KeychainContract.ApiAccounts.buildBaseUri(mPackageName); // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. @@ -131,46 +128,46 @@ public class AccountsListFragment extends ListFragment implements mAdapter.swapCursor(null); } -// private class RegisteredAppsAdapter extends CursorAdapter { -// -// private LayoutInflater mInflater; -// private PackageManager mPM; -// -// public RegisteredAppsAdapter(Context context, Cursor c, int flags) { -// super(context, c, flags); -// -// mInflater = LayoutInflater.from(context); -// mPM = context.getApplicationContext().getPackageManager(); -// } -// -// @Override -// public void bindView(View view, Context context, Cursor cursor) { -// TextView text = (TextView) view.findViewById(R.id.api_apps_adapter_item_name); -// ImageView icon = (ImageView) view.findViewById(R.id.api_apps_adapter_item_icon); -// -// String packageName = cursor.getString(cursor.getColumnIndex(ApiApps.PACKAGE_NAME)); -// if (packageName != null) { -// // get application name -// try { -// ApplicationInfo ai = mPM.getApplicationInfo(packageName, 0); -// -// text.setText(mPM.getApplicationLabel(ai)); -// icon.setImageDrawable(mPM.getApplicationIcon(ai)); -// } catch (final PackageManager.NameNotFoundException e) { -// // fallback -// text.setText(packageName); -// } -// } else { -// // fallback -// text.setText(packageName); -// } -// -// } -// -// @Override -// public View newView(Context context, Cursor cursor, ViewGroup parent) { -// return mInflater.inflate(R.layout.api_apps_adapter_list_item, null); -// } -// } + private class AccountsAdapter extends CursorAdapter { + private LayoutInflater mInflater; + + public AccountsAdapter(Context context, Cursor c, int flags) { + super(context, c, flags); + + mInflater = LayoutInflater.from(context); + } + + /** + * Similar to CursorAdapter.getItemId(). + * Required to build Uris for api app view, which is not based on row ids + * + * @param position + * @return + */ + public String getItemAccountName(int position) { + if (mDataValid && mCursor != null) { + if (mCursor.moveToPosition(position)) { + return mCursor.getString(1); + } else { + return null; + } + } else { + return null; + } + } + + @Override + public void bindView(View view, Context context, Cursor cursor) { + TextView text = (TextView) view.findViewById(R.id.api_accounts_adapter_item_name); + + String accountName = cursor.getString(1); + text.setText(accountName); + } + + @Override + public View newView(Context context, Cursor cursor, ViewGroup parent) { + return mInflater.inflate(R.layout.api_accounts_adapter_list_item, null); + } + } } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java index 33cde49ba..e4b943734 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java @@ -18,16 +18,17 @@ package org.sufficientlysecure.keychain.remote.ui; import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; +import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarActivity; import android.view.Menu; import android.view.MenuItem; -import android.view.View; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; -import org.sufficientlysecure.keychain.helper.ActionBarHelper; import org.sufficientlysecure.keychain.provider.KeychainContract; import org.sufficientlysecure.keychain.provider.ProviderHelper; import org.sufficientlysecure.keychain.remote.AppSettings; @@ -43,16 +44,11 @@ public class AppSettingsActivity extends ActionBarActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - // Inflate a "Done" custom action bar - ActionBarHelper.setOneButtonView(getSupportActionBar(), - R.string.api_settings_save, R.drawable.ic_action_done, - new View.OnClickListener() { - @Override - public void onClick(View v) { - // "Done" - save(); - } - }); + // let the actionbar look like Android's contact app + ActionBar actionBar = getSupportActionBar(); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setIcon(android.R.color.transparent); + actionBar.setHomeButtonEnabled(true); setContentView(R.layout.api_app_settings_activity); @@ -96,6 +92,18 @@ public class AppSettingsActivity extends ActionBarActivity { AppSettings settings = ProviderHelper.getApiAppSettings(this, appUri); mSettingsFragment.setAppSettings(settings); + String appName; + PackageManager pm = getPackageManager(); + try { + ApplicationInfo ai = pm.getApplicationInfo(settings.getPackageName(), 0); + appName = (String) pm.getApplicationLabel(ai); + } catch (PackageManager.NameNotFoundException e) { + // fallback + appName = settings.getPackageName(); + } + setTitle(appName); + + Uri accountsUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ACCOUNTS).build(); Log.d(Constants.TAG, "accountsUri: " + accountsUri); startListFragment(savedInstanceState, accountsUri); @@ -128,10 +136,4 @@ public class AppSettingsActivity extends ActionBarActivity { finish(); } - private void save() { - ProviderHelper.updateApiApp(this, mSettingsFragment.getAppSettings(), mAppUri); - - finish(); - } - } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java index 8bcd83fc7..5a6151d88 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java @@ -26,19 +26,13 @@ import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ImageView; -import android.widget.Spinner; import android.widget.TextView; import org.spongycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.remote.AppSettings; -import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment; -import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter; -import org.sufficientlysecure.keychain.util.AlgorithmNames; import org.sufficientlysecure.keychain.util.Log; import java.security.MessageDigest; @@ -100,14 +94,14 @@ public class AppSettingsFragment extends Fragment { PackageManager pm = getActivity().getApplicationContext().getPackageManager(); // get application name and icon from package manager - String appName = null; + String appName; Drawable appIcon = null; try { ApplicationInfo ai = pm.getApplicationInfo(packageName, 0); appName = (String) pm.getApplicationLabel(ai); appIcon = pm.getApplicationIcon(ai); - } catch (final NameNotFoundException e) { + } catch (NameNotFoundException e) { // fallback appName = packageName; } diff --git a/OpenPGP-Keychain/src/main/res/layout/api_account_settings_activity.xml b/OpenPGP-Keychain/src/main/res/layout/api_account_settings_activity.xml new file mode 100644 index 000000000..3557c1f00 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/api_account_settings_activity.xml @@ -0,0 +1,20 @@ + + + + + + + + + diff --git a/OpenPGP-Keychain/src/main/res/layout/api_accounts_adapter_list_item.xml b/OpenPGP-Keychain/src/main/res/layout/api_accounts_adapter_list_item.xml new file mode 100644 index 000000000..bbe6bc2ef --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/layout/api_accounts_adapter_list_item.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file From 438c05025054ac06e136432ff880c4f55e3d3be5 Mon Sep 17 00:00:00 2001 From: uberspot Date: Wed, 26 Mar 2014 13:44:30 +0200 Subject: [PATCH 135/253] put expiry on a separate line --- .../main/res/layout/view_key_keys_item.xml | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml index 43f06e246..13813f7e7 100644 --- a/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml +++ b/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml @@ -4,8 +4,7 @@ android:layout_height="wrap_content" android:orientation="horizontal" android:paddingLeft="8dip" - android:paddingRight="3dip" - android:singleLine="true" > + android:paddingRight="3dip" > - + - + + + + Date: Wed, 26 Mar 2014 14:06:40 +0200 Subject: [PATCH 136/253] add strike through text for revoked or expired keys and disable views for revoked keys as well --- .../keychain/helper/OtherHelper.java | 14 ++++++----- .../ui/adapter/ViewKeyKeysAdapter.java | 23 +++++++++++++++---- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java index d0ba20ea6..b31a889f0 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/helper/OtherHelper.java @@ -17,18 +17,14 @@ package org.sufficientlysecure.keychain.helper; -import android.graphics.Color; import android.os.Bundle; -import android.text.Spannable; import android.text.SpannableStringBuilder; -import android.text.style.ForegroundColorSpan; +import android.text.Spanned; +import android.text.style.StrikethroughSpan; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.util.Log; -import java.security.DigestException; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.Iterator; import java.util.Set; @@ -65,4 +61,10 @@ public class OtherHelper { } } + public static SpannableStringBuilder strikeOutText(CharSequence text) { + SpannableStringBuilder sb = new SpannableStringBuilder(text); + sb.setSpan(new StrikethroughSpan(), 0, text.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); + return sb; + } + } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java index d925480e9..ed4113fb8 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java @@ -29,6 +29,7 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import org.sufficientlysecure.keychain.R; +import org.sufficientlysecure.keychain.helper.OtherHelper; import org.sufficientlysecure.keychain.pgp.PgpKeyHelper; import org.sufficientlysecure.keychain.provider.KeychainContract.Keys; @@ -126,28 +127,42 @@ public class ViewKeyKeysAdapter extends CursorAdapter { signIcon.setVisibility(View.VISIBLE); } + boolean valid = true; if (cursor.getInt(mIndexRevokedKey) > 0) { revokedKeyIcon.setVisibility(View.VISIBLE); + keyId.setTextColor(Color.RED); keyDetails.setTextColor(Color.RED); + keyExpiry.setTextColor(Color.RED); + + valid = false; } else { keyId.setTextColor(mDefaultTextColor); keyDetails.setTextColor(mDefaultTextColor); + keyExpiry.setTextColor(mDefaultTextColor); + revokedKeyIcon.setVisibility(View.GONE); } - boolean valid = true; if (!cursor.isNull(mIndexExpiry)) { Date expiryDate = new Date(cursor.getLong(mIndexExpiry) * 1000); - valid = expiryDate.after(new Date()); + + valid = valid && expiryDate.after(new Date()); keyExpiry.setText("(" + - context.getString(R.string.label_expiry) + ": " + - DateFormat.getDateFormat(context).format(expiryDate) + ")"); + context.getString(R.string.label_expiry) + ": " + + DateFormat.getDateFormat(context).format(expiryDate) + ")"); + keyExpiry.setVisibility(View.VISIBLE); } else { keyExpiry.setVisibility(View.GONE); } + // if key is expired or revoked, strike through text + if (!valid) { + keyId.setText(OtherHelper.strikeOutText(keyId.getText())); + keyDetails.setText(OtherHelper.strikeOutText(keyDetails.getText())); + keyExpiry.setText(OtherHelper.strikeOutText(keyExpiry.getText())); + } keyId.setEnabled(valid); keyDetails.setEnabled(valid); keyExpiry.setEnabled(valid); From 92a4d0e914d7cd132b7e755caf914710969e512d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 13:21:29 +0100 Subject: [PATCH 137/253] Part 2 of basic user interface for api accounts --- .../remote/ui/AccountSettingsActivity.java | 17 ++-- .../remote/ui/AccountSettingsFragment.java | 77 ++++++++----------- .../layout/api_account_settings_fragment.xml | 75 +++++------------- .../src/main/res/values/strings.xml | 2 + 4 files changed, 63 insertions(+), 108 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java index 1092c7dc8..671a3e0aa 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsActivity.java @@ -21,6 +21,7 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; +import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -68,20 +69,20 @@ public class AccountSettingsActivity extends ActionBarActivity { } } -// @Override -// public boolean onCreateOptionsMenu(Menu menu) { -// super.onCreateOptionsMenu(menu); -// getMenuInflater().inflate(R.menu.api_app_settings, menu); -// return true; -// } + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + getMenuInflater().inflate(R.menu.api_account_settings, menu); + return true; + } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.menu_api_settings_revoke: + case R.id.menu_account_settings_delete: deleteAccount(); return true; - case R.id.menu_api_settings_cancel: + case R.id.menu_account_settings_cancel: finish(); return true; } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java index 3d88d216e..91e74f2bf 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java @@ -17,6 +17,7 @@ package org.sufficientlysecure.keychain.remote.ui; +import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -32,11 +33,14 @@ import android.widget.ImageView; import android.widget.Spinner; import android.widget.TextView; +import com.beardedhen.androidbootstrap.BootstrapButton; + import org.spongycastle.util.encoders.Hex; import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.remote.AccountSettings; import org.sufficientlysecure.keychain.remote.AppSettings; +import org.sufficientlysecure.keychain.ui.EditKeyActivity; import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment; import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter; import org.sufficientlysecure.keychain.util.AlgorithmNames; @@ -52,15 +56,13 @@ public class AccountSettingsFragment extends Fragment implements private AccountSettings mAccSettings; // view - private TextView mAppNameView; - private ImageView mAppIconView; + private TextView mAccNameView; private Spinner mEncryptionAlgorithm; private Spinner mHashAlgorithm; private Spinner mCompression; - private TextView mPackageName; - private TextView mPackageSignature; private SelectSecretKeyLayoutFragment mSelectKeyFragment; + private BootstrapButton mCreateKeyButton; KeyValueSpinnerAdapter mEncryptionAdapter; KeyValueSpinnerAdapter mHashAdapter; @@ -70,27 +72,15 @@ public class AccountSettingsFragment extends Fragment implements return mAccSettings; } - public void setAccSettings(AccountSettings appSettings) { - this.mAccSettings = appSettings; -// setPackage(appSettings.getPackageName()); -// mPackageName.setText(appSettings.getPackageName()); + public void setAccSettings(AccountSettings accountSettings) { + this.mAccSettings = accountSettings; -// try { -// MessageDigest md = MessageDigest.getInstance("SHA-256"); -// md.update(appSettings.getPackageSignature()); -// byte[] digest = md.digest(); -// String signature = new String(Hex.encode(digest)); -// -// mPackageSignature.setText(signature); -// } catch (NoSuchAlgorithmException e) { -// Log.e(Constants.TAG, "Should not happen!", e); -// } - - mSelectKeyFragment.selectKey(appSettings.getKeyId()); - mEncryptionAlgorithm.setSelection(mEncryptionAdapter.getPosition(appSettings + mAccNameView.setText(accountSettings.getAccountName()); + mSelectKeyFragment.selectKey(accountSettings.getKeyId()); + mEncryptionAlgorithm.setSelection(mEncryptionAdapter.getPosition(accountSettings .getEncryptionAlgorithm())); - mHashAlgorithm.setSelection(mHashAdapter.getPosition(appSettings.getHashAlgorithm())); - mCompression.setSelection(mCompressionAdapter.getPosition(appSettings.getCompression())); + mHashAlgorithm.setSelection(mHashAdapter.getPosition(accountSettings.getHashAlgorithm())); + mCompression.setSelection(mCompressionAdapter.getPosition(accountSettings.getCompression())); } /** @@ -117,14 +107,19 @@ public class AccountSettingsFragment extends Fragment implements R.id.api_account_settings_select_key_fragment); mSelectKeyFragment.setCallback(this); - mAppNameView = (TextView) view.findViewById(R.id.api_account_settings_app_name); - mAppIconView = (ImageView) view.findViewById(R.id.api_account_settings_app_icon); + mAccNameView = (TextView) view.findViewById(R.id.api_account_settings_acc_name); mEncryptionAlgorithm = (Spinner) view .findViewById(R.id.api_account_settings_encryption_algorithm); mHashAlgorithm = (Spinner) view.findViewById(R.id.api_account_settings_hash_algorithm); mCompression = (Spinner) view.findViewById(R.id.api_account_settings_compression); - mPackageName = (TextView) view.findViewById(R.id.api_account_settings_package_name); - mPackageSignature = (TextView) view.findViewById(R.id.api_account_settings_package_signature); + mCreateKeyButton = (BootstrapButton) view.findViewById(R.id.api_account_settings_create_key); + + mCreateKeyButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + createKey(); + } + }); AlgorithmNames algorithmNames = new AlgorithmNames(getActivity()); @@ -172,25 +167,15 @@ public class AccountSettingsFragment extends Fragment implements } }); } -// -// private void setPackage(String packageName) { -// PackageManager pm = getActivity().getApplicationContext().getPackageManager(); -// -// // get application name and icon from package manager -// String appName = null; -// Drawable appIcon = null; -// try { -// ApplicationInfo ai = pm.getApplicationInfo(packageName, 0); -// -// appName = (String) pm.getApplicationLabel(ai); -// appIcon = pm.getApplicationIcon(ai); -// } catch (final NameNotFoundException e) { -// // fallback -// appName = packageName; -// } -// mAppNameView.setText(appName); -// mAppIconView.setImageDrawable(appIcon); -// } + + private void createKey() { + Intent intent = new Intent(getActivity(), EditKeyActivity.class); + intent.setAction(EditKeyActivity.ACTION_CREATE_KEY); + intent.putExtra(EditKeyActivity.EXTRA_GENERATE_DEFAULT_KEYS, true); + // set default user id to account name TODO: not working currently in EditKey + intent.putExtra(EditKeyActivity.EXTRA_USER_IDS, mAccSettings.getAccountName()); + startActivityForResult(intent, 0); + } /** * callback from select secret key fragment diff --git a/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml index 3284b5b0a..de58b6d19 100644 --- a/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml +++ b/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml @@ -7,34 +7,14 @@ android:layout_height="wrap_content" android:orientation="vertical"> - - - - - - + + + - - - - - - - - \ No newline at end of file diff --git a/OpenPGP-Keychain/src/main/res/values/strings.xml b/OpenPGP-Keychain/src/main/res/values/strings.xml index 5bf7ca8ca..0d9cdc1bb 100644 --- a/OpenPGP-Keychain/src/main/res/values/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values/strings.xml @@ -418,9 +418,11 @@ Hide advanced settings No key selected Select key + Create new key for this account Save Cancel Revoke access + Delete account Package Name SHA-256 of Package Signature Accounts From f10235220d08628892432142ad632512e3f2c68e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 13:32:36 +0100 Subject: [PATCH 138/253] Forgot resource --- .../remote/ui/AccountSettingsFragment.java | 12 ------------ .../src/main/res/menu/api_account_settings.xml | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 12 deletions(-) create mode 100644 OpenPGP-Keychain/src/main/res/menu/api_account_settings.xml diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java index 91e74f2bf..0931e6e31 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountSettingsFragment.java @@ -18,10 +18,6 @@ package org.sufficientlysecure.keychain.remote.ui; import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.graphics.drawable.Drawable; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; @@ -29,25 +25,17 @@ import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.ImageView; import android.widget.Spinner; import android.widget.TextView; import com.beardedhen.androidbootstrap.BootstrapButton; -import org.spongycastle.util.encoders.Hex; -import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.remote.AccountSettings; -import org.sufficientlysecure.keychain.remote.AppSettings; import org.sufficientlysecure.keychain.ui.EditKeyActivity; import org.sufficientlysecure.keychain.ui.SelectSecretKeyLayoutFragment; import org.sufficientlysecure.keychain.ui.adapter.KeyValueSpinnerAdapter; import org.sufficientlysecure.keychain.util.AlgorithmNames; -import org.sufficientlysecure.keychain.util.Log; - -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; public class AccountSettingsFragment extends Fragment implements SelectSecretKeyLayoutFragment.SelectSecretKeyCallback { diff --git a/OpenPGP-Keychain/src/main/res/menu/api_account_settings.xml b/OpenPGP-Keychain/src/main/res/menu/api_account_settings.xml new file mode 100644 index 000000000..d08fc7f42 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/menu/api_account_settings.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file From b75428daf71ace4b295dddbad2094a593996c085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 13:38:45 +0100 Subject: [PATCH 139/253] Revert "Update README.md, icon set not used anymore" This reverts commit 9d4582f9689f53589bbf7cc3e224f9fd061f1d51. --- README.md | 66 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 958a445be..8954bdcda 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # OpenKeychain (for Android) -OpenKeychain is an OpenPGP implementation for Android. +OpenKeychain is an OpenPGP implementation for Android. For a more detailed description and installation instructions go to http://www.openkeychain.org . ### Travis CI Build Status @@ -31,9 +31,9 @@ Development mailinglist at http://groups.google.com/d/forum/openpgp-keychain-dev ### Build with Gradle 1. Have Android SDK "tools", "platform-tools", and "build-tools" directories in your PATH (http://developer.android.com/sdk/index.html) -2. Open the Android SDK Manager (shell command: ``android``). -Expand the Tools directory and select "Android SDK Build-tools (Version 19.0.3)". -Expand the Extras directory and install "Android Support Repository" +2. Open the Android SDK Manager (shell command: ``android``). +Expand the Tools directory and select "Android SDK Build-tools (Version 19.0.3)". +Expand the Extras directory and install "Android Support Repository" Select everything for the newest SDK (API-Level 19) 3. Export ANDROID_HOME pointing to your Android SDK 4. Execute ``./gradlew build`` @@ -59,11 +59,11 @@ I am using the newest [Android Studio](http://developer.android.com/sdk/installi OpenKeychain provides two APIs, namely the Intent API and the Remote OpenPGP API. The Intent API can be used without permissions to start OpenKeychain's activities for cryptographic operations, import of keys, etc. -However, it always requires user input, so that no malicious application can use this API without user intervention. +However, it always requires user input, so that no malicious application can use this API without user intervention. The Remote OpenPGP API is more sophisticated and allows to to operations without user interaction in the background. When utilizing this API, OpenKeychain asks the user on first use to grant access for the calling client application. -More technical information and examples about these APIs can be found in the project's wiki: +More technical information and examples about these APIs can be found in the project's wiki: * [Intent API](https://github.com/openpgp-keychain/openpgp-keychain/wiki/Intent-API) * [Remote OpenPGP API](https://github.com/openpgp-keychain/openpgp-keychain/wiki/OpenPGP-API) @@ -110,7 +110,7 @@ see ### Gradle Build System -We try to make our builds as [reproducible/deterministic](https://blog.torproject.org/blog/deterministic-builds-part-one-cyberwar-and-global-compromise) as possible. +We try to make our builds as [reproducible/deterministic](https://blog.torproject.org/blog/deterministic-builds-part-one-cyberwar-and-global-compromise) as possible. When changing build files or dependencies, respect the following requirements: * No precompiled libraries. All libraries should be provided as sourcecode in "libraries" folder (you never know what pre-compiled jar files really contain! The library files are currently directly commited, because git submodules/git subtree are too much of a hassle for new contributors. This could change in the future!) * No dependencies from Maven (also a soft requirement for inclusion in [F-Droid](https://f-droid.org)) @@ -180,55 +180,59 @@ Some parts (older parts and some libraries are Apache License v2, MIT X11 Licens > 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 . ### Libraries -* SpongyCastle - https://github.com/rtyley/spongycastle +* SpongyCastle + https://github.com/rtyley/spongycastle MIT X11 License -* Android Support Library v4 - http://developer.android.com/tools/support-library/index.html +* Android Support Library v4 + http://developer.android.com/tools/support-library/index.html + Apache License v2 + +* Android Support Library v7 'appcompat' + http://developer.android.com/tools/support-library/index.html Apache License v2 -* Android Support Library v7 'appcompat' - http://developer.android.com/tools/support-library/index.html +* HtmlTextView + https://github.com/dschuermann/html-textview Apache License v2 -* HtmlTextView - https://github.com/dschuermann/html-textview +* ZXing + https://github.com/zxing/zxing Apache License v2 - -* ZXing - https://github.com/zxing/zxing + +* StickyListHeaders + https://github.com/emilsjolander/StickyListHeaders Apache License v2 - -* StickyListHeaders - https://github.com/emilsjolander/StickyListHeaders - Apache License v2 - -* Android-Bootstrap - https://github.com/Bearded-Hen/Android-Bootstrap + +* Android-Bootstrap + https://github.com/Bearded-Hen/Android-Bootstrap MIT License -* Android AppMsg - https://github.com/johnkil/Android-AppMsg +* Android AppMsg + https://github.com/johnkil/Android-AppMsg Apache License v2 ### Images -* icon.svg +* icon.svg modified version of kgpg_key2_kopete.svgz -* Menu icons +* key.svg + http://rrze-icon-set.berlios.de/ + Creative Commons Attribution Share-Alike licence 3.0 + +* Menu icons http://developer.android.com/design/downloads/index.html#action-bar-icon-pack From e5fe4245bf4ab247a8caa5e89a710a8b3269ece6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 13:39:30 +0100 Subject: [PATCH 140/253] remove old icon entry --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index 8954bdcda..8c813a4e0 100644 --- a/README.md +++ b/README.md @@ -227,10 +227,6 @@ Some parts (older parts and some libraries are Apache License v2, MIT X11 Licens ### Images * icon.svg modified version of kgpg_key2_kopete.svgz - -* key.svg - http://rrze-icon-set.berlios.de/ - Creative Commons Attribution Share-Alike licence 3.0 * Menu icons http://developer.android.com/design/downloads/index.html#action-bar-icon-pack From 0d5e99e1f04d6fd24aa66eea987f9e4178731736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 13:42:16 +0100 Subject: [PATCH 141/253] Coding style --- README.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 8c813a4e0..474bf180b 100644 --- a/README.md +++ b/README.md @@ -145,15 +145,9 @@ see http://help.transifex.net/features/client/index.html#user-client * Opening braces don't go on their own line * Field names: Non-public, non-static fields start with m. * Acronyms are words: Treat acronyms as words in names, yielding !XmlHttpRequest, getUrl(), etc. +* Fully Qualify Imports: Do *not* use wildcard-imports such as ``import foo.*;`` -See http://source.android.com/source/code-style.html - -### XML Eclipse Settings -* XML Maximum line width 999 -* XML: Split multiple attributes each on a new line (Eclipse: Properties -> XML -> XML Files -> Editor) -* XML: Indent using spaces with Indention size 4 (Eclipse: Properties -> XML -> XML Files -> Editor) - -See http://www.androidpolice.com/2009/11/04/auto-formatting-android-xml-files-with-eclipse/ +The full coding style can be found at http://source.android.com/source/code-style.html ### Automated syntax check with CheckStyle From c8b77171051a4b7403c3a62e526798a40e250898 Mon Sep 17 00:00:00 2001 From: uberspot Date: Wed, 26 Mar 2014 14:45:56 +0200 Subject: [PATCH 142/253] fix capability icons and remove red text for revoked keys --- .../ui/adapter/ViewKeyKeysAdapter.java | 4 --- .../main/res/layout/view_key_keys_item.xml | 33 ++++++++++--------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java index ed4113fb8..9d60c1530 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/adapter/ViewKeyKeysAdapter.java @@ -131,10 +131,6 @@ public class ViewKeyKeysAdapter extends CursorAdapter { if (cursor.getInt(mIndexRevokedKey) > 0) { revokedKeyIcon.setVisibility(View.VISIBLE); - keyId.setTextColor(Color.RED); - keyDetails.setTextColor(Color.RED); - keyExpiry.setTextColor(Color.RED); - valid = false; } else { keyId.setTextColor(mDefaultTextColor); diff --git a/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml b/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml index 13813f7e7..aecedc39b 100644 --- a/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml +++ b/OpenPGP-Keychain/src/main/res/layout/view_key_keys_item.xml @@ -14,21 +14,24 @@ android:paddingRight="6dip" android:src="@drawable/key_small" /> - - + + - + From ec3459733180a5d83805b6fe9ddf5571f7b0380f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 14:11:28 +0100 Subject: [PATCH 143/253] Make PendingIntents with FLAG_CANCEL_CURRENT and FLAG_ONE_SHOT --- OpenPGP-Keychain/src/main/AndroidManifest.xml | 6 +- .../keychain/remote/OpenPgpService.java | 29 +++---- .../keychain/remote/RemoteService.java | 24 +++--- .../remote/ui/AppSettingsFragment.java | 75 +++++++++---------- .../remote/ui/RemoteServiceActivity.java | 1 + 5 files changed, 67 insertions(+), 68 deletions(-) diff --git a/OpenPGP-Keychain/src/main/AndroidManifest.xml b/OpenPGP-Keychain/src/main/AndroidManifest.xml index 142174749..e6a082743 100644 --- a/OpenPGP-Keychain/src/main/AndroidManifest.xml +++ b/OpenPGP-Keychain/src/main/AndroidManifest.xml @@ -384,9 +384,9 @@ - - + android:label="@string/app_name" + android:launchMode="singleTop" + android:process=":remote_api" /> allowedPkgs = ProviderHelper.getRegisteredApiApps(this); Log.d(Constants.TAG, "allowed: " + allowedPkgs); @@ -246,6 +247,7 @@ public abstract class RemoteService extends Service { } } + Log.d(Constants.TAG, "Package is NOT allowed! packageName: " + packageName); return false; } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java index 5a6151d88..a6db02708 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsFragment.java @@ -55,9 +55,43 @@ public class AppSettingsFragment extends Fragment { public void setAppSettings(AppSettings appSettings) { this.mAppSettings = appSettings; - setPackage(appSettings.getPackageName()); + updateView(appSettings); + } + + /** + * Inflate the layout for this fragment + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false); + mAppNameView = (TextView) view.findViewById(R.id.api_app_settings_app_name); + mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon); + mPackageName = (TextView) view.findViewById(R.id.api_app_settings_package_name); + mPackageSignature = (TextView) view.findViewById(R.id.api_app_settings_package_signature); + return view; + } + + private void updateView(AppSettings appSettings) { + // get application name and icon from package manager + String appName; + Drawable appIcon = null; + PackageManager pm = getActivity().getApplicationContext().getPackageManager(); + try { + ApplicationInfo ai = pm.getApplicationInfo(appSettings.getPackageName(), 0); + + appName = (String) pm.getApplicationLabel(ai); + appIcon = pm.getApplicationIcon(ai); + } catch (NameNotFoundException e) { + // fallback + appName = appSettings.getPackageName(); + } + mAppNameView.setText(appName); + mAppIconView.setImageDrawable(appIcon); + + // advanced info: package name mPackageName.setText(appSettings.getPackageName()); + // advanced info: package signature SHA-256 try { MessageDigest md = MessageDigest.getInstance("SHA-256"); md.update(appSettings.getPackageSignature()); @@ -68,45 +102,6 @@ public class AppSettingsFragment extends Fragment { } catch (NoSuchAlgorithmException e) { Log.e(Constants.TAG, "Should not happen!", e); } - - } - - /** - * Inflate the layout for this fragment - */ - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View view = inflater.inflate(R.layout.api_app_settings_fragment, container, false); - initView(view); - return view; - } - - - private void initView(View view) { - mAppNameView = (TextView) view.findViewById(R.id.api_app_settings_app_name); - mAppIconView = (ImageView) view.findViewById(R.id.api_app_settings_app_icon); - - mPackageName = (TextView) view.findViewById(R.id.api_app_settings_package_name); - mPackageSignature = (TextView) view.findViewById(R.id.api_app_settings_package_signature); - } - - private void setPackage(String packageName) { - PackageManager pm = getActivity().getApplicationContext().getPackageManager(); - - // get application name and icon from package manager - String appName; - Drawable appIcon = null; - try { - ApplicationInfo ai = pm.getApplicationInfo(packageName, 0); - - appName = (String) pm.getApplicationLabel(ai); - appIcon = pm.getApplicationIcon(ai); - } catch (NameNotFoundException e) { - // fallback - appName = packageName; - } - mAppNameView.setText(appName); - mAppIconView.setImageDrawable(appIcon); } diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java index d3ac5cade..ba0d4d088 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java @@ -95,6 +95,7 @@ public class RemoteServiceActivity extends ActionBarActivity { if (ACTION_REGISTER.equals(action)) { final String packageName = extras.getString(EXTRA_PACKAGE_NAME); final byte[] packageSignature = extras.getByteArray(EXTRA_PACKAGE_SIGNATURE); + Log.d(Constants.TAG, "ACTION_REGISTER packageName: "+packageName); // Inflate a "Done"/"Cancel" custom action bar view ActionBarHelper.setTwoButtonView(getSupportActionBar(), From 65857d2b5fea5cbb5d156b1b4ddfd68ded311624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 17:43:26 +0100 Subject: [PATCH 144/253] Accounts list prettified --- .../remote/ui/AccountsListFragment.java | 27 ++++++++++++++++++ .../remote/ui/AppSettingsActivity.java | 1 - .../res/drawable-hdpi/ic_action_person.png | Bin 0 -> 573 bytes .../res/drawable-mdpi/ic_action_person.png | Bin 0 -> 468 bytes .../res/drawable-xhdpi/ic_action_person.png | Bin 0 -> 781 bytes .../res/drawable-xxhdpi/ic_action_person.png | Bin 0 -> 1004 bytes .../layout/api_accounts_adapter_list_item.xml | 15 ++++++++-- .../res/layout/api_app_settings_activity.xml | 6 ++-- 8 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 OpenPGP-Keychain/src/main/res/drawable-hdpi/ic_action_person.png create mode 100644 OpenPGP-Keychain/src/main/res/drawable-mdpi/ic_action_person.png create mode 100644 OpenPGP-Keychain/src/main/res/drawable-xhdpi/ic_action_person.png create mode 100644 OpenPGP-Keychain/src/main/res/drawable-xxhdpi/ic_action_person.png diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java index 22ee7db76..8e65a2f04 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AccountsListFragment.java @@ -32,11 +32,15 @@ import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; +import android.widget.LinearLayout; +import android.widget.ListView; import android.widget.TextView; import org.sufficientlysecure.keychain.Constants; +import org.sufficientlysecure.keychain.Id; import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.provider.KeychainContract; +import org.sufficientlysecure.keychain.ui.widget.FixedListView; import org.sufficientlysecure.keychain.util.Log; public class AccountsListFragment extends ListFragment implements @@ -63,6 +67,29 @@ public class AccountsListFragment extends ListFragment implements return frag; } + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View layout = super.onCreateView(inflater, container, + savedInstanceState); + ListView lv = (ListView) layout.findViewById(android.R.id.list); + ViewGroup parent = (ViewGroup) lv.getParent(); + + /* + * http://stackoverflow.com/a/15880684 + * Remove ListView and add FixedListView in its place. + * This is done here programatically to be still able to use the progressBar of ListFragment. + * + * We want FixedListView to be able to put this ListFragment inside a ScrollView + */ + int lvIndex = parent.indexOfChild(lv); + parent.removeViewAt(lvIndex); + FixedListView newLv = new FixedListView(getActivity()); + newLv.setId(android.R.id.list); + parent.addView(newLv, lvIndex, lv.getLayoutParams()); + return layout; + } + @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java index e4b943734..9e0ba49eb 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/AppSettingsActivity.java @@ -103,7 +103,6 @@ public class AppSettingsActivity extends ActionBarActivity { } setTitle(appName); - Uri accountsUri = appUri.buildUpon().appendPath(KeychainContract.PATH_ACCOUNTS).build(); Log.d(Constants.TAG, "accountsUri: " + accountsUri); startListFragment(savedInstanceState, accountsUri); diff --git a/OpenPGP-Keychain/src/main/res/drawable-hdpi/ic_action_person.png b/OpenPGP-Keychain/src/main/res/drawable-hdpi/ic_action_person.png new file mode 100644 index 0000000000000000000000000000000000000000..9fd81097bad0bafa8ba6f419b6b0ebc4652977ec GIT binary patch literal 573 zcmV-D0>b@?P)NoT2edg zONyxl3wC-I3zq*eJM1ia7{pYQVMxG2GF5{Z0>gRBU2?N*Wv}n{gK$rB)3j;dJE!mK zoK{PrP$(3NKa6%6jmB}@$K`b6@oSoer@)0iA zsBq6uGSmnn-hKP-2$8=*9XotxN9e5A>%A-%i_h8w(6qVBnKF4eBoR*#@m-Wd;5h(; zg5>!~415R#9)I7?_e5X`CDc~sBVYt~LsN%rzt{wkcfTXV}+}43d zr*qULyC0F)ahdl+)>gFwtdGI|ov{U&;MvIG9JyWlA|FrWy9UO5F9)gNp5*O&{Vi=& z??yppKsK6Bt@~|unND0z-rDL(6YTVG!&X@G&o;!6bq*=u@ONe)KlbfgGSC<@X1^Uu zR?r0G=Mw#jM@k4u$lF9rj3sRs>Kw#pn&hQMfaT!z+@8{3&T=fcVT*p|n~&Qb2#7Dris>9xWx!CCsDuqI!P$(2q_z_?L%;tYYNJMwr00000 LNkvXXu0mjfvIqx? literal 0 HcmV?d00001 diff --git a/OpenPGP-Keychain/src/main/res/drawable-mdpi/ic_action_person.png b/OpenPGP-Keychain/src/main/res/drawable-mdpi/ic_action_person.png new file mode 100644 index 0000000000000000000000000000000000000000..359da1c124a16fb2c03c9c6cdd83a9ad5a24d680 GIT binary patch literal 468 zcmV;_0W1EAP)`CV+)Vb#KtJ8a}`Y#Ik65D0aX_ApQmoR#1Vn7f*sMhN`_twOjzo z)4){C22EJnK=xap*c0?}0w^90Eg^RT9eI@&B_XI3w1jd&#qM#aZ$Uxs3@wO`11(=b zv$_E22#^DOk?H{kr21ep(DLInuZ)liCr&8$JJ90CG^-7v^?);4@PjZc9mqiA-T=x6 zg~DE-qrOq>0H{whu>?6G0E&|7ghGay2%i^|VL7ZM1m&3H_-aN%C1DtumLpfsVPu5> z)b}Y=C~akdp|B5ED6kN4{VfLS0AXl(G5Ek}vwYM61MUET00RKPq-2upj;8Sd0000< KMNUMnLSTaC)V22j literal 0 HcmV?d00001 diff --git a/OpenPGP-Keychain/src/main/res/drawable-xhdpi/ic_action_person.png b/OpenPGP-Keychain/src/main/res/drawable-xhdpi/ic_action_person.png new file mode 100644 index 0000000000000000000000000000000000000000..03eeb8d6a6c13aa38f3e6f26ed12d3a54bfd4976 GIT binary patch literal 781 zcmV+o1M>WdP)gB*aA7yNAs z@(l9CMTW~@cm*D(ApVM`mIpk_7Xrd zbZl^6J=X07x;@|^$Bv%{Wg5d;0vZglOT%UYY77L_Y$l-2KtSDQ0@e;ffad-p2Aw0D z3E)U!wT`i=S-DXn?Zw#|Xz14?iN~;(fRJ*3HGF-AbT+fnbE35?avy4e#HU81GRr#Z#KGNcw0(SJO4Y4m?igyo#&8xM#f5 zGO3f-Bx?x|V0_<{jvQ5CoXH?Vkp#)PG2!;C1Smw{dhVnk0uAqj;R6o6*l&jDo&ddK zH}UEdgB;WM00IsVpjr0m7{k-xX_X*`Vg~%SO&caIhtPy~xS9AMV~hxs!`d>K~! z2QneibiEyA;zxA&NbSzS>Fqh+_xt@;nx<z{i!L~JEPmcoutchQPBIEAT53YB4|0W_dA$q! z;vBK6h%`;Nwlt(5{SRGKEU>ZJ+M^|nelY{2^MJ*f>@(()m0O8#g`ehRz6(hKMC*cR zg#V_I{zQL{3tlpKX(^#y^XVM6-0?%szdzCF|0St`isD=z>2XPnzPL6~;%Fz%S@SKQ z>3mD+YgbbpeSryw^_tqZvTT|wtS6@UF{29cRT*$%f{jwjz82o`tjOM^t>ACZynn|t zX{qI!HSQ0vQPJGo{^5vSz8)mL@2Qmmnb{B(tl!sQ6RaV#1zGcyjq6 zZO7uEU6nC3Eo0Sz+%00000000000000000000037m1 afB^t`*axuJxE1OE0000 + android:gravity="center_vertical" + android:singleLine="true" + android:orientation="horizontal"> + + @@ -27,9 +27,7 @@ android:id="@+id/api_accounts_list_fragment" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" - android:paddingLeft="4dp" - android:paddingRight="4dp" /> + android:orientation="vertical" /> From ba37fc254d53d27403226771060a2c235a264c43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 17:50:16 +0100 Subject: [PATCH 145/253] Accounts activity prettified --- .../layout/api_account_settings_fragment.xml | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml b/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml index de58b6d19..32843eb29 100644 --- a/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml +++ b/OpenPGP-Keychain/src/main/res/layout/api_account_settings_fragment.xml @@ -7,14 +7,34 @@ android:layout_height="wrap_content" android:orientation="vertical"> - + + + + + + Date: Wed, 26 Mar 2014 17:55:48 +0100 Subject: [PATCH 146/253] Fix provider for API_ACCOUNTS --- .../keychain/provider/KeychainContract.java | 2 +- .../keychain/provider/KeychainDatabase.java | 6 +++--- .../keychain/provider/KeychainProvider.java | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java index 6e4899fc2..e7b31bf65 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainContract.java @@ -65,7 +65,7 @@ public class KeychainContract { String ENCRYPTION_ALGORITHM = "encryption_algorithm"; String HASH_ALORITHM = "hash_algorithm"; String COMPRESSION = "compression"; - String PACKAGE_NAME_FK = "package_name"; // foreign key to api_apps.package_name + String PACKAGE_NAME = "package_name"; // foreign key to api_apps.package_name } public static final class KeyTypes { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java index 4abcec435..8c33844b2 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainDatabase.java @@ -88,10 +88,10 @@ public class KeychainDatabase extends SQLiteOpenHelper { + ApiAppsAccountsColumns.ENCRYPTION_ALGORITHM + " INTEGER, " + ApiAppsAccountsColumns.HASH_ALORITHM + " INTEGER, " + ApiAppsAccountsColumns.COMPRESSION + " INTEGER, " - + ApiAppsAccountsColumns.PACKAGE_NAME_FK + " TEXT NOT NULL, " + + ApiAppsAccountsColumns.PACKAGE_NAME + " TEXT NOT NULL, " + "UNIQUE(" + ApiAppsAccountsColumns.ACCOUNT_NAME + ", " - + ApiAppsAccountsColumns.PACKAGE_NAME_FK + "), " - + "FOREIGN KEY(" + ApiAppsAccountsColumns.PACKAGE_NAME_FK + ") REFERENCES " + + ApiAppsAccountsColumns.PACKAGE_NAME + "), " + + "FOREIGN KEY(" + ApiAppsAccountsColumns.PACKAGE_NAME + ") REFERENCES " + Tables.API_APPS + "(" + ApiAppsColumns.PACKAGE_NAME + ") ON DELETE CASCADE)"; KeychainDatabase(Context context) { diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 6469da978..1c5e3ab36 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -687,13 +687,13 @@ public class KeychainProvider extends ContentProvider { break; case API_ACCOUNTS: qb.setTables(Tables.API_ACCOUNTS); + qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = "); + qb.appendWhereEscapeString(uri.getPathSegments().get(1)); break; case API_ACCOUNTS_BY_ACCOUNT_NAME: - qb.setTables(Tables.API_ACCOUNTS + " INNER JOIN " + Tables.API_APPS + " ON " + "(" - + Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = " + Tables.API_ACCOUNTS + "." - + ApiAccounts.PACKAGE_NAME_FK + " )"); - qb.appendWhere(Tables.API_APPS + "." + ApiApps.PACKAGE_NAME + " = "); + qb.setTables(Tables.API_ACCOUNTS); + qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = "); qb.appendWhereEscapeString(uri.getPathSegments().get(1)); qb.appendWhere(" AND " + Tables.API_ACCOUNTS + "." + ApiAccounts.ACCOUNT_NAME + " = "); @@ -800,7 +800,7 @@ public class KeychainProvider extends ContentProvider { // set foreign key automatically based on given uri // e.g., api_apps/com.example.app/accounts/ String packageName = uri.getPathSegments().get(1); - values.put(ApiAccounts.PACKAGE_NAME_FK, packageName); + values.put(ApiAccounts.PACKAGE_NAME, packageName); Log.d(Constants.TAG, "provider packageName: " + packageName); @@ -1061,7 +1061,7 @@ public class KeychainProvider extends ContentProvider { andSelection = " AND (" + selection + ")"; } - return ApiAccounts.PACKAGE_NAME_FK + "=" + packageName + " AND " + return ApiAccounts.PACKAGE_NAME + "=" + packageName + " AND " + ApiAccounts.ACCOUNT_NAME + "=" + accountName + andSelection; } From aba6a44a0a830136577d98f07e01db89f639fdfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 18:13:48 +0100 Subject: [PATCH 147/253] Improve demo for API accounts --- .../keychain/demo/OpenPgpProviderActivity.java | 18 +++++++++++------- .../src/main/res/layout/openpgp_provider.xml | 15 +++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/OpenPgpProviderActivity.java b/OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/OpenPgpProviderActivity.java index a660b1c9a..42722c5e1 100644 --- a/OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/OpenPgpProviderActivity.java +++ b/OpenPGP-Keychain-API/example-app/src/main/java/org/sufficientlysecure/keychain/demo/OpenPgpProviderActivity.java @@ -48,6 +48,7 @@ public class OpenPgpProviderActivity extends Activity { private Button mEncrypt; private Button mSignAndEncrypt; private Button mDecryptAndVerify; + private EditText mAccount; private OpenPgpServiceConnection mServiceConnection; @@ -68,6 +69,7 @@ public class OpenPgpProviderActivity extends Activity { mEncrypt = (Button) findViewById(R.id.crypto_provider_demo_encrypt); mSignAndEncrypt = (Button) findViewById(R.id.crypto_provider_demo_sign_and_encrypt); mDecryptAndVerify = (Button) findViewById(R.id.crypto_provider_demo_decrypt_and_verify); + mAccount = (EditText) findViewById(R.id.crypto_provider_demo_account); mSign.setOnClickListener(new View.OnClickListener() { @Override @@ -142,7 +144,7 @@ public class OpenPgpProviderActivity extends Activity { private InputStream getInputstream(boolean ciphertext) { InputStream is = null; try { - String inputStr = null; + String inputStr; if (ciphertext) { inputStr = mCiphertext.getText().toString(); } else { @@ -213,6 +215,7 @@ public class OpenPgpProviderActivity extends Activity { public void sign(Intent data) { data.setAction(OpenPgpApi.ACTION_SIGN); data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); + data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); InputStream is = getInputstream(false); final ByteArrayOutputStream os = new ByteArrayOutputStream(); @@ -225,6 +228,7 @@ public class OpenPgpProviderActivity extends Activity { data.setAction(OpenPgpApi.ACTION_ENCRYPT); data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mEncryptUserIds.getText().toString().split(",")); data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); + data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); InputStream is = getInputstream(false); final ByteArrayOutputStream os = new ByteArrayOutputStream(); @@ -237,6 +241,7 @@ public class OpenPgpProviderActivity extends Activity { data.setAction(OpenPgpApi.ACTION_SIGN_AND_ENCRYPT); data.putExtra(OpenPgpApi.EXTRA_USER_IDS, mEncryptUserIds.getText().toString().split(",")); data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); + data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); InputStream is = getInputstream(false); final ByteArrayOutputStream os = new ByteArrayOutputStream(); @@ -248,6 +253,7 @@ public class OpenPgpProviderActivity extends Activity { public void decryptAndVerify(Intent data) { data.setAction(OpenPgpApi.ACTION_DECRYPT_VERIFY); data.putExtra(OpenPgpApi.EXTRA_REQUEST_ASCII_ARMOR, true); + data.putExtra(OpenPgpApi.EXTRA_ACCOUNT_NAME, mAccount.getText().toString()); InputStream is = getInputstream(true); final ByteArrayOutputStream os = new ByteArrayOutputStream(); @@ -264,13 +270,11 @@ public class OpenPgpProviderActivity extends Activity { // try again after user interaction if (resultCode == RESULT_OK) { /* - * The data originally given to the pgp method are are again - * returned here to be used when calling again after user interaction. - * - * They also contain results from the user interaction which happened, - * for example selected key ids. + * The data originally given to one of the methods above, is again + * returned here to be used when calling the method again after user + * interaction. The Intent now also contains results from the user + * interaction, for example selected key ids. */ - switch (requestCode) { case REQUEST_CODE_SIGN: { sign(data); diff --git a/OpenPGP-Keychain-API/example-app/src/main/res/layout/openpgp_provider.xml b/OpenPGP-Keychain-API/example-app/src/main/res/layout/openpgp_provider.xml index 6c2ce1a7c..2b8e8016a 100644 --- a/OpenPGP-Keychain-API/example-app/src/main/res/layout/openpgp_provider.xml +++ b/OpenPGP-Keychain-API/example-app/src/main/res/layout/openpgp_provider.xml @@ -46,6 +46,7 @@ android:scrollHorizontally="true" android:scrollbars="vertical" android:text="message" + android:hint="cleartext message" android:textAppearance="@android:style/TextAppearance.Small" /> @@ -66,6 +67,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:text="ciphertext" + android:hint="ciphertext" android:textAppearance="@android:style/TextAppearance.Small" /> @@ -104,5 +106,18 @@ android:layout_height="wrap_content" android:text="Decrypt and Verify" /> + + + + \ No newline at end of file From 399106015e6d4a8d08385f9d5d360fe634e5cc56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 18:18:00 +0100 Subject: [PATCH 148/253] Rename layouts for remote activity --- .../keychain/remote/ui/RemoteServiceActivity.java | 8 ++++---- ..._create_activity.xml => api_remote_create_account.xml} | 0 ...app_error_message.xml => api_remote_error_message.xml} | 0 ..._register_activity.xml => api_remote_register_app.xml} | 0 ...b_keys_activity.xml => api_remote_select_pub_keys.xml} | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename OpenPGP-Keychain/src/main/res/layout/{api_account_create_activity.xml => api_remote_create_account.xml} (100%) rename OpenPGP-Keychain/src/main/res/layout/{api_app_error_message.xml => api_remote_error_message.xml} (100%) rename OpenPGP-Keychain/src/main/res/layout/{api_app_register_activity.xml => api_remote_register_app.xml} (100%) rename OpenPGP-Keychain/src/main/res/layout/{api_app_select_pub_keys_activity.xml => api_remote_select_pub_keys.xml} (100%) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java index ba0d4d088..a894da448 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/remote/ui/RemoteServiceActivity.java @@ -124,7 +124,7 @@ public class RemoteServiceActivity extends ActionBarActivity { } ); - setContentView(R.layout.api_app_register_activity); + setContentView(R.layout.api_remote_register_app); mAppSettingsFragment = (AppSettingsFragment) getSupportFragmentManager().findFragmentById( R.id.api_app_settings_fragment); @@ -169,7 +169,7 @@ public class RemoteServiceActivity extends ActionBarActivity { } ); - setContentView(R.layout.api_account_create_activity); + setContentView(R.layout.api_remote_create_account); mAccSettingsFragment = (AccountSettingsFragment) getSupportFragmentManager().findFragmentById( R.id.api_account_settings_fragment); @@ -235,7 +235,7 @@ public class RemoteServiceActivity extends ActionBarActivity { } ); - setContentView(R.layout.api_app_select_pub_keys_activity); + setContentView(R.layout.api_remote_select_pub_keys); // set text on view HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_select_pub_keys_text); @@ -277,7 +277,7 @@ public class RemoteServiceActivity extends ActionBarActivity { } }); - setContentView(R.layout.api_app_error_message); + setContentView(R.layout.api_remote_error_message); // set text on view HtmlTextView textView = (HtmlTextView) findViewById(R.id.api_app_error_message_text); diff --git a/OpenPGP-Keychain/src/main/res/layout/api_account_create_activity.xml b/OpenPGP-Keychain/src/main/res/layout/api_remote_create_account.xml similarity index 100% rename from OpenPGP-Keychain/src/main/res/layout/api_account_create_activity.xml rename to OpenPGP-Keychain/src/main/res/layout/api_remote_create_account.xml diff --git a/OpenPGP-Keychain/src/main/res/layout/api_app_error_message.xml b/OpenPGP-Keychain/src/main/res/layout/api_remote_error_message.xml similarity index 100% rename from OpenPGP-Keychain/src/main/res/layout/api_app_error_message.xml rename to OpenPGP-Keychain/src/main/res/layout/api_remote_error_message.xml diff --git a/OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml b/OpenPGP-Keychain/src/main/res/layout/api_remote_register_app.xml similarity index 100% rename from OpenPGP-Keychain/src/main/res/layout/api_app_register_activity.xml rename to OpenPGP-Keychain/src/main/res/layout/api_remote_register_app.xml diff --git a/OpenPGP-Keychain/src/main/res/layout/api_app_select_pub_keys_activity.xml b/OpenPGP-Keychain/src/main/res/layout/api_remote_select_pub_keys.xml similarity index 100% rename from OpenPGP-Keychain/src/main/res/layout/api_app_select_pub_keys_activity.xml rename to OpenPGP-Keychain/src/main/res/layout/api_remote_select_pub_keys.xml From ac134790a0e2fef7a6c5cb44818d58deeaec0b62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 18:23:48 +0100 Subject: [PATCH 149/253] Text for creation of new api accounts --- .../src/main/res/layout/api_remote_create_account.xml | 8 ++++++++ OpenPGP-Keychain/src/main/res/values/strings.xml | 1 + 2 files changed, 9 insertions(+) diff --git a/OpenPGP-Keychain/src/main/res/layout/api_remote_create_account.xml b/OpenPGP-Keychain/src/main/res/layout/api_remote_create_account.xml index ead336dbb..3aee9094f 100644 --- a/OpenPGP-Keychain/src/main/res/layout/api_remote_create_account.xml +++ b/OpenPGP-Keychain/src/main/res/layout/api_remote_create_account.xml @@ -10,6 +10,14 @@ android:padding="16dp" android:orientation="vertical"> + + SHA-256 of Package Signature
    Accounts No accounts attached to this application. + The application requests the creation of a new account. Please select an existing private key or create a new one.\nApplications are restricted to the usage of keys you select here! The displayed application requests access to OpenKeychain.\nAllow access?\n\nWARNING: If you do not know why this screen appeared, disallow access! You can revoke access later using the \'Registered Applications\' screen. Allow access Disallow access From cc8fd35137d1c8e78727a76a5cceb5f92b234a0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 18:34:48 +0100 Subject: [PATCH 150/253] Show divider only when used --- .../org/sufficientlysecure/keychain/ui/KeyListFragment.java | 6 ++++++ OpenPGP-Keychain/src/main/res/layout/key_list_item.xml | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java index daf455c03..957c822d2 100644 --- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java +++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/ui/KeyListFragment.java @@ -498,11 +498,15 @@ public class KeyListFragment extends Fragment } { // set edit button and revoked info, specific by key type + View statusDivider = (View) view.findViewById(R.id.status_divider); + FrameLayout statusLayout = (FrameLayout) view.findViewById(R.id.status_layout); Button button = (Button) view.findViewById(R.id.edit); TextView revoked = (TextView) view.findViewById(R.id.revoked); if (cursor.getInt(KeyListFragment.INDEX_TYPE) == KeyTypes.SECRET) { // this is a secret key - show the edit button + statusDivider.setVisibility(View.VISIBLE); + statusLayout.setVisibility(View.VISIBLE); revoked.setVisibility(View.GONE); button.setVisibility(View.VISIBLE); @@ -519,9 +523,11 @@ public class KeyListFragment extends Fragment }); } else { // this is a public key - hide the edit button, show if it's revoked + statusDivider.setVisibility(View.GONE); button.setVisibility(View.GONE); boolean isRevoked = cursor.getInt(INDEX_IS_REVOKED) > 0; + statusLayout.setVisibility(isRevoked ? View.VISIBLE : View.GONE); revoked.setVisibility(isRevoked ? View.VISIBLE : View.GONE); } } diff --git a/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml b/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml index bee56ddfe..0abae8bbb 100644 --- a/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml +++ b/OpenPGP-Keychain/src/main/res/layout/key_list_item.xml @@ -39,6 +39,7 @@ @@ -55,7 +57,6 @@ android:layout_height="match_parent" android:id="@+id/edit" android:focusable="false" - android:visibility="visible" android:enabled="true" android:textAppearance="?android:attr/textAppearanceSmall" android:textColor="@color/black" @@ -71,7 +72,6 @@ android:textAppearance="?android:attr/textAppearanceSmall" android:text="@string/revoked" android:textColor="#e00" - android:visibility="visible" android:layout_gravity="center" /> From 19bbaecf32e7c2f7a99bf0bafd4d78f6094c788b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 22:20:23 +0100 Subject: [PATCH 151/253] Update from transifex --- .../src/main/res/raw-cs-rCZ/help_about.html | 4 +- .../src/main/res/raw-de/help_about.html | 2 +- .../src/main/res/raw-es/help_start.html | 2 +- .../src/main/res/raw-et/help_about.html | 48 +++++++ .../src/main/res/raw-et/help_changelog.html | 108 +++++++++++++++ .../src/main/res/raw-et/help_nfc_beam.html | 12 ++ .../src/main/res/raw-et/help_start.html | 19 +++ .../src/main/res/raw-et/nfc_beam_share.html | 11 ++ .../src/main/res/raw-fa-rIR/help_start.html | 2 +- .../src/main/res/raw-fr/help_about.html | 2 +- .../src/main/res/raw-fr/help_start.html | 2 +- .../src/main/res/raw-it-rIT/help_about.html | 2 +- .../src/main/res/raw-it-rIT/help_start.html | 2 +- .../src/main/res/raw-ja/help_start.html | 2 +- .../src/main/res/raw-pl/help_about.html | 2 +- .../src/main/res/raw-pl/help_start.html | 2 +- .../src/main/res/raw-ru/help_about.html | 2 +- .../src/main/res/raw-ru/help_start.html | 2 +- .../src/main/res/raw-tr/help_about.html | 2 +- .../src/main/res/raw-uk/help_about.html | 12 +- .../src/main/res/raw-uk/help_start.html | 2 +- .../src/main/res/raw-zh/help_start.html | 2 +- .../src/main/res/values-cs-rCZ/strings.xml | 22 ++++ .../src/main/res/values-es/strings.xml | 7 + .../src/main/res/values-et/strings.xml | 123 ++++++++++++++++++ .../src/main/res/values-fr/strings.xml | 7 + .../src/main/res/values-it-rIT/strings.xml | 3 + .../src/main/res/values-ja/strings.xml | 3 + .../src/main/res/values-pl/strings.xml | 7 + .../src/main/res/values-ru/strings.xml | 22 ++++ .../src/main/res/values-uk/strings.xml | 7 + 31 files changed, 422 insertions(+), 23 deletions(-) create mode 100644 OpenPGP-Keychain/src/main/res/raw-et/help_about.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-et/help_changelog.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-et/help_nfc_beam.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-et/help_start.html create mode 100644 OpenPGP-Keychain/src/main/res/raw-et/nfc_beam_share.html create mode 100644 OpenPGP-Keychain/src/main/res/values-et/strings.xml diff --git a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html index d9150a5e8..066e53a89 100644 --- a/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-cs-rCZ/help_about.html @@ -3,11 +3,11 @@

    http://www.openkeychain.org

    OpenKeychain is an OpenPGP implementation for Android.

    -

    License: GPLv3+

    +

    Licence: GPLv3+

    Developers OpenKeychain

      -
    • Dominik Schürmann (Lead developer)
    • +
    • Dominik Schürmann (Hlavní vývojář)
    • Ash Hughes (crypto patches)
    • Brian C. Barnes
    • Bahtiar 'kalkin' Gadimov (UI)
    • diff --git a/OpenPGP-Keychain/src/main/res/raw-de/help_about.html b/OpenPGP-Keychain/src/main/res/raw-de/help_about.html index 676c2bdd2..9115ba9c5 100644 --- a/OpenPGP-Keychain/src/main/res/raw-de/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-de/help_about.html @@ -21,7 +21,7 @@

    Entwickler APG 1.x

      -
    • Thialfihar (Leitender Entwickler)
    • +
    • Thialfihar (Lead developer)
    • 'Senecaso' (QR-Code, Schlüssel signtieren, Schlüssel hochladen)
    • Markus Doits
    diff --git a/OpenPGP-Keychain/src/main/res/raw-es/help_start.html b/OpenPGP-Keychain/src/main/res/raw-es/help_start.html index 895b52469..d56399ef0 100644 --- a/OpenPGP-Keychain/src/main/res/raw-es/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-es/help_start.html @@ -2,7 +2,7 @@

    Primeros pasos

    -

    Primero necesitas un par de claves personales. Crea una a través del menú "Contactos" o importa un par de claves ya existentes a través de "Importar claves". Después, puedes descargar las claves de tus amigos o intercambiarlas a través de códigos QR o NFC.

    +

    Primero necesitas un par de claves personales. Crea una a través de las opciones del menú "Contactos" o importa un par de claves ya existentes a través de "Importar claves". Después, puedes descargar las claves de tus amigos o intercambiarlas a través de códigos QR o NFC.

    Es recomendable que instales OI File Manager para una mejor selección de archivos y Barcode Scanner para escanear los códigos QR generados. Pulsando en los enlaces se abrirá Google Play o F-Droid.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-et/help_about.html b/OpenPGP-Keychain/src/main/res/raw-et/help_about.html new file mode 100644 index 000000000..d9150a5e8 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-et/help_about.html @@ -0,0 +1,48 @@ + + + +

    http://www.openkeychain.org

    +

    OpenKeychain is an OpenPGP implementation for Android.

    +

    License: GPLv3+

    + +

    Developers OpenKeychain

    +
      +
    • Dominik Schürmann (Lead developer)
    • +
    • Ash Hughes (crypto patches)
    • +
    • Brian C. Barnes
    • +
    • Bahtiar 'kalkin' Gadimov (UI)
    • +
    • Daniel Hammann
    • +
    • Daniel Haß
    • +
    • Greg Witczak
    • +
    • Miroojin Bakshi
    • +
    • Paul Sarbinowski
    • +
    • Vincent Breitmoser
    • + +
    +

    Developers APG 1.x

    +
      +
    • Thialfihar (Lead developer)
    • +
    • 'Senecaso' (QRCode, sign key, upload key)
    • +
    • Markus Doits
    • +
    +

    Libraries

    + + + diff --git a/OpenPGP-Keychain/src/main/res/raw-et/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-et/help_changelog.html new file mode 100644 index 000000000..abf660ba8 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-et/help_changelog.html @@ -0,0 +1,108 @@ + + + +

    2.3

    +
      +
    • remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)
    • +
    • fix setting expiry dates on keys (thanks to Ash Hughes)
    • +
    • more internal fixes when editing keys (thanks to Ash Hughes)
    • +
    • querying keyservers directly from the import screen
    • +
    • fix layout and dialog style on Android 2.2-3.0
    • +
    • fix crash on keys with empty user ids
    • +
    • fix crash and empty lists when coming back from signing screen
    • +
    • Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source
    • +
    • fix upload of key from signing screen
    • +
    +

    2.2

    +
      +
    • new design with navigation drawer
    • +
    • new public key list design
    • +
    • new public key view
    • +
    • bug fixes for importing of keys
    • +
    • key cross-certification (thanks to Ash Hughes)
    • +
    • handle UTF-8 passwords properly (thanks to Ash Hughes)
    • +
    • first version with new languages (thanks to the contributors on Transifex)
    • +
    • sharing of keys via QR Codes fixed and improved
    • +
    • package signature verification for API
    • +
    +

    2.1.1

    +
      +
    • API Updates, preparation for K-9 Mail integration
    • +
    +

    2.1

    +
      +
    • lots of bug fixes
    • +
    • new API for developers
    • +
    • PRNG bug fix by Google
    • +
    +

    2.0

    +
      +
    • complete redesign
    • +
    • share public keys via qr codes, nfc beam
    • +
    • sign keys
    • +
    • upload keys to server
    • +
    • fixes import issues
    • +
    • new AIDL API
    • +
    +

    1.0.8

    +
      +
    • basic keyserver support
    • +
    • app2sd
    • +
    • more choices for pass phrase cache: 1, 2, 4, 8, hours
    • +
    • translations: Norwegian (thanks, Sander Danielsen), Chinese (thanks, Zhang Fredrick)
    • +
    • bugfixes
    • +
    • optimizations
    • +
    +

    1.0.7

    +
      +
    • fixed problem with signature verification of texts with trailing newline
    • +
    • more options for pass phrase cache time to live (20, 40, 60 mins)
    • +
    +

    1.0.6

    +
      +
    • account adding crash on Froyo fixed
    • +
    • secure file deletion
    • +
    • option to delete key file after import
    • +
    • stream encryption/decryption (gallery, etc.)
    • +
    • new options (language, force v3 signatures)
    • +
    • interface changes
    • +
    • bugfixes
    • +
    +

    1.0.5

    +
      +
    • German and Italian translation
    • +
    • much smaller package, due to reduced BC sources
    • +
    • new preferences GUI
    • +
    • layout adjustment for localization
    • +
    • signature bugfix
    • +
    +

    1.0.4

    +
      +
    • fixed another crash caused by some SDK bug with query builder
    • +
    +

    1.0.3

    +
      +
    • fixed crashes during encryption/signing and possibly key export
    • +
    +

    1.0.2

    +
      +
    • filterable key lists
    • +
    • smarter pre-selection of encryption keys
    • +
    • new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
    • +
    • fixes and additional features (key preselection) for K-9 Mail, new beta build available
    • +
    +

    1.0.1

    +
      +
    • GMail account listing was broken in 1.0.0, fixed again
    • +
    +

    1.0.0

    +
      +
    • K-9 Mail integration, APG supporting beta build of K-9 Mail
    • +
    • support of more file managers (including ASTRO)
    • +
    • Slovenian translation
    • +
    • new database, much faster, less memory usage
    • +
    • defined Intents and content provider for other apps
    • +
    • bugfixes
    • +
    + + diff --git a/OpenPGP-Keychain/src/main/res/raw-et/help_nfc_beam.html b/OpenPGP-Keychain/src/main/res/raw-et/help_nfc_beam.html new file mode 100644 index 000000000..88492731c --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-et/help_nfc_beam.html @@ -0,0 +1,12 @@ + + + +

    How to receive keys

    +
      +
    1. Go to your partners contacts and open the contact you want to share.
    2. +
    3. Hold the two devices back to back (they have to be almost touching) and you’ll feel a vibration.
    4. +
    5. After it vibrates you’ll see the content on your partners device turn into a card-like object with Star Trek warp speed-looking animation in the background.
    6. +
    7. Tap the card and the content will then load on the your device.
    8. +
    + + diff --git a/OpenPGP-Keychain/src/main/res/raw-et/help_start.html b/OpenPGP-Keychain/src/main/res/raw-et/help_start.html new file mode 100644 index 000000000..0e60c17a7 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-et/help_start.html @@ -0,0 +1,19 @@ + + + +

    Getting started

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    + +

    It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

    + +

    I found a bug in OpenKeychain!

    +

    Please report the bug using the issue tracker of OpenKeychain.

    + +

    Contribute

    +

    If you want to help us developing OpenKeychain by contributing code follow our small guide on Github.

    + +

    Translations

    +

    Help translating OpenKeychain! Everybody can participate at OpenKeychain on Transifex.

    + + + diff --git a/OpenPGP-Keychain/src/main/res/raw-et/nfc_beam_share.html b/OpenPGP-Keychain/src/main/res/raw-et/nfc_beam_share.html new file mode 100644 index 000000000..083e055c7 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/raw-et/nfc_beam_share.html @@ -0,0 +1,11 @@ + + + +
      +
    1. Make sure that NFC is turned on in Settings > More > NFC and make sure that Android Beam is also on in the same section.
    2. +
    3. Hold the two devices back to back (they have to be almost touching) and you'll feel a vibration.
    4. +
    5. After it vibrates you'll see the content on your device turn into a card-like object with Star Trek warp speed-looking animation in the background.
    6. +
    7. Tap the card and the content will then load on the other person’s device.
    8. +
    + + diff --git a/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_start.html b/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_start.html index f8c255232..93a305796 100644 --- a/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-fa-rIR/help_start.html @@ -2,7 +2,7 @@

    شروع کار

    -

    اول شما نیاز به یک جفت کلید شخصی دارید. از طریق منوها در "کلیدهای من" بسازید و یا از طریق"واردات کلیدهای" جفت کلیدهای موجود را وارد کنید. پس از آن، شما می توانید کلید های دوستان خود را دانلود کنید و یا آنها را از طریق کدهای QR یا NFC رد و بدل کنید.

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html b/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html index 43094ce9d..afafe0cef 100644 --- a/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-fr/help_about.html @@ -21,7 +21,7 @@

    Les développeurs d'APG 1.x

      -
    • Thialfihar (développeur principal)
    • +
    • Thialfihar (développeur principal)
    • « Senecaso » (Code QR, signer/téléverser la clef)
    • Markus Doits
    diff --git a/OpenPGP-Keychain/src/main/res/raw-fr/help_start.html b/OpenPGP-Keychain/src/main/res/raw-fr/help_start.html index 962c33d86..ddaac44b1 100644 --- a/OpenPGP-Keychain/src/main/res/raw-fr/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-fr/help_start.html @@ -2,7 +2,7 @@

    Commencer

    -

    Vous avez d'abord besoin d'une paire de clefs personelles. Créez-en une avec l'option du menu « Contacts » ou importez des paires de clefs existantes avec « Importer des clefs ». Ensuite vous pouvez télécharger les clefs de vos amis, ou les échanger par codes QR ou NFC.

    +

    Il vous faut d'abord une paire de clefs personnelles. Créez-en une avec le menu des options dans « Contacts » ou importez des paires de clefs existantes avec « Importer des clefs ». Ensuite vous pouvez télécharger les clefs de vos amis, ou les échanger par codes QR ou NFC.

    Il vous est recommendé d'installer le gestionnaire de fichiers OI pour sa fonction améliorée de séléction des fichiers et le lecteur de codes à barres pour balayer les codes QR générés. Cliquer sur les liens ouvrira Google Play Store ou F-Droid pour l'installation.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html index 2d17e1882..10f4bba90 100644 --- a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_about.html @@ -21,7 +21,7 @@

    Sviluppatori APG 1.x

      -
    • Thialfihar (Capo Sviluppatore)
    • +
    • Thialfihar (Lead developer)
    • 'Senecaso' (QRCode, firma chiavi, caricamento chiavi)
    • Markus Doits
    diff --git a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_start.html b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_start.html index 1a4a7303e..7255d70bb 100644 --- a/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-it-rIT/help_start.html @@ -2,7 +2,7 @@

    Per iniziare

    -

    Per prima cosa hai bisogno di un paio di chiavi personali. Creane una tramite i menu di opzione sotto 'Contatti' o importane di esistenti attraverso "Importa Chiavi". Dopodiche' puoi scaricare le chiavi dei tuoi amici o scambiarle tramite Codici QR o NFC.

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    Si raccomanda di installare OI File Manager per una migliore selezione dei file e Barcode Scanner per scansionare i codici QR. I collegamenti verranno aperti in Google Play Store o F-Droid per l'installazione.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-ja/help_start.html b/OpenPGP-Keychain/src/main/res/raw-ja/help_start.html index 04ad31352..0a92042b1 100644 --- a/OpenPGP-Keychain/src/main/res/raw-ja/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-ja/help_start.html @@ -2,7 +2,7 @@

    入門

    -

    最初にあなたの個人用鍵ペアが必要になります。オプションメニューの"連絡先"で生成するか、"鍵のインポート"から既存の鍵ペアをインポートします。その後、あなたの友人の鍵をダウンロード、もしくはQRコードやNFCで交換します。

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    ファイルの選択を拡張するにはOI File ManagerBarcode Scannerを生成したQRコードのスキャンのため、それぞれのインストールを必要とします。 リンクをクリックして、Google Play Store上かF-Droidからインストールしてください。

    diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html b/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html index c41859b60..04e2a727c 100644 --- a/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-pl/help_about.html @@ -21,7 +21,7 @@

    Deweloperzy APG 1.x

      -
    • Thialfihar (Wiodacy deweloper)
    • +
    • Thialfihar (Wiodący deweloper)
    • 'Senecaso' (kody QR, podpisy kluczy, wysyłanie kluczy)
    • Markus Doits
    diff --git a/OpenPGP-Keychain/src/main/res/raw-pl/help_start.html b/OpenPGP-Keychain/src/main/res/raw-pl/help_start.html index d2faa2db9..e88a1ad6d 100644 --- a/OpenPGP-Keychain/src/main/res/raw-pl/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-pl/help_start.html @@ -2,7 +2,7 @@

    Pierwsze kroki

    -

    Po pierwsze potrzebujesz swoją osobistą parę kluczy. Stwórz ją, korzystając z odpowiedniej opcji w sekcji "Kontakty" lub też zaimportuj istniejącą parę korzystając z sekcji "Importuj klucze". Następnie możesz pobrać klucze Twoich znajomych lub wymieniać się z nimi za pośrednictwem kodów QR lub technologii NFC.

    +

    Po pierwsze potrzebujesz swoją osobistą parę kluczy. Stwórz ją, korzystając z odpowiedniej opcji w sekcji "Kontakty" albo zainportuj istniejącą parę korzystając z sekcji "Inportuj klucze". Następnie możesz porać klucze Twoich znajomych lub wymieniać się z nimi za pośrednictwem kodów QR lub technologii NFC.

    Zalecana jest instalacja menadżera plików OI File Manager w celu zapewnienia wygodniejszego wyboru plików oraz programu Barcode Scanner, który jest w stanie skanować wygenerowane kody QR. Kliknięcie na powyższe linki przekieruje Cię do sklepu Google Play / F-Droid.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html b/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html index 63335110e..0178a8985 100644 --- a/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-ru/help_about.html @@ -21,7 +21,7 @@

    Разработчики APG 1.x

      -
    • Thialfihar (главный разработчик)
    • +
    • Thialfihar (Lead developer)
    • 'Senecaso' (QR коды, подписание и загрузка ключей)
    • Markus Doits
    diff --git a/OpenPGP-Keychain/src/main/res/raw-ru/help_start.html b/OpenPGP-Keychain/src/main/res/raw-ru/help_start.html index bc9dade67..4188f6d5f 100644 --- a/OpenPGP-Keychain/src/main/res/raw-ru/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-ru/help_start.html @@ -2,7 +2,7 @@

    Приступая

    -

    Для начала вам понадобится своя пара ключей. Воспользуйтесь меню в разделе "Контакты", что бы создать новую, или добавьте ранее созданную пару в разделе "Импорт ключей". После этого вы сможете скачать ключи ваших друзей или обменяться ключами посредством QR кодов или NFC.

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    Рекомендуется установить OI File Manager для удобного выбора файлов и Barcode Scanner для распознавания QR кодов. Перейдите по ссылкам на соответствующие страницы Google Play или F-Droid для дальнейшей установки.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html b/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html index d50154765..7c8b8662f 100644 --- a/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-tr/help_about.html @@ -21,7 +21,7 @@

    Geliştiriciler APG 1.x

      -
    • Thialfihar (Baş geliştirici)
    • +
    • Thialfihar (Lead developer)
    • 'Senecaso' (QR Kodu, anahtar imzalama, anahtar yükleme)
    • Markus Doits
    diff --git a/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html b/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html index 4e6bb02dd..df08ac72a 100644 --- a/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw-uk/help_about.html @@ -11,12 +11,12 @@
  • Аш Гюдж (латки шифрування)
  • Браян С. Барнс
  • Бахтіяр 'kalkin' Ґадімов (інтерфейс)
  • -
  • Daniel Hammann
  • -
  • Daniel Haß
  • -
  • Greg Witczak
  • -
  • Miroojin Bakshi
  • -
  • Paul Sarbinowski
  • -
  • Vincent Breitmoser
  • +
  • Даніель Гаман
  • +
  • Даніель Габ
  • +
  • Ґреґ Вітчак
  • +
  • Міроджін Бакші
  • +
  • Пауль Сарбіновський
  • +
  • Вінсент Брейтмозер
  • Розробники APG 1.x

    diff --git a/OpenPGP-Keychain/src/main/res/raw-uk/help_start.html b/OpenPGP-Keychain/src/main/res/raw-uk/help_start.html index 78443070d..45a3edb6a 100644 --- a/OpenPGP-Keychain/src/main/res/raw-uk/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-uk/help_start.html @@ -2,7 +2,7 @@

    Приступаючи до роботи

    -

    Спершу вам потрібна персональна в'язка ключів. Створіть одну через меню параметрів у "Контакти" або імпортуйте наявні в'язки ключів через "Імпорт ключів". Після цього ви зможете завантажувати ключі ваших друзів чи обміняти їх через штрих-коди або NFC.

    +

    Спершу вам потрібна персональна в'язка ключів. Створіть одну через меню параметрів у "Контактах" або імпортуйте наявні в'язки ключів через "Імпорт ключів". Після цього ви зможете завантажувати ключі ваших друзів чи обміняти їх через штрих-коди або NFC.

    Рекомендуємо вам встановити OI File Manager для поліпшеного виділення файлів та Barcode Scanner для сканування згенерованих штрих-кодів. Натискання посилань відкриє Google Play або F-Droid для встановлення.

    diff --git a/OpenPGP-Keychain/src/main/res/raw-zh/help_start.html b/OpenPGP-Keychain/src/main/res/raw-zh/help_start.html index 104bdd545..22ac99882 100644 --- a/OpenPGP-Keychain/src/main/res/raw-zh/help_start.html +++ b/OpenPGP-Keychain/src/main/res/raw-zh/help_start.html @@ -2,7 +2,7 @@

    快速上手

    -

    首先你需要屬於你個人的金鑰對,從〝我的金鑰〞選單中建立一個或是使用〝匯入金鑰〞匯入現有的金鑰對。在這之後,你可以下載你朋友的公鑰或者是透過二維條碼或NFC和他交換公鑰。

    +

    First you need a personal key pair. Create one via the option menus in "Contacts" or import existing key pairs via "Import Keys". Afterwards, you can download your friends' keys or exchange them via QR Codes or NFC.

    It is recommended that you install OI File Manager for enhanced file selection and Barcode Scanner to scan generated QR Codes. Clicking on the links will open Google Play Store or F-Droid for installation.

    diff --git a/OpenPGP-Keychain/src/main/res/values-cs-rCZ/strings.xml b/OpenPGP-Keychain/src/main/res/values-cs-rCZ/strings.xml index 3d00a143f..9207318d3 100644 --- a/OpenPGP-Keychain/src/main/res/values-cs-rCZ/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-cs-rCZ/strings.xml @@ -1,8 +1,30 @@ + Kontakty + Tajné klíče + Zvolit veřejný klíč + Zvolit tajný klíč + Zašifrovat + Dešifrovat + Heslo + Vytvořit klíč + Upravit klíč + Nastavení + Registrované aplikace + Nastavení serveru s klíči + Zadat heslo + Poslat zprávu... + Importovat klíče + Exportovat klíč + Exportovat klíče + Klíč nenalezen + Nahrát na server s klíči + Nápověda + Klíče + Podepsat diff --git a/OpenPGP-Keychain/src/main/res/values-es/strings.xml b/OpenPGP-Keychain/src/main/res/values-es/strings.xml index 85c77b0da..d7436e02d 100644 --- a/OpenPGP-Keychain/src/main/res/values-es/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-es/strings.xml @@ -208,6 +208,8 @@ ¿Quieres realmente borrar la clave \'%s\'?\n¡No podrás deshacerlo! ¿Quieres realmente borrar todas las claves seleccionadas?\n¡No podrás deshacerlo! ¿Quieres realmente borrar la clave SECRETA \'%s\'?\n¡No podrás deshacerlo! + ¿Quieres realmente borrar la clave PÚBLICA \'%s\'?\n¡No podrás deshacerlo! + ¿Borrar claves secretas? ¿Exportar también las claves secretas? %d clave añadida satisfactoriamente @@ -376,6 +378,8 @@ Descifrar con OpenKeychain ¡No hay aplicaciones registradas!\n\nLas aplicaciones de terceros pueden pedir permiso de acceso a OpenKeychain. Después de obtener acceso, serán enumeradas aquí. + Mostrar información avanzada + Ocultar información avanzada Mostrar la configuración avanzada Ocultar la configuración avanzada No se ha seleccionado ninguna clave @@ -385,6 +389,8 @@ Revocar acceso Nombre de paquete SHA-256 de firma de paquete + Cuentas + No hay cuentas asociadas a esta aplicación. La aplicación mostrada solicita acceso a OpenKeychain.\n¿Permitir el acceso?\n\nAVISO: Si no sabes por qué aparece esta pantalla, ¡deniega el acceso! Puedes revocarlo después usando la pantalla \'Aplicaciones registradas\'. Permitir el acceso Denegar el acceso @@ -432,4 +438,5 @@ IDs de usuario para firmar Nueva aplicación de certificados + Escribe aquí el mensaje que quieras cifrar... diff --git a/OpenPGP-Keychain/src/main/res/values-et/strings.xml b/OpenPGP-Keychain/src/main/res/values-et/strings.xml new file mode 100644 index 000000000..04570b4b6 --- /dev/null +++ b/OpenPGP-Keychain/src/main/res/values-et/strings.xml @@ -0,0 +1,123 @@ + + + + Kontaktid + Salajased võtmed + Vali avalik võti + Vali salajane võti + Krüpteeri + Dekrüpteeri + Salasõne + Loo võti + Muuda võtit + Seaded + Registreeritud rakendused + Võtmeserveri seaded + Määra salasõne + Saada kiri... + Impordi võtmeid + Ekspordi võti + Ekspordi võtmed + Võtit ei leitud + Päri võtmeserverist + Lae võtmeserverisse + Võõras allkirjastamise võti + Abi + + Kasutaja ID-d + Võtmed + Üldine + Vaikeseaded + + Allkirjasta + Dekrüpteeri + Vali saajad + Salvesta + Katkesta + Kustuta + Määra salasõne + Otsi + Saada võtmeserverisse + Järgmine + Tagasi + + Seaded + Kustuta võti + Loo võti + Otsi + Võtmeserver... + Uuenda võtmeserverist + Saada võtmeserverisse + Jaga... + Allkirjasta võti + + Allkirjasta + Sõnum + Fail + Salasõnet pole + Salasõne + Uuesti + Algoritm + Saajad + Kustuta peale šifreerimist + Räsialgoritm + Avalik võti + Salasõne + Salasõne puhverdus + Võtmeserverid + Loodud + Aegub + Kasutusvaldkond + Võtmepikkus + Nimi + Kommentaar + E-mail + + aegunud + Sõrmejälg: + Salajane võti: + + Ainult allkirjastamine + Ainult krüpteerimine + Allkirjastamine ja krüpteerimine + 15 sekundit + 1 minut + 3 minutit + 5 minutit + 10 minutit + 20 minutit + 40 minutit + 1 tund + 2 tundi + 4 tundi + 8 tundi + DSA + ElGamal + RSA + Ava... + Hoiatus + Viga + Viga: %s + + Vale salasõne + Määra enne salasõne. + Salasõned ei ühti. + Tühi salasõne pole lubatud. + Sümmeetriline krüpteering + + + + + + + + + + + + + + + diff --git a/OpenPGP-Keychain/src/main/res/values-fr/strings.xml b/OpenPGP-Keychain/src/main/res/values-fr/strings.xml index 4f92f7855..6c0d526d4 100644 --- a/OpenPGP-Keychain/src/main/res/values-fr/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-fr/strings.xml @@ -208,6 +208,8 @@ Voulez-vous vraiment supprimer la clef %s ?\nVous ne pourrez pas la restituer ! Voulez-vous vraiment supprimer toutes les clefs choisies ?\nCeci est irréversible ! Voulez-vous vraiment supprimer la clef SECRÈTE %s ?\nVous ne pourrez pas la restituer ! + Voulez-vous vraiment supprimer la clef PUBLIQUE « %s » ?\nVous ne pourrez pas la restituer ! + Supprimer les clefs privées ? Exporter aussi les clefs secrètes? %d clef ajoutée avec succès @@ -376,6 +378,8 @@ Déchiffrer avec OpenKeychain Aucune application enregistrée !\n\nLes applications tierces peuvent demander l\'accès à OpenKeychain. Après avoir autorisé l\'accès, elles seront listées ici. + Afficher les informations avancées + Masquer les informations avancées Afficher les paramètres avancés Masquer les paramètres avancés Aucune clef choisie @@ -385,6 +389,8 @@ Révoquer l\'accès Nom du paquet SHA-256 de la signature du paquet + Comptes + Aucun compte n\'est attaché à cette application. L\'application affichée demande l\'accès à OpenKeychain.\nPermettre l\'accès ?\n\nAvertissement : si vous ne savez pas pourquoi cet écran est apparu, refusez l\'accès ! Vous pourrez révoquer l\'accès plus tard en utilisant l\'écran « Applications enregistrées ». Permettre l\'accès Enlever l\'accès @@ -432,4 +438,5 @@ ID utilisateur pour signer Nouvel application des certificats + Écrire le message à chiffrer ici... diff --git a/OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml b/OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml index b125de448..23dd432a4 100644 --- a/OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-it-rIT/strings.xml @@ -208,6 +208,8 @@ Vuoi veramente eliminare la chiave \'%s\'?\nNon potrai annullare! Vuoi veramente eliminare le chiavi selezionate?\nNon potrai annullare! Vuoi veramente eliminare la chiave PRIVATA \'%s\'?\nNon potrai annullare! + Vuoi veramente eliminare la chiave PUBBLICA \'%s\'?\nNon potrai annullare! + Eliminare le Chiavi Segrete? Esportare anche le chiavi segrete? %d chiave aggiunta correttamente @@ -432,4 +434,5 @@ ID Utente da firmare Riapplicazione certificati + Scrivi qui il messaggio da codificare... diff --git a/OpenPGP-Keychain/src/main/res/values-ja/strings.xml b/OpenPGP-Keychain/src/main/res/values-ja/strings.xml index 97f0c6eed..f28f73262 100644 --- a/OpenPGP-Keychain/src/main/res/values-ja/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-ja/strings.xml @@ -205,6 +205,8 @@ 鍵\'%s\'を本当に削除してもよいですか?\nこれは元に戻せません! 選択したすべての鍵を本当に削除してよいですか?\nこれは元に戻せません。 秘密鍵\'%s\'を本当に削除してもよいですか?\nこれは元に戻せません! + 公開鍵\'%s\'を本当に削除してもよいですか?\nこれは元に戻せません! + 秘密鍵を削除しますか? 秘密鍵もエクスポートしますか? %d の鍵を追加しました @@ -417,4 +419,5 @@ 署名に使うユーザーID 検証を再適用する + ここに書いたメッセージを暗号化.. diff --git a/OpenPGP-Keychain/src/main/res/values-pl/strings.xml b/OpenPGP-Keychain/src/main/res/values-pl/strings.xml index afdf72223..e31216176 100644 --- a/OpenPGP-Keychain/src/main/res/values-pl/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-pl/strings.xml @@ -211,6 +211,8 @@ Czy na pewno chcesz usunąć klucz \'%s\'?\nNie można cofnąć tej operacji! Czy na pewno chcesz usunąć wszystkie zaznaczone klucze?\nTej operacji nie można cofnąć! Czy na pewno chcesz usunąć klucz prywatny \'%s\'?\nNie można cofnąć tej operacji! + Czy na pewno chcesz usunąć klucz publiczny \'%s\'?\nNie można cofnąć tej operacji! + Usunąć klucze prywatne? Czy wyeksportować również klucze prywatne? Pomyślnie dodano %d klucz @@ -390,6 +392,8 @@ Deszyfruj korzystając z OpenKeychain Brak zarejestrowanych aplikacji!\n\nZewnętrzne aplikacje mogą żądać dostępu do OpenKeychain. Po przyznaniu dostępu, będa wyświetlone tutaj. + Pokaż zaawansowane informacje + Ukryj zaawansowane informacje Pokaż zaawanowane ustawienia Ukryj zaawansowane ustawienia Nie wybrano klucza @@ -399,6 +403,8 @@ Odwołaj dostęp Nazwa paczki Skrót SHA-256 podpisu paczki + Konta + Nie przypisano żadnych kont do tej aplikacji Wyświetlona aplikacja prosi o dostęp do OpenKeychain.\nZezwolić?\n\nOSTRZEZENIE: Jeżeli nie wiesz, czemu wyświetlił się ten komunikat, nie zezwalaj na dostęp! Możesz to również zrobić później, korzystając z ekranu \'Zarejestrowane aplikacje\'. Zezwól na dostęp Odmów dostępu @@ -447,4 +453,5 @@ Identyfikator użytkownika do podpisu Ponowne stosowanie certyfikatów + Wpisz tutaj wiadomość do zaszyfrowania... diff --git a/OpenPGP-Keychain/src/main/res/values-ru/strings.xml b/OpenPGP-Keychain/src/main/res/values-ru/strings.xml index 55e778bee..88a529b66 100644 --- a/OpenPGP-Keychain/src/main/res/values-ru/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-ru/strings.xml @@ -74,13 +74,17 @@ Импорт Импорт из NFC Экспорт всех ключей + Экспорт всех секретных ключей Экспорт в файл Удалить ключ Создать ключ Создать ключ (эксперт) Поиск + Сервер ключей + Сервер ключей... Обновить с сервера ключей Загрузить на сервер ключей + Отправить... Отправить отпечаток... Отправить ключ... Отправить @@ -143,6 +147,12 @@ подпись просрочен отозван + ID пользователя + + 1 контакт + %d контактов + %d контактов + %d сервер ключей %d серверов ключей @@ -199,6 +209,9 @@ Вы уверены, что ходите удалить ключ \'%s\'?\nЭто действие нельзя отменить! Вы уверены, что хотите удалить ВСЕ выбранные ключи?\nЭто действие нельзя отменить! Вы уверены, что ходите удалить СЕКРЕТНЫЙ ключ \'%s\'?\nЭто действие нельзя отменить! + Вы правда хотите удалить ПУБЛИЧНЫЙ ключ \'%s\'?\nЭто нельзя отменить! + Удалить секретные ключи? + Экспортировать секретные ключи? Успешно добавлено %d ключ Успешно добавлено %d ключей @@ -224,6 +237,7 @@ Экспортировано %d ключей. Ключи не были экспортированы. Инфо: ElGamal подходит только для дополнительных ключей. При создании ключа будет использован ближайший из размеров: 1536, 2048, 3072, 4096, или 8192. + Внимание: создание ключей RSA длиной 1024 бита и менее признано небезопасным. Данная возможность отключена. Не удается найти ключ %08X. Найден %d ключ. @@ -347,6 +361,7 @@ очень медленно Начать + ЧаВо NFC Beam Изменения О программе @@ -410,6 +425,7 @@ создать свой ключ Импортировать ключи + Изменить ключ Зашифровать для этого получателя Сертифицировать ключ этого контакта Информация @@ -423,5 +439,11 @@ Связанные приложения Открыть панель навигации Закрыть панель навигации + Изменить + Мои ключи + Секретный ключ + доступен + не доступен + Напишите сообщение здесь, что бы зашифровать... diff --git a/OpenPGP-Keychain/src/main/res/values-uk/strings.xml b/OpenPGP-Keychain/src/main/res/values-uk/strings.xml index 58ac643af..00655fc53 100644 --- a/OpenPGP-Keychain/src/main/res/values-uk/strings.xml +++ b/OpenPGP-Keychain/src/main/res/values-uk/strings.xml @@ -211,6 +211,8 @@ Ви справді хочете вилучити ключ \'%s\'?\nВи не зможете це відмінити! Ви справді хочете вилучити усі вибрані ключі?\nВи не зможете це відмінити! Ви справді хочете вилучити секретний ключ \'%s\'?\nВи не зможете це відмінити! + Справді волієте вилучити ВІДКРИТИЙ ключ \'%s\'?\nВи е зможете відмінити цю дію! + Видалити секретні ключі? Також експортувати секретні ключі? Успішно додано %d ключ @@ -390,6 +392,8 @@ Розшифрувати з OpenKeychain Нема зареєстрованих програм!\n\nСтороні програми можуть вимагати доступ до OpenPGP Keychain. Після надання доступу вони будуть наведені тут. + Показати додаткову інформацію + Приховати додаткову інформацію Показати додаткові налаштування Приховати додаткові налаштування Не вибрано ключа @@ -399,6 +403,8 @@ Відкликати доступ Назва пакунку SHA-256 підписку пакунку + Облікові записи + Немає облікового запису приєднаного до цієї програми. Показана програма запитує доступ до OpenPGP Keychain.\nДозволити доступ?\n\nУВАГА: якщо ви не знаєте, чому цей екран появився, не дозволяйте доступ! Ви можете відкликати доступ пізніше, використовуючи екран \'Зареєстровані програми\'. Дозволити доступ Не дозволити доступ @@ -447,4 +453,5 @@ ІД користувача для реєстрації Перезастосування сертифікатів + Напишіть повідомлення для шифрування… From 8e9ceffae3a2c89063a3e3073c7d6424388275e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20Sch=C3=BCrmann?= Date: Wed, 26 Mar 2014 22:45:35 +0100 Subject: [PATCH 152/253] Changelog for 2.4 --- .../src/main/res/raw-de/help_changelog.html | 108 ------------------ .../src/main/res/raw/help_about.html | 4 +- .../src/main/res/raw/help_changelog.html | 18 +++ 3 files changed, 21 insertions(+), 109 deletions(-) delete mode 100644 OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html diff --git a/OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html b/OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html deleted file mode 100644 index e414324d0..000000000 --- a/OpenPGP-Keychain/src/main/res/raw-de/help_changelog.html +++ /dev/null @@ -1,108 +0,0 @@ - - - -

    2.3

    -
      -
    • remove unnecessary export of public keys when exporting secret key (thanks to Ash Hughes)
    • -
    • fix setting expiry dates on keys (thanks to Ash Hughes)
    • -
    • more internal fixes when editing keys (thanks to Ash Hughes)
    • -
    • querying keyservers directly from the import screen
    • -
    • fix layout and dialog style on Android 2.2-3.0
    • -
    • Absturz bei leeren Nutzer IDs behoben
    • -
    • fix crash and empty lists when coming back from signing screen
    • -
    • Bouncy Castle (cryptography library) updated from 1.47 to 1.50 and build from source
    • -
    • fix upload of key from signing screen
    • -
    -

    2.2

    -
      -
    • new design with navigation drawer
    • -
    • Neus Design für die Liste der öffentlichen Schlüssel
    • -
    • new public key view
    • -
    • Fehler beim Schlüsselimport behoben
    • -
    • key cross-certification (thanks to Ash Hughes)
    • -
    • handle UTF-8 passwords properly (thanks to Ash Hughes)
    • -
    • Erste Version mit neuen Sprachen (Danke an die Mitwirkenden bei Transifex)
    • -
    • sharing of keys via QR Codes fixed and improved
    • -
    • package signature verification for API
    • -
    -

    2.1.1

    -
      -
    • API Updates, preparation for K-9 Mail integration
    • -
    -

    2.1

    -
      -
    • Viele Fehler behoben
    • -
    • Neue API für Entwickler
    • -
    • PRNG Bugfix von Google
    • -
    -

    2.0

    -
      -
    • Komlett neu designd
    • -
    • Öffentliche Schlüssel teilen via QR Code, NFC Beam
    • -
    • Schlüssel signieren
    • -
    • Schlüssel auf den Server hochladen
    • -
    • Importprobleme behoben
    • -
    • new AIDL API
    • -
    -

    1.0.8

    -
      -
    • Grundlegende Schlüsselserverunterstützung
    • -
    • app2sd
    • -
    • mehr Auswahlmöglichkeiten für den Passwortcache: 1, 2, 4, 8, Stunden
    • -
    • Übersetzungen: norwegisch (Danke, Sander Danielsen), chinesisch (danke, Zhang Fredrick)
    • -
    • Fehlerbehebungen
    • -
    • Optimierungen
    • -
    -

    1.0.7

    -
      -
    • fixed problem with signature verification of texts with trailing newline
    • -
    • weitere Optionen für die Time-to-live des Passphrasencaches (20, 40, 60 mins)
    • -
    -

    1.0.6

    -
      -
    • crash beim Hinzufügen eines Kontos auf Froyo repariert
    • -
    • sichere Dateilöschung
    • -
    • Option, um Schlüsseldatei nach dem Import zu löschen
    • -
    • Streamverschlüsselung/-entschlüsselung (Galerie, etc.)
    • -
    • neue Optionen (Sprache, v3-Unterschriften erzwingen)
    • -
    • Interfaceänderungen
    • -
    • Fehlerbehebungen
    • -
    -

    1.0.5

    -
      -
    • Deutsche und Italienische Übersetzung
    • -
    • viel kleineres Paket, dank reduzierter BC Quellen
    • -
    • Neues Einstellungs-GUI
    • -
    • Lay-Out-Anpassung für die Lokalisierung
    • -
    • Fehler bei Signatur behoben
    • -
    -

    1.0.4

    -
      -
    • fixed another crash caused by some SDK bug with query builder
    • -
    -

    1.0.3

    -
      -
    • Absturz während der Verschlüsselung/Signierung und möglicherweise Schlüsselexport behoben.
    • -
    -

    1.0.2

    -
      -
    • Filterbare Schlüsselliste
    • -
    • smarter pre-selection of encryption keys
    • -
    • new Intent handling for VIEW and SEND, allows files to be encrypted/decrypted out of file managers
    • -
    • fixes and additional features (key preselection) for K-9 Mail, new beta build available
    • -
    -

    1.0.1

    -
      -
    • GMail account listing was broken in 1.0.0, fixed again
    • -
    -

    1.0.0

    -
      -
    • K-9 Mail integration, APG supporting beta build of K-9 Mail
    • -
    • Unterstützung von mehr Filemanagern (einschließlich ASTRO)
    • -
    • Slowenische Übersetzung
    • -
    • Neue Datenbank, viel schneller, weniger Speicherbedarf
    • -
    • defined Intents and content provider for other apps
    • -
    • Fehlerbehebungen
    • -
    - - diff --git a/OpenPGP-Keychain/src/main/res/raw/help_about.html b/OpenPGP-Keychain/src/main/res/raw/help_about.html index 2ffbb47a6..847168446 100644 --- a/OpenPGP-Keychain/src/main/res/raw/help_about.html +++ b/OpenPGP-Keychain/src/main/res/raw/help_about.html @@ -19,10 +19,12 @@ And don't add newlines before or after p tags because of transifex -->
  • Daniel Haß
  • Greg Witczak
  • Miroojin Bakshi
  • +
  • Nikhil Peter Raj
  • Paul Sarbinowski
  • +
  • Sreeram Boyapati
  • Vincent Breitmoser
  • - +

    Developers APG 1.x

      - *
    • %keyid% = this is either the fingerprint or the key ID of the key. Either the 16-digit or 8-digit - * key IDs are acceptable, but obviously the fingerprint is best.
    • - *
    • %algo% = the algorithm number, (i.e. 1==RSA, 17==DSA, etc). - * See RFC-2440
    • - *
    • %keylen% = the key length (i.e. 1024, 2048, 4096, etc.)
    • - *
    • %creationdate% = creation date of the key in standard - * RFC-2440 form (i.e. number of seconds since - * 1/1/1970 UTC time)
    • - *
    • %expirationdate% = expiration date of the key in standard - * RFC-2440 form (i.e. number of seconds since - * 1/1/1970 UTC time)
    • - *
    • %flags% = letter codes to indicate details of the key, if any. Flags may be in any order. The - * meaning of "disabled" is implementation-specific. Note that individual flags may be unimplemented, so - * the absence of a given flag does not necessarily mean the absence of the detail. - *
        - *
      • r == revoked
      • - *
      • d == disabled
      • - *
      • e == expired
      • - *
      - *
    • + *
    • %keyid% = this is either the fingerprint or the key ID of the key. Either the 16-digit or 8-digit + * key IDs are acceptable, but obviously the fingerprint is best.
    • + *
    • %algo% = the algorithm number, (i.e. 1==RSA, 17==DSA, etc). + * See RFC-2440
    • + *
    • %keylen% = the key length (i.e. 1024, 2048, 4096, etc.)
    • + *
    • %creationdate% = creation date of the key in standard + * RFC-2440 form (i.e. number of seconds since + * 1/1/1970 UTC time)
    • + *
    • %expirationdate% = expiration date of the key in standard + * RFC-2440 form (i.e. number of seconds since + * 1/1/1970 UTC time)
    • + *
    • %flags% = letter codes to indicate details of the key, if any. Flags may be in any order. The + * meaning of "disabled" is implementation-specific. Note that individual flags may be unimplemented, so + * the absence of a given flag does not necessarily mean the absence of the detail. + *
        + *
      • r == revoked
      • + *
      • d == disabled
      • + *
      • e == expired
      • + *
      + *
    • *