From 51c662f0d080da0f94f675467dbea49135674d73 Mon Sep 17 00:00:00 2001 From: ashley willis Date: Sat, 19 Nov 2011 00:49:04 -0600 Subject: [PATCH 1/5] pgp issues 2152, 2900, and 3673. also option to disable not-yet-encrypted drafts related to issue 1424. --- res/values/strings.xml | 4 ++ res/xml/account_settings_preferences.xml | 14 ++++++ src/com/fsck/k9/Account.java | 26 ++++++++++ src/com/fsck/k9/activity/MessageCompose.java | 49 +++++++++++++++++-- .../k9/activity/setup/AccountSettings.java | 16 ++++++ .../k9/controller/MessagingController.java | 19 +++---- src/com/fsck/k9/crypto/Apg.java | 35 +++++++++++++ src/com/fsck/k9/crypto/CryptoProvider.java | 1 + src/com/fsck/k9/crypto/None.java | 5 ++ .../fsck/k9/preferences/AccountSettings.java | 2 + 10 files changed, 157 insertions(+), 14 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 3cc393c03..a766be141 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -595,6 +595,10 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin not available Auto-sign Use the account\'s email address to guess the signature key. + Auto-encrypt + Automatically set encrypt if a public key matches a recipient. + Don\'t sync drafts + Don\'t sync drafts marked to be encrypted. Must change drafts folder\'s sync and push class to none, or these drafts will be deleted! Folder poll frequency 2nd class check frequency diff --git a/res/xml/account_settings_preferences.xml b/res/xml/account_settings_preferences.xml index eb1f7b5b2..9a81c2d51 100644 --- a/res/xml/account_settings_preferences.xml +++ b/res/xml/account_settings_preferences.xml @@ -474,6 +474,20 @@ android:summary="@string/account_settings_crypto_auto_signature_summary" android:dependency="crypto_app"/> + + + + diff --git a/src/com/fsck/k9/Account.java b/src/com/fsck/k9/Account.java index c3c0bbf99..b4aaf1908 100644 --- a/src/com/fsck/k9/Account.java +++ b/src/com/fsck/k9/Account.java @@ -149,6 +149,8 @@ public class Account implements BaseAccount { private boolean mSyncRemoteDeletions; private String mCryptoApp; private boolean mCryptoAutoSignature; + private boolean mCryptoAutoEncrypt; + private boolean mCryptoDontSyncDrafts; private CryptoProvider mCryptoProvider = null; @@ -240,6 +242,8 @@ public class Account implements BaseAccount { mSyncRemoteDeletions = true; mCryptoApp = Apg.NAME; mCryptoAutoSignature = false; + mCryptoAutoEncrypt = false; + mCryptoDontSyncDrafts = false; mEnabled = true; searchableFolders = Searchable.ALL; @@ -408,6 +412,8 @@ public class Account implements BaseAccount { mCryptoApp = prefs.getString(mUuid + ".cryptoApp", Apg.NAME); mCryptoAutoSignature = prefs.getBoolean(mUuid + ".cryptoAutoSignature", false); + mCryptoAutoEncrypt = prefs.getBoolean(mUuid + ".cryptoAutoEncrypt", false); + mCryptoDontSyncDrafts = prefs.getBoolean(mUuid + ".cryptoDontSyncDrafts", false); mEnabled = prefs.getBoolean(mUuid + ".enabled", true); } @@ -480,6 +486,8 @@ public class Account implements BaseAccount { editor.remove(mUuid + ".stripSignature"); editor.remove(mUuid + ".cryptoApp"); editor.remove(mUuid + ".cryptoAutoSignature"); + editor.remove(mUuid + ".cryptoAutoEncrypt"); + editor.remove(mUuid + ".cryptoDontSyncDrafts"); editor.remove(mUuid + ".enabled"); editor.remove(mUuid + ".enableMoveButtons"); editor.remove(mUuid + ".hideMoveButtonsEnum"); @@ -643,6 +651,8 @@ public class Account implements BaseAccount { editor.putBoolean(mUuid + ".stripSignature", mStripSignature); editor.putString(mUuid + ".cryptoApp", mCryptoApp); editor.putBoolean(mUuid + ".cryptoAutoSignature", mCryptoAutoSignature); + editor.putBoolean(mUuid + ".cryptoAutoEncrypt", mCryptoAutoEncrypt); + editor.putBoolean(mUuid + ".cryptoDontSyncDrafts", mCryptoAutoEncrypt); editor.putBoolean(mUuid + ".enabled", mEnabled); editor.putBoolean(mUuid + ".vibrate", mNotificationSetting.shouldVibrate()); @@ -1466,6 +1476,22 @@ public class Account implements BaseAccount { mCryptoAutoSignature = cryptoAutoSignature; } + public boolean isCryptoAutoEncrypt() { + return mCryptoAutoEncrypt; + } + + public void setCryptoAutoEncrypt(boolean cryptoAutoEncrypt) { + mCryptoAutoEncrypt = cryptoAutoEncrypt; + } + + public boolean isCryptoDontSyncDrafts() { + return mCryptoDontSyncDrafts; + } + + public void setCryptoDontSyncDrafts(boolean cryptoDontSyncDrafts) { + mCryptoDontSyncDrafts = cryptoDontSyncDrafts; + } + public String getInboxFolderName() { return mInboxFolderName; } diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java index c4ee2c4a1..de364cdd9 100644 --- a/src/com/fsck/k9/activity/MessageCompose.java +++ b/src/com/fsck/k9/activity/MessageCompose.java @@ -208,6 +208,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc private ImageButton mAddBccFromContacts; private PgpData mPgpData = null; + private boolean mAutoEncrypt = false; + private boolean mDontSyncDrafts = false; private String mReferences; private String mInReplyTo; @@ -442,6 +444,28 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc public void afterTextChanged(android.text.Editable s) { } }; + TextWatcher recipientWatcher = new TextWatcher() { + public void beforeTextChanged(CharSequence s, int start, int before, int after) { } + + public void onTextChanged(CharSequence s, int start, int before, int count) { + mDraftNeedsSaving = true; + } + + public void afterTextChanged(android.text.Editable s) { + final CryptoProvider crypto = mAccount.getCryptoProvider(); + if (mAutoEncrypt && crypto.isAvailable(getApplicationContext())) { + for (Address address : getRecipientAddresses()) { + long ids[] = crypto.getPublicKeyIdsFromEmail(getApplicationContext(), + address.getAddress()); + if (ids != null && ids.length > 0) { + mEncryptCheckbox.setChecked(true); + break; + } + } + } + } + }; + TextWatcher sigwatcher = new TextWatcher() { public void beforeTextChanged(CharSequence s, int start, int before, int after) { } @@ -455,9 +479,9 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc public void afterTextChanged(android.text.Editable s) { } }; - mToView.addTextChangedListener(watcher); - mCcView.addTextChangedListener(watcher); - mBccView.addTextChangedListener(watcher); + mToView.addTextChangedListener(recipientWatcher); + mCcView.addTextChangedListener(recipientWatcher); + mBccView.addTextChangedListener(recipientWatcher); mSubjectView.addTextChangedListener(watcher); mMessageContentView.addTextChangedListener(watcher); @@ -616,6 +640,11 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc mCryptoSignatureUserId = (TextView)findViewById(R.id.userId); mCryptoSignatureUserIdRest = (TextView)findViewById(R.id.userIdRest); mEncryptCheckbox = (CheckBox)findViewById(R.id.cb_encrypt); + if (mSourceMessageBody != null) { + // mSourceMessageBody is set to something when replying to and forwarding decrypted + // messages, so the sender probably wants the message to be encrypted. + mEncryptCheckbox.setChecked(true); + } initializeCrypto(); final CryptoProvider crypto = mAccount.getCryptoProvider(); @@ -649,6 +678,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc } } updateEncryptLayout(); + mAutoEncrypt = mAccount.isCryptoAutoEncrypt(); + mDontSyncDrafts = mAccount.isCryptoDontSyncDrafts(); } else { mEncryptLayout.setVisibility(View.GONE); } @@ -776,6 +807,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc mCryptoSignatureUserId.setVisibility(View.INVISIBLE); mCryptoSignatureUserIdRest.setVisibility(View.INVISIBLE); } else { + mMessageFormat = MessageFormat.TEXT; // if a signature key is selected, then the checkbox itself has no text mCryptoSignatureCheckbox.setText(""); mCryptoSignatureCheckbox.setChecked(true); @@ -919,6 +951,12 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc return Address.parseUnencoded(view.getText().toString().trim()); } + private Address[] getRecipientAddresses() { + String addresses = mToView.getText().toString() + mCcView.getText().toString() + + mBccView.getText().toString(); + return Address.parseUnencoded(addresses.trim()); + } + /* * Build the Body that will contain the text of the message. We'll decide where to * include it later. Draft messages are treated somewhat differently in that signatures are not @@ -960,7 +998,6 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc // Place the signature immediately after the reply. if (!isDraft) { if (mQuoteStyle == QuoteStyle.HEADER || replyAfterQuote || mAccount.isSignatureBeforeQuotedText()) { - Log.d("ASH", "appending signature after new content"); text = appendSignature(text); } } @@ -1461,6 +1498,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc return; } if (mEncryptCheckbox.isChecked() && !mPgpData.hasEncryptionKeys()) { + mMessageFormat = MessageFormat.TEXT; // key selection before encryption StringBuilder emails = new StringBuilder(); Address[][] addresses = new Address[][] { getAddresses(mToView), @@ -2876,7 +2914,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc } final MessagingController messagingController = MessagingController.getInstance(getApplication()); - Message draftMessage = messagingController.saveDraft(mAccount, message); + Message draftMessage = messagingController.saveDraft(mAccount, message, + mDontSyncDrafts && mEncryptCheckbox.isChecked()); mDraftUid = draftMessage.getUid(); // Don't display the toast if the user is just changing the orientation diff --git a/src/com/fsck/k9/activity/setup/AccountSettings.java b/src/com/fsck/k9/activity/setup/AccountSettings.java index f3acb7d15..96c715700 100644 --- a/src/com/fsck/k9/activity/setup/AccountSettings.java +++ b/src/com/fsck/k9/activity/setup/AccountSettings.java @@ -97,6 +97,8 @@ public class AccountSettings extends K9PreferenceActivity { private static final String PREFERENCE_SYNC_REMOTE_DELETIONS = "account_sync_remote_deletetions"; private static final String PREFERENCE_CRYPTO_APP = "crypto_app"; private static final String PREFERENCE_CRYPTO_AUTO_SIGNATURE = "crypto_auto_signature"; + private static final String PREFERENCE_CRYPTO_AUTO_ENCRYPT = "crypto_auto_encrypt"; + private static final String PREFERENCE_CRYPTO_DONT_SYNC_DRAFTS = "crypto_dont_sync_drafts"; private static final String PREFERENCE_LOCAL_STORAGE_PROVIDER = "local_storage_provider"; @@ -161,6 +163,8 @@ public class AccountSettings extends K9PreferenceActivity { private ListPreference mMaxPushFolders; private ListPreference mCryptoApp; private CheckBoxPreference mCryptoAutoSignature; + private CheckBoxPreference mCryptoAutoEncrypt; + private CheckBoxPreference mCryptoDontSyncDrafts; private ListPreference mLocalStorageProvider; @@ -681,14 +685,24 @@ public class AccountSettings extends K9PreferenceActivity { mCryptoAutoSignature = (CheckBoxPreference) findPreference(PREFERENCE_CRYPTO_AUTO_SIGNATURE); mCryptoAutoSignature.setChecked(mAccount.getCryptoAutoSignature()); + mCryptoAutoEncrypt = (CheckBoxPreference) findPreference(PREFERENCE_CRYPTO_AUTO_ENCRYPT); + mCryptoAutoEncrypt.setChecked(mAccount.isCryptoAutoEncrypt()); + + mCryptoDontSyncDrafts = (CheckBoxPreference) findPreference(PREFERENCE_CRYPTO_DONT_SYNC_DRAFTS); + mCryptoDontSyncDrafts.setChecked(mAccount.isCryptoDontSyncDrafts()); + handleCryptoAppDependencies(); } private void handleCryptoAppDependencies() { if ("".equals(mCryptoApp.getValue())) { mCryptoAutoSignature.setEnabled(false); + mCryptoAutoEncrypt.setEnabled(false); + mCryptoDontSyncDrafts.setEnabled(false); } else { mCryptoAutoSignature.setEnabled(true); + mCryptoAutoEncrypt.setEnabled(true); + mCryptoDontSyncDrafts.setEnabled(true); } } @@ -734,6 +748,8 @@ public class AccountSettings extends K9PreferenceActivity { mAccount.setStripSignature(mStripSignature.isChecked()); mAccount.setCryptoApp(mCryptoApp.getValue()); mAccount.setCryptoAutoSignature(mCryptoAutoSignature.isChecked()); + mAccount.setCryptoAutoEncrypt(mCryptoAutoEncrypt.isChecked()); + mAccount.setCryptoDontSyncDrafts(mCryptoDontSyncDrafts.isChecked()); mAccount.setLocalStorageProviderId(mLocalStorageProvider.getValue()); // In webdav account we use the exact folder name also for inbox, diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index 2eedc119e..d739c5fe3 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -4059,7 +4059,7 @@ public class MessagingController implements Runnable { * @param message Message to save. * @return Message representing the entry in the local store. */ - public Message saveDraft(final Account account, final Message message) { + public Message saveDraft(final Account account, final Message message, final boolean dontSyncDraft) { Message localMessage = null; try { LocalStore localStore = account.getLocalStore(); @@ -4072,14 +4072,15 @@ public class MessagingController implements Runnable { // Fetch the message back from the store. This is the Message that's returned to the caller. localMessage = localFolder.getMessage(message.getUid()); localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true); - - PendingCommand command = new PendingCommand(); - command.command = PENDING_COMMAND_APPEND; - command.arguments = new String[] { - localFolder.getName(), - localMessage.getUid() - }; - queuePendingCommand(account, command); + if (!dontSyncDraft) { + PendingCommand command = new PendingCommand(); + command.command = PENDING_COMMAND_APPEND; + command.arguments = new String[] { + localFolder.getName(), + localMessage.getUid() + }; + queuePendingCommand(account, command); + } processPendingCommands(account); } catch (MessagingException e) { diff --git a/src/com/fsck/k9/crypto/Apg.java b/src/com/fsck/k9/crypto/Apg.java index de1a2b77e..350a6c284 100644 --- a/src/com/fsck/k9/crypto/Apg.java +++ b/src/com/fsck/k9/crypto/Apg.java @@ -232,6 +232,41 @@ public class Apg extends CryptoProvider { return ids; } + /** + * Get public key ids based on a given email. + * + * @param context + * @param email The email in question. + * @return key ids + */ + @Override + public long[] getPublicKeyIdsFromEmail(Context context, String email) { + long ids[] = null; + try { + Uri contentUri = Uri.withAppendedPath(Apg.CONTENT_URI_PUBLIC_KEY_RING_BY_EMAILS, + email); + Cursor c = context.getContentResolver().query(contentUri, + new String[] { "master_key_id" }, + null, null, null); + if (c != null && c.getCount() > 0) { + ids = new long[c.getCount()]; + while (c.moveToNext()) { + ids[c.getPosition()] = c.getLong(0); + } + } + + if (c != null) { + c.close(); + } + } catch (SecurityException e) { + Toast.makeText(context, + context.getResources().getString(R.string.insufficient_apg_permissions), + Toast.LENGTH_LONG).show(); + } + + return ids; + } + /** * Get the user id based on the key id. * diff --git a/src/com/fsck/k9/crypto/CryptoProvider.java b/src/com/fsck/k9/crypto/CryptoProvider.java index 79401b154..373a62df9 100644 --- a/src/com/fsck/k9/crypto/CryptoProvider.java +++ b/src/com/fsck/k9/crypto/CryptoProvider.java @@ -24,6 +24,7 @@ abstract public class CryptoProvider { abstract public boolean encrypt(Activity activity, String data, PgpData pgpData); abstract public boolean decrypt(Activity activity, String data, PgpData pgpData); abstract public long[] getSecretKeyIdsFromEmail(Context context, String email); + abstract public long[] getPublicKeyIdsFromEmail(Context context, String email); abstract public String getUserId(Context context, long keyId); abstract public String getName(); abstract public boolean test(Context context); diff --git a/src/com/fsck/k9/crypto/None.java b/src/com/fsck/k9/crypto/None.java index eb83d9261..cf2af7bc4 100644 --- a/src/com/fsck/k9/crypto/None.java +++ b/src/com/fsck/k9/crypto/None.java @@ -37,6 +37,11 @@ public class None extends CryptoProvider { return null; } + @Override + public long[] getPublicKeyIdsFromEmail(Context context, String email) { + return null; + } + @Override public String getUserId(Context context, long keyId) { return null; diff --git a/src/com/fsck/k9/preferences/AccountSettings.java b/src/com/fsck/k9/preferences/AccountSettings.java index 167d9c914..b9a9cf593 100644 --- a/src/com/fsck/k9/preferences/AccountSettings.java +++ b/src/com/fsck/k9/preferences/AccountSettings.java @@ -27,6 +27,8 @@ public class AccountSettings { s.put("chipColor", new ColorSetting(0xFF0000FF)); s.put("cryptoApp", new StringSetting(Apg.NAME)); s.put("cryptoAutoSignature", new BooleanSetting(false)); + //s.put("cryptoAutoEncrypt", new BooleanSetting(false)); // added to version 3? + //s.put("cryptoDontSyncDrafts", new BooleanSetting(false)); // added to version 3? s.put("defaultQuotedTextShown", new BooleanSetting(Account.DEFAULT_QUOTED_TEXT_SHOWN)); s.put("deletePolicy", new DeletePolicySetting(Account.DELETE_POLICY_NEVER)); s.put("displayCount", new IntegerResourceSetting(K9.DEFAULT_VISIBLE_LIMIT, From c49d1ecc8e93b9db0b99c78611692630e88af759 Mon Sep 17 00:00:00 2001 From: ashley willis Date: Sat, 19 Nov 2011 13:34:26 -0600 Subject: [PATCH 2/5] removed code from previous commit disabling of syncing drafts to be encrypted. --- res/values/strings.xml | 2 -- res/xml/account_settings_preferences.xml | 7 ------- src/com/fsck/k9/Account.java | 13 ------------- src/com/fsck/k9/activity/MessageCompose.java | 5 +---- .../k9/activity/setup/AccountSettings.java | 8 -------- .../k9/controller/MessagingController.java | 18 ++++++++---------- .../fsck/k9/preferences/AccountSettings.java | 1 - 7 files changed, 9 insertions(+), 45 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index a766be141..d16e2bb94 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -597,8 +597,6 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin Use the account\'s email address to guess the signature key. Auto-encrypt Automatically set encrypt if a public key matches a recipient. - Don\'t sync drafts - Don\'t sync drafts marked to be encrypted. Must change drafts folder\'s sync and push class to none, or these drafts will be deleted! Folder poll frequency 2nd class check frequency diff --git a/res/xml/account_settings_preferences.xml b/res/xml/account_settings_preferences.xml index 9a81c2d51..a852549f5 100644 --- a/res/xml/account_settings_preferences.xml +++ b/res/xml/account_settings_preferences.xml @@ -481,13 +481,6 @@ android:summary="@string/account_settings_crypto_auto_encrypt_summary" android:dependency="crypto_app"/> - - diff --git a/src/com/fsck/k9/Account.java b/src/com/fsck/k9/Account.java index b4aaf1908..c1fc7a482 100644 --- a/src/com/fsck/k9/Account.java +++ b/src/com/fsck/k9/Account.java @@ -150,7 +150,6 @@ public class Account implements BaseAccount { private String mCryptoApp; private boolean mCryptoAutoSignature; private boolean mCryptoAutoEncrypt; - private boolean mCryptoDontSyncDrafts; private CryptoProvider mCryptoProvider = null; @@ -243,7 +242,6 @@ public class Account implements BaseAccount { mCryptoApp = Apg.NAME; mCryptoAutoSignature = false; mCryptoAutoEncrypt = false; - mCryptoDontSyncDrafts = false; mEnabled = true; searchableFolders = Searchable.ALL; @@ -413,7 +411,6 @@ public class Account implements BaseAccount { mCryptoApp = prefs.getString(mUuid + ".cryptoApp", Apg.NAME); mCryptoAutoSignature = prefs.getBoolean(mUuid + ".cryptoAutoSignature", false); mCryptoAutoEncrypt = prefs.getBoolean(mUuid + ".cryptoAutoEncrypt", false); - mCryptoDontSyncDrafts = prefs.getBoolean(mUuid + ".cryptoDontSyncDrafts", false); mEnabled = prefs.getBoolean(mUuid + ".enabled", true); } @@ -487,7 +484,6 @@ public class Account implements BaseAccount { editor.remove(mUuid + ".cryptoApp"); editor.remove(mUuid + ".cryptoAutoSignature"); editor.remove(mUuid + ".cryptoAutoEncrypt"); - editor.remove(mUuid + ".cryptoDontSyncDrafts"); editor.remove(mUuid + ".enabled"); editor.remove(mUuid + ".enableMoveButtons"); editor.remove(mUuid + ".hideMoveButtonsEnum"); @@ -652,7 +648,6 @@ public class Account implements BaseAccount { editor.putString(mUuid + ".cryptoApp", mCryptoApp); editor.putBoolean(mUuid + ".cryptoAutoSignature", mCryptoAutoSignature); editor.putBoolean(mUuid + ".cryptoAutoEncrypt", mCryptoAutoEncrypt); - editor.putBoolean(mUuid + ".cryptoDontSyncDrafts", mCryptoAutoEncrypt); editor.putBoolean(mUuid + ".enabled", mEnabled); editor.putBoolean(mUuid + ".vibrate", mNotificationSetting.shouldVibrate()); @@ -1484,14 +1479,6 @@ public class Account implements BaseAccount { mCryptoAutoEncrypt = cryptoAutoEncrypt; } - public boolean isCryptoDontSyncDrafts() { - return mCryptoDontSyncDrafts; - } - - public void setCryptoDontSyncDrafts(boolean cryptoDontSyncDrafts) { - mCryptoDontSyncDrafts = cryptoDontSyncDrafts; - } - public String getInboxFolderName() { return mInboxFolderName; } diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java index de364cdd9..09f984d0e 100644 --- a/src/com/fsck/k9/activity/MessageCompose.java +++ b/src/com/fsck/k9/activity/MessageCompose.java @@ -209,7 +209,6 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc private PgpData mPgpData = null; private boolean mAutoEncrypt = false; - private boolean mDontSyncDrafts = false; private String mReferences; private String mInReplyTo; @@ -679,7 +678,6 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc } updateEncryptLayout(); mAutoEncrypt = mAccount.isCryptoAutoEncrypt(); - mDontSyncDrafts = mAccount.isCryptoDontSyncDrafts(); } else { mEncryptLayout.setVisibility(View.GONE); } @@ -2914,8 +2912,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc } final MessagingController messagingController = MessagingController.getInstance(getApplication()); - Message draftMessage = messagingController.saveDraft(mAccount, message, - mDontSyncDrafts && mEncryptCheckbox.isChecked()); + Message draftMessage = messagingController.saveDraft(mAccount, message); mDraftUid = draftMessage.getUid(); // Don't display the toast if the user is just changing the orientation diff --git a/src/com/fsck/k9/activity/setup/AccountSettings.java b/src/com/fsck/k9/activity/setup/AccountSettings.java index 96c715700..b38f37bf3 100644 --- a/src/com/fsck/k9/activity/setup/AccountSettings.java +++ b/src/com/fsck/k9/activity/setup/AccountSettings.java @@ -98,7 +98,6 @@ public class AccountSettings extends K9PreferenceActivity { private static final String PREFERENCE_CRYPTO_APP = "crypto_app"; private static final String PREFERENCE_CRYPTO_AUTO_SIGNATURE = "crypto_auto_signature"; private static final String PREFERENCE_CRYPTO_AUTO_ENCRYPT = "crypto_auto_encrypt"; - private static final String PREFERENCE_CRYPTO_DONT_SYNC_DRAFTS = "crypto_dont_sync_drafts"; private static final String PREFERENCE_LOCAL_STORAGE_PROVIDER = "local_storage_provider"; @@ -164,7 +163,6 @@ public class AccountSettings extends K9PreferenceActivity { private ListPreference mCryptoApp; private CheckBoxPreference mCryptoAutoSignature; private CheckBoxPreference mCryptoAutoEncrypt; - private CheckBoxPreference mCryptoDontSyncDrafts; private ListPreference mLocalStorageProvider; @@ -688,9 +686,6 @@ public class AccountSettings extends K9PreferenceActivity { mCryptoAutoEncrypt = (CheckBoxPreference) findPreference(PREFERENCE_CRYPTO_AUTO_ENCRYPT); mCryptoAutoEncrypt.setChecked(mAccount.isCryptoAutoEncrypt()); - mCryptoDontSyncDrafts = (CheckBoxPreference) findPreference(PREFERENCE_CRYPTO_DONT_SYNC_DRAFTS); - mCryptoDontSyncDrafts.setChecked(mAccount.isCryptoDontSyncDrafts()); - handleCryptoAppDependencies(); } @@ -698,11 +693,9 @@ public class AccountSettings extends K9PreferenceActivity { if ("".equals(mCryptoApp.getValue())) { mCryptoAutoSignature.setEnabled(false); mCryptoAutoEncrypt.setEnabled(false); - mCryptoDontSyncDrafts.setEnabled(false); } else { mCryptoAutoSignature.setEnabled(true); mCryptoAutoEncrypt.setEnabled(true); - mCryptoDontSyncDrafts.setEnabled(true); } } @@ -749,7 +742,6 @@ public class AccountSettings extends K9PreferenceActivity { mAccount.setCryptoApp(mCryptoApp.getValue()); mAccount.setCryptoAutoSignature(mCryptoAutoSignature.isChecked()); mAccount.setCryptoAutoEncrypt(mCryptoAutoEncrypt.isChecked()); - mAccount.setCryptoDontSyncDrafts(mCryptoDontSyncDrafts.isChecked()); mAccount.setLocalStorageProviderId(mLocalStorageProvider.getValue()); // In webdav account we use the exact folder name also for inbox, diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index d739c5fe3..07bc16746 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -4059,7 +4059,7 @@ public class MessagingController implements Runnable { * @param message Message to save. * @return Message representing the entry in the local store. */ - public Message saveDraft(final Account account, final Message message, final boolean dontSyncDraft) { + public Message saveDraft(final Account account, final Message message) { Message localMessage = null; try { LocalStore localStore = account.getLocalStore(); @@ -4072,15 +4072,13 @@ public class MessagingController implements Runnable { // Fetch the message back from the store. This is the Message that's returned to the caller. localMessage = localFolder.getMessage(message.getUid()); localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true); - if (!dontSyncDraft) { - PendingCommand command = new PendingCommand(); - command.command = PENDING_COMMAND_APPEND; - command.arguments = new String[] { - localFolder.getName(), - localMessage.getUid() - }; - queuePendingCommand(account, command); - } + PendingCommand command = new PendingCommand(); + command.command = PENDING_COMMAND_APPEND; + command.arguments = new String[] { + localFolder.getName(), + localMessage.getUid() + }; + queuePendingCommand(account, command); processPendingCommands(account); } catch (MessagingException e) { diff --git a/src/com/fsck/k9/preferences/AccountSettings.java b/src/com/fsck/k9/preferences/AccountSettings.java index b9a9cf593..8008cac7d 100644 --- a/src/com/fsck/k9/preferences/AccountSettings.java +++ b/src/com/fsck/k9/preferences/AccountSettings.java @@ -28,7 +28,6 @@ public class AccountSettings { s.put("cryptoApp", new StringSetting(Apg.NAME)); s.put("cryptoAutoSignature", new BooleanSetting(false)); //s.put("cryptoAutoEncrypt", new BooleanSetting(false)); // added to version 3? - //s.put("cryptoDontSyncDrafts", new BooleanSetting(false)); // added to version 3? s.put("defaultQuotedTextShown", new BooleanSetting(Account.DEFAULT_QUOTED_TEXT_SHOWN)); s.put("deletePolicy", new DeletePolicySetting(Account.DELETE_POLICY_NEVER)); s.put("displayCount", new IntegerResourceSetting(K9.DEFAULT_VISIBLE_LIMIT, From 079996ebe57128dd9864425e453116797ba761eb Mon Sep 17 00:00:00 2001 From: ashley willis Date: Sat, 19 Nov 2011 18:05:24 -0600 Subject: [PATCH 3/5] refuse to save draft marked encrypted --- res/values/strings.xml | 3 +++ src/com/fsck/k9/activity/MessageCompose.java | 25 +++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index d16e2bb94..81fd592d8 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1023,6 +1023,9 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin Save draft message? Save or Discard this message? + Refuse to save draft message. + Refuse to save draft message marked encrypted. + This message can\'t be displayed because the charset \"%s\" wasn\'t found. Select text to copy. diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java index 09f984d0e..9c6e9b634 100644 --- a/src/com/fsck/k9/activity/MessageCompose.java +++ b/src/com/fsck/k9/activity/MessageCompose.java @@ -80,6 +80,7 @@ import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBody; public class MessageCompose extends K9Activity implements OnClickListener, OnFocusChangeListener { private static final int DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE = 1; + private static final int DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED = 2; private static final String ACTION_COMPOSE = "com.fsck.k9.intent.action.COMPOSE"; private static final String ACTION_REPLY = "com.fsck.k9.intent.action.REPLY"; @@ -1465,7 +1466,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc } private void saveIfNeeded() { - if (!mDraftNeedsSaving || mPreventDraftSaving || mPgpData.hasEncryptionKeys()) { + if (!mDraftNeedsSaving || mPreventDraftSaving || mPgpData.hasEncryptionKeys() || + mEncryptCheckbox.isChecked()) { return; } @@ -1892,7 +1894,11 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc onSend(); break; case R.id.save: - onSave(); + if (mEncryptCheckbox.isChecked()) { + showDialog(DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED); + } else { + onSave(); + } break; case R.id.discard: onDiscard(); @@ -1969,7 +1975,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc // This will be called either automatically for you on 2.0 // or later, or by the code above on earlier versions of the // platform. - if (mDraftNeedsSaving) { + if (mEncryptCheckbox.isChecked()) { + showDialog(DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED); + } + else if (mDraftNeedsSaving) { showDialog(DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE); } else { finish(); @@ -1996,6 +2005,16 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc } }) .create(); + case DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED: + return new AlertDialog.Builder(this) + .setTitle(R.string.refuse_to_save_draft_marked_encrypted_dlg_title) + .setMessage(R.string.refuse_to_save_draft_marked_encrypted_instructions_fmt) + .setNeutralButton(R.string.okay_action, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + dismissDialog(DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED); + } + }) + .create(); } return super.onCreateDialog(id); } From 18ad12eda758e5d8258376b5a24a14ee2b6d8481 Mon Sep 17 00:00:00 2001 From: ashley willis Date: Sat, 19 Nov 2011 18:15:20 -0600 Subject: [PATCH 4/5] incremented Settings.VERSION --- src/com/fsck/k9/controller/MessagingController.java | 1 + src/com/fsck/k9/preferences/AccountSettings.java | 2 +- src/com/fsck/k9/preferences/Settings.java | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index 07bc16746..2eedc119e 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -4072,6 +4072,7 @@ public class MessagingController implements Runnable { // Fetch the message back from the store. This is the Message that's returned to the caller. localMessage = localFolder.getMessage(message.getUid()); localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true); + PendingCommand command = new PendingCommand(); command.command = PENDING_COMMAND_APPEND; command.arguments = new String[] { diff --git a/src/com/fsck/k9/preferences/AccountSettings.java b/src/com/fsck/k9/preferences/AccountSettings.java index 8008cac7d..5dc4adf29 100644 --- a/src/com/fsck/k9/preferences/AccountSettings.java +++ b/src/com/fsck/k9/preferences/AccountSettings.java @@ -27,7 +27,7 @@ public class AccountSettings { s.put("chipColor", new ColorSetting(0xFF0000FF)); s.put("cryptoApp", new StringSetting(Apg.NAME)); s.put("cryptoAutoSignature", new BooleanSetting(false)); - //s.put("cryptoAutoEncrypt", new BooleanSetting(false)); // added to version 3? + s.put("cryptoAutoEncrypt", new BooleanSetting(false)); // added to version 3 s.put("defaultQuotedTextShown", new BooleanSetting(Account.DEFAULT_QUOTED_TEXT_SHOWN)); s.put("deletePolicy", new DeletePolicySetting(Account.DELETE_POLICY_NEVER)); s.put("displayCount", new IntegerResourceSetting(K9.DEFAULT_VISIBLE_LIMIT, diff --git a/src/com/fsck/k9/preferences/Settings.java b/src/com/fsck/k9/preferences/Settings.java index 60da58266..d97d94969 100644 --- a/src/com/fsck/k9/preferences/Settings.java +++ b/src/com/fsck/k9/preferences/Settings.java @@ -32,7 +32,7 @@ public class Settings { * * @see SettingsExporter */ - public static final int VERSION = 2; + public static final int VERSION = 3; public static Map validate(Map settings, Map importedSettings, boolean useDefaultValues) { From 94ba9bf71fa0b125180b9395b7d7f154acc5899e Mon Sep 17 00:00:00 2001 From: ashley willis Date: Mon, 21 Nov 2011 01:59:51 -0600 Subject: [PATCH 5/5] added comments, reworked a bit, and created warning dialog if some recipients don't have keys saved. --- res/values/strings.xml | 4 ++ src/com/fsck/k9/activity/MessageCompose.java | 56 +++++++++++++----- src/com/fsck/k9/crypto/Apg.java | 62 ++++++++++++++++++-- src/com/fsck/k9/crypto/CryptoProvider.java | 2 + src/com/fsck/k9/crypto/None.java | 10 ++++ 5 files changed, 115 insertions(+), 19 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 81fd592d8..84b8ec6f5 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -61,6 +61,7 @@ Forward Move Continue + Back Done Remove Discard @@ -1026,6 +1027,9 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin Refuse to save draft message. Refuse to save draft message marked encrypted. + Continue without public key? + One or more recipients do not have a saved public key. Continue? + This message can\'t be displayed because the charset \"%s\" wasn\'t found. Select text to copy. diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java index 9c6e9b634..86e618c20 100644 --- a/src/com/fsck/k9/activity/MessageCompose.java +++ b/src/com/fsck/k9/activity/MessageCompose.java @@ -81,6 +81,7 @@ import com.fsck.k9.mail.store.LocalStore.LocalAttachmentBody; public class MessageCompose extends K9Activity implements OnClickListener, OnFocusChangeListener { private static final int DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE = 1; private static final int DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED = 2; + private static final int DIALOG_CONTINUE_WITHOUT_PUBLIC_KEY = 3; private static final String ACTION_COMPOSE = "com.fsck.k9.intent.action.COMPOSE"; private static final String ACTION_REPLY = "com.fsck.k9.intent.action.REPLY"; @@ -210,6 +211,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc private PgpData mPgpData = null; private boolean mAutoEncrypt = false; + private boolean mContinueWithoutPublicKey = false; private String mReferences; private String mInReplyTo; @@ -444,6 +446,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc public void afterTextChanged(android.text.Editable s) { } }; + // For watching changes to the To:, Cc:, and Bcc: fields for auto-encryption on a matching + // address. TextWatcher recipientWatcher = new TextWatcher() { public void beforeTextChanged(CharSequence s, int start, int before, int after) { } @@ -455,10 +459,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc final CryptoProvider crypto = mAccount.getCryptoProvider(); if (mAutoEncrypt && crypto.isAvailable(getApplicationContext())) { for (Address address : getRecipientAddresses()) { - long ids[] = crypto.getPublicKeyIdsFromEmail(getApplicationContext(), - address.getAddress()); - if (ids != null && ids.length > 0) { + if (crypto.hasPublicKeyForEmail(getApplicationContext(), + address.getAddress())) { mEncryptCheckbox.setChecked(true); + mContinueWithoutPublicKey = false; break; } } @@ -950,6 +954,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc return Address.parseUnencoded(view.getText().toString().trim()); } + /* + * Returns an Address array of recipients this email will be sent to. + * @return Address array of recipients this email will be sent to. + */ private Address[] getRecipientAddresses() { String addresses = mToView.getText().toString() + mCcView.getText().toString() + mBccView.getText().toString(); @@ -1497,20 +1505,20 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc Toast.makeText(this, getString(R.string.message_compose_error_no_recipients), Toast.LENGTH_LONG).show(); return; } + final CryptoProvider crypto = mAccount.getCryptoProvider(); if (mEncryptCheckbox.isChecked() && !mPgpData.hasEncryptionKeys()) { mMessageFormat = MessageFormat.TEXT; // key selection before encryption StringBuilder emails = new StringBuilder(); - Address[][] addresses = new Address[][] { getAddresses(mToView), - getAddresses(mCcView), - getAddresses(mBccView) - }; - for (Address[] addressArray : addresses) { - for (Address address : addressArray) { - if (emails.length() != 0) { - emails.append(','); - } - emails.append(address.getAddress()); + for (Address address : getRecipientAddresses()) { + if (emails.length() != 0) { + emails.append(','); + } + emails.append(address.getAddress()); + if (!mContinueWithoutPublicKey && + !crypto.hasPublicKeyForEmail(this, address.getAddress())) { + showDialog(DIALOG_CONTINUE_WITHOUT_PUBLIC_KEY); + return; } } if (emails.length() != 0) { @@ -1519,7 +1527,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc emails.append(mIdentity.getEmail()); mPreventDraftSaving = true; - if (!mAccount.getCryptoProvider().selectEncryptionKeys(MessageCompose.this, emails.toString(), mPgpData)) { + if (!crypto.selectEncryptionKeys(MessageCompose.this, emails.toString(), mPgpData)) { mPreventDraftSaving = false; } return; @@ -1528,7 +1536,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc if (mPgpData.getEncryptedData() == null) { String text = buildText(false).getText(); mPreventDraftSaving = true; - if (!mAccount.getCryptoProvider().encrypt(this, text, mPgpData)) { + if (!crypto.encrypt(this, text, mPgpData)) { mPreventDraftSaving = false; } return; @@ -2015,6 +2023,24 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc } }) .create(); + case DIALOG_CONTINUE_WITHOUT_PUBLIC_KEY: + return new AlertDialog.Builder(this) + .setTitle(R.string.continue_without_public_key_dlg_title) + .setMessage(R.string.continue_without_public_key_instructions_fmt) + .setPositiveButton(R.string.continue_action, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + dismissDialog(DIALOG_CONTINUE_WITHOUT_PUBLIC_KEY); + mContinueWithoutPublicKey = true; + onSend(); + } + }) + .setNegativeButton(R.string.back_action, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int whichButton) { + dismissDialog(DIALOG_CONTINUE_WITHOUT_PUBLIC_KEY); + mContinueWithoutPublicKey = false; + } + }) + .create(); } return super.onCreateDialog(id); } diff --git a/src/com/fsck/k9/crypto/Apg.java b/src/com/fsck/k9/crypto/Apg.java index 350a6c284..58af5ac49 100644 --- a/src/com/fsck/k9/crypto/Apg.java +++ b/src/com/fsck/k9/crypto/Apg.java @@ -243,11 +243,9 @@ public class Apg extends CryptoProvider { public long[] getPublicKeyIdsFromEmail(Context context, String email) { long ids[] = null; try { - Uri contentUri = Uri.withAppendedPath(Apg.CONTENT_URI_PUBLIC_KEY_RING_BY_EMAILS, - email); + Uri contentUri = Uri.withAppendedPath(Apg.CONTENT_URI_PUBLIC_KEY_RING_BY_EMAILS, email); Cursor c = context.getContentResolver().query(contentUri, - new String[] { "master_key_id" }, - null, null, null); + new String[] { "master_key_id" }, null, null, null); if (c != null && c.getCount() > 0) { ids = new long[c.getCount()]; while (c.moveToNext()) { @@ -267,6 +265,62 @@ public class Apg extends CryptoProvider { return ids; } + /** + * Find out if a given email has a secret key. + * + * @param context + * @param email The email in question. + * @return true if there is a secret key for this email. + */ + @Override + public boolean hasSecretKeyForEmail(Context context, String email) { + try { + Uri contentUri = Uri.withAppendedPath(Apg.CONTENT_URI_SECRET_KEY_RING_BY_EMAILS, email); + Cursor c = context.getContentResolver().query(contentUri, + new String[] { "master_key_id" }, null, null, null); + if (c != null && c.getCount() > 0) { + c.close(); + return true; + } + if (c != null) { + c.close(); + } + } catch (SecurityException e) { + Toast.makeText(context, + context.getResources().getString(R.string.insufficient_apg_permissions), + Toast.LENGTH_LONG).show(); + } + return false; + } + + /** + * Find out if a given email has a public key. + * + * @param context + * @param email The email in question. + * @return true if there is a public key for this email. + */ + @Override + public boolean hasPublicKeyForEmail(Context context, String email) { + try { + Uri contentUri = Uri.withAppendedPath(Apg.CONTENT_URI_PUBLIC_KEY_RING_BY_EMAILS, email); + Cursor c = context.getContentResolver().query(contentUri, + new String[] { "master_key_id" }, null, null, null); + if (c != null && c.getCount() > 0) { + c.close(); + return true; + } + if (c != null) { + c.close(); + } + } catch (SecurityException e) { + Toast.makeText(context, + context.getResources().getString(R.string.insufficient_apg_permissions), + Toast.LENGTH_LONG).show(); + } + return false; + } + /** * Get the user id based on the key id. * diff --git a/src/com/fsck/k9/crypto/CryptoProvider.java b/src/com/fsck/k9/crypto/CryptoProvider.java index 373a62df9..73ce8ba4d 100644 --- a/src/com/fsck/k9/crypto/CryptoProvider.java +++ b/src/com/fsck/k9/crypto/CryptoProvider.java @@ -25,6 +25,8 @@ abstract public class CryptoProvider { abstract public boolean decrypt(Activity activity, String data, PgpData pgpData); abstract public long[] getSecretKeyIdsFromEmail(Context context, String email); abstract public long[] getPublicKeyIdsFromEmail(Context context, String email); + abstract public boolean hasSecretKeyForEmail(Context context, String email); + abstract public boolean hasPublicKeyForEmail(Context context, String email); abstract public String getUserId(Context context, long keyId); abstract public String getName(); abstract public boolean test(Context context); diff --git a/src/com/fsck/k9/crypto/None.java b/src/com/fsck/k9/crypto/None.java index cf2af7bc4..2ce271e3d 100644 --- a/src/com/fsck/k9/crypto/None.java +++ b/src/com/fsck/k9/crypto/None.java @@ -42,6 +42,16 @@ public class None extends CryptoProvider { return null; } + @Override + public boolean hasSecretKeyForEmail(Context context, String email) { + return false; + } + + @Override + public boolean hasPublicKeyForEmail(Context context, String email) { + return false; + } + @Override public String getUserId(Context context, long keyId) { return null;