1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-11-02 08:35:08 -04:00

Merge pull request #102 from ashleywillis/pgp-fixes

Pgp-related fixes (issues 2152, 2900, 3673, partially 1424)
This commit is contained in:
Andrew Chen 2011-11-21 07:28:16 -08:00
commit 6fc22283db
10 changed files with 246 additions and 20 deletions

View File

@ -61,6 +61,7 @@
<string name="forward_action">Forward</string>
<string name="move_action">Move</string>
<string name="continue_action">Continue</string>
<string name="back_action">Back</string>
<string name="done_action">Done</string> <!-- Used to complete a multi-step process -->
<string name="remove_action">Remove</string>
<string name="discard_action">Discard</string>
@ -595,6 +596,8 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
<string name="account_settings_crypto_app_not_available">not available</string>
<string name="account_settings_crypto_auto_signature">Auto-sign</string>
<string name="account_settings_crypto_auto_signature_summary">Use the account\'s email address to guess the signature key.</string>
<string name="account_settings_crypto_auto_encrypt">Auto-encrypt</string>
<string name="account_settings_crypto_auto_encrypt_summary">Automatically set encrypt if a public key matches a recipient.</string>
<string name="account_settings_mail_check_frequency_label">Folder poll frequency</string>
<string name="account_settings_second_class_check_frequency_label">2nd class check frequency</string>
@ -1021,6 +1024,12 @@ Welcome to K-9 Mail setup. K-9 is an open source mail client for Android origin
<string name="save_or_discard_draft_message_dlg_title">Save draft message?</string>
<string name="save_or_discard_draft_message_instructions_fmt">Save or Discard this message?</string>
<string name="refuse_to_save_draft_marked_encrypted_dlg_title">Refuse to save draft message.</string>
<string name="refuse_to_save_draft_marked_encrypted_instructions_fmt">Refuse to save draft message marked encrypted.</string>
<string name="continue_without_public_key_dlg_title">Continue without public key?</string>
<string name="continue_without_public_key_instructions_fmt">One or more recipients do not have a saved public key. Continue?</string>
<string name="charset_not_found">This message can\'t be displayed because the charset \"<xliff:g id="charset">%s</xliff:g>\" wasn\'t found.</string>
<string name="select_text_now">Select text to copy.</string>

View File

@ -474,6 +474,13 @@
android:summary="@string/account_settings_crypto_auto_signature_summary"
android:dependency="crypto_app"/>
<CheckBoxPreference
android:persistent="false"
android:key="crypto_auto_encrypt"
android:title="@string/account_settings_crypto_auto_encrypt"
android:summary="@string/account_settings_crypto_auto_encrypt_summary"
android:dependency="crypto_app"/>
</PreferenceScreen>
</PreferenceScreen>

View File

@ -149,6 +149,7 @@ public class Account implements BaseAccount {
private boolean mSyncRemoteDeletions;
private String mCryptoApp;
private boolean mCryptoAutoSignature;
private boolean mCryptoAutoEncrypt;
private CryptoProvider mCryptoProvider = null;
@ -240,6 +241,7 @@ public class Account implements BaseAccount {
mSyncRemoteDeletions = true;
mCryptoApp = Apg.NAME;
mCryptoAutoSignature = false;
mCryptoAutoEncrypt = false;
mEnabled = true;
searchableFolders = Searchable.ALL;
@ -408,6 +410,7 @@ public class Account implements BaseAccount {
mCryptoApp = prefs.getString(mUuid + ".cryptoApp", Apg.NAME);
mCryptoAutoSignature = prefs.getBoolean(mUuid + ".cryptoAutoSignature", false);
mCryptoAutoEncrypt = prefs.getBoolean(mUuid + ".cryptoAutoEncrypt", false);
mEnabled = prefs.getBoolean(mUuid + ".enabled", true);
}
@ -480,6 +483,7 @@ public class Account implements BaseAccount {
editor.remove(mUuid + ".stripSignature");
editor.remove(mUuid + ".cryptoApp");
editor.remove(mUuid + ".cryptoAutoSignature");
editor.remove(mUuid + ".cryptoAutoEncrypt");
editor.remove(mUuid + ".enabled");
editor.remove(mUuid + ".enableMoveButtons");
editor.remove(mUuid + ".hideMoveButtonsEnum");
@ -643,6 +647,7 @@ 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 + ".enabled", mEnabled);
editor.putBoolean(mUuid + ".vibrate", mNotificationSetting.shouldVibrate());
@ -1466,6 +1471,14 @@ public class Account implements BaseAccount {
mCryptoAutoSignature = cryptoAutoSignature;
}
public boolean isCryptoAutoEncrypt() {
return mCryptoAutoEncrypt;
}
public void setCryptoAutoEncrypt(boolean cryptoAutoEncrypt) {
mCryptoAutoEncrypt = cryptoAutoEncrypt;
}
public String getInboxFolderName() {
return mInboxFolderName;
}

View File

@ -79,6 +79,8 @@ 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";
@ -207,6 +209,8 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
private ImageButton mAddBccFromContacts;
private PgpData mPgpData = null;
private boolean mAutoEncrypt = false;
private boolean mContinueWithoutPublicKey = false;
private String mReferences;
private String mInReplyTo;
@ -441,6 +445,30 @@ 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) { }
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()) {
if (crypto.hasPublicKeyForEmail(getApplicationContext(),
address.getAddress())) {
mEncryptCheckbox.setChecked(true);
mContinueWithoutPublicKey = false;
break;
}
}
}
}
};
TextWatcher sigwatcher = new TextWatcher() {
public void beforeTextChanged(CharSequence s, int start,
int before, int after) { }
@ -454,9 +482,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);
@ -615,6 +643,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();
@ -648,6 +681,7 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
}
}
updateEncryptLayout();
mAutoEncrypt = mAccount.isCryptoAutoEncrypt();
} else {
mEncryptLayout.setVisibility(View.GONE);
}
@ -773,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);
@ -916,6 +951,16 @@ 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();
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
@ -957,7 +1002,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);
}
}
@ -1427,7 +1471,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;
}
@ -1457,19 +1502,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) {
@ -1478,7 +1524,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;
@ -1487,7 +1533,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;
@ -1853,7 +1899,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();
@ -1927,7 +1977,10 @@ public class MessageCompose extends K9Activity implements OnClickListener, OnFoc
@Override
public void onBackPressed() {
if (mDraftNeedsSaving) {
if (mEncryptCheckbox.isChecked()) {
showDialog(DIALOG_REFUSE_TO_SAVE_DRAFT_MARKED_ENCRYPTED);
}
else if (mDraftNeedsSaving) {
showDialog(DIALOG_SAVE_OR_DISCARD_DRAFT_MESSAGE);
} else {
super.onBackPressed();
@ -1954,6 +2007,34 @@ 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();
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);
}

View File

@ -96,6 +96,7 @@ 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_LOCAL_STORAGE_PROVIDER = "local_storage_provider";
@ -160,6 +161,7 @@ public class AccountSettings extends K9PreferenceActivity {
private ListPreference mMaxPushFolders;
private ListPreference mCryptoApp;
private CheckBoxPreference mCryptoAutoSignature;
private CheckBoxPreference mCryptoAutoEncrypt;
private ListPreference mLocalStorageProvider;
@ -680,14 +682,19 @@ 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());
handleCryptoAppDependencies();
}
private void handleCryptoAppDependencies() {
if ("".equals(mCryptoApp.getValue())) {
mCryptoAutoSignature.setEnabled(false);
mCryptoAutoEncrypt.setEnabled(false);
} else {
mCryptoAutoSignature.setEnabled(true);
mCryptoAutoEncrypt.setEnabled(true);
}
}
@ -733,6 +740,7 @@ public class AccountSettings extends K9PreferenceActivity {
mAccount.setStripSignature(mStripSignature.isChecked());
mAccount.setCryptoApp(mCryptoApp.getValue());
mAccount.setCryptoAutoSignature(mCryptoAutoSignature.isChecked());
mAccount.setCryptoAutoEncrypt(mCryptoAutoEncrypt.isChecked());
mAccount.setLocalStorageProviderId(mLocalStorageProvider.getValue());
// In webdav account we use the exact folder name also for inbox,

View File

@ -232,6 +232,95 @@ 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;
}
/**
* 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.
*

View File

@ -24,6 +24,9 @@ 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 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);

View File

@ -37,6 +37,21 @@ public class None extends CryptoProvider {
return null;
}
@Override
public long[] getPublicKeyIdsFromEmail(Context context, String email) {
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;

View File

@ -27,6 +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("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,

View File

@ -32,7 +32,7 @@ public class Settings {
*
* @see SettingsExporter
*/
public static final int VERSION = 2;
public static final int VERSION = 3;
public static Map<String, String> validate(Map<String, SettingsDescription> settings,
Map<String, String> importedSettings, boolean useDefaultValues) {