respond to software version requests
This commit is contained in:
parent
e073f22ec0
commit
f1e1cf9653
|
@ -1,6 +1,7 @@
|
||||||
package im.conversations.android.xmpp.manager;
|
package im.conversations.android.xmpp.manager;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
@ -25,8 +26,10 @@ 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.error.Condition;
|
||||||
import im.conversations.android.xmpp.model.stanza.Iq;
|
import im.conversations.android.xmpp.model.stanza.Iq;
|
||||||
|
import im.conversations.android.xmpp.model.version.Version;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -34,10 +37,8 @@ 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 Logger LOGGER = LoggerFactory.getLogger(DiscoManager.class);
|
||||||
private static final Collection<String> FEATURES_BASE =
|
private static final Collection<String> FEATURES_BASE =
|
||||||
Arrays.asList(
|
Arrays.asList(
|
||||||
Namespace.JINGLE,
|
Namespace.JINGLE,
|
||||||
|
@ -55,7 +56,6 @@ public class DiscoManager extends AbstractManager {
|
||||||
Namespace.ENTITY_CAPABILITIES_2,
|
Namespace.ENTITY_CAPABILITIES_2,
|
||||||
Namespace.DISCO_INFO,
|
Namespace.DISCO_INFO,
|
||||||
Namespace.PING,
|
Namespace.PING,
|
||||||
Namespace.VERSION,
|
|
||||||
Namespace.CHAT_STATES,
|
Namespace.CHAT_STATES,
|
||||||
Namespace.LAST_MESSAGE_CORRECTION,
|
Namespace.LAST_MESSAGE_CORRECTION,
|
||||||
Namespace.DELIVERY_RECEIPTS);
|
Namespace.DELIVERY_RECEIPTS);
|
||||||
|
@ -69,6 +69,9 @@ public class DiscoManager extends AbstractManager {
|
||||||
Namespace.JINGLE_APPS_DTLS,
|
Namespace.JINGLE_APPS_DTLS,
|
||||||
Namespace.JINGLE_MESSAGE);
|
Namespace.JINGLE_MESSAGE);
|
||||||
|
|
||||||
|
private static final Collection<String> FEATURES_IMPACTING_PRIVACY =
|
||||||
|
Collections.singleton(Namespace.VERSION);
|
||||||
|
|
||||||
private static final Collection<String> FEATURES_NOTIFY =
|
private static final Collection<String> FEATURES_NOTIFY =
|
||||||
Arrays.asList(Namespace.NICK, Namespace.AVATAR_METADATA, Namespace.BOOKMARKS2);
|
Arrays.asList(Namespace.NICK, Namespace.AVATAR_METADATA, Namespace.BOOKMARKS2);
|
||||||
|
|
||||||
|
@ -76,6 +79,38 @@ public class DiscoManager extends AbstractManager {
|
||||||
super(context, connection);
|
super(context, connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
public ListenableFuture<InfoQuery> info(final Entity entity) {
|
public ListenableFuture<InfoQuery> info(final Entity entity) {
|
||||||
return info(entity, null);
|
return info(entity, null);
|
||||||
}
|
}
|
||||||
|
@ -210,30 +245,29 @@ public class DiscoManager extends AbstractManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ServiceDescription getServiceDescription() {
|
public ServiceDescription getServiceDescription() {
|
||||||
return getServiceDescription(false);
|
return getServiceDescription(isPrivacyModeEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
private ServiceDescription getServiceDescription(final boolean privacyMode) {
|
private ServiceDescription getServiceDescription(final boolean privacyMode) {
|
||||||
final ImmutableList.Builder<String> stringFeatureBuilder = ImmutableList.builder();
|
final ImmutableList.Builder<String> builder = ImmutableList.builder();
|
||||||
stringFeatureBuilder.addAll(FEATURES_BASE);
|
final List<String> features;
|
||||||
stringFeatureBuilder.addAll(
|
builder.addAll(FEATURES_BASE);
|
||||||
|
builder.addAll(
|
||||||
Collections2.transform(FEATURES_NOTIFY, fn -> String.format("%s+notify", fn)));
|
Collections2.transform(FEATURES_NOTIFY, fn -> String.format("%s+notify", fn)));
|
||||||
if (!privacyMode) {
|
if (privacyMode) {
|
||||||
stringFeatureBuilder.addAll(FEATURES_AV_CALLS);
|
features = builder.build();
|
||||||
|
} else {
|
||||||
|
features = builder.addAll(FEATURES_AV_CALLS).addAll(FEATURES_IMPACTING_PRIVACY).build();
|
||||||
}
|
}
|
||||||
return new ServiceDescription(
|
return new ServiceDescription(
|
||||||
stringFeatureBuilder.build(),
|
features,
|
||||||
new ServiceDescription.Identity(getIdentityName(), "client", getIdentityType()));
|
new ServiceDescription.Identity(BuildConfig.APP_NAME, "client", getIdentityType()));
|
||||||
}
|
}
|
||||||
|
|
||||||
String getIdentityVersion() {
|
String getIdentityVersion() {
|
||||||
return BuildConfig.VERSION_NAME;
|
return BuildConfig.VERSION_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getIdentityName() {
|
|
||||||
return BuildConfig.APP_NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
String getIdentityType() {
|
String getIdentityType() {
|
||||||
if ("chromium".equals(android.os.Build.BRAND)) {
|
if ("chromium".equals(android.os.Build.BRAND)) {
|
||||||
return "pc";
|
return "pc";
|
||||||
|
@ -270,35 +304,19 @@ public class DiscoManager extends AbstractManager {
|
||||||
connection.sendResultFor(request, infoQuery);
|
connection.sendResultFor(request, infoQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EntityCapabilities.Hash buildHashFromNode(final String node) {
|
public void handleVersion(final Iq request) {
|
||||||
final var capsPrefix = CAPABILITY_NODE + "#";
|
if (isPrivacyModeEnabled()) {
|
||||||
final var caps2Prefix = Namespace.ENTITY_CAPABILITIES_2 + "#";
|
connection.sendErrorFor(request, new Condition.ServiceUnavailable());
|
||||||
if (node.startsWith(capsPrefix)) {
|
} else {
|
||||||
final String hash = node.substring(capsPrefix.length());
|
final var version = new Version();
|
||||||
if (Strings.isNullOrEmpty(hash)) {
|
version.setSoftwareName(BuildConfig.APP_NAME);
|
||||||
return null;
|
version.setVersion(BuildConfig.VERSION_NAME);
|
||||||
}
|
version.setOs(String.format("Android %s", Build.VERSION.RELEASE));
|
||||||
if (BaseEncoding.base64().canDecode(hash)) {
|
connection.sendResultFor(request, version);
|
||||||
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;
|
}
|
||||||
|
|
||||||
|
private boolean isPrivacyModeEnabled() {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package im.conversations.android.xmpp.model.version;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.xml.Namespace;
|
||||||
|
import im.conversations.android.annotation.XmlElement;
|
||||||
|
import im.conversations.android.xmpp.model.Extension;
|
||||||
|
|
||||||
|
@XmlElement(name = "query", namespace = Namespace.VERSION)
|
||||||
|
public class Version extends Extension {
|
||||||
|
|
||||||
|
public Version() {
|
||||||
|
super(Version.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSoftwareName(final String name) {
|
||||||
|
this.addChild("name").setContent(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setVersion(final String version) {
|
||||||
|
this.addChild("version").setContent(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOs(final String os) {
|
||||||
|
this.addChild("os").setContent(os);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ import im.conversations.android.xmpp.model.error.Condition;
|
||||||
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;
|
||||||
|
import im.conversations.android.xmpp.model.version.Version;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -62,6 +63,11 @@ public class IqProcessor extends XmppConnection.Delegate implements Consumer<Iq>
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == Iq.Type.GET && packet.hasExtension(Version.class)) {
|
||||||
|
getManager(DiscoManager.class).handleVersion(packet);
|
||||||
|
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);
|
||||||
connection.sendErrorFor(packet, new Condition.FeatureNotImplemented());
|
connection.sendErrorFor(packet, new Condition.FeatureNotImplemented());
|
||||||
|
|
Loading…
Reference in a new issue