From 1a09b3ed05f465fc731251c889459da4905840fd Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 19 Jan 2023 19:48:57 +0100 Subject: [PATCH] use empty string instead of null for 'no node' and 'no resource' --- .../1.json | 42 +++++++++-- .../eu/siacs/conversations/xml/Element.java | 2 +- .../android/database/dao/DiscoDao.java | 68 ++++++++++++----- .../android/database/dao/PresenceDao.java | 14 ++-- .../DiscoExtensionFieldValueEntity.java | 7 +- .../database/entity/DiscoItemEntity.java | 2 +- .../database/entity/PresenceEntity.java | 30 +++++--- .../im/conversations/android/xmpp/Entity.java | 32 +++++++- .../android/xmpp/XmppConnection.java | 74 +++++++++---------- .../android/xmpp/manager/DiscoManager.java | 21 +++--- .../android/xmpp/model/disco/items/Item.java | 3 +- .../xmpp/processor/PresenceProcessor.java | 4 +- 12 files changed, 203 insertions(+), 96 deletions(-) diff --git a/schemas/im.conversations.android.database.ConversationsDatabase/1.json b/schemas/im.conversations.android.database.ConversationsDatabase/1.json index fd5fadb33..f2eb613f1 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": "a521ff7a3e16cca9f6cbfe51241ec021", + "identityHash": "b28e01dcbb5d9774a4b36783d0db6c73", "entities": [ { "tableName": "account", @@ -427,7 +427,7 @@ }, { "tableName": "disco_ext_field_value", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `fieldId` INTEGER NOT NULL, `value` TEXT, FOREIGN KEY(`fieldId`) REFERENCES `disco_ext`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `fieldId` INTEGER NOT NULL, `value` TEXT, FOREIGN KEY(`fieldId`) REFERENCES `disco_ext_field`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", "fields": [ { "fieldPath": "id", @@ -467,7 +467,7 @@ ], "foreignKeys": [ { - "table": "disco_ext", + "table": "disco_ext_field", "onDelete": "CASCADE", "onUpdate": "NO ACTION", "columns": [ @@ -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, `parent` TEXT, `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, `parent` TEXT, `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", @@ -625,7 +625,7 @@ "fieldPath": "node", "columnName": "node", "affinity": "TEXT", - "notNull": false + "notNull": true }, { "fieldPath": "parent", @@ -975,7 +975,7 @@ }, { "tableName": "presence", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `accountId` INTEGER NOT NULL, `address` TEXT NOT NULL, `resource` TEXT, `type` TEXT, `show` TEXT, `status` TEXT, `vCardPhoto` TEXT, `occupantId` TEXT, `mucUserAffiliation` TEXT, `mucUserRole` TEXT, `mucUserJid` TEXT, `mucUserSelf` INTEGER NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account`(`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, `resource` TEXT NOT NULL, `type` TEXT, `show` TEXT, `status` TEXT, `vCardPhoto` TEXT, `occupantId` TEXT, `mucUserAffiliation` TEXT, `mucUserRole` TEXT, `mucUserJid` TEXT, `mucUserSelf` INTEGER 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", @@ -999,7 +999,7 @@ "fieldPath": "resource", "columnName": "resource", "affinity": "TEXT", - "notNull": false + "notNull": true }, { "fieldPath": "type", @@ -1054,6 +1054,12 @@ "columnName": "mucUserSelf", "affinity": "INTEGER", "notNull": true + }, + { + "fieldPath": "discoId", + "columnName": "discoId", + "affinity": "INTEGER", + "notNull": false } ], "primaryKey": { @@ -1073,6 +1079,15 @@ ], "orders": [], "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_presence_accountId_address_resource` ON `${TABLE_NAME}` (`accountId`, `address`, `resource`)" + }, + { + "name": "index_presence_discoId", + "unique": false, + "columnNames": [ + "discoId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_presence_discoId` ON `${TABLE_NAME}` (`discoId`)" } ], "foreignKeys": [ @@ -1086,6 +1101,17 @@ "referencedColumns": [ "id" ] + }, + { + "table": "disco", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "discoId" + ], + "referencedColumns": [ + "id" + ] } ] }, @@ -1310,7 +1336,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, 'a521ff7a3e16cca9f6cbfe51241ec021')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'b28e01dcbb5d9774a4b36783d0db6c73')" ] } } \ 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 964bf2558..4fdef2543 100644 --- a/src/main/java/eu/siacs/conversations/xml/Element.java +++ b/src/main/java/eu/siacs/conversations/xml/Element.java @@ -72,7 +72,7 @@ public class Element { } public E getExtension(final Class clazz) { - final var extension = Iterables.find(this.children, clazz::isInstance); + final var extension = Iterables.find(this.children, clazz::isInstance, null); if (extension == null) { return null; } 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 392f09b9e..0a61d5374 100644 --- a/src/main/java/im/conversations/android/database/dao/DiscoDao.java +++ b/src/main/java/im/conversations/android/database/dao/DiscoDao.java @@ -1,10 +1,13 @@ package im.conversations.android.database.dao; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.room.Dao; import androidx.room.Insert; import androidx.room.Query; import androidx.room.Transaction; import androidx.room.Upsert; +import com.google.common.base.Strings; import com.google.common.collect.Collections2; import eu.siacs.conversations.xmpp.Jid; import im.conversations.android.database.entity.DiscoEntity; @@ -15,6 +18,7 @@ import im.conversations.android.database.entity.DiscoFeatureEntity; import im.conversations.android.database.entity.DiscoIdentityEntity; import im.conversations.android.database.entity.DiscoItemEntity; import im.conversations.android.database.model.Account; +import im.conversations.android.xmpp.Entity; import im.conversations.android.xmpp.EntityCapabilities; import im.conversations.android.xmpp.EntityCapabilities2; import im.conversations.android.xmpp.model.data.Data; @@ -43,12 +47,18 @@ public abstract class DiscoDao { protected abstract void deleteNonExistentDiscoItems( final long account, final Jid parent, final Collection existent); + @Query( + "UPDATE presence SET discoId=:discoId WHERE accountId=:account AND address=:address" + + " AND resource=:resource") + protected abstract void updateDiscoIdInPresence( + long account, Jid address, String resource, long discoId); + @Insert protected abstract void insertDiscoFieldValues( Collection value); @Upsert(entity = DiscoItemEntity.class) - protected abstract void insert(DiscoItemWithDiscoId item); + protected abstract void updateDiscoIdInDiscoItem(DiscoItemWithDiscoId item); @Insert protected abstract long insert(DiscoEntity entity); @@ -60,19 +70,20 @@ public abstract class DiscoDao { protected abstract long insert(DiscoExtensionFieldEntity entity); @Transaction - public void set(final Account account, final Jid parent, final Collection items) { + public void set( + final Account account, final Entity.DiscoItem parent, final Collection items) { final var entities = Collections2.transform(items, i -> DiscoItemWithParent.of(account.id, parent, i)); insertDiscoItems(entities); deleteNonExistentDiscoItems( - account.id, parent, Collections2.transform(items, Item::getJid)); + account.id, parent.address, Collections2.transform(items, Item::getJid)); } @Transaction public boolean set( final Account account, - final Jid address, - final String node, + final Entity entity, + @Nullable final String node, final EntityCapabilities.Hash capsHash) { final Long existingDiscoId; if (capsHash instanceof EntityCapabilities2.EntityCaps2Hash) { @@ -85,14 +96,31 @@ public abstract class DiscoDao { if (existingDiscoId == null) { return false; } - insert(DiscoItemWithDiscoId.of(account.id, address, node, existingDiscoId)); + updateDiscoId(account.id, entity, node, existingDiscoId); return true; } + protected void updateDiscoId( + final long account, + final Entity entity, + @Nullable final String node, + final long discoId) { + if (entity instanceof Entity.DiscoItem) { + updateDiscoIdInDiscoItem( + DiscoItemWithDiscoId.of(account, (Entity.DiscoItem) entity, node, discoId)); + } else if (entity instanceof Entity.Presence) { + updateDiscoIdInPresence( + account, + entity.address.asBareJid(), + Strings.nullToEmpty(entity.address.getResource()), + discoId); + } + } + @Transaction public void set( final Account account, - final Jid address, + final Entity entity, final String node, final byte[] capsHash, final byte[] caps2HashSha256, @@ -100,7 +128,7 @@ public abstract class DiscoDao { final Long existingDiscoId = getDiscoId(account.id, caps2HashSha256); if (existingDiscoId != null) { - insert(DiscoItemWithDiscoId.of(account.id, address, node, existingDiscoId)); + updateDiscoId(account.id, entity, node, existingDiscoId); return; } final long discoId = insert(DiscoEntity.of(account.id, capsHash, caps2HashSha256)); @@ -125,6 +153,7 @@ public abstract class DiscoDao { v -> DiscoExtensionFieldValueEntity.of(fieldId, v.getContent()))); } } + updateDiscoId(account.id, entity, node, discoId); } @Query("SELECT id FROM disco WHERE accountId=:accountId AND caps2HashSha256=:caps2HashSha256") @@ -141,32 +170,35 @@ public abstract class DiscoDao { public static class DiscoItemWithParent { public long accountId; - public Jid address; - public String node; - public Jid parent; + public @NonNull Jid address; + public @NonNull String node; + public @Nullable Jid parent; public static DiscoItemWithParent of( - final long account, final Jid parent, final Item item) { + final long account, Entity.DiscoItem parent, final Item item) { final var entity = new DiscoItemWithParent(); entity.accountId = account; entity.address = item.getJid(); - entity.node = item.getNode(); - entity.parent = parent; + entity.node = Strings.nullToEmpty(item.getNode()); + entity.parent = parent.address; return entity; } } public static class DiscoItemWithDiscoId { public long accountId; - public Jid address; - public String node; + public @NonNull Jid address; + public @NonNull String node; public long discoId; public static DiscoItemWithDiscoId of( - final long account, final Jid address, final String node, final long discoId) { + final long account, + final Entity.DiscoItem discoItem, + @NonNull final String node, + final long discoId) { final var entity = new DiscoItemWithDiscoId(); entity.accountId = account; - entity.address = address; + entity.address = discoItem.address; entity.node = node; entity.discoId = discoId; return entity; diff --git a/src/main/java/im/conversations/android/database/dao/PresenceDao.java b/src/main/java/im/conversations/android/database/dao/PresenceDao.java index 3942bc6dd..1ba5a7836 100644 --- a/src/main/java/im/conversations/android/database/dao/PresenceDao.java +++ b/src/main/java/im/conversations/android/database/dao/PresenceDao.java @@ -1,5 +1,7 @@ package im.conversations.android.database.dao; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.room.Dao; import androidx.room.Query; import androidx.room.Upsert; @@ -28,12 +30,12 @@ public abstract class PresenceDao { abstract void insert(PresenceEntity entity); public void set( - Account account, - Jid address, - String resource, - PresenceType type, - PresenceShow show, - String status) { + @NonNull final Account account, + @NonNull final Jid address, + @Nullable final String resource, + @Nullable final PresenceType type, + @Nullable final PresenceShow show, + @Nullable final String status) { if (resource == null && Arrays.asList(PresenceType.ERROR, PresenceType.UNAVAILABLE).contains(type)) { deletePresences(account.id, address); diff --git a/src/main/java/im/conversations/android/database/entity/DiscoExtensionFieldValueEntity.java b/src/main/java/im/conversations/android/database/entity/DiscoExtensionFieldValueEntity.java index 706b4751f..391e42d0f 100644 --- a/src/main/java/im/conversations/android/database/entity/DiscoExtensionFieldValueEntity.java +++ b/src/main/java/im/conversations/android/database/entity/DiscoExtensionFieldValueEntity.java @@ -10,7 +10,7 @@ import androidx.room.PrimaryKey; tableName = "disco_ext_field_value", foreignKeys = @ForeignKey( - entity = DiscoExtensionEntity.class, + entity = DiscoExtensionFieldEntity.class, parentColumns = {"id"}, childColumns = {"fieldId"}, onDelete = ForeignKey.CASCADE), @@ -25,6 +25,9 @@ public class DiscoExtensionFieldValueEntity { public String value; public static DiscoExtensionFieldValueEntity of(long fieldId, final String value) { - return null; + final var entity = new DiscoExtensionFieldValueEntity(); + entity.fieldId = fieldId; + entity.value = value; + return entity; } } 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 363dc1441..ada7d2606 100644 --- a/src/main/java/im/conversations/android/database/entity/DiscoItemEntity.java +++ b/src/main/java/im/conversations/android/database/entity/DiscoItemEntity.java @@ -40,7 +40,7 @@ public class DiscoItemEntity { @NonNull Jid address; - @Nullable public String node; + @NonNull public String node; @Nullable public Jid parent; diff --git a/src/main/java/im/conversations/android/database/entity/PresenceEntity.java b/src/main/java/im/conversations/android/database/entity/PresenceEntity.java index 22409eed3..6092829ab 100644 --- a/src/main/java/im/conversations/android/database/entity/PresenceEntity.java +++ b/src/main/java/im/conversations/android/database/entity/PresenceEntity.java @@ -6,6 +6,7 @@ import androidx.room.Entity; import androidx.room.ForeignKey; import androidx.room.Index; import androidx.room.PrimaryKey; +import com.google.common.base.Strings; import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.xmpp.Jid; import im.conversations.android.database.model.PresenceShow; @@ -13,16 +14,23 @@ import im.conversations.android.database.model.PresenceType; @Entity( tableName = "presence", - foreignKeys = - @ForeignKey( - entity = AccountEntity.class, - parentColumns = {"id"}, - childColumns = {"accountId"}, - onDelete = ForeignKey.CASCADE), + foreignKeys = { + @ForeignKey( + entity = AccountEntity.class, + parentColumns = {"id"}, + childColumns = {"accountId"}, + onDelete = ForeignKey.CASCADE), + @ForeignKey( + entity = DiscoEntity.class, + parentColumns = {"id"}, + childColumns = {"discoId"}, + onDelete = ForeignKey.CASCADE) + }, indices = { @Index( value = {"accountId", "address", "resource"}, - unique = true) + unique = true), + @Index(value = {"discoId"}) }) public class PresenceEntity { @@ -33,7 +41,7 @@ public class PresenceEntity { @NonNull public Jid address; - @Nullable public String resource; + @NonNull public String resource; @Nullable public PresenceType type; @@ -54,17 +62,19 @@ public class PresenceEntity { // set to true if presence has status code 110 (this means we are online) public boolean mucUserSelf; + public Long discoId; + public static PresenceEntity of( long account, Jid address, - String resource, + @Nullable String resource, PresenceType type, PresenceShow show, String status) { final var entity = new PresenceEntity(); entity.accountId = account; entity.address = address; - entity.resource = resource; + entity.resource = Strings.nullToEmpty(resource); entity.type = type; entity.show = show; entity.status = status; diff --git a/src/main/java/im/conversations/android/xmpp/Entity.java b/src/main/java/im/conversations/android/xmpp/Entity.java index b3061ed0e..799d7a5ea 100644 --- a/src/main/java/im/conversations/android/xmpp/Entity.java +++ b/src/main/java/im/conversations/android/xmpp/Entity.java @@ -1,4 +1,34 @@ package im.conversations.android.xmpp; -public class Entity { +import eu.siacs.conversations.xmpp.Jid; + +public abstract class Entity { + + public final Jid address; + + private Entity(final Jid address) { + this.address = address; + } + + public static class DiscoItem extends Entity { + + private DiscoItem(Jid address) { + super(address); + } + } + + public static class Presence extends Entity { + + private Presence(Jid address) { + super(address); + } + } + + public static Presence presence(final Jid address) { + return new Presence(address); + } + + public static DiscoItem discoItem(final Jid address) { + return new DiscoItem(address); + } } diff --git a/src/main/java/im/conversations/android/xmpp/XmppConnection.java b/src/main/java/im/conversations/android/xmpp/XmppConnection.java index 79e2d58df..3c169f6ef 100644 --- a/src/main/java/im/conversations/android/xmpp/XmppConnection.java +++ b/src/main/java/im/conversations/android/xmpp/XmppConnection.java @@ -1735,43 +1735,40 @@ public class XmppConnection implements Runnable { final Element bind = packet.findChild("bind"); if (bind != null && packet.getType() == IqPacket.TYPE.RESULT) { isBound = true; - final Element jid = bind.findChild("jid"); - if (jid != null && jid.getContent() != null) { - try { - final Jid assignedJid = Jid.ofEscaped(jid.getContent()); - if (!account.address.getDomain().equals(assignedJid.getDomain())) { - Log.d( - Config.LOGTAG, - account.address - + ": server tried to re-assign domain to " - + assignedJid.getDomain()); - throw new StateChangingError(ConnectionState.BIND_FAILURE); - } - setConnectionAddress(assignedJid); - if (streamFeatures.hasChild("session") - && !streamFeatures - .findChild("session") - .hasChild("optional")) { - sendStartSession(); - } else { - enableStreamManagement(); - sendPostBindInitialization(false); - } - return; - } catch (final IllegalArgumentException e) { - Log.d( - Config.LOGTAG, - account.address - + ": server reported invalid jid (" - + jid.getContent() - + ") on bind"); - } - } else { + final String jid = bind.findChildContent("jid"); + if (Strings.isNullOrEmpty(jid)) { + throw new StateChangingError(ConnectionState.BIND_FAILURE); + } + final Jid assignedJid; + try { + assignedJid = Jid.ofEscaped(jid); + } catch (final IllegalArgumentException e) { Log.d( Config.LOGTAG, account.address - + ": disconnecting because of bind failure. (no jid)"); + + ": server reported invalid jid (" + + jid + + ") on bind"); + throw new StateChangingError(ConnectionState.BIND_FAILURE); } + + if (!account.address.getDomain().equals(assignedJid.getDomain())) { + Log.d( + Config.LOGTAG, + account.address + + ": server tried to re-assign domain to " + + assignedJid.getDomain()); + throw new StateChangingError(ConnectionState.BIND_FAILURE); + } + setConnectionAddress(assignedJid); + if (streamFeatures.hasChild("session") + && !streamFeatures.findChild("session").hasChild("optional")) { + sendStartSession(); + } else { + enableStreamManagement(); + sendPostBindInitialization(false); + } + return; } else { Log.d( Config.LOGTAG, @@ -1885,14 +1882,14 @@ public class XmppConnection implements Runnable { final var discoManager = getManager(DiscoManager.class); final var nodeHash = this.streamFeatures.getCapabilities(); + final var domainDiscoItem = Entity.discoItem(account.address.getDomain()); if (nodeHash != null) { - discoFutures.add( - discoManager.info(account.address.getDomain(), nodeHash.node, nodeHash.hash)); + discoFutures.add(discoManager.info(domainDiscoItem, nodeHash.node, nodeHash.hash)); } else { - discoFutures.add(discoManager.info(account.address.getDomain())); + discoFutures.add(discoManager.info(domainDiscoItem)); } - discoFutures.add(discoManager.info(account.address)); - discoFutures.add(discoManager.itemsWithInfo(account.address.getDomain())); + discoFutures.add(discoManager.info(Entity.discoItem(account.address))); + discoFutures.add(discoManager.itemsWithInfo(domainDiscoItem)); final var discoFuture = Futures.withTimeout( @@ -1912,6 +1909,7 @@ public class XmppConnection implements Runnable { @Override public void onFailure(@NonNull Throwable t) { + Log.d(Config.LOGTAG, "unable to fetch disco foo " + t); // TODO reset stream ID so we get a proper connect next time finalizeBind(); } 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 a87679685..00eac5710 100644 --- a/src/main/java/im/conversations/android/xmpp/manager/DiscoManager.java +++ b/src/main/java/im/conversations/android/xmpp/manager/DiscoManager.java @@ -8,6 +8,7 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.stanzas.IqPacket; +import im.conversations.android.xmpp.Entity; import im.conversations.android.xmpp.EntityCapabilities; import im.conversations.android.xmpp.EntityCapabilities2; import im.conversations.android.xmpp.XmppConnection; @@ -24,12 +25,12 @@ public class DiscoManager extends AbstractManager { super(context, connection); } - public ListenableFuture info(final Jid entity) { + public ListenableFuture info(final Entity entity) { return info(entity, null); } public ListenableFuture info( - final Jid entity, @Nullable final String node, final EntityCapabilities.Hash hash) { + final Entity entity, @Nullable final String node, final EntityCapabilities.Hash hash) { final String capabilityNode = hash.capabilityNode(node); if (getDatabase().discoDao().set(getAccount(), entity, capabilityNode, hash)) { return Futures.immediateFuture(null); @@ -38,9 +39,9 @@ public class DiscoManager extends AbstractManager { info(entity, capabilityNode), f -> null, MoreExecutors.directExecutor()); } - public ListenableFuture info(final Jid entity, final String node) { + public ListenableFuture info(final Entity entity, final String node) { final var iqRequest = new IqPacket(IqPacket.TYPE.GET); - iqRequest.setTo(entity); + iqRequest.setTo(entity.address); final var infoQueryRequest = new InfoQuery(); if (node != null) { infoQueryRequest.setNode(node); @@ -70,9 +71,9 @@ public class DiscoManager extends AbstractManager { MoreExecutors.directExecutor()); } - public ListenableFuture> items(final Jid entity) { + public ListenableFuture> items(final Entity.DiscoItem entity) { final var iqPacket = new IqPacket(IqPacket.TYPE.GET); - iqPacket.setTo(entity); + iqPacket.setTo(entity.address); iqPacket.addChild(new ItemsQuery()); final var future = connection.sendIqPacket(iqPacket); return Futures.transform( @@ -91,14 +92,16 @@ public class DiscoManager extends AbstractManager { MoreExecutors.directExecutor()); } - public ListenableFuture> itemsWithInfo(final Jid entity) { + public ListenableFuture> itemsWithInfo(final Entity.DiscoItem entity) { final var itemsFutures = items(entity); return Futures.transformAsync( itemsFutures, items -> { - // TODO filter out items with empty jid + final var filtered = + Collections2.filter(items, i -> Objects.nonNull(i.getJid())); Collection> infoFutures = - Collections2.transform(items, i -> info(i.getJid(), i.getNode())); + Collections2.transform( + filtered, i -> info(Entity.discoItem(i.getJid()), i.getNode())); return Futures.allAsList(infoFutures); }, MoreExecutors.directExecutor()); diff --git a/src/main/java/im/conversations/android/xmpp/model/disco/items/Item.java b/src/main/java/im/conversations/android/xmpp/model/disco/items/Item.java index c99fd6c8a..9412616f5 100644 --- a/src/main/java/im/conversations/android/xmpp/model/disco/items/Item.java +++ b/src/main/java/im/conversations/android/xmpp/model/disco/items/Item.java @@ -1,5 +1,6 @@ package im.conversations.android.xmpp.model.disco.items; +import androidx.annotation.Nullable; import eu.siacs.conversations.xmpp.Jid; import im.conversations.android.annotation.XmlElement; import im.conversations.android.xmpp.model.Extension; @@ -14,7 +15,7 @@ public class Item extends Extension { return getAttributeAsJid("jid"); } - public String getNode() { + public @Nullable String getNode() { return this.getAttribute("node"); } } diff --git a/src/main/java/im/conversations/android/xmpp/processor/PresenceProcessor.java b/src/main/java/im/conversations/android/xmpp/processor/PresenceProcessor.java index 98c9560dd..54a8fc26e 100644 --- a/src/main/java/im/conversations/android/xmpp/processor/PresenceProcessor.java +++ b/src/main/java/im/conversations/android/xmpp/processor/PresenceProcessor.java @@ -4,6 +4,7 @@ import android.content.Context; import eu.siacs.conversations.xmpp.stanzas.PresencePacket; import im.conversations.android.database.model.PresenceShow; import im.conversations.android.database.model.PresenceType; +import im.conversations.android.xmpp.Entity; import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.manager.DiscoManager; import java.util.function.Consumer; @@ -41,7 +42,8 @@ public class PresenceProcessor extends XmppConnection.Delegate implements Consum final var entity = presencePacket.getFrom(); final var nodeHash = presencePacket.getCapabilities(); if (nodeHash != null) { - getManager(DiscoManager.class).info(entity, nodeHash.node, nodeHash.hash); + getManager(DiscoManager.class) + .info(Entity.presence(entity), nodeHash.node, nodeHash.hash); } } }