make name+namespace assignment in xmpp less error prone

This commit is contained in:
Daniel Gultsch 2023-01-16 09:57:49 +01:00
parent 07c1669813
commit 38c612d35d
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
24 changed files with 268 additions and 23 deletions

View file

@ -20,8 +20,9 @@ public class DiscoEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull Long accountId;
public byte[] capsHash;
public byte[] caps2Hash;
public String caps2Algorithm;
@NonNull Long accountId;
}

View file

@ -0,0 +1,45 @@
package im.conversations.android.database.entity;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import eu.siacs.conversations.xmpp.Jid;
@Entity(
tableName = "disco_item",
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"},
unique = true),
@Index(
value = {"accountId", "parent"},
unique = false)
})
public class DiscoItemEntity {
@PrimaryKey(autoGenerate = true)
public Long id;
@NonNull Long accountId;
@NonNull Jid address;
@Nullable public Jid parent;
public Long discoId;
}

View file

@ -2,7 +2,8 @@ package im.conversations.android.xmpp;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import eu.siacs.conversations.xml.Element;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.annotation.XmlPackage;
@ -12,31 +13,37 @@ import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public final class Extensions {
// TODO these two maps can easily be generated by an annotation processor
private static final List<Class<? extends Extension>> ELEMENTS =
Arrays.asList(
im.conversations.android.xmpp.model.roster.Query.class,
im.conversations.android.xmpp.model.roster.Item.class,
im.conversations.android.xmpp.model.blocking.Item.class,
im.conversations.android.xmpp.model.blocking.Block.class,
im.conversations.android.xmpp.model.blocking.Blocklist.class,
im.conversations.android.xmpp.model.blocking.Unblock.class);
im.conversations.android.xmpp.model.blocking.Unblock.class,
im.conversations.android.xmpp.model.data.Data.class,
im.conversations.android.xmpp.model.data.Field.class,
im.conversations.android.xmpp.model.disco.info.Feature.class,
im.conversations.android.xmpp.model.disco.info.Identity.class,
im.conversations.android.xmpp.model.disco.info.InfoQuery.class,
im.conversations.android.xmpp.model.disco.items.Item.class,
im.conversations.android.xmpp.model.disco.items.ItemsQuery.class,
im.conversations.android.xmpp.model.roster.Query.class,
im.conversations.android.xmpp.model.roster.Item.class);
private static final Map<Id, Class<? extends Extension>> EXTENSION_CLASS_MAP;
private static final BiMap<Id, Class<? extends Extension>> EXTENSION_CLASS_MAP;
static {
final var builder = new ImmutableMap.Builder<Id, Class<? extends Extension>>();
final var builder = new ImmutableBiMap.Builder<Id, Class<? extends Extension>>();
for (final Class<? extends Extension> clazz : ELEMENTS) {
builder.put(id(clazz), clazz);
builder.put(of(clazz), clazz);
}
EXTENSION_CLASS_MAP = builder.build();
}
private static Id id(final Class<? extends Extension> clazz) {
private static Id of(final Class<? extends Extension> clazz) {
final XmlElement xmlElement = clazz.getAnnotation(XmlElement.class);
final Package clazzPackage = clazz.getPackage();
final XmlPackage xmlPackage =
@ -92,6 +99,10 @@ public final class Extensions {
return EXTENSION_CLASS_MAP.get(new Id(name, namespace));
}
public static Id id(final Class<? extends Extension> clazz) {
return EXTENSION_CLASS_MAP.inverse().get(clazz);
}
private Extensions() {}
public static class Id {

View file

@ -6,6 +6,7 @@ import com.google.common.collect.ImmutableClassToInstanceMap;
import im.conversations.android.xmpp.manager.AbstractManager;
import im.conversations.android.xmpp.manager.BlockingManager;
import im.conversations.android.xmpp.manager.BookmarkManager;
import im.conversations.android.xmpp.manager.DiscoManager;
import im.conversations.android.xmpp.manager.RosterManager;
public final class Managers {
@ -17,6 +18,7 @@ public final class Managers {
return new ImmutableClassToInstanceMap.Builder<AbstractManager>()
.put(BlockingManager.class, new BlockingManager(context, connection))
.put(BookmarkManager.class, new BookmarkManager(context, connection))
.put(DiscoManager.class, new DiscoManager(context, connection))
.put(RosterManager.class, new RosterManager(context, connection))
.build();
}

View file

@ -17,6 +17,8 @@ import androidx.annotation.Nullable;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.ClassToInstanceMap;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.XmppDomainVerifier;
@ -97,6 +99,7 @@ import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiFunction;
@ -2235,6 +2238,24 @@ public class XmppConnection implements Runnable {
return String.format("%s.%s", context.getString(R.string.app_name), postfixId);
}
public ListenableFuture<IqPacket> sendIqPacket(final IqPacket packet) {
final SettableFuture<IqPacket> future = SettableFuture.create();
sendIqPacket(
packet,
result -> {
final var type = result.getType();
if (type == IqPacket.TYPE.RESULT) {
future.set(result);
} else if (type == IqPacket.TYPE.TIMEOUT) {
future.setException(new TimeoutException());
} else {
// TODO some sort of IqErrorException
future.setException(new IOException());
}
});
return future;
}
public String sendIqPacket(final IqPacket packet, final Consumer<IqPacket> callback) {
packet.setFrom(account.address);
return this.sendUnmodifiedIqPacket(packet, callback, false);

View file

@ -0,0 +1,57 @@
package im.conversations.android.xmpp.manager;
import android.content.Context;
import com.google.common.util.concurrent.Futures;
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.XmppConnection;
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
import im.conversations.android.xmpp.model.disco.items.Item;
import im.conversations.android.xmpp.model.disco.items.ItemsQuery;
import java.util.Collection;
public class DiscoManager extends AbstractManager {
public DiscoManager(Context context, XmppConnection connection) {
super(context, connection);
}
public ListenableFuture<InfoQuery> info(final Jid entity) {
final var iqPacket = new IqPacket(IqPacket.TYPE.GET);
iqPacket.setTo(entity);
iqPacket.addChild(new InfoQuery());
final var future = connection.sendIqPacket(iqPacket);
return Futures.transform(
future,
iqResult -> {
final var infoQuery = iqResult.getExtension(InfoQuery.class);
if (infoQuery == null) {
throw new IllegalStateException();
}
// TODO store query
return infoQuery;
},
MoreExecutors.directExecutor());
}
public ListenableFuture<Collection<Item>> items(final Jid entity) {
final var iqPacket = new IqPacket(IqPacket.TYPE.GET);
iqPacket.setTo(entity);
iqPacket.addChild(new ItemsQuery());
final var future = connection.sendIqPacket(iqPacket);
return Futures.transform(
future,
iqResult -> {
final var itemsQuery = iqResult.getExtension(ItemsQuery.class);
if (itemsQuery == null) {
throw new IllegalStateException();
}
// TODO store items
final var items = itemsQuery.getExtensions(Item.class);
return items;
},
MoreExecutors.directExecutor());
}
}

View file

@ -1,9 +1,15 @@
package im.conversations.android.xmpp.model;
import eu.siacs.conversations.xml.Element;
import im.conversations.android.xmpp.Extensions;
public class Extension extends Element {
public Extension(String name, String xmlns) {
super(name, xmlns);
private Extension(final Extensions.Id id) {
super(id.name, id.namespace);
}
public Extension(final Class<? extends Extension> clazz) {
this(Extensions.id(clazz));
}
}

View file

@ -1,6 +1,5 @@
package im.conversations.android.xmpp.model.blocking;
import eu.siacs.conversations.xml.Namespace;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@ -8,6 +7,6 @@ import im.conversations.android.xmpp.model.Extension;
public class Block extends Extension {
public Block() {
super("block", Namespace.BLOCKING);
super(Block.class);
}
}

View file

@ -1,12 +1,11 @@
package im.conversations.android.xmpp.model.blocking;
import eu.siacs.conversations.xml.Namespace;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@XmlElement
public class Blocklist extends Extension {
public Blocklist() {
super("blocklist", Namespace.BLOCKING);
super(Blocklist.class);
}
}

View file

@ -1,6 +1,5 @@
package im.conversations.android.xmpp.model.blocking;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@ -9,7 +8,7 @@ import im.conversations.android.xmpp.model.Extension;
public class Item extends Extension {
public Item() {
super("item", Namespace.BLOCKING);
super(Item.class);
}
public Jid getJid() {

View file

@ -1,6 +1,5 @@
package im.conversations.android.xmpp.model.blocking;
import eu.siacs.conversations.xml.Namespace;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@ -8,6 +7,6 @@ import im.conversations.android.xmpp.model.Extension;
public class Unblock extends Extension {
public Unblock() {
super("unblock", Namespace.BLOCKING);
super(Unblock.class);
}
}

View file

@ -0,0 +1,5 @@
@XmlPackage(namespace = Namespace.BLOCKING)
package im.conversations.android.xmpp.model.blocking;
import eu.siacs.conversations.xml.Namespace;
import im.conversations.android.annotation.XmlPackage;

View file

@ -0,0 +1,11 @@
package im.conversations.android.xmpp.model.data;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@XmlElement(name = "x")
public class Data extends Extension {
public Data() {
super(Data.class);
}
}

View file

@ -0,0 +1,11 @@
package im.conversations.android.xmpp.model.data;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@XmlElement
public class Field extends Extension {
public Field() {
super(Field.class);
}
}

View file

@ -0,0 +1,5 @@
@XmlPackage(namespace = Namespace.DATA)
package im.conversations.android.xmpp.model.data;
import eu.siacs.conversations.xml.Namespace;
import im.conversations.android.annotation.XmlPackage;

View file

@ -0,0 +1,11 @@
package im.conversations.android.xmpp.model.disco.info;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@XmlElement
public class Feature extends Extension {
public Feature() {
super(Feature.class);
}
}

View file

@ -0,0 +1,11 @@
package im.conversations.android.xmpp.model.disco.info;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@XmlElement
public class Identity extends Extension {
public Identity() {
super(Identity.class);
}
}

View file

@ -0,0 +1,12 @@
package im.conversations.android.xmpp.model.disco.info;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@XmlElement(name = "query")
public class InfoQuery extends Extension {
public InfoQuery() {
super(InfoQuery.class);
}
}

View file

@ -0,0 +1,5 @@
@XmlPackage(namespace = Namespace.DISCO_INFO)
package im.conversations.android.xmpp.model.disco.info;
import eu.siacs.conversations.xml.Namespace;
import im.conversations.android.annotation.XmlPackage;

View file

@ -0,0 +1,20 @@
package im.conversations.android.xmpp.model.disco.items;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@XmlElement
public class Item extends Extension {
public Item() {
super(Item.class);
}
public Jid getJid() {
return getAttributeAsJid("jid");
}
public String getNode() {
return this.getAttribute("node");
}
}

View file

@ -0,0 +1,11 @@
package im.conversations.android.xmpp.model.disco.items;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@XmlElement(name = "query")
public class ItemsQuery extends Extension {
public ItemsQuery() {
super(ItemsQuery.class);
}
}

View file

@ -0,0 +1,5 @@
@XmlPackage(namespace = Namespace.DISCO_ITEMS)
package im.conversations.android.xmpp.model.disco.items;
import eu.siacs.conversations.xml.Namespace;
import im.conversations.android.annotation.XmlPackage;

View file

@ -1,6 +1,5 @@
package im.conversations.android.xmpp.model.roster;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.annotation.XmlElement;
import im.conversations.android.xmpp.model.Extension;
@ -15,7 +14,7 @@ public class Item extends Extension {
Arrays.asList(Subscription.NONE, Subscription.TO, Subscription.FROM, Subscription.BOTH);
public Item() {
super("item", Namespace.ROSTER);
super(Item.class);
}
public Jid getJid() {

View file

@ -8,7 +8,7 @@ import im.conversations.android.xmpp.model.Extension;
public class Query extends Extension {
public Query() {
super("query", Namespace.ROSTER);
super(Query.class);
}
public void setVersion(final String rosterVersion) {