From 8cc418e89b2aea3492bb2f6d11c7c244e061e1fc Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Fri, 23 Dec 2022 23:11:29 -0500 Subject: [PATCH 1/2] Support extended addressing ofrom when domain matches Allow a message to appear as from a different jid using ofrom if the domain matches the actual from and source domain advertises support for extended addressing. This is safe because spoofing control is up to the source server, and the source server could have chosen to write a different from with the same domain if it wished. If there is a true counterpart (set from ofrom) then display nickname like in a MUC because this message isn't from the normal counterpart of this conversation. --- .../eu/siacs/conversations/entities/Message.java | 5 +++++ .../conversations/parser/MessageParser.java | 16 ++++++++++++++++ .../conversations/ui/adapter/MessageAdapter.java | 2 +- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index 04175632ef46035b175a40da6cf09727de0b2c14..399dc61734db6647bafc08ea726ebfec23fbddce 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -392,6 +392,11 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable public Contact getContact() { if (this.conversation.getMode() == Conversation.MODE_SINGLE) { + if (this.trueCounterpart != null) { + return this.conversation.getAccount().getRoster() + .getContact(this.trueCounterpart); + } + return this.conversation.getContact(); } else { if (this.trueCounterpart == null) { diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 6ccfe69b4cde5c2b07cb84656c6b2bd5354a7a17..655ddcbf8ff6dc54ee1fc6853f29d88217e08a46 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -602,6 +602,22 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece } } + Element addresses = packet.findChild("addresses", "http://jabber.org/protocol/address"); + if (status == Message.STATUS_RECEIVED && addresses != null) { + for (Element address : addresses.getChildren()) { + if (!address.getName().equals("address") || !address.getNamespace().equals("http://jabber.org/protocol/address")) continue; + + if (address.getAttribute("type").equals("ofrom") && address.getAttribute("jid") != null) { + Jid ofrom = address.getAttributeAsJid("jid"); + if (InvalidJid.isValid(ofrom) && ofrom.getDomain().equals(counterpart.getDomain()) && + conversation.getAccount().getRoster().getContact(counterpart.getDomain()).getPresences().anySupport("http://jabber.org/protocol/address")) { + + message.setTrueCounterpart(ofrom); + } + } + } + } + if (html != null) message.addPayload(html); message.setSubject(original.findChildContent("subject")); message.setCounterpart(counterpart); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index 7194f5c80ec1f7a890a454bd0fcfbc02ffde8a00..a6e53d77a3b45713efd628a1d1c8511185e98c00 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -267,7 +267,7 @@ public class MessageAdapter extends ArrayAdapter { error = true; break; default: - if (mForceNames || multiReceived) { + if (mForceNames || multiReceived || (message.getTrueCounterpart() != null && message.getContact() != null)) { info = UIHelper.getMessageDisplayName(message); } break; From 5a2c074a8b110e64ce9f0984e6fbf349f4b9d3ad Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Fri, 23 Dec 2022 23:36:41 -0500 Subject: [PATCH 2/2] Support for XEP-0428 v0.2.0 Store `fallback` elements in payloads. If there is a `fallback` for something we support (currently extended addressing and oob) then use the data provided to snip the relevant portion out of the body. Generate `fallback` element for oob we send, since we do send a fallback body. --- .../siacs/conversations/entities/Message.java | 44 +++++++++++++++++-- .../generator/MessageGenerator.java | 4 ++ .../conversations/parser/MessageParser.java | 3 +- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index 399dc61734db6647bafc08ea726ebfec23fbddce..5c6f263af9d7b20597dff87fe8cf048029be44e0 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -9,6 +9,7 @@ import android.text.Html; import android.text.SpannableStringBuilder; import android.util.Base64; import android.util.Log; +import android.util.Pair; import com.cheogram.android.BobTransfer; import com.cheogram.android.GetThumbnailForCid; @@ -409,10 +410,29 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable } public String getBody() { - if (getOob() != null) { - return body.replace(getOob().toString(), ""); + StringBuilder body = new StringBuilder(this.body); + + List fallbacks = getFallbacks(); + List> spans = new ArrayList<>(); + for (Element fallback : fallbacks) { + for (Element span : fallback.getChildren()) { + if (!span.getName().equals("body") && !span.getNamespace().equals("urn:xmpp:fallback:0")) continue; + if (span.getAttribute("start") == null || span.getAttribute("end") == null) return ""; + spans.add(new Pair(parseInt(span.getAttribute("start")), parseInt(span.getAttribute("end")))); + } + } + // Do them in reverse order so that span deletions don't affect the indexes of other spans + spans.sort((x, y) -> y.first.compareTo(x.first)); + try { + for (Pair span : spans) { + body.delete(span.first, span.second); + } + } catch (final StringIndexOutOfBoundsException e) { spans.clear(); } + + if (spans.isEmpty() && getOob() != null) { + return body.toString().replace(getOob().toString(), ""); } else { - return body; + return body.toString(); } } @@ -953,6 +973,24 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable return new ArrayList<>(this.payloads); } + public List getFallbacks() { + List fallbacks = new ArrayList<>(); + + if (this.payloads == null) return fallbacks; + + for (Element el : this.payloads) { + if (el.getName().equals("fallback") && el.getNamespace().equals("urn:xmpp:fallback:0")) { + final String fallbackFor = el.getAttribute("for"); + if (fallbackFor == null) continue; + if (fallbackFor.equals("http://jabber.org/protocol/address") || fallbackFor.equals(Namespace.OOB)) { + fallbacks.add(el); + } + } + } + + return fallbacks; + } + public Element getHtml() { if (this.payloads == null) return null; diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java index 90a312204094c58cc2bda1baf3e27ed4aa7f25bf..b1d57fef74cc7b7933866fe646cfd2ddbef7e040 100644 --- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java @@ -107,6 +107,8 @@ public class MessageGenerator extends AbstractGenerator { final Message.FileParams fileParams = message.getFileParams(); content = fileParams.url; packet.addChild("x", Namespace.OOB).addChild("url").setContent(content); + packet.addChild("fallback", "urn:xmpp:fallback:0").setAttribute("for", Namespace.OOB) + .addChild("body", "urn:xmpp:fallback:0"); } else { content = message.getBody(); } @@ -121,6 +123,8 @@ public class MessageGenerator extends AbstractGenerator { final String url = fileParams.url; packet.setBody(url); packet.addChild("x", Namespace.OOB).addChild("url").setContent(url); + packet.addChild("fallback", "urn:xmpp:fallback:0").setAttribute("for", Namespace.OOB) + .addChild("body", "urn:xmpp:fallback:0"); } else { if (Config.supportUnencrypted()) { packet.setBody(PGP_FALLBACK_MESSAGE); diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 655ddcbf8ff6dc54ee1fc6853f29d88217e08a46..f0b3d83e27289f9bac282e4d9f723fba2c4a211d 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -633,7 +633,8 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece } message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0"); for (Element el : packet.getChildren()) { - if (el.getName().equals("query") && el.getNamespace().equals("http://jabber.org/protocol/disco#items") && el.getAttribute("node").equals("http://jabber.org/protocol/commands")) { + if ((el.getName().equals("query") && el.getNamespace().equals("http://jabber.org/protocol/disco#items") && el.getAttribute("node").equals("http://jabber.org/protocol/commands")) || + (el.getName().equals("fallback") && el.getNamespace().equals("urn:xmpp:fallback:0"))) { message.addPayload(el); } if (el.getName().equals("thread") && (el.getNamespace() == null || el.getNamespace().equals("jabber:client"))) {