From c31fa7ed2bedcefc1480f2223b49abc18530a5dc Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 21 Jan 2023 11:11:52 +0100 Subject: [PATCH] include parentNode in disco items table --- .../1.json | 29 ++++++++++++------- .../eu/siacs/conversations/xml/Element.java | 5 ++++ .../android/database/dao/DiscoDao.java | 21 ++++++++++---- .../database/entity/DiscoItemEntity.java | 16 ++++++---- .../android/xmpp/manager/DiscoManager.java | 21 +++++++++++--- .../xmpp/model/disco/items/ItemsQuery.java | 8 +++++ 6 files changed, 73 insertions(+), 27 deletions(-) diff --git a/schemas/im.conversations.android.database.ConversationsDatabase/1.json b/schemas/im.conversations.android.database.ConversationsDatabase/1.json index e0e04eece..1a91e1323 100644 --- a/schemas/im.conversations.android.database.ConversationsDatabase/1.json +++ b/schemas/im.conversations.android.database.ConversationsDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "8f1d4d8d2bdb8b2358132202037aba7a", + "identityHash": "e2dbbac3327bc8ef188286642b379e7d", "entities": [ { "tableName": "account", @@ -601,7 +601,7 @@ }, { "tableName": "disco_item", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `address` TEXT NOT NULL, `node` TEXT NOT NULL, `parent` TEXT NOT NULL, `discoId` INTEGER, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`discoId`) REFERENCES `disco`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `address` TEXT NOT NULL, `node` TEXT NOT NULL, `parentAddress` TEXT NOT NULL, `parentNode` TEXT NOT NULL, `discoId` INTEGER, FOREIGN KEY(`accountId`) REFERENCES `account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`discoId`) REFERENCES `disco`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ { "fieldPath": "id", @@ -628,8 +628,14 @@ "notNull": true }, { - "fieldPath": "parent", - "columnName": "parent", + "fieldPath": "parentAddress", + "columnName": "parentAddress", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentNode", + "columnName": "parentNode", "affinity": "TEXT", "notNull": true }, @@ -648,26 +654,27 @@ }, "indices": [ { - "name": "index_disco_item_accountId_address_node_parent", + "name": "index_disco_item_accountId_address_node_parentAddress_parentNode", "unique": true, "columnNames": [ "accountId", "address", "node", - "parent" + "parentAddress", + "parentNode" ], "orders": [], - "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_disco_item_accountId_address_node_parent` ON `${TABLE_NAME}` (`accountId`, `address`, `node`, `parent`)" + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_disco_item_accountId_address_node_parentAddress_parentNode` ON `${TABLE_NAME}` (`accountId`, `address`, `node`, `parentAddress`, `parentNode`)" }, { - "name": "index_disco_item_accountId_parent", + "name": "index_disco_item_accountId_parentAddress", "unique": false, "columnNames": [ "accountId", - "parent" + "parentAddress" ], "orders": [], - "createSql": "CREATE INDEX IF NOT EXISTS `index_disco_item_accountId_parent` ON `${TABLE_NAME}` (`accountId`, `parent`)" + "createSql": "CREATE INDEX IF NOT EXISTS `index_disco_item_accountId_parentAddress` ON `${TABLE_NAME}` (`accountId`, `parentAddress`)" }, { "name": "index_disco_item_discoId", @@ -1337,7 +1344,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8f1d4d8d2bdb8b2358132202037aba7a')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e2dbbac3327bc8ef188286642b379e7d')" ] } } \ No newline at end of file diff --git a/src/main/java/eu/siacs/conversations/xml/Element.java b/src/main/java/eu/siacs/conversations/xml/Element.java index 4fdef2543..f8e9e523b 100644 --- a/src/main/java/eu/siacs/conversations/xml/Element.java +++ b/src/main/java/eu/siacs/conversations/xml/Element.java @@ -37,6 +37,11 @@ public class Element { return child; } + public T addChild(T child) { + this.addChild(child); + return child; + } + public Element addChild(String name) { this.content = null; Element child = new Element(name); diff --git a/src/main/java/im/conversations/android/database/dao/DiscoDao.java b/src/main/java/im/conversations/android/database/dao/DiscoDao.java index d8f6aa727..1499d9b31 100644 --- a/src/main/java/im/conversations/android/database/dao/DiscoDao.java +++ b/src/main/java/im/conversations/android/database/dao/DiscoDao.java @@ -44,10 +44,13 @@ public abstract class DiscoDao { protected abstract void insertDiscoFeatures(Collection features); @Query( - "DELETE FROM disco_item WHERE accountId=:account AND parent=:parent AND address NOT" - + " IN(:existent)") + "DELETE FROM disco_item WHERE accountId=:account AND parentAddress=:parent AND" + + " parentNode=:parentNode AND address NOT IN(:existent)") protected abstract void deleteNonExistentDiscoItems( - final long account, final Jid parent, final Collection existent); + final long account, + final Jid parent, + final String parentNode, + final Collection existent); @Query( "UPDATE presence SET discoId=:discoId WHERE accountId=:account AND address=:address" @@ -76,13 +79,19 @@ public abstract class DiscoDao { @Transaction public void set( - final Account account, final Entity.DiscoItem parent, final Collection items) { + final Account account, + final Entity.DiscoItem parent, + final String parentNode, + final Collection items) { final var entities = Collections2.transform( - items, i -> DiscoItemEntity.of(account.id, parent.address, i)); + items, i -> DiscoItemEntity.of(account.id, parent.address, parentNode, i)); insertDiscoItems(entities); deleteNonExistentDiscoItems( - account.id, parent.address, Collections2.transform(items, Item::getJid)); + account.id, + parent.address, + Strings.nullToEmpty(parentNode), + Collections2.transform(items, Item::getJid)); } @Transaction diff --git a/src/main/java/im/conversations/android/database/entity/DiscoItemEntity.java b/src/main/java/im/conversations/android/database/entity/DiscoItemEntity.java index 23302d5a2..0bfd9b69d 100644 --- a/src/main/java/im/conversations/android/database/entity/DiscoItemEntity.java +++ b/src/main/java/im/conversations/android/database/entity/DiscoItemEntity.java @@ -25,10 +25,10 @@ import im.conversations.android.xmpp.model.disco.items.Item; }, indices = { @Index( - value = {"accountId", "address", "node", "parent"}, + value = {"accountId", "address", "node", "parentAddress", "parentNode"}, unique = true), @Index( - value = {"accountId", "parent"}, + value = {"accountId", "parentAddress"}, unique = false), @Index(value = {"discoId"}) }) @@ -43,16 +43,20 @@ public class DiscoItemEntity { @NonNull public String node; - @NonNull public String parent; + @NonNull public String parentAddress; + + @NonNull public String parentNode; public Long discoId; - public static DiscoItemEntity of(long accountId, final Jid parent, Item item) { + public static DiscoItemEntity of( + long accountId, final Jid parent, final String parentNode, final Item item) { final var entity = new DiscoItemEntity(); entity.accountId = accountId; entity.address = item.getJid(); entity.node = Strings.nullToEmpty(item.getNode()); - entity.parent = parent.toEscapedString(); + entity.parentAddress = parent.toEscapedString(); + entity.parentNode = Strings.nullToEmpty(parentNode); return entity; } @@ -62,7 +66,7 @@ public class DiscoItemEntity { entity.accountId = accountId; entity.address = address; entity.node = Strings.nullToEmpty(node); - entity.parent = ""; + entity.parentAddress = ""; entity.discoId = discoId; return entity; } diff --git a/src/main/java/im/conversations/android/xmpp/manager/DiscoManager.java b/src/main/java/im/conversations/android/xmpp/manager/DiscoManager.java index a99ec7cc8..e0af50364 100644 --- a/src/main/java/im/conversations/android/xmpp/manager/DiscoManager.java +++ b/src/main/java/im/conversations/android/xmpp/manager/DiscoManager.java @@ -3,6 +3,7 @@ package im.conversations.android.xmpp.manager; import android.content.Context; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.google.common.base.Strings; import com.google.common.collect.Collections2; import com.google.common.io.BaseEncoding; import com.google.common.util.concurrent.Futures; @@ -53,11 +54,10 @@ public class DiscoManager extends AbstractManager { final var requestNode = hash != null && node != null ? hash.capabilityNode(node) : node; final var iqRequest = new IqPacket(IqPacket.TYPE.GET); iqRequest.setTo(entity.address); - final var infoQueryRequest = new InfoQuery(); + final var infoQueryRequest = iqRequest.addChild(new InfoQuery()); if (requestNode != null) { infoQueryRequest.setNode(requestNode); } - iqRequest.addChild(infoQueryRequest); final var future = connection.sendIqPacket(iqRequest); // TODO we need to remove the disco info associated with $entity in case of failure // this might happen in (rather unlikely) scenarios where an item no longer speaks disco @@ -108,9 +108,18 @@ public class DiscoManager extends AbstractManager { } public ListenableFuture> items(final Entity.DiscoItem entity) { + return items(entity, null); + } + + public ListenableFuture> items( + @NonNull final Entity.DiscoItem entity, @Nullable final String node) { + final var requestNode = Strings.emptyToNull(node); final var iqPacket = new IqPacket(IqPacket.TYPE.GET); iqPacket.setTo(entity.address); - iqPacket.addChild(new ItemsQuery()); + final var itemsQueryRequest = iqPacket.addChild(new ItemsQuery()); + if (requestNode != null) { + itemsQueryRequest.setNode(requestNode); + } final var future = connection.sendIqPacket(iqPacket); return Futures.transform( future, @@ -119,10 +128,14 @@ public class DiscoManager extends AbstractManager { if (itemsQuery == null) { throw new IllegalStateException(); } + if (!Objects.equals(requestNode, itemsQuery.getNode())) { + throw new IllegalStateException( + "Node in response did not match node in request"); + } final var items = itemsQuery.getExtensions(Item.class); final var validItems = Collections2.filter(items, i -> Objects.nonNull(i.getJid())); - getDatabase().discoDao().set(getAccount(), entity, validItems); + getDatabase().discoDao().set(getAccount(), entity, requestNode, validItems); return validItems; }, MoreExecutors.directExecutor()); diff --git a/src/main/java/im/conversations/android/xmpp/model/disco/items/ItemsQuery.java b/src/main/java/im/conversations/android/xmpp/model/disco/items/ItemsQuery.java index 608743410..981132ed6 100644 --- a/src/main/java/im/conversations/android/xmpp/model/disco/items/ItemsQuery.java +++ b/src/main/java/im/conversations/android/xmpp/model/disco/items/ItemsQuery.java @@ -8,4 +8,12 @@ public class ItemsQuery extends Extension { public ItemsQuery() { super(ItemsQuery.class); } + + public void setNode(final String node) { + this.setAttribute("node", node); + } + + public String getNode() { + return this.getAttribute("node"); + } }