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;