Rewritten identity handling

This commit is contained in:
M66B 2019-09-22 20:03:31 +02:00
parent a60d06c348
commit 6242348d32
5 changed files with 210 additions and 174 deletions

View File

@ -1804,14 +1804,24 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
return null;
EntityFolder folder = db.folder().getFolder(message.folder);
if (folder == 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));
boolean outgoing = EntityFolder.isOutgoing(folder.type);
if (message.identity != null) {
Address[] senders = (message.reply == null || message.reply.length == 0 ? message.from : message.reply);
if (senders != null && senders.length > 0) {
EntityIdentity identity = db.identity().getIdentity(message.identity);
if (identity == null)
return null;
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<AdapterMessage.ViewHold
@Override
protected void onExecuted(Bundle args, Address[] addresses) {
if (addresses == null || addresses.length == 0)
return;
String query = ((InternetAddress) addresses[0]).getAddress();
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
lbm.sendBroadcast(
@ -2174,8 +2187,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
if (amessage == null || !amessage.id.equals(message.id))
return;
String via = (identity == null ? null : MessageHelper.canonicalAddress(identity.email));
Address[] recipients = message.getAllRecipients(via);
Address[] recipients = message.getAllRecipients(identity == null ? null : identity.email);
View anchor = bnvActions.findViewById(R.id.action_reply);
PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(context, powner, anchor);

View File

@ -2031,21 +2031,17 @@ class Core {
}
// Search for matching identity
for (Address address : addresses) {
String email = ((InternetAddress) address).getAddress();
if (!TextUtils.isEmpty(email)) {
EntityIdentity ident = db.identity().getIdentity(folder.account, email);
if (ident != null)
return ident;
List<EntityIdentity> 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;
String canonical = MessageHelper.canonicalAddress(email);
if (canonical.equals(email))
continue;
ident = db.identity().getIdentity(folder.account, canonical);
if (ident != null)
return ident;
}
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<EntityIdentity> 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;
}

View File

@ -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]);
}

View File

@ -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", "<br>");
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 += "<p>" + res.getString(R.string.title_receipt_text) + "</p>";
} 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<TupleIdentityEx> identities = db.identity().getComposableIdentities(null);
EntityIdentity selected = null;
long aid = args.getLong("account", -1);
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)};
break;
}
if (identity.account.equals(draft.account)) {
icount++;
if (identity.primary)
primary = identity;
if (first == null)
first = identity;
}
}
if (draft.identity != null)
break;
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;
}
iindex++;
} while (iindex < (draft.from == null ? -1 : draft.from.length));
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;
}
// 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 (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;
}
}
draft.sender = MessageHelper.getSortKey(draft.from);
Uri lookupUri = ContactInfo.getLookupUri(context, draft.from);
draft.avatar = (lookupUri == null ? null : lookupUri.toString());
if (selected == null)
for (EntityIdentity identity : data.identities)
if (identity.account.equals(aid) && identity.primary) {
selected = identity;
break;
}
draft.received = new Date().getTime();
draft.seen = true;
draft.ui_seen = true;
if (selected == null)
for (EntityIdentity identity : data.identities)
if (identity.account.equals(aid)) {
selected = identity;
break;
}
draft.id = db.message().insertMessage(draft);
Helper.writeText(draft.getFile(context), body);
if (selected == null)
for (EntityIdentity identity : data.identities)
if (identity.primary) {
selected = identity;
break;
}
db.message().setMessageContent(draft.id,
if (selected == null)
for (EntityIdentity identity : data.identities) {
selected = identity;
break;
}
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());
data.draft.received = new Date().getTime();
data.draft.seen = true;
data.draft.ui_seen = true;
data.draft.id = db.message().insertMessage(data.draft);
Helper.writeText(data.draft.getFile(context), body);
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<Uri> 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<EntityAttachment> attachments = db.attachment().getAttachments(draft.id);
List<EntityAttachment> 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");

View File

@ -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) {