use empty string instead of null for 'no node' and 'no resource'

This commit is contained in:
Daniel Gultsch 2023-01-19 19:48:57 +01:00
parent 90e613f94e
commit 1a09b3ed05
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
12 changed files with 203 additions and 96 deletions

View file

@ -2,7 +2,7 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 1, "version": 1,
"identityHash": "a521ff7a3e16cca9f6cbfe51241ec021", "identityHash": "b28e01dcbb5d9774a4b36783d0db6c73",
"entities": [ "entities": [
{ {
"tableName": "account", "tableName": "account",
@ -427,7 +427,7 @@
}, },
{ {
"tableName": "disco_ext_field_value", "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": [ "fields": [
{ {
"fieldPath": "id", "fieldPath": "id",
@ -467,7 +467,7 @@
], ],
"foreignKeys": [ "foreignKeys": [
{ {
"table": "disco_ext", "table": "disco_ext_field",
"onDelete": "CASCADE", "onDelete": "CASCADE",
"onUpdate": "NO ACTION", "onUpdate": "NO ACTION",
"columns": [ "columns": [
@ -601,7 +601,7 @@
}, },
{ {
"tableName": "disco_item", "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": [ "fields": [
{ {
"fieldPath": "id", "fieldPath": "id",
@ -625,7 +625,7 @@
"fieldPath": "node", "fieldPath": "node",
"columnName": "node", "columnName": "node",
"affinity": "TEXT", "affinity": "TEXT",
"notNull": false "notNull": true
}, },
{ {
"fieldPath": "parent", "fieldPath": "parent",
@ -975,7 +975,7 @@
}, },
{ {
"tableName": "presence", "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": [ "fields": [
{ {
"fieldPath": "id", "fieldPath": "id",
@ -999,7 +999,7 @@
"fieldPath": "resource", "fieldPath": "resource",
"columnName": "resource", "columnName": "resource",
"affinity": "TEXT", "affinity": "TEXT",
"notNull": false "notNull": true
}, },
{ {
"fieldPath": "type", "fieldPath": "type",
@ -1054,6 +1054,12 @@
"columnName": "mucUserSelf", "columnName": "mucUserSelf",
"affinity": "INTEGER", "affinity": "INTEGER",
"notNull": true "notNull": true
},
{
"fieldPath": "discoId",
"columnName": "discoId",
"affinity": "INTEGER",
"notNull": false
} }
], ],
"primaryKey": { "primaryKey": {
@ -1073,6 +1079,15 @@
], ],
"orders": [], "orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_presence_accountId_address_resource` ON `${TABLE_NAME}` (`accountId`, `address`, `resource`)" "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": [ "foreignKeys": [
@ -1086,6 +1101,17 @@
"referencedColumns": [ "referencedColumns": [
"id" "id"
] ]
},
{
"table": "disco",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"discoId"
],
"referencedColumns": [
"id"
]
} }
] ]
}, },
@ -1310,7 +1336,7 @@
"views": [], "views": [],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "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')"
] ]
} }
} }

View file

@ -72,7 +72,7 @@ public class Element {
} }
public <E extends Extension> E getExtension(final Class<E> clazz) { public <E extends Extension> E getExtension(final Class<E> clazz) {
final var extension = Iterables.find(this.children, clazz::isInstance); final var extension = Iterables.find(this.children, clazz::isInstance, null);
if (extension == null) { if (extension == null) {
return null; return null;
} }

View file

@ -1,10 +1,13 @@
package im.conversations.android.database.dao; package im.conversations.android.database.dao;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.room.Dao; import androidx.room.Dao;
import androidx.room.Insert; import androidx.room.Insert;
import androidx.room.Query; import androidx.room.Query;
import androidx.room.Transaction; import androidx.room.Transaction;
import androidx.room.Upsert; import androidx.room.Upsert;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.entity.DiscoEntity; 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.DiscoIdentityEntity;
import im.conversations.android.database.entity.DiscoItemEntity; import im.conversations.android.database.entity.DiscoItemEntity;
import im.conversations.android.database.model.Account; import im.conversations.android.database.model.Account;
import im.conversations.android.xmpp.Entity;
import im.conversations.android.xmpp.EntityCapabilities; import im.conversations.android.xmpp.EntityCapabilities;
import im.conversations.android.xmpp.EntityCapabilities2; import im.conversations.android.xmpp.EntityCapabilities2;
import im.conversations.android.xmpp.model.data.Data; import im.conversations.android.xmpp.model.data.Data;
@ -43,12 +47,18 @@ public abstract class DiscoDao {
protected abstract void deleteNonExistentDiscoItems( protected abstract void deleteNonExistentDiscoItems(
final long account, final Jid parent, final Collection<Jid> existent); final long account, final Jid parent, final Collection<Jid> 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 @Insert
protected abstract void insertDiscoFieldValues( protected abstract void insertDiscoFieldValues(
Collection<DiscoExtensionFieldValueEntity> value); Collection<DiscoExtensionFieldValueEntity> value);
@Upsert(entity = DiscoItemEntity.class) @Upsert(entity = DiscoItemEntity.class)
protected abstract void insert(DiscoItemWithDiscoId item); protected abstract void updateDiscoIdInDiscoItem(DiscoItemWithDiscoId item);
@Insert @Insert
protected abstract long insert(DiscoEntity entity); protected abstract long insert(DiscoEntity entity);
@ -60,19 +70,20 @@ public abstract class DiscoDao {
protected abstract long insert(DiscoExtensionFieldEntity entity); protected abstract long insert(DiscoExtensionFieldEntity entity);
@Transaction @Transaction
public void set(final Account account, final Jid parent, final Collection<Item> items) { public void set(
final Account account, final Entity.DiscoItem parent, final Collection<Item> items) {
final var entities = final var entities =
Collections2.transform(items, i -> DiscoItemWithParent.of(account.id, parent, i)); Collections2.transform(items, i -> DiscoItemWithParent.of(account.id, parent, i));
insertDiscoItems(entities); insertDiscoItems(entities);
deleteNonExistentDiscoItems( deleteNonExistentDiscoItems(
account.id, parent, Collections2.transform(items, Item::getJid)); account.id, parent.address, Collections2.transform(items, Item::getJid));
} }
@Transaction @Transaction
public boolean set( public boolean set(
final Account account, final Account account,
final Jid address, final Entity entity,
final String node, @Nullable final String node,
final EntityCapabilities.Hash capsHash) { final EntityCapabilities.Hash capsHash) {
final Long existingDiscoId; final Long existingDiscoId;
if (capsHash instanceof EntityCapabilities2.EntityCaps2Hash) { if (capsHash instanceof EntityCapabilities2.EntityCaps2Hash) {
@ -85,14 +96,31 @@ public abstract class DiscoDao {
if (existingDiscoId == null) { if (existingDiscoId == null) {
return false; return false;
} }
insert(DiscoItemWithDiscoId.of(account.id, address, node, existingDiscoId)); updateDiscoId(account.id, entity, node, existingDiscoId);
return true; 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 @Transaction
public void set( public void set(
final Account account, final Account account,
final Jid address, final Entity entity,
final String node, final String node,
final byte[] capsHash, final byte[] capsHash,
final byte[] caps2HashSha256, final byte[] caps2HashSha256,
@ -100,7 +128,7 @@ public abstract class DiscoDao {
final Long existingDiscoId = getDiscoId(account.id, caps2HashSha256); final Long existingDiscoId = getDiscoId(account.id, caps2HashSha256);
if (existingDiscoId != null) { if (existingDiscoId != null) {
insert(DiscoItemWithDiscoId.of(account.id, address, node, existingDiscoId)); updateDiscoId(account.id, entity, node, existingDiscoId);
return; return;
} }
final long discoId = insert(DiscoEntity.of(account.id, capsHash, caps2HashSha256)); final long discoId = insert(DiscoEntity.of(account.id, capsHash, caps2HashSha256));
@ -125,6 +153,7 @@ public abstract class DiscoDao {
v -> DiscoExtensionFieldValueEntity.of(fieldId, v.getContent()))); v -> DiscoExtensionFieldValueEntity.of(fieldId, v.getContent())));
} }
} }
updateDiscoId(account.id, entity, node, discoId);
} }
@Query("SELECT id FROM disco WHERE accountId=:accountId AND caps2HashSha256=:caps2HashSha256") @Query("SELECT id FROM disco WHERE accountId=:accountId AND caps2HashSha256=:caps2HashSha256")
@ -141,32 +170,35 @@ public abstract class DiscoDao {
public static class DiscoItemWithParent { public static class DiscoItemWithParent {
public long accountId; public long accountId;
public Jid address; public @NonNull Jid address;
public String node; public @NonNull String node;
public Jid parent; public @Nullable Jid parent;
public static DiscoItemWithParent of( 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(); final var entity = new DiscoItemWithParent();
entity.accountId = account; entity.accountId = account;
entity.address = item.getJid(); entity.address = item.getJid();
entity.node = item.getNode(); entity.node = Strings.nullToEmpty(item.getNode());
entity.parent = parent; entity.parent = parent.address;
return entity; return entity;
} }
} }
public static class DiscoItemWithDiscoId { public static class DiscoItemWithDiscoId {
public long accountId; public long accountId;
public Jid address; public @NonNull Jid address;
public String node; public @NonNull String node;
public long discoId; public long discoId;
public static DiscoItemWithDiscoId of( 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(); final var entity = new DiscoItemWithDiscoId();
entity.accountId = account; entity.accountId = account;
entity.address = address; entity.address = discoItem.address;
entity.node = node; entity.node = node;
entity.discoId = discoId; entity.discoId = discoId;
return entity; return entity;

View file

@ -1,5 +1,7 @@
package im.conversations.android.database.dao; package im.conversations.android.database.dao;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.room.Dao; import androidx.room.Dao;
import androidx.room.Query; import androidx.room.Query;
import androidx.room.Upsert; import androidx.room.Upsert;
@ -28,12 +30,12 @@ public abstract class PresenceDao {
abstract void insert(PresenceEntity entity); abstract void insert(PresenceEntity entity);
public void set( public void set(
Account account, @NonNull final Account account,
Jid address, @NonNull final Jid address,
String resource, @Nullable final String resource,
PresenceType type, @Nullable final PresenceType type,
PresenceShow show, @Nullable final PresenceShow show,
String status) { @Nullable final String status) {
if (resource == null if (resource == null
&& Arrays.asList(PresenceType.ERROR, PresenceType.UNAVAILABLE).contains(type)) { && Arrays.asList(PresenceType.ERROR, PresenceType.UNAVAILABLE).contains(type)) {
deletePresences(account.id, address); deletePresences(account.id, address);

View file

@ -10,7 +10,7 @@ import androidx.room.PrimaryKey;
tableName = "disco_ext_field_value", tableName = "disco_ext_field_value",
foreignKeys = foreignKeys =
@ForeignKey( @ForeignKey(
entity = DiscoExtensionEntity.class, entity = DiscoExtensionFieldEntity.class,
parentColumns = {"id"}, parentColumns = {"id"},
childColumns = {"fieldId"}, childColumns = {"fieldId"},
onDelete = ForeignKey.CASCADE), onDelete = ForeignKey.CASCADE),
@ -25,6 +25,9 @@ public class DiscoExtensionFieldValueEntity {
public String value; public String value;
public static DiscoExtensionFieldValueEntity of(long fieldId, final 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;
} }
} }

View file

@ -40,7 +40,7 @@ public class DiscoItemEntity {
@NonNull Jid address; @NonNull Jid address;
@Nullable public String node; @NonNull public String node;
@Nullable public Jid parent; @Nullable public Jid parent;

View file

@ -6,6 +6,7 @@ import androidx.room.Entity;
import androidx.room.ForeignKey; import androidx.room.ForeignKey;
import androidx.room.Index; import androidx.room.Index;
import androidx.room.PrimaryKey; import androidx.room.PrimaryKey;
import com.google.common.base.Strings;
import eu.siacs.conversations.entities.MucOptions; import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.database.model.PresenceShow; import im.conversations.android.database.model.PresenceShow;
@ -13,16 +14,23 @@ import im.conversations.android.database.model.PresenceType;
@Entity( @Entity(
tableName = "presence", tableName = "presence",
foreignKeys = foreignKeys = {
@ForeignKey( @ForeignKey(
entity = AccountEntity.class, entity = AccountEntity.class,
parentColumns = {"id"}, parentColumns = {"id"},
childColumns = {"accountId"}, childColumns = {"accountId"},
onDelete = ForeignKey.CASCADE), onDelete = ForeignKey.CASCADE),
@ForeignKey(
entity = DiscoEntity.class,
parentColumns = {"id"},
childColumns = {"discoId"},
onDelete = ForeignKey.CASCADE)
},
indices = { indices = {
@Index( @Index(
value = {"accountId", "address", "resource"}, value = {"accountId", "address", "resource"},
unique = true) unique = true),
@Index(value = {"discoId"})
}) })
public class PresenceEntity { public class PresenceEntity {
@ -33,7 +41,7 @@ public class PresenceEntity {
@NonNull public Jid address; @NonNull public Jid address;
@Nullable public String resource; @NonNull public String resource;
@Nullable public PresenceType type; @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) // set to true if presence has status code 110 (this means we are online)
public boolean mucUserSelf; public boolean mucUserSelf;
public Long discoId;
public static PresenceEntity of( public static PresenceEntity of(
long account, long account,
Jid address, Jid address,
String resource, @Nullable String resource,
PresenceType type, PresenceType type,
PresenceShow show, PresenceShow show,
String status) { String status) {
final var entity = new PresenceEntity(); final var entity = new PresenceEntity();
entity.accountId = account; entity.accountId = account;
entity.address = address; entity.address = address;
entity.resource = resource; entity.resource = Strings.nullToEmpty(resource);
entity.type = type; entity.type = type;
entity.show = show; entity.show = show;
entity.status = status; entity.status = status;

View file

@ -1,4 +1,34 @@
package im.conversations.android.xmpp; 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);
}
} }

View file

@ -1735,10 +1735,23 @@ public class XmppConnection implements Runnable {
final Element bind = packet.findChild("bind"); final Element bind = packet.findChild("bind");
if (bind != null && packet.getType() == IqPacket.TYPE.RESULT) { if (bind != null && packet.getType() == IqPacket.TYPE.RESULT) {
isBound = true; isBound = true;
final Element jid = bind.findChild("jid"); final String jid = bind.findChildContent("jid");
if (jid != null && jid.getContent() != null) { if (Strings.isNullOrEmpty(jid)) {
throw new StateChangingError(ConnectionState.BIND_FAILURE);
}
final Jid assignedJid;
try { try {
final Jid assignedJid = Jid.ofEscaped(jid.getContent()); assignedJid = Jid.ofEscaped(jid);
} catch (final IllegalArgumentException e) {
Log.d(
Config.LOGTAG,
account.address
+ ": server reported invalid jid ("
+ jid
+ ") on bind");
throw new StateChangingError(ConnectionState.BIND_FAILURE);
}
if (!account.address.getDomain().equals(assignedJid.getDomain())) { if (!account.address.getDomain().equals(assignedJid.getDomain())) {
Log.d( Log.d(
Config.LOGTAG, Config.LOGTAG,
@ -1749,29 +1762,13 @@ public class XmppConnection implements Runnable {
} }
setConnectionAddress(assignedJid); setConnectionAddress(assignedJid);
if (streamFeatures.hasChild("session") if (streamFeatures.hasChild("session")
&& !streamFeatures && !streamFeatures.findChild("session").hasChild("optional")) {
.findChild("session")
.hasChild("optional")) {
sendStartSession(); sendStartSession();
} else { } else {
enableStreamManagement(); enableStreamManagement();
sendPostBindInitialization(false); sendPostBindInitialization(false);
} }
return; return;
} catch (final IllegalArgumentException e) {
Log.d(
Config.LOGTAG,
account.address
+ ": server reported invalid jid ("
+ jid.getContent()
+ ") on bind");
}
} else {
Log.d(
Config.LOGTAG,
account.address
+ ": disconnecting because of bind failure. (no jid)");
}
} else { } else {
Log.d( Log.d(
Config.LOGTAG, Config.LOGTAG,
@ -1885,14 +1882,14 @@ public class XmppConnection implements Runnable {
final var discoManager = getManager(DiscoManager.class); final var discoManager = getManager(DiscoManager.class);
final var nodeHash = this.streamFeatures.getCapabilities(); final var nodeHash = this.streamFeatures.getCapabilities();
final var domainDiscoItem = Entity.discoItem(account.address.getDomain());
if (nodeHash != null) { if (nodeHash != null) {
discoFutures.add( discoFutures.add(discoManager.info(domainDiscoItem, nodeHash.node, nodeHash.hash));
discoManager.info(account.address.getDomain(), nodeHash.node, nodeHash.hash));
} else { } else {
discoFutures.add(discoManager.info(account.address.getDomain())); discoFutures.add(discoManager.info(domainDiscoItem));
} }
discoFutures.add(discoManager.info(account.address)); discoFutures.add(discoManager.info(Entity.discoItem(account.address)));
discoFutures.add(discoManager.itemsWithInfo(account.address.getDomain())); discoFutures.add(discoManager.itemsWithInfo(domainDiscoItem));
final var discoFuture = final var discoFuture =
Futures.withTimeout( Futures.withTimeout(
@ -1912,6 +1909,7 @@ public class XmppConnection implements Runnable {
@Override @Override
public void onFailure(@NonNull Throwable t) { 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 // TODO reset stream ID so we get a proper connect next time
finalizeBind(); finalizeBind();
} }

View file

@ -8,6 +8,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.stanzas.IqPacket; import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import im.conversations.android.xmpp.Entity;
import im.conversations.android.xmpp.EntityCapabilities; import im.conversations.android.xmpp.EntityCapabilities;
import im.conversations.android.xmpp.EntityCapabilities2; import im.conversations.android.xmpp.EntityCapabilities2;
import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.XmppConnection;
@ -24,12 +25,12 @@ public class DiscoManager extends AbstractManager {
super(context, connection); super(context, connection);
} }
public ListenableFuture<InfoQuery> info(final Jid entity) { public ListenableFuture<InfoQuery> info(final Entity entity) {
return info(entity, null); return info(entity, null);
} }
public ListenableFuture<Void> info( public ListenableFuture<Void> 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); final String capabilityNode = hash.capabilityNode(node);
if (getDatabase().discoDao().set(getAccount(), entity, capabilityNode, hash)) { if (getDatabase().discoDao().set(getAccount(), entity, capabilityNode, hash)) {
return Futures.immediateFuture(null); return Futures.immediateFuture(null);
@ -38,9 +39,9 @@ public class DiscoManager extends AbstractManager {
info(entity, capabilityNode), f -> null, MoreExecutors.directExecutor()); info(entity, capabilityNode), f -> null, MoreExecutors.directExecutor());
} }
public ListenableFuture<InfoQuery> info(final Jid entity, final String node) { public ListenableFuture<InfoQuery> info(final Entity entity, final String node) {
final var iqRequest = new IqPacket(IqPacket.TYPE.GET); final var iqRequest = new IqPacket(IqPacket.TYPE.GET);
iqRequest.setTo(entity); iqRequest.setTo(entity.address);
final var infoQueryRequest = new InfoQuery(); final var infoQueryRequest = new InfoQuery();
if (node != null) { if (node != null) {
infoQueryRequest.setNode(node); infoQueryRequest.setNode(node);
@ -70,9 +71,9 @@ public class DiscoManager extends AbstractManager {
MoreExecutors.directExecutor()); MoreExecutors.directExecutor());
} }
public ListenableFuture<Collection<Item>> items(final Jid entity) { public ListenableFuture<Collection<Item>> items(final Entity.DiscoItem entity) {
final var iqPacket = new IqPacket(IqPacket.TYPE.GET); final var iqPacket = new IqPacket(IqPacket.TYPE.GET);
iqPacket.setTo(entity); iqPacket.setTo(entity.address);
iqPacket.addChild(new ItemsQuery()); iqPacket.addChild(new ItemsQuery());
final var future = connection.sendIqPacket(iqPacket); final var future = connection.sendIqPacket(iqPacket);
return Futures.transform( return Futures.transform(
@ -91,14 +92,16 @@ public class DiscoManager extends AbstractManager {
MoreExecutors.directExecutor()); MoreExecutors.directExecutor());
} }
public ListenableFuture<List<InfoQuery>> itemsWithInfo(final Jid entity) { public ListenableFuture<List<InfoQuery>> itemsWithInfo(final Entity.DiscoItem entity) {
final var itemsFutures = items(entity); final var itemsFutures = items(entity);
return Futures.transformAsync( return Futures.transformAsync(
itemsFutures, itemsFutures,
items -> { items -> {
// TODO filter out items with empty jid final var filtered =
Collections2.filter(items, i -> Objects.nonNull(i.getJid()));
Collection<ListenableFuture<InfoQuery>> infoFutures = Collection<ListenableFuture<InfoQuery>> 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); return Futures.allAsList(infoFutures);
}, },
MoreExecutors.directExecutor()); MoreExecutors.directExecutor());

View file

@ -1,5 +1,6 @@
package im.conversations.android.xmpp.model.disco.items; package im.conversations.android.xmpp.model.disco.items;
import androidx.annotation.Nullable;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.annotation.XmlElement; import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension; import im.conversations.android.xmpp.model.Extension;
@ -14,7 +15,7 @@ public class Item extends Extension {
return getAttributeAsJid("jid"); return getAttributeAsJid("jid");
} }
public String getNode() { public @Nullable String getNode() {
return this.getAttribute("node"); return this.getAttribute("node");
} }
} }

View file

@ -4,6 +4,7 @@ import android.content.Context;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket; import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
import im.conversations.android.database.model.PresenceShow; import im.conversations.android.database.model.PresenceShow;
import im.conversations.android.database.model.PresenceType; import im.conversations.android.database.model.PresenceType;
import im.conversations.android.xmpp.Entity;
import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.manager.DiscoManager; import im.conversations.android.xmpp.manager.DiscoManager;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -41,7 +42,8 @@ public class PresenceProcessor extends XmppConnection.Delegate implements Consum
final var entity = presencePacket.getFrom(); final var entity = presencePacket.getFrom();
final var nodeHash = presencePacket.getCapabilities(); final var nodeHash = presencePacket.getCapabilities();
if (nodeHash != null) { if (nodeHash != null) {
getManager(DiscoManager.class).info(entity, nodeHash.node, nodeHash.hash); getManager(DiscoManager.class)
.info(Entity.presence(entity), nodeHash.node, nodeHash.hash);
} }
} }
} }