diff --git a/src/main/java/eu/siacs/conversations/entities/MucOptions.java b/src/main/java/eu/siacs/conversations/entities/MucOptions.java index 30481b3ee..bfb9dd38f 100644 --- a/src/main/java/eu/siacs/conversations/entities/MucOptions.java +++ b/src/main/java/eu/siacs/conversations/entities/MucOptions.java @@ -11,6 +11,7 @@ import java.util.Set; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; +import eu.siacs.conversations.services.MessageArchiveService; import eu.siacs.conversations.utils.JidHelper; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xml.Namespace; @@ -66,6 +67,10 @@ public class MucOptions { } } + public boolean mamSupport() { + return MessageArchiveService.Version.has(getFeatures()); + } + public enum Affiliation { OWNER("owner", 4, R.string.owner), ADMIN("admin", 3, R.string.admin), @@ -456,12 +461,9 @@ public class MucOptions { return conversation.getBooleanAttribute(Conversation.ATTRIBUTE_MEMBERS_ONLY, false); } - public boolean mamSupport() { - return hasFeature(Namespace.MAM) || hasFeature(Namespace.MAM_LEGACY); - } - public boolean mamLegacy() { - return hasFeature(Namespace.MAM_LEGACY) && !hasFeature(Namespace.MAM); + public List getFeatures() { + return this.serviceDiscoveryResult != null ? this.serviceDiscoveryResult.features : Collections.emptyList(); } public boolean nonanonymous() { diff --git a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java index 18f6bd58f..f0fe64232 100644 --- a/src/main/java/eu/siacs/conversations/generator/IqGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/IqGenerator.java @@ -248,10 +248,10 @@ public class IqGenerator extends AbstractGenerator { public IqPacket queryMessageArchiveManagement(final MessageArchiveService.Query mam) { final IqPacket packet = new IqPacket(IqPacket.TYPE.SET); - final Element query = packet.query(mam.isLegacy() ? Namespace.MAM_LEGACY : Namespace.MAM); + final Element query = packet.query(mam.version.namespace); query.setAttribute("queryid", mam.getQueryId()); final Data data = new Data(); - data.setFormType(mam.isLegacy() ? Namespace.MAM_LEGACY : Namespace.MAM); + data.setFormType(mam.version.namespace); if (mam.muc()) { packet.setTo(mam.getWith()); } else if (mam.getWith() != null) { diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 26ded9f1f..5a4ea1020 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -239,16 +239,15 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece Long timestamp = null; boolean isCarbon = false; String serverMsgId = null; - final Element fin = original.findChild("fin", Namespace.MAM_LEGACY); + final Element fin = original.findChild("fin", MessageArchiveService.Version.MAM_0.namespace); if (fin != null) { mXmppConnectionService.getMessageArchiveService().processFinLegacy(fin, original.getFrom()); return; } - final boolean mamLegacy = original.hasChild("result", Namespace.MAM_LEGACY); - final Element result = original.findChild("result", mamLegacy ? Namespace.MAM_LEGACY : Namespace.MAM); + final Element result = MessageArchiveService.Version.findResult(original); final MessageArchiveService.Query query = result == null ? null : mXmppConnectionService.getMessageArchiveService().findQuery(result.getAttribute("queryid")); if (query != null && query.validFrom(original.getFrom())) { - Pair f = original.getForwardedMessagePacket("result", mamLegacy ? Namespace.MAM_LEGACY : Namespace.MAM); + Pair f = original.getForwardedMessagePacket("result", query.version.namespace); if (f == null) { return; } diff --git a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java index 10f643909..0b139c0e6 100644 --- a/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java +++ b/src/main/java/eu/siacs/conversations/services/MessageArchiveService.java @@ -20,6 +20,7 @@ import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.OnAdvancedStreamFeaturesLoaded; import eu.siacs.conversations.xmpp.mam.MamReference; import eu.siacs.conversations.xmpp.stanzas.IqPacket; +import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import rocks.xmpp.addr.Jid; public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { @@ -29,6 +30,66 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { private final HashSet queries = new HashSet<>(); private final ArrayList pendingQueries = new ArrayList<>(); + public enum Version { + MAM_0("urn:xmpp:mam:0", true), + MAM_1("urn:xmpp:mam:1", false), + MAM_2("urn:xmpp:mam:2", false); + + public final boolean legacy; + public final String namespace; + + Version(String namespace, boolean legacy) { + this.namespace = namespace; + this.legacy = legacy; + } + + public static Version get(Account account) { + return get(account,null); + } + + public static Version get(Account account, Conversation conversation) { + if (conversation == null || conversation.getMode() == Conversation.MODE_SINGLE) { + return get(account.getXmppConnection().getFeatures().getAccountFeatures()); + } else { + return get(conversation.getMucOptions().getFeatures()); + } + } + + private static Version get(List features) { + final Version[] values = values(); + for(int i = values.length -1; i >= 0; --i) { + for(String feature : features) { + if (values[i].namespace.equals(feature)) { + return values[i]; + } + } + } + return MAM_0; + } + + public static boolean has(List features) { + for(String feature : features) { + for(Version version : values()) { + if (version.namespace.equals(feature)) { + return true; + } + } + } + return false; + } + + public static Element findResult(MessagePacket packet) { + for(Version version : values()) { + Element result = packet.findChild("result", version.namespace); + if (result != null) { + return result; + } + } + return null; + } + + }; + MessageArchiveService(final XmppConnectionService service) { this.mXmppConnectionService = service; } @@ -168,7 +229,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { Log.d(Config.LOGTAG, account.getJid().asBareJid().toString() + ": running mam query " + query.toString()); IqPacket packet = this.mXmppConnectionService.getIqGenerator().queryMessageArchiveManagement(query); this.mXmppConnectionService.sendIqPacket(account, packet, (a, p) -> { - Element fin = p.findChild("fin", Namespace.MAM); + Element fin = p.findChild("fin", query.version.namespace); if (p.getType() == IqPacket.TYPE.TIMEOUT) { synchronized (MessageArchiveService.this.queries) { MessageArchiveService.this.queries.remove(query); @@ -390,16 +451,21 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { private PagingOrder pagingOrder = PagingOrder.NORMAL; private XmppConnectionService.OnMoreMessagesLoaded callback = null; private boolean catchup = true; + public final Version version; Query(Conversation conversation, MamReference start, long end, boolean catchup) { - this(conversation.getAccount(), catchup ? start : start.timeOnly(), end); + this(conversation.getAccount(), Version.get(conversation.getAccount(), conversation), catchup ? start : start.timeOnly(), end); this.conversation = conversation; this.pagingOrder = catchup ? PagingOrder.NORMAL : PagingOrder.REVERSE; this.catchup = catchup; } Query(Account account, MamReference start, long end) { + this(account, Version.get(account), start, end); + } + + Query(Account account, Version version, MamReference start, long end) { this.account = account; if (start.getReference() != null) { this.reference = start.getReference(); @@ -408,10 +474,11 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { } this.end = end; this.queryId = new BigInteger(50, mXmppConnectionService.getRNG()).toString(32); + this.version = version; } private Query page(String reference) { - Query query = new Query(this.account, new MamReference(this.start, reference), this.end); + Query query = new Query(this.account, this.version, new MamReference(this.start, reference), this.end); query.conversation = conversation; query.totalCount = totalCount; query.actualCount = actualCount; @@ -433,11 +500,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { } public boolean isLegacy() { - if (conversation == null || conversation.getMode() == Conversation.MODE_SINGLE) { - return account.getXmppConnection().getFeatures().mamLegacy(); - } else { - return conversation.getMucOptions().mamLegacy(); - } + return version.legacy; } public boolean safeToExtractTrueCounterpart() { @@ -570,6 +633,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded { builder.append(this.reference); } builder.append(", catchup=").append(Boolean.toString(catchup)); + builder.append(", ns=").append(version.namespace); return builder.toString(); } diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 8c11974ef..91e51e129 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -3766,11 +3766,11 @@ public class XmppConnectionService extends Service { } public void fetchMamPreferences(Account account, final OnMamPreferencesFetched callback) { - final boolean legacy = account.getXmppConnection().getFeatures().mamLegacy(); + final MessageArchiveService.Version version = MessageArchiveService.Version.get(account); IqPacket request = new IqPacket(IqPacket.TYPE.GET); - request.addChild("prefs", legacy ? Namespace.MAM_LEGACY : Namespace.MAM); + request.addChild("prefs", version.namespace); sendIqPacket(account, request, (account1, packet) -> { - Element prefs = packet.findChild("prefs", legacy ? Namespace.MAM_LEGACY : Namespace.MAM); + Element prefs = packet.findChild("prefs", version.namespace); if (packet.getType() == IqPacket.TYPE.RESULT && prefs != null) { callback.onPreferencesFetched(prefs); } else { diff --git a/src/main/java/eu/siacs/conversations/xml/Namespace.java b/src/main/java/eu/siacs/conversations/xml/Namespace.java index 3d96a28c8..64f68cacb 100644 --- a/src/main/java/eu/siacs/conversations/xml/Namespace.java +++ b/src/main/java/eu/siacs/conversations/xml/Namespace.java @@ -8,8 +8,6 @@ public final class Namespace { public static final String HTTP_UPLOAD = "urn:xmpp:http:upload:0"; public static final String HTTP_UPLOAD_LEGACY = "urn:xmpp:http:upload"; public static final String STANZA_IDS = "urn:xmpp:sid:0"; - public static final String MAM = "urn:xmpp:mam:2"; - public static final String MAM_LEGACY = "urn:xmpp:mam:0"; public static final String IDLE = "urn:xmpp:idle:1"; public static final String DATA = "jabber:x:data"; public static final String OOB = "jabber:x:oob"; diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java index 4d5c68ac4..f97d7c374 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java @@ -31,6 +31,7 @@ import java.security.PrivateKey; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; @@ -70,6 +71,7 @@ import eu.siacs.conversations.entities.ServiceDiscoveryResult; import eu.siacs.conversations.generator.IqGenerator; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.services.MemorizingTrustManager; +import eu.siacs.conversations.services.MessageArchiveService; import eu.siacs.conversations.services.NotificationService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.CryptoHelper; @@ -1786,13 +1788,12 @@ public class XmppConnection implements Runnable { } public boolean mam() { - return hasDiscoFeature(account.getJid().asBareJid(), Namespace.MAM) - || hasDiscoFeature(account.getJid().asBareJid(), Namespace.MAM_LEGACY); + return MessageArchiveService.Version.has(getAccountFeatures()); } - public boolean mamLegacy() { - return !hasDiscoFeature(account.getJid().asBareJid(), Namespace.MAM) - && hasDiscoFeature(account.getJid().asBareJid(), Namespace.MAM_LEGACY); + public List getAccountFeatures() { + ServiceDiscoveryResult result = connection.disco.get(account.getJid().asBareJid()); + return result == null ? Collections.emptyList() : result.getFeatures(); } public boolean push() {