diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index 5da6cb559a..7865eacfe7 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -1804,14 +1804,24 @@ public class AdapterMessage extends RecyclerView.Adapter 0) { + EntityIdentity identity = db.identity().getIdentity(message.identity); + if (identity == null) + return null; - boolean outgoing; - if (message.identity == null || message.from == null || message.from.length == 0) - outgoing = EntityFolder.isOutgoing(folder.type); - else { - String from = ((InternetAddress) message.from[0]).getAddress(); - EntityIdentity identity = db.identity().getIdentity(message.identity); - outgoing = MessageHelper.canonicalAddress(identity.email).equals(MessageHelper.canonicalAddress(from)); + for (Address sender : senders) + if (MessageHelper.similarAddress(sender, identity.email)) { + outgoing = true; + break; + } + } } return (outgoing ? message.to : message.from); @@ -1819,6 +1829,9 @@ public class AdapterMessage extends RecyclerView.Adapter identities = db.identity().getIdentities(folder.account); + if (identities != null) { + for (Address address : addresses) + for (EntityIdentity identity : identities) + if (MessageHelper.sameAddress(address, identity.email)) + return identity; + + for (Address address : addresses) + for (EntityIdentity identity : identities) + if (MessageHelper.similarAddress(address, identity.email)) + return identity; } return null; @@ -2095,18 +2091,18 @@ class Core { // Check if from self if (type == EntityContact.TYPE_FROM && recipients != null && recipients.length > 0) { - boolean me = true; - for (Address reply : recipients) { - String email = ((InternetAddress) reply).getAddress(); - String canonical = MessageHelper.canonicalAddress(email); - if (!TextUtils.isEmpty(email) && - db.identity().getIdentity(folder.account, email) == null && - (canonical.equals(email) || - db.identity().getIdentity(folder.account, canonical) == null)) { - me = false; - break; + boolean me = false; + List identities = db.identity().getIdentities(folder.account); + if (identities != null) + for (Address recipient : recipients) { + for (EntityIdentity identity : identities) + if (MessageHelper.similarAddress(recipient, identity.email)) { + me = true; + break; + } + if (me) + break; } - } if (me) recipients = message.to; } diff --git a/app/src/main/java/eu/faircode/email/EntityMessage.java b/app/src/main/java/eu/faircode/email/EntityMessage.java index c512c4ce7e..87aa7e428a 100644 --- a/app/src/main/java/eu/faircode/email/EntityMessage.java +++ b/app/src/main/java/eu/faircode/email/EntityMessage.java @@ -40,7 +40,6 @@ import java.util.Objects; import java.util.UUID; import javax.mail.Address; -import javax.mail.internet.InternetAddress; import static androidx.room.ForeignKey.CASCADE; import static androidx.room.ForeignKey.SET_NULL; @@ -164,12 +163,14 @@ public class EntityMessage implements Serializable { } boolean replySelf(String via) { - Address[] replying = (reply == null || reply.length == 0 ? from : reply); - if (replying == null || replying.length != 1) - return false; + Address[] senders = (reply == null || reply.length == 0 ? from : reply); - String recipient = MessageHelper.canonicalAddress(((InternetAddress) replying[0]).getAddress()); - return recipient.equals(via); + if (senders != null) + for (Address sender : senders) + if (MessageHelper.similarAddress(sender, via)) + return true; + + return false; } Address[] getAllRecipients(String via) { @@ -182,11 +183,9 @@ public class EntityMessage implements Serializable { addresses.addAll(Arrays.asList(cc)); // Filter self - for (Address address : new ArrayList<>(addresses)) { - String recipient = MessageHelper.canonicalAddress(((InternetAddress) address).getAddress()); - if (recipient.equals(via)) + for (Address address : new ArrayList<>(addresses)) + if (MessageHelper.similarAddress(address, via)) addresses.remove(address); - } return addresses.toArray(new Address[0]); } diff --git a/app/src/main/java/eu/faircode/email/FragmentCompose.java b/app/src/main/java/eu/faircode/email/FragmentCompose.java index 6f5422a339..e5ef750d1b 100644 --- a/app/src/main/java/eu/faircode/email/FragmentCompose.java +++ b/app/src/main/java/eu/faircode/email/FragmentCompose.java @@ -2021,78 +2021,61 @@ public class FragmentCompose extends FragmentBase { crumb.put("action", action); Log.breadcrumb("compose", crumb); - EntityMessage draft; + DraftData data = new DraftData(); DB db = DB.getInstance(context); try { db.beginTransaction(); - draft = db.message().getMessage(id); - if (draft == null || draft.ui_hide != 0) { + data.identities = db.identity().getComposableIdentities(null); + if (data.identities == null || data.identities.size() == 0) + throw new IllegalStateException(getString(R.string.title_no_identities)); + + data.draft = db.message().getMessage(id); + if (data.draft == null || data.draft.ui_hide != 0) { // New draft if ("edit".equals(action)) - throw new MessageRemovedException("Draft for edit was deleted hide=" + (draft != null)); + throw new MessageRemovedException("Draft for edit was deleted hide=" + (data.draft != null)); - EntityFolder drafts; EntityMessage ref = db.message().getMessage(reference); - if (ref == null) { - long aid = args.getLong("account", -1); - if (aid < 0) - drafts = db.folder().getPrimaryDrafts(); - else { - drafts = db.folder().getFolderByType(aid, EntityFolder.DRAFTS); - if (drafts == null) - drafts = db.folder().getPrimaryDrafts(); - } - if (drafts == null) - throw new IllegalArgumentException(context.getString(R.string.title_no_primary_drafts)); - } else { - drafts = db.folder().getFolderByType(ref.account, EntityFolder.DRAFTS); - if (drafts == null) - drafts = db.folder().getPrimaryDrafts(); - if (drafts == null) - throw new IllegalArgumentException(context.getString(R.string.title_no_primary_drafts)); - } String body = ""; - draft = new EntityMessage(); - draft.account = drafts.account; - draft.folder = drafts.id; - draft.msgid = EntityMessage.generateMessageId(); + data.draft = new EntityMessage(); + data.draft.msgid = EntityMessage.generateMessageId(); if (ref == null) { - draft.thread = draft.msgid; + data.draft.thread = data.draft.msgid; try { String to = args.getString("to"); - draft.to = (TextUtils.isEmpty(to) ? null : InternetAddress.parse(to)); + data.draft.to = (TextUtils.isEmpty(to) ? null : InternetAddress.parse(to)); } catch (AddressException ex) { Log.w(ex); } try { String cc = args.getString("cc"); - draft.cc = (TextUtils.isEmpty(cc) ? null : InternetAddress.parse(cc)); + data.draft.cc = (TextUtils.isEmpty(cc) ? null : InternetAddress.parse(cc)); } catch (AddressException ex) { Log.w(ex); } try { String bcc = args.getString("bcc"); - draft.bcc = (TextUtils.isEmpty(bcc) ? null : InternetAddress.parse(bcc)); + data.draft.bcc = (TextUtils.isEmpty(bcc) ? null : InternetAddress.parse(bcc)); } catch (AddressException ex) { Log.w(ex); } - draft.subject = args.getString("subject", ""); + data.draft.subject = args.getString("subject", ""); body = args.getString("body", ""); body = body.replaceAll("\\r?\\n", "
"); if (answer > 0) { EntityAnswer a = db.answer().getAnswer(answer); if (a != null) { - draft.subject = a.name; + data.draft.subject = a.name; body = EntityAnswer.getAnswerText(a, null) + body; } } @@ -2115,40 +2098,42 @@ public class FragmentCompose extends FragmentBase { String s = ((InternetAddress) sender[0]).getAddress(); int at = s.indexOf('@'); if (at > 0) - draft.extra = s.substring(0, at); + data.draft.extra = s.substring(0, at); } - draft.references = (ref.references == null ? "" : ref.references + " ") + ref.msgid; - draft.inreplyto = ref.msgid; - draft.thread = ref.thread; + data.draft.references = (ref.references == null ? "" : ref.references + " ") + ref.msgid; + data.draft.inreplyto = ref.msgid; + data.draft.thread = ref.thread; String via = null; if (ref.identity != null) { EntityIdentity identity = db.identity().getIdentity(ref.identity); - draft.from = new Address[]{new InternetAddress(identity.email, identity.name)}; - via = MessageHelper.canonicalAddress(identity.email); + if (identity != null) { + data.draft.from = new Address[]{new InternetAddress(identity.email, identity.name)}; + via = identity.email; + } } if ("list".equals(action) && ref.list_post != null) - draft.to = ref.list_post; + data.draft.to = ref.list_post; else if ("receipt".equals(action) && ref.receipt_to != null) - draft.to = ref.receipt_to; + data.draft.to = ref.receipt_to; else { // Prevent replying to self if (ref.replySelf(via)) { - draft.to = ref.to; - draft.from = ref.from; + data.draft.to = ref.to; + data.draft.from = ref.from; } else - draft.to = (ref.reply == null || ref.reply.length == 0 ? ref.from : ref.reply); + data.draft.to = (ref.reply == null || ref.reply.length == 0 ? ref.from : ref.reply); } if ("reply_all".equals(action)) - draft.cc = ref.getAllRecipients(via); + data.draft.cc = ref.getAllRecipients(via); else if ("receipt".equals(action)) - draft.receipt_request = true; + data.draft.receipt_request = true; } else if ("forward".equals(action) || "editasnew".equals(action)) - draft.thread = draft.msgid; // new thread + data.draft.thread = data.draft.msgid; // new thread String subject = (ref.subject == null ? "" : ref.subject); if ("reply".equals(action) || "reply_all".equals(action)) { @@ -2156,23 +2141,23 @@ public class FragmentCompose extends FragmentBase { String re = context.getString(R.string.title_subject_reply, ""); subject = subject.replaceAll("(?i)" + Pattern.quote(re.trim()), "").trim(); } - draft.subject = context.getString(R.string.title_subject_reply, subject); + data.draft.subject = context.getString(R.string.title_subject_reply, subject); } else if ("forward".equals(action)) { if (prefix_once) { String fwd = context.getString(R.string.title_subject_forward, ""); subject = subject.replaceAll("(?i)" + Pattern.quote(fwd.trim()), "").trim(); } - draft.subject = context.getString(R.string.title_subject_forward, subject); + data.draft.subject = context.getString(R.string.title_subject_forward, subject); } else if ("editasnew".equals(action)) { - draft.subject = ref.subject; + data.draft.subject = ref.subject; if (ref.content) { String html = Helper.readText(ref.getFile(context)); body = HtmlHelper.sanitize(context, html, true); } } else if ("list".equals(action)) { - draft.subject = ref.subject; + data.draft.subject = ref.subject; } else if ("receipt".equals(action)) { - draft.subject = context.getString(R.string.title_receipt_subject, subject); + data.draft.subject = context.getString(R.string.title_receipt_subject, subject); Configuration configuration = new Configuration(context.getResources().getConfiguration()); configuration.setLocale(new Locale("en")); @@ -2182,79 +2167,114 @@ public class FragmentCompose extends FragmentBase { if (!Locale.getDefault().getLanguage().equals("en")) body += "

" + res.getString(R.string.title_receipt_text) + "

"; } else if ("participation".equals(action)) - draft.subject = status + ": " + ref.subject; + data.draft.subject = status + ": " + ref.subject; - draft.plain_only = ref.plain_only; + data.draft.plain_only = ref.plain_only; if (answer > 0) - body = EntityAnswer.getAnswerText(context, answer, draft.to) + body; + body = EntityAnswer.getAnswerText(context, answer, data.draft.to) + body; } if (plain_only) - draft.plain_only = true; + data.draft.plain_only = true; // Select identity matching from address - int icount = 0; - EntityIdentity first = null; - EntityIdentity primary = null; - List identities = db.identity().getComposableIdentities(null); - - int iindex = -1; - do { - String from = null; - if (iindex >= 0) - from = MessageHelper.canonicalAddress(((InternetAddress) draft.from[iindex]).getAddress()); - for (EntityIdentity identity : identities) { - String email = MessageHelper.canonicalAddress(identity.email); - if (email.equals(from)) { - draft.identity = identity.id; - draft.from = new InternetAddress[]{new InternetAddress(identity.email, identity.name)}; + EntityIdentity selected = null; + long aid = args.getLong("account", -1); + + if (data.draft.from != null && data.draft.from.length > 0) { + for (Address sender : data.draft.from) + for (EntityIdentity identity : data.identities) + if (identity.account.equals(aid) && + MessageHelper.sameAddress(sender, identity.email)) { + selected = identity; + break; + } + + if (selected == null) + for (Address sender : data.draft.from) + for (EntityIdentity identity : data.identities) + if (identity.account.equals(aid) && + MessageHelper.similarAddress(sender, identity.email)) { + selected = identity; + break; + } + + if (selected == null) + for (Address sender : data.draft.from) + for (EntityIdentity identity : data.identities) + if (MessageHelper.sameAddress(sender, identity.email)) { + selected = identity; + break; + } + + if (selected == null) + for (Address sender : data.draft.from) + for (EntityIdentity identity : data.identities) + if (MessageHelper.similarAddress(sender, identity.email)) { + selected = identity; + break; + } + } + + if (selected == null) + for (EntityIdentity identity : data.identities) + if (identity.account.equals(aid) && identity.primary) { + selected = identity; + break; + } + + if (selected == null) + for (EntityIdentity identity : data.identities) + if (identity.account.equals(aid)) { + selected = identity; break; } - if (identity.account.equals(draft.account)) { - icount++; - if (identity.primary) - primary = identity; - if (first == null) - first = identity; + + if (selected == null) + for (EntityIdentity identity : data.identities) + if (identity.primary) { + selected = identity; + break; } - } - if (draft.identity != null) - break; - iindex++; - } while (iindex < (draft.from == null ? -1 : draft.from.length)); - - // Select identity - if (draft.identity == null) { - if (primary != null) { - draft.identity = primary.id; - draft.from = new InternetAddress[]{new InternetAddress(primary.email, primary.name)}; - } else if (first != null && icount == 1) { - draft.identity = first.id; - draft.from = new InternetAddress[]{new InternetAddress(first.email, first.name)}; + if (selected == null) + for (EntityIdentity identity : data.identities) { + selected = identity; + break; } - } - draft.sender = MessageHelper.getSortKey(draft.from); - Uri lookupUri = ContactInfo.getLookupUri(context, draft.from); - draft.avatar = (lookupUri == null ? null : lookupUri.toString()); + if (selected == null) + throw new IllegalArgumentException(context.getString(R.string.title_no_identities)); + + EntityFolder drafts = db.folder().getFolderByType(selected.account, EntityFolder.DRAFTS); + if (drafts == null) + throw new IllegalArgumentException(context.getString(R.string.title_no_primary_drafts)); + + data.draft.account = drafts.account; + data.draft.folder = drafts.id; + data.draft.identity = selected.id; + data.draft.from = new InternetAddress[]{new InternetAddress(selected.email, selected.name)}; + + data.draft.sender = MessageHelper.getSortKey(data.draft.from); + Uri lookupUri = ContactInfo.getLookupUri(context, data.draft.from); + data.draft.avatar = (lookupUri == null ? null : lookupUri.toString()); - draft.received = new Date().getTime(); - draft.seen = true; - draft.ui_seen = true; + data.draft.received = new Date().getTime(); + data.draft.seen = true; + data.draft.ui_seen = true; - draft.id = db.message().insertMessage(draft); - Helper.writeText(draft.getFile(context), body); + data.draft.id = db.message().insertMessage(data.draft); + Helper.writeText(data.draft.getFile(context), body); - db.message().setMessageContent(draft.id, + db.message().setMessageContent(data.draft.id, true, - draft.plain_only, + data.draft.plain_only, HtmlHelper.getPreview(body), null); if ("participation".equals(action)) { EntityAttachment attachment = new EntityAttachment(); - attachment.message = draft.id; + attachment.message = data.draft.id; attachment.sequence = 1; attachment.name = "meeting.ics"; attachment.type = "text/calendar"; @@ -2312,14 +2332,14 @@ public class FragmentCompose extends FragmentBase { Html.escapeHtml(new Date(ref.received).toString()), Html.escapeHtml(MessageHelper.formatAddresses(ref.from)), refText); - Helper.writeText(draft.getRefFile(context), refBody); + Helper.writeText(data.draft.getRefFile(context), refBody); } if ("new".equals(action)) { ArrayList uris = args.getParcelableArrayList("attachments"); if (uris != null) for (Uri uri : uris) - addAttachment(context, draft.id, uri, false); + addAttachment(context, data.draft.id, uri, false); } else if (ref != null && ("reply".equals(action) || "reply_all".equals(action) || "forward".equals(action) || "editasnew".equals(action))) { @@ -2328,8 +2348,8 @@ public class FragmentCompose extends FragmentBase { for (EntityAttachment attachment : attachments) if (attachment.encryption != null && attachment.encryption.equals(EntityAttachment.PGP_MESSAGE)) { - draft.encrypt = true; - db.message().setMessageEncrypt(draft.id, true); + data.draft.encrypt = true; + db.message().setMessageEncrypt(data.draft.id, true); } else if (attachment.encryption == null && ("forward".equals(action) || "editasnew".equals(action) || @@ -2338,7 +2358,7 @@ public class FragmentCompose extends FragmentBase { File source = attachment.getFile(context); attachment.id = null; - attachment.message = draft.id; + attachment.message = data.draft.id; attachment.sequence = ++sequence; attachment.id = db.attachment().insertAttachment(attachment); @@ -2352,18 +2372,18 @@ public class FragmentCompose extends FragmentBase { } } - EntityOperation.queue(context, draft, EntityOperation.ADD); + EntityOperation.queue(context, data.draft, EntityOperation.ADD); } else { - if (!draft.content) { - if (draft.uid == null) + if (!data.draft.content) { + if (data.draft.uid == null) throw new IllegalStateException("Draft without uid"); - EntityOperation.queue(context, draft, EntityOperation.BODY); + EntityOperation.queue(context, data.draft, EntityOperation.BODY); } - List attachments = db.attachment().getAttachments(draft.id); + List attachments = db.attachment().getAttachments(data.draft.id); for (EntityAttachment attachment : attachments) if (!attachment.available) - EntityOperation.queue(context, draft, EntityOperation.ATTACHMENT, attachment.id); + EntityOperation.queue(context, data.draft, EntityOperation.ATTACHMENT, attachment.id); } db.setTransactionSuccessful(); @@ -2371,18 +2391,11 @@ public class FragmentCompose extends FragmentBase { db.endTransaction(); } - DraftData data = new DraftData(); - data.draft = draft; - data.identities = db.identity().getComposableIdentities(null); - return data; } @Override protected void onExecuted(Bundle args, final DraftData data) { - if (data.identities == null || data.identities.size() == 0) - throw new IllegalStateException(getString(R.string.title_no_identities)); - working = data.draft.id; final String action = getArguments().getString("action"); diff --git a/app/src/main/java/eu/faircode/email/MessageHelper.java b/app/src/main/java/eu/faircode/email/MessageHelper.java index a8d02cce05..105e7c0080 100644 --- a/app/src/main/java/eu/faircode/email/MessageHelper.java +++ b/app/src/main/java/eu/faircode/email/MessageHelper.java @@ -713,14 +713,30 @@ public class MessageHelper { return TextUtils.join(", ", formatted); } - static String canonicalAddress(String address) { - String[] a = address.split("@"); - if (a.length > 0) { - String[] extra = a[0].split("\\+"); - if (extra.length > 0) - a[0] = extra[0]; - } - return TextUtils.join("@", a).toLowerCase(); + static boolean sameAddress(Address address1, String email2) { + String email1 = ((InternetAddress) address1).getAddress(); + return email1.equalsIgnoreCase(email2); + } + + static boolean similarAddress(Address address1, String email2) { + String email1 = ((InternetAddress) address1).getAddress(); + + if (!email1.contains("@") || !email2.contains("@")) + return false; + + String[] e1 = email1.split("@"); + String[] e2 = email2.split("@"); + + if (e1.length != 2 || e2.length != 2) + return false; + + // Domain + if (!e1[1].equalsIgnoreCase(e2[1])) + return false; + + String user1 = (e1[0].contains("+") ? e1[0].split("\\+")[0] : e1[0]); + + return user1.equalsIgnoreCase(e2[0]); } static String decodeMime(String text) {