From b81c29bcf346934f093db4ad76ddaa4b84b615b7 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 22 May 2025 18:18:10 +0200 Subject: [PATCH] dispatch PEP events via manager --- .../conversations/parser/MessageParser.java | 195 ------------------ .../services/XmppConnectionService.java | 46 ----- .../eu/siacs/conversations/xmpp/Managers.java | 4 + .../xmpp/manager/AbstractBookmarkManager.java | 19 +- .../xmpp/manager/AvatarManager.java | 57 ++++- .../xmpp/manager/AxolotlManager.java | 27 ++- .../xmpp/manager/BookmarkManager.java | 54 ++++- .../xmpp/manager/LegacyBookmarkManager.java | 30 ++- ...essageDisplayedSynchronizationManager.java | 70 +++++++ .../xmpp/manager/NickManager.java | 37 +++- .../xmpp/manager/PubSubManager.java | 24 ++- .../android/xmpp/processor/BindProcessor.java | 3 +- 12 files changed, 294 insertions(+), 272 deletions(-) create mode 100644 src/main/java/eu/siacs/conversations/xmpp/manager/MessageDisplayedSynchronizationManager.java diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 77da8fd1eac11d6ba3d4aaf44e061dcf4f9475e9..35c1f4b3b6d604808f5be8b98497a0e1b31dc63a 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -2,7 +2,6 @@ package eu.siacs.conversations.parser; import android.util.Log; import android.util.Pair; -import androidx.annotation.NonNull; import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import eu.siacs.conversations.AppSettings; @@ -14,7 +13,6 @@ import eu.siacs.conversations.crypto.axolotl.NotEncryptedForThisDeviceException; import eu.siacs.conversations.crypto.axolotl.OutdatedSenderException; import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage; import eu.siacs.conversations.entities.Account; -import eu.siacs.conversations.entities.Bookmark; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversational; @@ -26,7 +24,6 @@ import eu.siacs.conversations.entities.ReceiptRequest; import eu.siacs.conversations.entities.RtpSessionStatus; import eu.siacs.conversations.http.HttpConnectionManager; import eu.siacs.conversations.services.MessageArchiveService; -import eu.siacs.conversations.services.QuickConversationsService; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.utils.CryptoHelper; import eu.siacs.conversations.xml.Element; @@ -39,25 +36,16 @@ import eu.siacs.conversations.xmpp.jingle.JingleConnectionManager; import eu.siacs.conversations.xmpp.jingle.JingleRtpConnection; import eu.siacs.conversations.xmpp.manager.PubSubManager; import eu.siacs.conversations.xmpp.manager.RosterManager; -import eu.siacs.conversations.xmpp.pep.Avatar; import im.conversations.android.xmpp.model.Extension; -import im.conversations.android.xmpp.model.avatar.Metadata; -import im.conversations.android.xmpp.model.axolotl.DeviceList; import im.conversations.android.xmpp.model.axolotl.Encrypted; -import im.conversations.android.xmpp.model.bookmark.Storage; -import im.conversations.android.xmpp.model.bookmark2.Conference; import im.conversations.android.xmpp.model.carbons.Received; import im.conversations.android.xmpp.model.carbons.Sent; import im.conversations.android.xmpp.model.correction.Replace; import im.conversations.android.xmpp.model.forward.Forwarded; import im.conversations.android.xmpp.model.markers.Displayed; -import im.conversations.android.xmpp.model.nick.Nick; import im.conversations.android.xmpp.model.occupant.OccupantId; import im.conversations.android.xmpp.model.oob.OutOfBandData; -import im.conversations.android.xmpp.model.pubsub.Items; -import im.conversations.android.xmpp.model.pubsub.event.Delete; import im.conversations.android.xmpp.model.pubsub.event.Event; -import im.conversations.android.xmpp.model.pubsub.event.Purge; import im.conversations.android.xmpp.model.reactions.Reactions; import im.conversations.android.xmpp.model.receipts.Request; import im.conversations.android.xmpp.model.unique.StanzaId; @@ -65,10 +53,8 @@ import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Collections; import java.util.Date; -import java.util.HashSet; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Set; import java.util.UUID; import java.util.function.Consumer; @@ -257,169 +243,6 @@ public class MessageParser extends AbstractParser return null; } - private void parseEvent(final Items items, final Jid from, final Account account) { - final String node = items.getNode(); - if ("urn:xmpp:avatar:metadata".equals(node)) { - // TODO support retract - final var entry = items.getFirstItemWithId(Metadata.class); - final var avatar = - entry == null ? null : Avatar.parseMetadata(entry.getKey(), entry.getValue()); - if (avatar != null) { - avatar.owner = from.asBareJid(); - if (mXmppConnectionService.getFileBackend().isAvatarCached(avatar)) { - if (account.getJid().asBareJid().equals(from)) { - if (account.setAvatar(avatar.getFilename())) { - mXmppConnectionService.databaseBackend.updateAccount(account); - mXmppConnectionService.notifyAccountAvatarHasChanged(account); - } - mXmppConnectionService.getAvatarService().clear(account); - mXmppConnectionService.updateConversationUi(); - mXmppConnectionService.updateAccountUi(); - } else { - final Contact contact = account.getRoster().getContact(from); - if (contact.setAvatar(avatar)) { - connection.getManager(RosterManager.class).writeToDatabaseAsync(); - mXmppConnectionService.getAvatarService().clear(contact); - mXmppConnectionService.updateConversationUi(); - mXmppConnectionService.updateRosterUi(); - } - } - } else if (mXmppConnectionService.isDataSaverDisabled()) { - mXmppConnectionService.fetchAvatar(account, avatar); - } - } - } else if (Namespace.NICK.equals(node)) { - final var nickItem = items.getFirstItem(Nick.class); - final String nick = nickItem == null ? null : nickItem.getContent(); - if (nick != null) { - setNick(account, from, nick); - } - } else if (AxolotlService.PEP_DEVICE_LIST.equals(node)) { - final var deviceList = items.getFirstItem(DeviceList.class); - if (deviceList != null) { - final Set deviceIds = deviceList.getDeviceIds(); - Log.d( - Config.LOGTAG, - AxolotlService.getLogprefix(account) - + "Received PEP device list " - + deviceIds - + " update from " - + from - + ", processing... "); - final AxolotlService axolotlService = account.getAxolotlService(); - axolotlService.registerDevices(from, new HashSet<>(deviceIds)); - } - - } else if (Namespace.BOOKMARKS.equals(node) && account.getJid().asBareJid().equals(from)) { - final var connection = account.getXmppConnection(); - if (connection.getFeatures().bookmarksConversion()) { - if (connection.getFeatures().bookmarks2()) { - Log.w( - Config.LOGTAG, - account.getJid().asBareJid() - + ": received storage:bookmark notification even though we" - + " opted into bookmarks:1"); - } - final var storage = items.getFirstItem(Storage.class); - final Map bookmarks = Bookmark.parseFromStorage(storage, account); - // mXmppConnectionService.processBookmarksInitial(account, bookmarks, true); - Log.d( - Config.LOGTAG, - account.getJid().asBareJid() + ": processing bookmark PEP event"); - } else { - Log.d( - Config.LOGTAG, - account.getJid().asBareJid() - + ": ignoring bookmark PEP event because bookmark conversion was" - + " not detected"); - } - } else if (Namespace.BOOKMARKS2.equals(node) && account.getJid().asBareJid().equals(from)) { - final var retractions = items.getRetractions(); - for (final var item : items.getItemMap(Conference.class).entrySet()) { - final Bookmark bookmark = - Bookmark.parseFromItem(item.getKey(), item.getValue(), account); - if (bookmark == null) { - continue; - } - account.putBookmark(bookmark); - mXmppConnectionService.processModifiedBookmark(bookmark); - mXmppConnectionService.updateConversationUi(); - } - for (final var retract : retractions) { - final Jid id = Jid.Invalid.getNullForInvalid(retract.getAttributeAsJid("id")); - if (id != null) { - account.removeBookmark(id); - Log.d( - Config.LOGTAG, - account.getJid().asBareJid() + ": deleted bookmark for " + id); - // mXmppConnectionService.processDeletedBookmark(account, id); - mXmppConnectionService.updateConversationUi(); - } - } - } else if (Config.MESSAGE_DISPLAYED_SYNCHRONIZATION - && Namespace.MDS_DISPLAYED.equals(node) - && account.getJid().asBareJid().equals(from)) { - for (final var item : - items.getItemMap(im.conversations.android.xmpp.model.mds.Displayed.class) - .entrySet()) { - mXmppConnectionService.processMdsItem(account, item); - } - } - } - - private void parseDeleteEvent(final Delete delete, final Jid from, final Account account) { - final String node = delete.getNode(); - if (Namespace.NICK.equals(node)) { - setNick(account, from, null); - } else if (Namespace.BOOKMARKS2.equals(node) && account.getJid().asBareJid().equals(from)) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": deleted bookmarks node"); - deleteAllBookmarks(account); - } else if (Namespace.AVATAR_METADATA.equals(node)) { - final boolean isAccount = account.getJid().asBareJid().equals(from); - if (isAccount) { - account.setAvatar(null); - mXmppConnectionService.databaseBackend.updateAccount(account); - mXmppConnectionService.getAvatarService().clear(account); - Log.d( - Config.LOGTAG, - account.getJid().asBareJid() + ": deleted avatar metadata node"); - } - } - } - - private void parsePurgeEvent( - @NonNull final Purge purge, final Jid from, final Account account) { - final String node = purge.getNode(); - if (Namespace.BOOKMARKS2.equals(node) && account.getJid().asBareJid().equals(from)) { - Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": purged bookmarks"); - deleteAllBookmarks(account); - } - } - - private void deleteAllBookmarks(final Account account) { - final var previous = account.getBookmarkedJids(); - account.setBookmarks(Collections.emptyMap()); - // mXmppConnectionService.processDeletedBookmarks(account, previous); - } - - private void setNick(final Account account, final Jid user, final String nick) { - if (user.asBareJid().equals(account.getJid().asBareJid())) { - account.setDisplayName(nick); - if (QuickConversationsService.isQuicksy()) { - mXmppConnectionService.getAvatarService().clear(account); - } - mXmppConnectionService.checkMucRequiresRename(); - } else { - Contact contact = account.getRoster().getContact(user); - if (contact.setPresenceName(nick)) { - connection.getManager(RosterManager.class).writeToDatabaseAsync(); - mXmppConnectionService.getAvatarService().clear(contact); - } - } - mXmppConnectionService.updateConversationUi(); - mXmppConnectionService.updateAccountUi(); - } - private boolean handleErrorMessage( final Account account, final im.conversations.android.xmpp.model.stanza.Message packet) { @@ -1414,24 +1237,6 @@ public class MessageParser extends AbstractParser if (original.hasExtension(Event.class)) { getManager(PubSubManager.class).handleEvent(original); } - final var event = original.getExtension(Event.class); - if (event != null && Jid.Invalid.hasValidFrom(original) && original.getFrom().isBareJid()) { - final var action = event.getAction(); - final var node = action == null ? null : action.getNode(); - if (node == null) { - Log.d( - Config.LOGTAG, - account.getJid().asBareJid() - + ": no node found in PubSub event from " - + original.getFrom()); - } else if (action instanceof Items items) { - parseEvent(items, original.getFrom(), account); - } else if (action instanceof Purge purge) { - parsePurgeEvent(purge, original.getFrom(), account); - } else if (action instanceof Delete delete) { - parseDeleteEvent(delete, from, account); - } - } final String nick = packet.findChildContent("nick", Namespace.NICK); if (nick != null && Jid.Invalid.hasValidFrom(original)) { diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 1fc8667cf74efd068e28816858807a2cdf29ba87..c7f9baacdbf0bc51a8040286c99a57c234b9f09d 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -142,7 +142,6 @@ import im.conversations.android.xmpp.Entity; import im.conversations.android.xmpp.IqErrorException; import im.conversations.android.xmpp.model.avatar.Metadata; import im.conversations.android.xmpp.model.disco.info.InfoQuery; -import im.conversations.android.xmpp.model.mds.Displayed; import im.conversations.android.xmpp.model.pubsub.PubSub; import im.conversations.android.xmpp.model.stanza.Iq; import im.conversations.android.xmpp.model.up.Push; @@ -1944,51 +1943,6 @@ public class XmppConnectionService extends Service { }); } - public void fetchMessageDisplayedSynchronization(final Account account) { - Log.d(Config.LOGTAG, account.getJid() + ": retrieve mds"); - final var retrieve = mIqGenerator.retrieveMds(); - sendIqPacket( - account, - retrieve, - (response) -> { - if (response.getType() != Iq.Type.RESULT) { - return; - } - final var pubsub = response.getExtension(PubSub.class); - if (pubsub == null) { - return; - } - final var items = pubsub.getItems(); - if (items == null) { - return; - } - if (Namespace.MDS_DISPLAYED.equals(items.getNode())) { - for (final var item : - items.getItemMap( - im.conversations.android.xmpp.model.mds.Displayed - .class) - .entrySet()) { - processMdsItem(account, item); - } - } - }); - } - - public void processMdsItem(final Account account, final Map.Entry item) { - final Jid jid = Jid.Invalid.getNullForInvalid(Jid.ofOrInvalid(item.getKey())); - if (jid == null) { - return; - } - final var displayed = item.getValue(); - final var stanzaId = displayed.getStanzaId(); - final String id = stanzaId == null ? null : stanzaId.getId(); - final Conversation conversation = find(account, jid); - if (id != null && conversation != null) { - conversation.setDisplayState(id); - markReadUpToStanzaId(conversation, id); - } - } - public void markReadUpToStanzaId(final Conversation conversation, final String stanzaId) { final Message message = conversation.findMessageWithServerMsgId(stanzaId); if (message == null) { // do we want to check if isRead? diff --git a/src/main/java/eu/siacs/conversations/xmpp/Managers.java b/src/main/java/eu/siacs/conversations/xmpp/Managers.java index 1c709aa2d2914b5b4dd92be2df706757334c5326..e4761de5fa73ed3332c5e40542708900a2454228 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/Managers.java +++ b/src/main/java/eu/siacs/conversations/xmpp/Managers.java @@ -12,6 +12,7 @@ import eu.siacs.conversations.xmpp.manager.CarbonsManager; import eu.siacs.conversations.xmpp.manager.DiscoManager; import eu.siacs.conversations.xmpp.manager.EntityTimeManager; import eu.siacs.conversations.xmpp.manager.LegacyBookmarkManager; +import eu.siacs.conversations.xmpp.manager.MessageDisplayedSynchronizationManager; import eu.siacs.conversations.xmpp.manager.NickManager; import eu.siacs.conversations.xmpp.manager.PepManager; import eu.siacs.conversations.xmpp.manager.PingManager; @@ -38,6 +39,9 @@ public class Managers { .put(DiscoManager.class, new DiscoManager(context, connection)) .put(EntityTimeManager.class, new EntityTimeManager(context, connection)) .put(LegacyBookmarkManager.class, new LegacyBookmarkManager(context, connection)) + .put( + MessageDisplayedSynchronizationManager.class, + new MessageDisplayedSynchronizationManager(context, connection)) .put(NickManager.class, new NickManager(context, connection)) .put(PepManager.class, new PepManager(context, connection)) .put(PingManager.class, new PingManager(context, connection)) diff --git a/src/main/java/eu/siacs/conversations/xmpp/manager/AbstractBookmarkManager.java b/src/main/java/eu/siacs/conversations/xmpp/manager/AbstractBookmarkManager.java index 6ecf0edf874d0a07abdeff3feac4551b785e326a..3039b1020d714a1d75fe0ffc0bd85e1e500fd281 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/manager/AbstractBookmarkManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/manager/AbstractBookmarkManager.java @@ -2,7 +2,6 @@ package eu.siacs.conversations.xmpp.manager; import android.util.Log; import eu.siacs.conversations.Config; -import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Bookmark; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.services.XmppConnectionService; @@ -14,7 +13,7 @@ import java.util.Set; public class AbstractBookmarkManager extends AbstractManager { - private final XmppConnectionService service; + protected final XmppConnectionService service; protected AbstractBookmarkManager( final XmppConnectionService service, final XmppConnection connection) { @@ -23,7 +22,7 @@ public class AbstractBookmarkManager extends AbstractManager { } // TODO rename to setBookmarks? - public void processBookmarksInitial(final Map bookmarks, final boolean pep) { + protected void processBookmarksInitial(final Map bookmarks, final boolean pep) { final var account = getAccount(); // TODO we can internalize this getBookmarkedJid final Set previousBookmarks = account.getBookmarkedJids(); @@ -32,31 +31,31 @@ public class AbstractBookmarkManager extends AbstractManager { service.processModifiedBookmark(bookmark, pep); } if (pep) { - this.processDeletedBookmarks(account, previousBookmarks); + this.processDeletedBookmarks(previousBookmarks); } account.setBookmarks(bookmarks); } - public void processDeletedBookmarks(final Account account, final Collection bookmarks) { + protected void processDeletedBookmarks(final Collection bookmarks) { Log.d( Config.LOGTAG, - account.getJid().asBareJid() + getAccount().getJid().asBareJid() + ": " + bookmarks.size() + " bookmarks have been removed"); for (final Jid bookmark : bookmarks) { - processDeletedBookmark(account, bookmark); + processDeletedBookmark(bookmark); } } - public void processDeletedBookmark(final Account account, final Jid jid) { - final Conversation conversation = service.find(account, jid); + protected void processDeletedBookmark(final Jid jid) { + final Conversation conversation = service.find(getAccount(), jid); if (conversation == null) { return; } Log.d( Config.LOGTAG, - account.getJid().asBareJid() + ": archiving MUC " + jid + " after PEP update"); + getAccount().getJid().asBareJid() + ": archiving MUC " + jid + " after PEP update"); this.service.archiveConversation(conversation, false); } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/manager/AvatarManager.java b/src/main/java/eu/siacs/conversations/xmpp/manager/AvatarManager.java index 977627bc6b7643c62afe0e57fc683dec3d85ccb4..e173197fcfc64968af5f95142753a7ec6d74653d 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/manager/AvatarManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/manager/AvatarManager.java @@ -1,15 +1,64 @@ package eu.siacs.conversations.xmpp.manager; -import android.content.Context; +import android.util.Log; +import eu.siacs.conversations.Config; +import eu.siacs.conversations.entities.Contact; +import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.XmppConnection; +import eu.siacs.conversations.xmpp.pep.Avatar; +import im.conversations.android.xmpp.model.avatar.Metadata; import im.conversations.android.xmpp.model.pubsub.Items; public class AvatarManager extends AbstractManager { - public AvatarManager(Context context, XmppConnection connection) { - super(context, connection); + private final XmppConnectionService service; + + public AvatarManager(final XmppConnectionService service, XmppConnection connection) { + super(service.getApplicationContext(), connection); + this.service = service; + } + + public void handleItems(final Jid from, final Items items) { + final var account = getAccount(); + // TODO support retract + final var entry = items.getFirstItemWithId(Metadata.class); + final var avatar = + entry == null ? null : Avatar.parseMetadata(entry.getKey(), entry.getValue()); + if (avatar != null) { + avatar.owner = from.asBareJid(); + if (service.getFileBackend().isAvatarCached(avatar)) { + if (account.getJid().asBareJid().equals(from)) { + if (account.setAvatar(avatar.getFilename())) { + service.databaseBackend.updateAccount(account); + service.notifyAccountAvatarHasChanged(account); + } + service.getAvatarService().clear(account); + service.updateConversationUi(); + service.updateAccountUi(); + } else { + final Contact contact = account.getRoster().getContact(from); + if (contact.setAvatar(avatar)) { + connection.getManager(RosterManager.class).writeToDatabaseAsync(); + service.getAvatarService().clear(contact); + service.updateConversationUi(); + service.updateRosterUi(); + } + } + } else if (service.isDataSaverDisabled()) { + service.fetchAvatar(account, avatar); + } + } } - public void handleItems(Jid from, final Items items) {} + public void handleDelete(final Jid from) { + final var account = getAccount(); + final boolean isAccount = account.getJid().asBareJid().equals(from); + if (isAccount) { + account.setAvatar(null); + getDatabase().updateAccount(account); + service.getAvatarService().clear(account); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": deleted avatar metadata node"); + } + } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/manager/AxolotlManager.java b/src/main/java/eu/siacs/conversations/xmpp/manager/AxolotlManager.java index 889054032f2c982861163a523a797982e089f4ea..72625504732deb19abf2552d070a4b14be2813c4 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/manager/AxolotlManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/manager/AxolotlManager.java @@ -1,15 +1,38 @@ package eu.siacs.conversations.xmpp.manager; import android.content.Context; +import android.util.Log; +import eu.siacs.conversations.Config; +import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.XmppConnection; +import im.conversations.android.xmpp.model.axolotl.DeviceList; import im.conversations.android.xmpp.model.pubsub.Items; +import java.util.HashSet; +import java.util.Set; public class AxolotlManager extends AbstractManager { - public AxolotlManager(Context context, XmppConnection connection) { + public AxolotlManager(final Context context, final XmppConnection connection) { super(context, connection); } - public void handleItems(Jid from, final Items items) {} + public void handleItems(final Jid from, final Items items) { + final var account = getAccount(); + final var deviceList = items.getFirstItem(DeviceList.class); + if (deviceList == null) { + return; + } + final Set deviceIds = deviceList.getDeviceIds(); + Log.d( + Config.LOGTAG, + AxolotlService.getLogprefix(account) + + "Received PEP device list " + + deviceIds + + " update from " + + from + + ", processing... "); + final AxolotlService axolotlService = account.getAxolotlService(); + axolotlService.registerDevices(from, new HashSet<>(deviceIds)); + } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/manager/BookmarkManager.java b/src/main/java/eu/siacs/conversations/xmpp/manager/BookmarkManager.java index 4bfb5d080d9685ad2b95735e169c311aaa8a006f..ab8fffab1e6138c1fa28fa39f6a988b8af49aab8 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/manager/BookmarkManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/manager/BookmarkManager.java @@ -17,6 +17,9 @@ import im.conversations.android.xmpp.NodeConfiguration; import im.conversations.android.xmpp.model.bookmark2.Conference; import im.conversations.android.xmpp.model.bookmark2.Nick; import im.conversations.android.xmpp.model.pubsub.Items; +import im.conversations.android.xmpp.model.pubsub.event.Retract; +import java.util.Collection; +import java.util.Collections; import java.util.Map; public class BookmarkManager extends AbstractBookmarkManager { @@ -54,13 +57,35 @@ public class BookmarkManager extends AbstractBookmarkManager { } public void handleItems(final Items items) { - final var retractions = items.getRetractions(); - final var itemMap = items.getItemMap(Conference.class); - if (!retractions.isEmpty()) { - // deleteItems(retractions); + this.handleItems(items.getItemMap(Conference.class)); + this.handleRetractions(items.getRetractions()); + } + + private void handleRetractions(final Collection retractions) { + final var account = getAccount(); + for (final var retract : retractions) { + final Jid id = Jid.Invalid.getNullForInvalid(retract.getAttributeAsJid("id")); + if (id != null) { + account.removeBookmark(id); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": deleted bookmark for " + id); + processDeletedBookmark(id); + service.updateConversationUi(); + } } - if (!itemMap.isEmpty()) { - // updateItems(itemMap); + } + + private void handleItems(final Map items) { + final var account = getAccount(); + for (final var item : items.entrySet()) { + // TODO parseFromItem can be included in this Manager + final Bookmark bookmark = + Bookmark.parseFromItem(item.getKey(), item.getValue(), account); + if (bookmark == null) { + continue; + } + account.putBookmark(bookmark); + service.processModifiedBookmark(bookmark); + service.updateConversationUi(); } } @@ -91,5 +116,20 @@ public class BookmarkManager extends AbstractBookmarkManager { MoreExecutors.directExecutor()); } - public void deleteAllItems() {} + private void deleteAllItems() { + final var account = getAccount(); + final var previous = account.getBookmarkedJids(); + account.setBookmarks(Collections.emptyMap()); + processDeletedBookmarks(previous); + } + + public void handleDelete() { + Log.d(Config.LOGTAG, getAccount().getJid().asBareJid() + ": deleted bookmarks node"); + this.deleteAllItems(); + } + + public void handlePurge() { + Log.d(Config.LOGTAG, getAccount().getJid().asBareJid() + ": purged bookmarks"); + this.deleteAllItems(); + } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/manager/LegacyBookmarkManager.java b/src/main/java/eu/siacs/conversations/xmpp/manager/LegacyBookmarkManager.java index 71817f42e829de5c5964ed99ba344081de1fa246..e58f541de66928a4950321f52894e05224f9194a 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/manager/LegacyBookmarkManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/manager/LegacyBookmarkManager.java @@ -1,8 +1,14 @@ package eu.siacs.conversations.xmpp.manager; +import android.util.Log; +import eu.siacs.conversations.Config; +import eu.siacs.conversations.entities.Bookmark; import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.XmppConnection; +import im.conversations.android.xmpp.model.bookmark.Storage; import im.conversations.android.xmpp.model.pubsub.Items; +import java.util.Map; public class LegacyBookmarkManager extends AbstractBookmarkManager { @@ -11,5 +17,27 @@ public class LegacyBookmarkManager extends AbstractBookmarkManager { super(service, connection); } - public void handleItems(final Items items) {} + public void handleItems(final Items items) { + final var account = this.getAccount(); + final var connection = this.connection; + if (connection.getFeatures().bookmarksConversion()) { + if (connection.getFeatures().bookmarks2()) { + Log.w( + Config.LOGTAG, + account.getJid().asBareJid() + + ": received storage:bookmark notification even though we" + + " opted into bookmarks:1"); + } + final var storage = items.getFirstItem(Storage.class); + final Map bookmarks = Bookmark.parseFromStorage(storage, account); + this.processBookmarksInitial(bookmarks, true); + Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": processing bookmark PEP event"); + } else { + Log.d( + Config.LOGTAG, + account.getJid().asBareJid() + + ": ignoring bookmark PEP event because bookmark conversion was" + + " not detected"); + } + } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/manager/MessageDisplayedSynchronizationManager.java b/src/main/java/eu/siacs/conversations/xmpp/manager/MessageDisplayedSynchronizationManager.java new file mode 100644 index 0000000000000000000000000000000000000000..6c1af04b382548b985b8b1adcdf817dca2ea64ee --- /dev/null +++ b/src/main/java/eu/siacs/conversations/xmpp/manager/MessageDisplayedSynchronizationManager.java @@ -0,0 +1,70 @@ +package eu.siacs.conversations.xmpp.manager; + +import android.util.Log; + +import androidx.annotation.NonNull; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.MoreExecutors; + +import eu.siacs.conversations.Config; +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.services.XmppConnectionService; +import eu.siacs.conversations.xmpp.Jid; +import eu.siacs.conversations.xmpp.XmppConnection; +import im.conversations.android.xmpp.model.mds.Displayed; +import im.conversations.android.xmpp.model.pubsub.Items; +import java.util.Map; + +public class MessageDisplayedSynchronizationManager extends AbstractManager { + + private final XmppConnectionService service; + + public MessageDisplayedSynchronizationManager( + final XmppConnectionService service, XmppConnection connection) { + super(service.getApplicationContext(), connection); + this.service = service; + } + + public void handleItems(final Items items) { + for (final var item : items.getItemMap(Displayed.class).entrySet()) { + this.processMdsItem(item); + } + } + + public void processMdsItem(final Map.Entry item) { + final var account = getAccount(); + final Jid jid = Jid.Invalid.getNullForInvalid(Jid.ofOrInvalid(item.getKey())); + if (jid == null) { + return; + } + final var displayed = item.getValue(); + final var stanzaId = displayed.getStanzaId(); + final String id = stanzaId == null ? null : stanzaId.getId(); + final Conversation conversation = this.service.find(account, jid); + if (id != null && conversation != null) { + conversation.setDisplayState(id); + this.service.markReadUpToStanzaId(conversation, id); + } + } + + public void fetch() { + final var future = getManager(PepManager.class).fetchItems(Displayed.class); + Futures.addCallback( + future, + new FutureCallback<>() { + @Override + public void onSuccess(Map result) { + for (final var entry : result.entrySet()) { + processMdsItem(entry); + } + } + + @Override + public void onFailure(@NonNull Throwable t) { + Log.d(Config.LOGTAG,getAccount().getJid().asBareJid()+": could not retrieve MDS items", t); + } + }, + MoreExecutors.directExecutor()); + } +} diff --git a/src/main/java/eu/siacs/conversations/xmpp/manager/NickManager.java b/src/main/java/eu/siacs/conversations/xmpp/manager/NickManager.java index 56f8766adbbe456b768e5148f57a135a6a1eb54b..9beeaf4c4830389614c9537393d17ecea7b51ef9 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/manager/NickManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/manager/NickManager.java @@ -1,8 +1,10 @@ package eu.siacs.conversations.xmpp.manager; -import android.content.Context; import com.google.common.base.Strings; import com.google.common.util.concurrent.ListenableFuture; +import eu.siacs.conversations.entities.Contact; +import eu.siacs.conversations.services.QuickConversationsService; +import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.XmppConnection; import im.conversations.android.xmpp.NodeConfiguration; @@ -11,16 +13,39 @@ import im.conversations.android.xmpp.model.pubsub.Items; public class NickManager extends AbstractManager { - public NickManager(Context context, XmppConnection connection) { - super(context, connection); + private final XmppConnectionService service; + + public NickManager(final XmppConnectionService service, final XmppConnection connection) { + super(service.getApplicationContext(), connection); + this.service = service; } - public void handleItems(final Jid from, Items items) { + public void handleItems(final Jid from, final Items items) { final var item = items.getFirstItem(Nick.class); final var nick = item == null ? null : item.getContent(); if (from == null || Strings.isNullOrEmpty(nick)) { return; } + setNick(from, nick); + } + + private void setNick(final Jid user, final String nick) { + final var account = getAccount(); + if (user.asBareJid().equals(account.getJid().asBareJid())) { + account.setDisplayName(nick); + if (QuickConversationsService.isQuicksy()) { + service.getAvatarService().clear(account); + } + service.checkMucRequiresRename(); + } else { + final Contact contact = account.getRoster().getContact(user); + if (contact.setPresenceName(nick)) { + connection.getManager(RosterManager.class).writeToDatabaseAsync(); + service.getAvatarService().clear(contact); + } + } + service.updateConversationUi(); + service.updateAccountUi(); } public ListenableFuture publishNick(final String name) { @@ -28,4 +53,8 @@ public class NickManager extends AbstractManager { nick.setContent(name); return getManager(PepManager.class).publishSingleton(nick, NodeConfiguration.PRESENCE); } + + public void handleDelete(final Jid from) { + this.setNick(from, null); + } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/manager/PubSubManager.java b/src/main/java/eu/siacs/conversations/xmpp/manager/PubSubManager.java index 47856e26729ce138814061b80f87f659fbe1329d..2d1132b96f966722f7129d5c381570404c553c38 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/manager/PubSubManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/manager/PubSubManager.java @@ -172,6 +172,10 @@ public class PubSubManager extends AbstractManager { getManager(LegacyBookmarkManager.class).handleItems(items); return; } + if (connection.fromAccount(message) && Namespace.MDS_DISPLAYED.equals(node)) { + getManager(MessageDisplayedSynchronizationManager.class).handleItems(items); + return; + } if (isFromBare && Namespace.AVATAR_METADATA.equals(node)) { getManager(AvatarManager.class).handleItems(from, items); return; @@ -187,13 +191,29 @@ public class PubSubManager extends AbstractManager { private void handlePurge(final Message message, final Purge purge) { final var from = message.getFrom(); + final var isFromBare = from == null || from.isBareJid(); final var node = purge.getNode(); if (connection.fromAccount(message) && Namespace.BOOKMARKS2.equals(node)) { - getManager(BookmarkManager.class).deleteAllItems(); + getManager(BookmarkManager.class).handlePurge(); } } - private void handleDelete(final Message message, final Delete delete) {} + private void handleDelete(final Message message, final Delete delete) { + final var from = message.getFrom(); + final var isFromBare = from == null || from.isBareJid(); + final var node = delete.getNode(); + if (connection.fromAccount(message) && Namespace.BOOKMARKS2.equals(node)) { + getManager(BookmarkManager.class).handleDelete(); + return; + } + if (isFromBare && Namespace.AVATAR_METADATA.equals(node)) { + getManager(AvatarManager.class).handleDelete(from); + return; + } + if (isFromBare && Namespace.NICK.equals(node)) { + getManager(NickManager.class).handleDelete(from); + } + } public ListenableFuture publishSingleton( Jid address, Extension item, final NodeConfiguration nodeConfiguration) { diff --git a/src/main/java/im/conversations/android/xmpp/processor/BindProcessor.java b/src/main/java/im/conversations/android/xmpp/processor/BindProcessor.java index dc8e1dd5ff95dd545011292b53c232c84744bb55..96b2be840c80b04aa6659c00a4cafedda148a2b5 100644 --- a/src/main/java/im/conversations/android/xmpp/processor/BindProcessor.java +++ b/src/main/java/im/conversations/android/xmpp/processor/BindProcessor.java @@ -8,6 +8,7 @@ import eu.siacs.conversations.generator.IqGenerator; import eu.siacs.conversations.services.XmppConnectionService; import eu.siacs.conversations.xmpp.XmppConnection; import eu.siacs.conversations.xmpp.manager.BookmarkManager; +import eu.siacs.conversations.xmpp.manager.MessageDisplayedSynchronizationManager; import eu.siacs.conversations.xmpp.manager.PrivateStorageManager; import eu.siacs.conversations.xmpp.manager.RosterManager; import im.conversations.android.xmpp.model.stanza.Iq; @@ -73,7 +74,7 @@ public class BindProcessor extends XmppConnection.Delegate implements Runnable { } if (features.mds()) { - service.fetchMessageDisplayedSynchronization(account); + connection.getManager(MessageDisplayedSynchronizationManager.class).fetch(); } else { Log.d(Config.LOGTAG, account.getJid() + ": server has no support for mds"); }