aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/de/thedevstack/conversationsplus/xmpp/disco
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/de/thedevstack/conversationsplus/xmpp/disco')
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/disco/FeatureRegistry.java90
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscovery.java8
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryIqPacketGenerator.java38
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryIqPacketHandler.java21
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryIqPacketParser.java15
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryXep.java51
6 files changed, 223 insertions, 0 deletions
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/FeatureRegistry.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/FeatureRegistry.java
new file mode 100644
index 00000000..1c266545
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/FeatureRegistry.java
@@ -0,0 +1,90 @@
+package de.thedevstack.conversationsplus.xmpp.disco;
+
+import android.util.Base64;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.crypto.axolotl.AxolotlService;
+import de.thedevstack.conversationsplus.xmpp.Xep;
+import de.thedevstack.conversationsplus.xmpp.chatstate.ChatState;
+import de.tzur.conversations.Settings;
+
+/**
+ */
+public final class FeatureRegistry {
+ public static final String IDENTITY_TYPE = "phone";
+
+ private static final String[] LEGACY_FEATURES = {
+ "urn:xmpp:jingle:1",
+ "urn:xmpp:jingle:apps:file-transfer:3",
+ "urn:xmpp:jingle:transports:s5b:1",
+ "urn:xmpp:jingle:transports:ibb:1",
+ "http://jabber.org/protocol/muc",
+ "jabber:x:conference",
+ "http://jabber.org/protocol/caps",
+ "http://jabber.org/protocol/disco#info",
+ "urn:xmpp:avatar:metadata+notify",
+ "http://jabber.org/protocol/nick+notify",
+ ChatState.NAMESPACE,
+ AxolotlService.PEP_DEVICE_LIST+"+notify"};
+ private static final String[] LEGACY_MESSAGE_CONFIRMATION_FEATURES = {
+ "urn:xmpp:chat-markers:0",
+ "urn:xmpp:receipts"
+ };
+ private final List<String> features = new ArrayList<>();
+ private static final FeatureRegistry INSTANCE = new FeatureRegistry();
+
+ public static void add(String featureNamespace) {
+ if (null != featureNamespace) {
+ INSTANCE.features.add(featureNamespace);
+ }
+ }
+
+ public static void remove(String featureNamespace) {
+ if (null != featureNamespace && INSTANCE.features.contains(featureNamespace)) {
+ INSTANCE.features.remove(featureNamespace);
+ }
+ }
+ public static void add(Xep xep) {
+ add(xep.featureNamespace());
+ }
+
+ public static void remove(Xep xep) {
+ remove(xep.featureNamespace());
+ }
+
+ public static List<String> getFeatures() {
+ Collections.sort(INSTANCE.features);
+ return INSTANCE.features;
+ }
+
+ public static String getCapHash() {
+ StringBuilder s = new StringBuilder();
+ s.append("client/" + IDENTITY_TYPE + "//" + ConversationsPlusApplication.getNameAndVersion() + "<");
+ MessageDigest md;
+ try {
+ md = MessageDigest.getInstance("SHA-1");
+ } catch (NoSuchAlgorithmException e) {
+ return null;
+ }
+
+ for (String feature : getFeatures()) {
+ s.append(feature + "<");
+ }
+ byte[] sha1 = md.digest(s.toString().getBytes());
+ return new String(Base64.encode(sha1, Base64.DEFAULT));
+ }
+
+ private FeatureRegistry() {
+ this.features.addAll(Arrays.asList(LEGACY_FEATURES));
+ if (Settings.CONFIRM_MESSAGE_RECEIVED) {
+ this.features.addAll(Arrays.asList(LEGACY_MESSAGE_CONFIRMATION_FEATURES));
+ }
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscovery.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscovery.java
new file mode 100644
index 00000000..59a4212b
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscovery.java
@@ -0,0 +1,8 @@
+package de.thedevstack.conversationsplus.xmpp.disco;
+
+/**
+ */
+public interface ServiceDiscovery {
+ String NAMESPACE = "http://jabber.org/protocol/disco#info";
+ String ELEMENT = "query";
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryIqPacketGenerator.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryIqPacketGenerator.java
new file mode 100644
index 00000000..8e1fc075
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryIqPacketGenerator.java
@@ -0,0 +1,38 @@
+package de.thedevstack.conversationsplus.xmpp.disco;
+
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.xml.Element;
+import de.thedevstack.conversationsplus.xmpp.jid.Jid;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacketGenerator;
+
+/**
+ */
+public class ServiceDiscoveryIqPacketGenerator {
+
+ public static IqPacket generateRequest(Jid jid) {
+ IqPacket request = IqPacketGenerator.generateIqGetPacket();
+ request.setTo(jid);
+ request.query(ServiceDiscovery.NAMESPACE);
+
+ return request;
+ }
+
+ public static IqPacket generateResponse(IqPacket packet) {
+ IqPacket responsePacket = IqPacketGenerator.generateIqResultPacket();
+ responsePacket.setTo(packet.getFrom());
+ responsePacket.setId(packet.getId());
+
+ Element query = responsePacket.addChild(ServiceDiscovery.ELEMENT, ServiceDiscovery.NAMESPACE);
+ query.setAttribute("node", packet.query().getAttribute("node"));
+ final Element identity = query.addChild("identity");
+ identity.setAttribute("category", "client");
+ identity.setAttribute("type", FeatureRegistry.IDENTITY_TYPE);
+ identity.setAttribute("name", ConversationsPlusApplication.getNameAndVersion());
+ for (final String feature : FeatureRegistry.getFeatures()) {
+ query.addChild("feature").setAttribute("var", feature);
+ }
+
+ return responsePacket;
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryIqPacketHandler.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryIqPacketHandler.java
new file mode 100644
index 00000000..6f1b0676
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryIqPacketHandler.java
@@ -0,0 +1,21 @@
+package de.thedevstack.conversationsplus.xmpp.disco;
+
+import de.thedevstack.conversationsplus.entities.Account;
+import de.thedevstack.conversationsplus.utils.XmppSendUtil;
+import de.thedevstack.conversationsplus.xmpp.IqPacketHandler;
+import de.thedevstack.conversationsplus.xmpp.exceptions.IqPacketErrorException;
+import de.thedevstack.conversationsplus.xmpp.exceptions.NotAllowedIqException;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
+
+/**
+ */
+public class ServiceDiscoveryIqPacketHandler implements IqPacketHandler {
+ @Override
+ public void handleIqPacket(Account account, IqPacket packet) throws IqPacketErrorException {
+ if (packet.getType() == IqPacket.TYPE.GET) {
+ XmppSendUtil.sendIqPacket(account, ServiceDiscoveryIqPacketGenerator.generateResponse(packet));
+ } else {
+ throw new NotAllowedIqException(packet, "only type=get allowed for processing an service discovery (XEP-0030)");
+ }
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryIqPacketParser.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryIqPacketParser.java
new file mode 100644
index 00000000..0ea0d752
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryIqPacketParser.java
@@ -0,0 +1,15 @@
+package de.thedevstack.conversationsplus.xmpp.disco;
+
+import de.thedevstack.conversationsplus.entities.ServiceDiscoveryResult;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
+
+/**
+ */
+public class ServiceDiscoveryIqPacketParser {
+ public static ServiceDiscoveryResult parse(IqPacket packet) {
+ if (packet.getType() == IqPacket.TYPE.RESULT) {
+ return new ServiceDiscoveryResult(packet);
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryXep.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryXep.java
new file mode 100644
index 00000000..6a21c71b
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/disco/ServiceDiscoveryXep.java
@@ -0,0 +1,51 @@
+package de.thedevstack.conversationsplus.xmpp.disco;
+
+import de.thedevstack.conversationsplus.xmpp.AbstractXep;
+import de.thedevstack.conversationsplus.xmpp.IqPacketHandler;
+
+/**
+ */
+public class ServiceDiscoveryXep extends AbstractXep {
+ public ServiceDiscoveryXep() {
+ super();
+ }
+
+ public ServiceDiscoveryXep(boolean enabled) {
+ super(enabled);
+ }
+
+ @Override
+ public String xepNumber() {
+ return "0030";
+ }
+
+ @Override
+ public String shortName() {
+ return "disco";
+ }
+
+ @Override
+ public String name() {
+ return "Service Discovery";
+ }
+
+ @Override
+ public String namespace() {
+ return ServiceDiscovery.NAMESPACE;
+ }
+
+ @Override
+ public String featureNamespace() {
+ return ServiceDiscovery.NAMESPACE;
+ }
+
+ @Override
+ public String elementName() {
+ return ServiceDiscovery.ELEMENT;
+ }
+
+ @Override
+ public IqPacketHandler handler() {
+ return new ServiceDiscoveryIqPacketHandler();
+ }
+}