respond to disco#info queries
This commit is contained in:
parent
57d264d72e
commit
e073f22ec0
|
@ -1,23 +1,17 @@
|
||||||
package eu.siacs.conversations.xmpp.jingle.stanzas;
|
package eu.siacs.conversations.xmpp.jingle.stanzas;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
|
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
|
||||||
import eu.siacs.conversations.entities.DownloadableFile;
|
import eu.siacs.conversations.entities.DownloadableFile;
|
||||||
import eu.siacs.conversations.xml.Element;
|
import eu.siacs.conversations.xml.Element;
|
||||||
import eu.siacs.conversations.xml.Namespace;
|
import eu.siacs.conversations.xml.Namespace;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class FileTransferDescription extends GenericDescription {
|
public class FileTransferDescription extends GenericDescription {
|
||||||
|
|
||||||
public static List<String> NAMESPACES = Arrays.asList(
|
public static List<String> NAMESPACES =
|
||||||
Version.FT_3.namespace,
|
Arrays.asList(Version.FT_3.namespace, Version.FT_4.namespace, Version.FT_5.namespace);
|
||||||
Version.FT_4.namespace,
|
|
||||||
Version.FT_5.namespace
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
private FileTransferDescription(String name, String namespace) {
|
private FileTransferDescription(String name, String namespace) {
|
||||||
super(name, namespace);
|
super(name, namespace);
|
||||||
|
@ -46,8 +40,10 @@ public class FileTransferDescription extends GenericDescription {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FileTransferDescription of(DownloadableFile file, Version version, XmppAxolotlMessage axolotlMessage) {
|
public static FileTransferDescription of(
|
||||||
final FileTransferDescription description = new FileTransferDescription("description", version.getNamespace());
|
DownloadableFile file, Version version, XmppAxolotlMessage axolotlMessage) {
|
||||||
|
final FileTransferDescription description =
|
||||||
|
new FileTransferDescription("description", version.getNamespace());
|
||||||
final Element fileElement;
|
final Element fileElement;
|
||||||
if (version == Version.FT_3) {
|
if (version == Version.FT_3) {
|
||||||
Element offer = description.addChild("offer");
|
Element offer = description.addChild("offer");
|
||||||
|
@ -64,9 +60,14 @@ public class FileTransferDescription extends GenericDescription {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FileTransferDescription upgrade(final Element element) {
|
public static FileTransferDescription upgrade(final Element element) {
|
||||||
Preconditions.checkArgument("description".equals(element.getName()), "Name of provided element is not description");
|
Preconditions.checkArgument(
|
||||||
Preconditions.checkArgument(NAMESPACES.contains(element.getNamespace()), "Element does not match a file transfer namespace");
|
"description".equals(element.getName()),
|
||||||
final FileTransferDescription description = new FileTransferDescription("description", element.getNamespace());
|
"Name of provided element is not description");
|
||||||
|
Preconditions.checkArgument(
|
||||||
|
NAMESPACES.contains(element.getNamespace()),
|
||||||
|
"Element does not match a file transfer namespace");
|
||||||
|
final FileTransferDescription description =
|
||||||
|
new FileTransferDescription("description", element.getNamespace());
|
||||||
description.setAttributes(element.getAttributes());
|
description.setAttributes(element.getAttributes());
|
||||||
description.setChildren(element.getChildren());
|
description.setChildren(element.getChildren());
|
||||||
return description;
|
return description;
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package im.conversations.android.xmpp;
|
||||||
|
|
||||||
|
import com.google.common.collect.Collections2;
|
||||||
|
import im.conversations.android.xmpp.model.disco.info.Feature;
|
||||||
|
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ServiceDescription {
|
||||||
|
public final List<String> features;
|
||||||
|
public final Identity identity;
|
||||||
|
|
||||||
|
public ServiceDescription(List<String> features, Identity identity) {
|
||||||
|
this.features = features;
|
||||||
|
this.identity = identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public InfoQuery asInfoQuery() {
|
||||||
|
final var infoQuery = new InfoQuery();
|
||||||
|
final Collection<Feature> features =
|
||||||
|
Collections2.transform(
|
||||||
|
this.features,
|
||||||
|
sf -> {
|
||||||
|
final var feature = new Feature();
|
||||||
|
feature.setVar(sf);
|
||||||
|
return feature;
|
||||||
|
});
|
||||||
|
infoQuery.addExtensions(features);
|
||||||
|
final var identity =
|
||||||
|
infoQuery.addExtension(
|
||||||
|
new im.conversations.android.xmpp.model.disco.info.Identity());
|
||||||
|
identity.setIdentityName(this.identity.name);
|
||||||
|
identity.setCategory(this.identity.category);
|
||||||
|
identity.setType(this.identity.type);
|
||||||
|
return infoQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Identity {
|
||||||
|
public final String name;
|
||||||
|
public final String category;
|
||||||
|
public final String type;
|
||||||
|
|
||||||
|
public Identity(String name, String category, String type) {
|
||||||
|
this.name = name;
|
||||||
|
this.category = category;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,9 +57,12 @@ import im.conversations.android.xml.TagWriter;
|
||||||
import im.conversations.android.xmpp.manager.AbstractManager;
|
import im.conversations.android.xmpp.manager.AbstractManager;
|
||||||
import im.conversations.android.xmpp.manager.CarbonsManager;
|
import im.conversations.android.xmpp.manager.CarbonsManager;
|
||||||
import im.conversations.android.xmpp.manager.DiscoManager;
|
import im.conversations.android.xmpp.manager.DiscoManager;
|
||||||
|
import im.conversations.android.xmpp.model.Extension;
|
||||||
import im.conversations.android.xmpp.model.StreamElement;
|
import im.conversations.android.xmpp.model.StreamElement;
|
||||||
import im.conversations.android.xmpp.model.csi.Active;
|
import im.conversations.android.xmpp.model.csi.Active;
|
||||||
import im.conversations.android.xmpp.model.csi.Inactive;
|
import im.conversations.android.xmpp.model.csi.Inactive;
|
||||||
|
import im.conversations.android.xmpp.model.error.Condition;
|
||||||
|
import im.conversations.android.xmpp.model.error.Error;
|
||||||
import im.conversations.android.xmpp.model.ping.Ping;
|
import im.conversations.android.xmpp.model.ping.Ping;
|
||||||
import im.conversations.android.xmpp.model.register.Register;
|
import im.conversations.android.xmpp.model.register.Register;
|
||||||
import im.conversations.android.xmpp.model.sm.Ack;
|
import im.conversations.android.xmpp.model.sm.Ack;
|
||||||
|
@ -2042,6 +2045,29 @@ public class XmppConnection implements Runnable {
|
||||||
return packet.getId();
|
return packet.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void sendResultFor(final Iq request, final Extension... extensions) {
|
||||||
|
final var from = request.getFrom();
|
||||||
|
final var id = request.getId();
|
||||||
|
final var response = new Iq(Iq.Type.RESULT);
|
||||||
|
response.setTo(from);
|
||||||
|
response.setId(id);
|
||||||
|
for (final Extension extension : extensions) {
|
||||||
|
response.addExtension(extension);
|
||||||
|
}
|
||||||
|
this.sendPacket(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendErrorFor(final Iq request, final Condition condition) {
|
||||||
|
final var from = request.getFrom();
|
||||||
|
final var id = request.getId();
|
||||||
|
final var response = new Iq(Iq.Type.ERROR);
|
||||||
|
response.setTo(from);
|
||||||
|
response.setId(id);
|
||||||
|
final Error error = response.addExtension(new Error());
|
||||||
|
error.setCondition(condition);
|
||||||
|
this.sendPacket(response);
|
||||||
|
}
|
||||||
|
|
||||||
public void sendMessagePacket(final Message packet) {
|
public void sendMessagePacket(final Message packet) {
|
||||||
this.sendPacket(packet);
|
this.sendPacket(packet);
|
||||||
}
|
}
|
||||||
|
@ -2325,7 +2351,7 @@ public class XmppConnection implements Runnable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class StateChangingError extends Error {
|
private static class StateChangingError extends java.lang.Error {
|
||||||
private final ConnectionState state;
|
private final ConnectionState state;
|
||||||
|
|
||||||
public StateChangingError(ConnectionState state) {
|
public StateChangingError(ConnectionState state) {
|
||||||
|
|
|
@ -10,7 +10,6 @@ import com.google.common.io.BaseEncoding;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
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.BuildConfig;
|
import eu.siacs.conversations.BuildConfig;
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.xml.Namespace;
|
import eu.siacs.conversations.xml.Namespace;
|
||||||
|
@ -18,20 +17,25 @@ import eu.siacs.conversations.xmpp.Jid;
|
||||||
import im.conversations.android.xmpp.Entity;
|
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.ServiceDescription;
|
||||||
import im.conversations.android.xmpp.XmppConnection;
|
import im.conversations.android.xmpp.XmppConnection;
|
||||||
import im.conversations.android.xmpp.model.disco.info.Feature;
|
import im.conversations.android.xmpp.model.Hash;
|
||||||
import im.conversations.android.xmpp.model.disco.info.Identity;
|
|
||||||
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
|
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.Item;
|
||||||
import im.conversations.android.xmpp.model.disco.items.ItemsQuery;
|
import im.conversations.android.xmpp.model.disco.items.ItemsQuery;
|
||||||
|
import im.conversations.android.xmpp.model.error.Condition;
|
||||||
import im.conversations.android.xmpp.model.stanza.Iq;
|
import im.conversations.android.xmpp.model.stanza.Iq;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class DiscoManager extends AbstractManager {
|
public class DiscoManager extends AbstractManager {
|
||||||
|
|
||||||
|
private static final Logger LOGGER = LoggerFactory.getLogger(DiscoManager.class);
|
||||||
|
|
||||||
public static final String CAPABILITY_NODE = "http://conversations.im";
|
public static final String CAPABILITY_NODE = "http://conversations.im";
|
||||||
|
|
||||||
private static final Collection<String> FEATURES_BASE =
|
private static final Collection<String> FEATURES_BASE =
|
||||||
|
@ -205,12 +209,11 @@ public class DiscoManager extends AbstractManager {
|
||||||
return hasFeature(getAccount().address.getDomain(), feature);
|
return hasFeature(getAccount().address.getDomain(), feature);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InfoQuery getInfo() {
|
public ServiceDescription getServiceDescription() {
|
||||||
return getInfo(false);
|
return getServiceDescription(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private InfoQuery getInfo(final boolean privacyMode) {
|
private ServiceDescription getServiceDescription(final boolean privacyMode) {
|
||||||
final var infoQuery = new InfoQuery();
|
|
||||||
final ImmutableList.Builder<String> stringFeatureBuilder = ImmutableList.builder();
|
final ImmutableList.Builder<String> stringFeatureBuilder = ImmutableList.builder();
|
||||||
stringFeatureBuilder.addAll(FEATURES_BASE);
|
stringFeatureBuilder.addAll(FEATURES_BASE);
|
||||||
stringFeatureBuilder.addAll(
|
stringFeatureBuilder.addAll(
|
||||||
|
@ -218,21 +221,9 @@ public class DiscoManager extends AbstractManager {
|
||||||
if (!privacyMode) {
|
if (!privacyMode) {
|
||||||
stringFeatureBuilder.addAll(FEATURES_AV_CALLS);
|
stringFeatureBuilder.addAll(FEATURES_AV_CALLS);
|
||||||
}
|
}
|
||||||
final var stringFeatures = stringFeatureBuilder.build();
|
return new ServiceDescription(
|
||||||
final Collection<Feature> features =
|
stringFeatureBuilder.build(),
|
||||||
Collections2.transform(
|
new ServiceDescription.Identity(getIdentityName(), "client", getIdentityType()));
|
||||||
stringFeatures,
|
|
||||||
sf -> {
|
|
||||||
final var feature = new Feature();
|
|
||||||
feature.setVar(sf);
|
|
||||||
return feature;
|
|
||||||
});
|
|
||||||
infoQuery.addExtensions(features);
|
|
||||||
final var identity = infoQuery.addExtension(new Identity());
|
|
||||||
identity.setIdentityName(getIdentityName());
|
|
||||||
identity.setCategory("client");
|
|
||||||
identity.setType(getIdentityType());
|
|
||||||
return infoQuery;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String getIdentityVersion() {
|
String getIdentityVersion() {
|
||||||
|
@ -252,4 +243,62 @@ public class DiscoManager extends AbstractManager {
|
||||||
return "phone";
|
return "phone";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void handleInfoQuery(final Iq request) {
|
||||||
|
final var infoQueryRequest = request.getExtension(InfoQuery.class);
|
||||||
|
final var nodeRequest = infoQueryRequest.getNode();
|
||||||
|
LOGGER.warn("{} requested disco info for node {}", request.getFrom(), nodeRequest);
|
||||||
|
final ServiceDescription serviceDescription;
|
||||||
|
if (Strings.isNullOrEmpty(nodeRequest)) {
|
||||||
|
serviceDescription = getServiceDescription();
|
||||||
|
} else {
|
||||||
|
final var hash = buildHashFromNode(nodeRequest);
|
||||||
|
final var cachedServiceDescription =
|
||||||
|
hash != null
|
||||||
|
? getManager(PresenceManager.class).getCachedServiceDescription(hash)
|
||||||
|
: null;
|
||||||
|
if (cachedServiceDescription != null) {
|
||||||
|
serviceDescription = cachedServiceDescription;
|
||||||
|
} else {
|
||||||
|
LOGGER.warn("No disco info was cached for node {}", nodeRequest);
|
||||||
|
connection.sendErrorFor(request, new Condition.ItemNotFound());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final var infoQuery = serviceDescription.asInfoQuery();
|
||||||
|
infoQuery.setNode(nodeRequest);
|
||||||
|
connection.sendResultFor(request, infoQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EntityCapabilities.Hash buildHashFromNode(final String node) {
|
||||||
|
final var capsPrefix = CAPABILITY_NODE + "#";
|
||||||
|
final var caps2Prefix = Namespace.ENTITY_CAPABILITIES_2 + "#";
|
||||||
|
if (node.startsWith(capsPrefix)) {
|
||||||
|
final String hash = node.substring(capsPrefix.length());
|
||||||
|
if (Strings.isNullOrEmpty(hash)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (BaseEncoding.base64().canDecode(hash)) {
|
||||||
|
return EntityCapabilities.EntityCapsHash.of(hash);
|
||||||
|
}
|
||||||
|
} else if (node.startsWith(caps2Prefix)) {
|
||||||
|
final String caps = node.substring(caps2Prefix.length());
|
||||||
|
if (Strings.isNullOrEmpty(caps)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final int separator = caps.lastIndexOf('.');
|
||||||
|
if (separator < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final Hash.Algorithm algorithm = Hash.Algorithm.tryParse(caps.substring(0, separator));
|
||||||
|
final String hash = caps.substring(separator + 1);
|
||||||
|
if (algorithm == null || Strings.isNullOrEmpty(hash)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (BaseEncoding.base64().canDecode(hash)) {
|
||||||
|
return EntityCapabilities2.EntityCaps2Hash.of(algorithm, hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,36 @@
|
||||||
package im.conversations.android.xmpp.manager;
|
package im.conversations.android.xmpp.manager;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
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.ServiceDescription;
|
||||||
import im.conversations.android.xmpp.XmppConnection;
|
import im.conversations.android.xmpp.XmppConnection;
|
||||||
import im.conversations.android.xmpp.model.capabilties.Capabilities;
|
import im.conversations.android.xmpp.model.capabilties.Capabilities;
|
||||||
import im.conversations.android.xmpp.model.capabilties.LegacyCapabilities;
|
import im.conversations.android.xmpp.model.capabilties.LegacyCapabilities;
|
||||||
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
|
|
||||||
import im.conversations.android.xmpp.model.stanza.Presence;
|
import im.conversations.android.xmpp.model.stanza.Presence;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class PresenceManager extends AbstractManager {
|
public class PresenceManager extends AbstractManager {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(PresenceManager.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(PresenceManager.class);
|
||||||
|
|
||||||
private final Map<EntityCapabilities.Hash, InfoQuery> outgoingCapsHash = new HashMap<>();
|
private final Map<EntityCapabilities.Hash, ServiceDescription> serviceDescriptions =
|
||||||
|
new HashMap<>();
|
||||||
|
|
||||||
public PresenceManager(Context context, XmppConnection connection) {
|
public PresenceManager(Context context, XmppConnection connection) {
|
||||||
super(context, connection);
|
super(context, connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPresence() {
|
public void sendPresence() {
|
||||||
final var infoQuery = getManager(DiscoManager.class).getInfo();
|
final var serviceDiscoveryFeatures = getManager(DiscoManager.class).getServiceDescription();
|
||||||
|
final var infoQuery = serviceDiscoveryFeatures.asInfoQuery();
|
||||||
final var capsHash = EntityCapabilities.hash(infoQuery);
|
final var capsHash = EntityCapabilities.hash(infoQuery);
|
||||||
final var caps2Hash = EntityCapabilities2.hash(infoQuery);
|
final var caps2Hash = EntityCapabilities2.hash(infoQuery);
|
||||||
outgoingCapsHash.put(capsHash, infoQuery);
|
serviceDescriptions.put(capsHash, serviceDiscoveryFeatures);
|
||||||
outgoingCapsHash.put(caps2Hash, infoQuery);
|
serviceDescriptions.put(caps2Hash, serviceDiscoveryFeatures);
|
||||||
final var capabilities = new Capabilities();
|
final var capabilities = new Capabilities();
|
||||||
capabilities.setHash(caps2Hash);
|
capabilities.setHash(caps2Hash);
|
||||||
final var legacyCapabilities = new LegacyCapabilities();
|
final var legacyCapabilities = new LegacyCapabilities();
|
||||||
|
@ -45,4 +44,8 @@ public class PresenceManager extends AbstractManager {
|
||||||
|
|
||||||
connection.sendPresencePacket(presence);
|
connection.sendPresencePacket(presence);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ServiceDescription getCachedServiceDescription(final EntityCapabilities.Hash hash) {
|
||||||
|
return this.serviceDescriptions.get(hash);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,12 @@ import android.content.Context;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import im.conversations.android.xmpp.XmppConnection;
|
import im.conversations.android.xmpp.XmppConnection;
|
||||||
import im.conversations.android.xmpp.manager.BlockingManager;
|
import im.conversations.android.xmpp.manager.BlockingManager;
|
||||||
|
import im.conversations.android.xmpp.manager.DiscoManager;
|
||||||
import im.conversations.android.xmpp.manager.RosterManager;
|
import im.conversations.android.xmpp.manager.RosterManager;
|
||||||
import im.conversations.android.xmpp.model.Extension;
|
|
||||||
import im.conversations.android.xmpp.model.blocking.Block;
|
import im.conversations.android.xmpp.model.blocking.Block;
|
||||||
import im.conversations.android.xmpp.model.blocking.Unblock;
|
import im.conversations.android.xmpp.model.blocking.Unblock;
|
||||||
|
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
|
||||||
import im.conversations.android.xmpp.model.error.Condition;
|
import im.conversations.android.xmpp.model.error.Condition;
|
||||||
import im.conversations.android.xmpp.model.error.Error;
|
|
||||||
import im.conversations.android.xmpp.model.ping.Ping;
|
import im.conversations.android.xmpp.model.ping.Ping;
|
||||||
import im.conversations.android.xmpp.model.roster.Query;
|
import im.conversations.android.xmpp.model.roster.Query;
|
||||||
import im.conversations.android.xmpp.model.stanza.Iq;
|
import im.conversations.android.xmpp.model.stanza.Iq;
|
||||||
|
@ -34,54 +34,36 @@ public class IqProcessor extends XmppConnection.Delegate implements Consumer<Iq>
|
||||||
&& connection.fromAccount(packet)
|
&& connection.fromAccount(packet)
|
||||||
&& packet.hasExtension(Query.class)) {
|
&& packet.hasExtension(Query.class)) {
|
||||||
getManager(RosterManager.class).handlePush(packet.getExtension(Query.class));
|
getManager(RosterManager.class).handlePush(packet.getExtension(Query.class));
|
||||||
sendResultFor(packet);
|
connection.sendResultFor(packet);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type == Iq.Type.SET
|
if (type == Iq.Type.SET
|
||||||
&& connection.fromAccount(packet)
|
&& connection.fromAccount(packet)
|
||||||
&& packet.hasExtension(Block.class)) {
|
&& packet.hasExtension(Block.class)) {
|
||||||
getManager(BlockingManager.class).handlePush(packet.getExtension(Block.class));
|
getManager(BlockingManager.class).handlePush(packet.getExtension(Block.class));
|
||||||
sendResultFor(packet);
|
connection.sendResultFor(packet);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type == Iq.Type.SET
|
if (type == Iq.Type.SET
|
||||||
&& connection.fromAccount(packet)
|
&& connection.fromAccount(packet)
|
||||||
&& packet.hasExtension(Unblock.class)) {
|
&& packet.hasExtension(Unblock.class)) {
|
||||||
getManager(BlockingManager.class).handlePush(packet.getExtension(Unblock.class));
|
getManager(BlockingManager.class).handlePush(packet.getExtension(Unblock.class));
|
||||||
sendResultFor(packet);
|
connection.sendResultFor(packet);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (type == Iq.Type.GET && packet.hasExtension(Ping.class)) {
|
if (type == Iq.Type.GET && packet.hasExtension(Ping.class)) {
|
||||||
LOGGER.debug("Responding to ping from {}", packet.getFrom());
|
LOGGER.debug("Responding to ping from {}", packet.getFrom());
|
||||||
sendResultFor(packet);
|
connection.sendResultFor(packet);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == Iq.Type.GET && packet.hasExtension(InfoQuery.class)) {
|
||||||
|
getManager(DiscoManager.class).handleInfoQuery(packet);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final var extensionIds = packet.getExtensionIds();
|
final var extensionIds = packet.getExtensionIds();
|
||||||
LOGGER.info("Could not handle {}. Sending feature-not-implemented", extensionIds);
|
LOGGER.info("Could not handle {}. Sending feature-not-implemented", extensionIds);
|
||||||
sendErrorFor(packet, new Condition.FeatureNotImplemented());
|
connection.sendErrorFor(packet, new Condition.FeatureNotImplemented());
|
||||||
}
|
|
||||||
|
|
||||||
public void sendResultFor(final Iq request, final Extension... extensions) {
|
|
||||||
final var from = request.getFrom();
|
|
||||||
final var id = request.getId();
|
|
||||||
final var response = new Iq(Iq.Type.RESULT);
|
|
||||||
response.setTo(from);
|
|
||||||
response.setId(id);
|
|
||||||
for (final Extension extension : extensions) {
|
|
||||||
response.addExtension(extension);
|
|
||||||
}
|
|
||||||
connection.sendIqPacket(response, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void sendErrorFor(final Iq request, final Condition condition) {
|
|
||||||
final var from = request.getFrom();
|
|
||||||
final var id = request.getId();
|
|
||||||
final var response = new Iq(Iq.Type.ERROR);
|
|
||||||
response.setTo(from);
|
|
||||||
response.setId(id);
|
|
||||||
final Error error = response.addExtension(new Error());
|
|
||||||
error.setCondition(condition);
|
|
||||||
connection.sendIqPacket(response, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,8 @@ public class MessageProcessor extends XmppConnection.Delegate implements Consume
|
||||||
LOGGER.info("'{}' from {}", body, message.getFrom());
|
LOGGER.info("'{}' from {}", body, message.getFrom());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOGGER.info("Message received {}", message.getExtensionIds());
|
||||||
|
|
||||||
// TODO process receipt requests (184 + 333)
|
// TODO process receipt requests (184 + 333)
|
||||||
|
|
||||||
// TODO collect Extensions that require transformation (everything that will end up in the
|
// TODO collect Extensions that require transformation (everything that will end up in the
|
||||||
|
|
|
@ -2,9 +2,11 @@ package im.conversations.android.xmpp;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
import eu.siacs.conversations.xml.Element;
|
import eu.siacs.conversations.xml.Element;
|
||||||
import eu.siacs.conversations.xml.XmlElementReader;
|
import eu.siacs.conversations.xml.XmlElementReader;
|
||||||
|
import im.conversations.android.xmpp.manager.DiscoManager;
|
||||||
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
|
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
@ -26,8 +28,8 @@ public class EntityCapabilitiesTest {
|
||||||
+ " node='http://code.google.com/p/exodus#QgayPKawpkPSDYmwT/WM94uAlu0='>\n"
|
+ " node='http://code.google.com/p/exodus#QgayPKawpkPSDYmwT/WM94uAlu0='>\n"
|
||||||
+ " <identity category='client' name='Exodus 0.9.1' type='pc'/>\n"
|
+ " <identity category='client' name='Exodus 0.9.1' type='pc'/>\n"
|
||||||
+ " <feature var='http://jabber.org/protocol/caps'/>\n"
|
+ " <feature var='http://jabber.org/protocol/caps'/>\n"
|
||||||
+ " <feature var='http://jabber.org/protocol/disco#info'/>\n"
|
|
||||||
+ " <feature var='http://jabber.org/protocol/disco#items'/>\n"
|
+ " <feature var='http://jabber.org/protocol/disco#items'/>\n"
|
||||||
|
+ " <feature var='http://jabber.org/protocol/disco#info'/>\n"
|
||||||
+ " <feature var='http://jabber.org/protocol/muc'/>\n"
|
+ " <feature var='http://jabber.org/protocol/muc'/>\n"
|
||||||
+ " </query>";
|
+ " </query>";
|
||||||
final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
|
final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
|
||||||
|
@ -182,4 +184,30 @@ public class EntityCapabilitiesTest {
|
||||||
final String var = EntityCapabilities2.hash(info).encoded();
|
final String var = EntityCapabilities2.hash(info).encoded();
|
||||||
Assert.assertEquals("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=", var);
|
Assert.assertEquals("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=", var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseCaps2Node() {
|
||||||
|
final var caps =
|
||||||
|
DiscoManager.buildHashFromNode(
|
||||||
|
"urn:xmpp:caps#sha-256.u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=");
|
||||||
|
assertThat(caps, instanceOf(EntityCapabilities2.EntityCaps2Hash.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseCaps2NodeMissingHash() {
|
||||||
|
final var caps = DiscoManager.buildHashFromNode("urn:xmpp:caps#sha-256.");
|
||||||
|
assertNull(caps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseCaps2NodeInvalid() {
|
||||||
|
final var caps = DiscoManager.buildHashFromNode("urn:xmpp:caps#-");
|
||||||
|
assertNull(caps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void parseCaps2NodeUnknownAlgo() {
|
||||||
|
final var caps = DiscoManager.buildHashFromNode("urn:xmpp:caps#test.test");
|
||||||
|
assertNull(caps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue