aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java12
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java13
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/generator/AbstractGenerator.java63
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/generator/IqGenerator.java20
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/generator/MessageGenerator.java5
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/generator/PresenceGenerator.java10
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/parser/IqParser.java21
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java8
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/parser/PresenceParser.java5
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java178
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/mam/MessageArchiveService.java (renamed from src/main/java/de/thedevstack/conversationsplus/services/MessageArchiveService.java)11
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/mam/OnMoreMessagesLoaded.java13
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/muc/OnConferenceConfigurationFetched.java10
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/muc/OnConferenceJoined.java7
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/muc/listener/ConferenceConfigurationFetched.java96
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/services/muc/listener/ConferenceServiceDiscoveryReceived.java59
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/SettingsActivity.java14
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/listeners/ConversationMoreMessagesLoadedListener.java3
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/ui/listeners/ConversationSwipeRefreshListener.java2
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/UiUpdateHelper.java9
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java6
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/AbstractXep.java30
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/IqPacketHandler.java12
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/Xep.java16
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/XepRegistry.java51
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java6
-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
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/exceptions/NotAllowedIqException.java11
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersion.java9
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersionPacket.java23
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersionPacketGenerator.java21
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersionPacketHandler.java21
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/SoftwareVersionXep.java51
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/openpgp/OpenPgpXep.java48
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/ping/Ping.java8
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/ping/PingPacketHandler.java22
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/ping/PingXep.java51
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/ErrorIqPacket.java16
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqErrorCondition.java48
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqErrorType.java12
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacket.java4
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketGenerator.java17
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketReceiver.java96
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/csi/ActivePacket.java2
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/csi/Csi.java7
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/csi/InactivePacket.java2
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/time/EntityTime.java8
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/time/EntityTimeXep.java51
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/time/TimeIqPacketHandler.java21
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/time/TimePacket.java35
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/time/TimePacketGenerator.java31
-rw-r--r--src/main/java/de/thedevstack/conversationsplus/xmpp/utils/ErrorIqPacketExceptionHelper.java16
57 files changed, 1185 insertions, 349 deletions
diff --git a/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java
index 546ad7ee..e59f5b57 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ConversationsPlusPreferences.java
@@ -14,6 +14,18 @@ public class ConversationsPlusPreferences extends Settings {
private static ConversationsPlusPreferences instance;
private final SharedPreferences sharedPreferences;
+ public static boolean sendEntityTime() {
+ return getBoolean("send_entity_time", true);
+ }
+
+ public static boolean sendSoftwareVersion() {
+ return getBoolean("send_software_info", true);
+ }
+
+ public static boolean sendOsInformation() {
+ return getBoolean("send_os_info", true);
+ }
+
public static boolean logStanzas() {
return getBoolean("log_stanzas", true);
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java
index 215e0995..9cf540a3 100644
--- a/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java
+++ b/src/main/java/de/thedevstack/conversationsplus/crypto/axolotl/AxolotlServiceImpl.java
@@ -50,6 +50,7 @@ import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived;
import de.thedevstack.conversationsplus.xmpp.jid.InvalidJidException;
import de.thedevstack.conversationsplus.xmpp.jid.Jid;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacketReceiver;
public class AxolotlServiceImpl implements OnAdvancedStreamFeaturesLoaded, AxolotlService {
@@ -414,8 +415,8 @@ public class AxolotlServiceImpl implements OnAdvancedStreamFeaturesLoaded, Axolo
if (packet.getType() == IqPacket.TYPE.TIMEOUT) {
Log.d(Config.LOGTAG, getLogprefix(account) + "Timeout received while retrieving own Device Ids.");
} else {
- Element item = mXmppConnectionService.getIqParser().getItem(packet);
- Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
+ Element item = IqPacketReceiver.getInstance().getLegacyIqParser().getItem(packet);
+ Set<Integer> deviceIds = IqPacketReceiver.getInstance().getLegacyIqParser().deviceIds(item);
if (!deviceIds.contains(getOwnDeviceId())) {
publishOwnDeviceId(deviceIds);
}
@@ -502,8 +503,8 @@ public class AxolotlServiceImpl implements OnAdvancedStreamFeaturesLoaded, Axolo
}
}
- PreKeyBundle bundle = mXmppConnectionService.getIqParser().bundle(packet);
- Map<Integer, ECPublicKey> keys = mXmppConnectionService.getIqParser().preKeyPublics(packet);
+ PreKeyBundle bundle = IqPacketReceiver.getInstance().getLegacyIqParser().bundle(packet);
+ Map<Integer, ECPublicKey> keys = IqPacketReceiver.getInstance().getLegacyIqParser().preKeyPublics(packet);
boolean flush = false;
if (bundle == null) {
Log.w(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Received invalid bundle:" + packet);
@@ -662,7 +663,7 @@ public class AxolotlServiceImpl implements OnAdvancedStreamFeaturesLoaded, Axolo
mXmppConnectionService.sendIqPacket(account, packet, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
- Pair<X509Certificate[],byte[]> verification = mXmppConnectionService.getIqParser().verification(packet);
+ Pair<X509Certificate[],byte[]> verification = IqPacketReceiver.getInstance().getLegacyIqParser().verification(packet);
if (verification != null) {
try {
Signature verifier = Signature.getInstance("sha256WithRSA");
@@ -741,7 +742,7 @@ public class AxolotlServiceImpl implements OnAdvancedStreamFeaturesLoaded, Axolo
fetchStatusMap.put(address, FetchStatus.TIMEOUT);
} else if (packet.getType() == IqPacket.TYPE.RESULT) {
Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account) + "Received preKey IQ packet, processing...");
- final IqParser parser = mXmppConnectionService.getIqParser();
+ final IqParser parser = IqPacketReceiver.getInstance().getLegacyIqParser();
final List<PreKeyBundle> preKeyBundleList = parser.preKeys(packet);
final PreKeyBundle bundle = parser.bundle(packet);
if (preKeyBundleList.isEmpty() || bundle == null) {
diff --git a/src/main/java/de/thedevstack/conversationsplus/generator/AbstractGenerator.java b/src/main/java/de/thedevstack/conversationsplus/generator/AbstractGenerator.java
index 7302b2df..a8447a32 100644
--- a/src/main/java/de/thedevstack/conversationsplus/generator/AbstractGenerator.java
+++ b/src/main/java/de/thedevstack/conversationsplus/generator/AbstractGenerator.java
@@ -1,78 +1,15 @@
package de.thedevstack.conversationsplus.generator;
-import android.util.Base64;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
-import de.thedevstack.conversationsplus.ConversationsPlusApplication;
-import de.thedevstack.conversationsplus.xmpp.chatstate.ChatState;
-import de.thedevstack.conversationsplus.xmpp.iqversion.IqVersionPacket;
-import de.thedevstack.conversationsplus.xmpp.time.TimePacket;
-import de.tzur.conversations.Settings;
-import de.thedevstack.conversationsplus.crypto.axolotl.AxolotlService;
-
public abstract class AbstractGenerator {
- private static final String[] 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",
- "urn:xmpp:ping",
- TimePacket.NAMESPACE,
- IqVersionPacket.NAMESPACE,
- ChatState.NAMESPACE,
- AxolotlService.PEP_DEVICE_LIST+"+notify"};
- private static final String[] MESSAGE_CONFIRMATION_FEATURES = {
- "urn:xmpp:chat-markers:0",
- "urn:xmpp:receipts"
- };
- protected static final String IDENTITY_TYPE = "phone";
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
- 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));
- }
-
public static String getTimestamp(long time) {
DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
return DATE_FORMAT.format(time);
}
-
- public static List<String> getFeatures() {
- ArrayList<String> features = new ArrayList<>();
- features.addAll(Arrays.asList(FEATURES));
- if (Settings.CONFIRM_MESSAGE_RECEIVED) {
- features.addAll(Arrays.asList(MESSAGE_CONFIRMATION_FEATURES));
- }
- Collections.sort(features);
- return features;
- }
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/generator/IqGenerator.java b/src/main/java/de/thedevstack/conversationsplus/generator/IqGenerator.java
index aa279e40..88652b39 100644
--- a/src/main/java/de/thedevstack/conversationsplus/generator/IqGenerator.java
+++ b/src/main/java/de/thedevstack/conversationsplus/generator/IqGenerator.java
@@ -15,12 +15,11 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Set;
-import de.thedevstack.conversationsplus.ConversationsPlusApplication;
import de.thedevstack.conversationsplus.Config;
import de.thedevstack.conversationsplus.crypto.axolotl.AxolotlService;
import de.thedevstack.conversationsplus.entities.Account;
import de.thedevstack.conversationsplus.entities.Conversation;
-import de.thedevstack.conversationsplus.services.MessageArchiveService;
+import de.thedevstack.conversationsplus.services.mam.MessageArchiveService;
import de.thedevstack.conversationsplus.utils.Xmlns;
import de.thedevstack.conversationsplus.xml.Element;
import de.thedevstack.conversationsplus.xmpp.forms.Data;
@@ -30,23 +29,6 @@ import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
public class IqGenerator extends AbstractGenerator {
- public IqPacket discoResponse(final IqPacket request) {
- final IqPacket packet = new IqPacket(IqPacket.TYPE.RESULT);
- packet.setId(request.getId());
- packet.setTo(request.getFrom());
- final Element query = packet.addChild("query",
- "http://jabber.org/protocol/disco#info");
- query.setAttribute("node", request.query().getAttribute("node"));
- final Element identity = query.addChild("identity");
- identity.setAttribute("category", "client");
- identity.setAttribute("type", IDENTITY_TYPE);
- identity.setAttribute("name", ConversationsPlusApplication.getNameAndVersion());
- for (final String feature : getFeatures()) {
- query.addChild("feature").setAttribute("var", feature);
- }
- return packet;
- }
-
protected IqPacket publish(final String node, final Element item) {
final IqPacket packet = new IqPacket(IqPacket.TYPE.SET);
final Element pubsub = packet.addChild("pubsub",
diff --git a/src/main/java/de/thedevstack/conversationsplus/generator/MessageGenerator.java b/src/main/java/de/thedevstack/conversationsplus/generator/MessageGenerator.java
index e76d25f6..2e580f09 100644
--- a/src/main/java/de/thedevstack/conversationsplus/generator/MessageGenerator.java
+++ b/src/main/java/de/thedevstack/conversationsplus/generator/MessageGenerator.java
@@ -20,6 +20,7 @@ import de.thedevstack.conversationsplus.xmpp.carbons.Carbons;
import de.thedevstack.conversationsplus.xmpp.chatstate.ChatState;
import de.thedevstack.conversationsplus.xmpp.httpuploadim.HttpUploadHint;
import de.thedevstack.conversationsplus.xmpp.jid.Jid;
+import de.thedevstack.conversationsplus.xmpp.openpgp.OpenPgpXep;
import de.thedevstack.conversationsplus.xmpp.stanzas.MessagePacket;
public class MessageGenerator extends AbstractGenerator {
@@ -128,9 +129,9 @@ public class MessageGenerator extends AbstractGenerator {
MessagePacket packet = preparePacket(message);
packet.setBody("This is an XEP-0027 encrypted message");
if (message.getEncryption() == Message.ENCRYPTION_DECRYPTED) {
- packet.addChild("x", "jabber:x:encrypted").setContent(message.getEncryptedBody());
+ packet.addChild(OpenPgpXep.ENCRYPTED_ELEMENT, OpenPgpXep.ENCRYPTED_NAMESPACE).setContent(message.getEncryptedBody());
} else if (message.getEncryption() == Message.ENCRYPTION_PGP) {
- packet.addChild("x", "jabber:x:encrypted").setContent(message.getBody());
+ packet.addChild(OpenPgpXep.ENCRYPTED_ELEMENT, OpenPgpXep.ENCRYPTED_NAMESPACE).setContent(message.getBody());
}
return packet;
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/generator/PresenceGenerator.java b/src/main/java/de/thedevstack/conversationsplus/generator/PresenceGenerator.java
index fbe7c911..bfede624 100644
--- a/src/main/java/de/thedevstack/conversationsplus/generator/PresenceGenerator.java
+++ b/src/main/java/de/thedevstack/conversationsplus/generator/PresenceGenerator.java
@@ -5,7 +5,9 @@ import de.thedevstack.conversationsplus.entities.Contact;
import de.thedevstack.conversationsplus.entities.MucOptions;
import de.thedevstack.conversationsplus.entities.Presence;
import de.thedevstack.conversationsplus.xml.Element;
+import de.thedevstack.conversationsplus.xmpp.disco.FeatureRegistry;
import de.thedevstack.conversationsplus.xmpp.jid.Jid;
+import de.thedevstack.conversationsplus.xmpp.openpgp.OpenPgpXep;
import de.thedevstack.conversationsplus.xmpp.stanzas.PresencePacket;
public class PresenceGenerator extends AbstractGenerator {
@@ -54,9 +56,9 @@ public class PresenceGenerator extends AbstractGenerator {
packet.setFrom(account.getJid());
String sig = account.getPgpSignature();
if (sig != null) {
- packet.addChild("x", "jabber:x:signed").setContent(sig);
+ packet.addChild(OpenPgpXep.SIGNED_ELEMENT, OpenPgpXep.SIGNED_NAMESPACE).setContent(sig);
}
- String capHash = getCapHash();
+ String capHash = FeatureRegistry.getCapHash();
if (capHash != null) {
Element cap = packet.addChild("c",
"http://jabber.org/protocol/caps");
@@ -97,7 +99,7 @@ public class PresenceGenerator extends AbstractGenerator {
}
String sig = account.getPgpSignature();
if (sig != null) {
- packet.addChild("x", "jabber:x:signed").setContent(sig);
+ packet.addChild(OpenPgpXep.SIGNED_ELEMENT, OpenPgpXep.SIGNED_NAMESPACE).setContent(sig);
}
return packet;
@@ -109,7 +111,7 @@ public class PresenceGenerator extends AbstractGenerator {
String sig = account.getPgpSignature();
if (sig != null) {
packet.addChild("status").setContent("online");
- packet.addChild("x", "jabber:x:signed").setContent(sig);
+ packet.addChild(OpenPgpXep.SIGNED_ELEMENT, OpenPgpXep.SIGNED_NAMESPACE).setContent(sig);
}
return packet;
diff --git a/src/main/java/de/thedevstack/conversationsplus/parser/IqParser.java b/src/main/java/de/thedevstack/conversationsplus/parser/IqParser.java
index 35b41d61..b3cfc813 100644
--- a/src/main/java/de/thedevstack/conversationsplus/parser/IqParser.java
+++ b/src/main/java/de/thedevstack/conversationsplus/parser/IqParser.java
@@ -28,19 +28,14 @@ import de.thedevstack.conversationsplus.crypto.axolotl.AxolotlService;
import de.thedevstack.conversationsplus.entities.Account;
import de.thedevstack.conversationsplus.entities.Contact;
import de.thedevstack.conversationsplus.services.avatar.AvatarCache;
-import de.thedevstack.conversationsplus.services.avatar.AvatarService;
import de.thedevstack.conversationsplus.services.XmppConnectionService;
+import de.thedevstack.conversationsplus.utils.UiUpdateHelper;
import de.thedevstack.conversationsplus.utils.Xmlns;
-import de.thedevstack.conversationsplus.utils.XmppSendUtil;
import de.thedevstack.conversationsplus.xml.Element;
import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived;
import de.thedevstack.conversationsplus.xmpp.OnUpdateBlocklist;
-import de.thedevstack.conversationsplus.xmpp.iqversion.IqVersionPacket;
-import de.thedevstack.conversationsplus.xmpp.iqversion.IqVersionPacketGenerator;
import de.thedevstack.conversationsplus.xmpp.jid.Jid;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
-import de.thedevstack.conversationsplus.xmpp.time.TimePacket;
-import de.thedevstack.conversationsplus.xmpp.time.TimePacketGenerator;
public class IqParser extends AbstractParser implements OnIqPacketReceived {
@@ -300,7 +295,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
account.getBlocklist().addAll(jids);
}
// Update the UI
- mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
+ UiUpdateHelper.updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
} else if (packet.hasChild("unblock", Xmlns.BLOCKING) &&
packet.fromServer(account) && packet.getType() == IqPacket.TYPE.SET) {
Logging.d(Config.LOGTAG, "Received unblock update from server");
@@ -320,21 +315,11 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
}
account.getBlocklist().removeAll(jids);
}
- mXmppConnectionService.updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
+ UiUpdateHelper.updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
} else if (packet.hasChild("open", "http://jabber.org/protocol/ibb")
|| packet.hasChild("data", "http://jabber.org/protocol/ibb")) {
mXmppConnectionService.getJingleConnectionManager()
.deliverIbbPacket(account, packet);
- } else if (packet.hasChild("query", "http://jabber.org/protocol/disco#info")) {
- final IqPacket response = mXmppConnectionService.getIqGenerator().discoResponse(packet);
- mXmppConnectionService.sendIqPacket(account, response, null);
- } else if (packet.hasChild("query", IqVersionPacket.NAMESPACE)) {
- XmppSendUtil.sendIqPacket(account, IqVersionPacketGenerator.generateResponse(packet), null);
- } else if (packet.hasChild("ping", "urn:xmpp:ping")) {
- final IqPacket response = packet.generateResponse(IqPacket.TYPE.RESULT);
- mXmppConnectionService.sendIqPacket(account, response, null);
- } else if (packet.hasChild("time", TimePacket.NAMESPACE)) {
- XmppSendUtil.sendIqPacket(account, TimePacketGenerator.generateResponse(packet), null);
} else {
if (packet.getType() == IqPacket.TYPE.GET || packet.getType() == IqPacket.TYPE.SET) {
final IqPacket response = packet.generateResponse(IqPacket.TYPE.ERROR);
diff --git a/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java b/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java
index 52d35f58..4cc6f8b2 100644
--- a/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java
+++ b/src/main/java/de/thedevstack/conversationsplus/parser/MessageParser.java
@@ -17,6 +17,8 @@ import de.thedevstack.conversationsplus.xmpp.avatar.AvatarPacketParser;
import de.thedevstack.conversationsplus.xmpp.carbons.Carbons;
import de.thedevstack.conversationsplus.xmpp.httpuploadim.HttpUploadHint;
import de.thedevstack.conversationsplus.xmpp.mam.Mam;
+import de.thedevstack.conversationsplus.xmpp.openpgp.OpenPgpXep;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacketReceiver;
import de.tzur.conversations.Settings;
import net.java.otr4j.session.Session;
@@ -40,7 +42,7 @@ import de.thedevstack.conversationsplus.entities.Conversation;
import de.thedevstack.conversationsplus.entities.Message;
import de.thedevstack.conversationsplus.entities.MucOptions;
import de.thedevstack.conversationsplus.services.avatar.AvatarService;
-import de.thedevstack.conversationsplus.services.MessageArchiveService;
+import de.thedevstack.conversationsplus.services.mam.MessageArchiveService;
import de.thedevstack.conversationsplus.services.XmppConnectionService;
import de.thedevstack.conversationsplus.utils.CryptoHelper;
import de.thedevstack.conversationsplus.xml.Element;
@@ -231,7 +233,7 @@ public class MessageParser extends AbstractParser implements
} else if (ConversationsPlusPreferences.omemoEnabled() && AxolotlService.PEP_DEVICE_LIST.equals(node)) {
Log.d(Config.LOGTAG, AxolotlServiceImpl.getLogprefix(account)+"Received PEP device list update from "+ from + ", processing...");
Element item = items.findChild("item");
- Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
+ Set<Integer> deviceIds = IqPacketReceiver.getInstance().getLegacyIqParser().deviceIds(item);
AxolotlService axolotlService = account.getAxolotlService();
axolotlService.registerDevices(from, deviceIds);
UiUpdateHelper.updateAccountUi();
@@ -313,7 +315,7 @@ public class MessageParser extends AbstractParser implements
}
final String body = packet.getBody();
final Element mucUserElement = packet.findChild("x", "http://jabber.org/protocol/muc#user");
- final String pgpEncrypted = packet.findChildContent("x", "jabber:x:encrypted");
+ final String pgpEncrypted = packet.findChildContent(OpenPgpXep.ENCRYPTED_ELEMENT, OpenPgpXep.ENCRYPTED_NAMESPACE);
final Element oob = packet.findChild("x", "jabber:x:oob");
final boolean isOob = oob!= null && body != null && body.equals(oob.findChildContent("url"));
final Element axolotlEncrypted = packet.findChild(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX);
diff --git a/src/main/java/de/thedevstack/conversationsplus/parser/PresenceParser.java b/src/main/java/de/thedevstack/conversationsplus/parser/PresenceParser.java
index 5270eefe..ed366a2c 100644
--- a/src/main/java/de/thedevstack/conversationsplus/parser/PresenceParser.java
+++ b/src/main/java/de/thedevstack/conversationsplus/parser/PresenceParser.java
@@ -28,6 +28,7 @@ import de.thedevstack.conversationsplus.xmpp.OnPresencePacketReceived;
import de.thedevstack.conversationsplus.xmpp.avatar.AvatarVcardParser;
import de.thedevstack.conversationsplus.xmpp.jid.Jid;
import de.thedevstack.conversationsplus.dto.Avatar;
+import de.thedevstack.conversationsplus.xmpp.openpgp.OpenPgpXep;
import de.thedevstack.conversationsplus.xmpp.stanzas.PresencePacket;
public class PresenceParser extends AbstractParser implements
@@ -91,7 +92,7 @@ public class PresenceParser extends AbstractParser implements
mucOptions.addUser(user);
}
if (mXmppConnectionService.getPgpEngine() != null) {
- Element signed = packet.findChild("x", "jabber:x:signed");
+ Element signed = packet.findChild(OpenPgpXep.SIGNED_ELEMENT, OpenPgpXep.SIGNED_NAMESPACE);
if (signed != null) {
Element status = packet.findChild("status");
String msg = status == null ? "" : status.getContent();
@@ -213,7 +214,7 @@ public class PresenceParser extends AbstractParser implements
}
PgpEngine pgp = mXmppConnectionService.getPgpEngine();
- Element x = packet.findChild("x", "jabber:x:signed");
+ Element x = packet.findChild(OpenPgpXep.SIGNED_ELEMENT, OpenPgpXep.SIGNED_NAMESPACE);
if (pgp != null && x != null) {
Element status = packet.findChild("status");
String msg = status != null ? status.getContent() : "";
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java b/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java
index ac6ef464..963adfe4 100644
--- a/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java
+++ b/src/main/java/de/thedevstack/conversationsplus/services/XmppConnectionService.java
@@ -58,13 +58,28 @@ import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import de.thedevstack.conversationsplus.services.avatar.AvatarCache;
import de.thedevstack.conversationsplus.services.avatar.AvatarService;
import de.thedevstack.conversationsplus.services.filetransfer.FileTransferManager;
+import de.thedevstack.conversationsplus.services.mam.MessageArchiveService;
+import de.thedevstack.conversationsplus.services.mam.OnMoreMessagesLoaded;
+import de.thedevstack.conversationsplus.services.muc.OnConferenceConfigurationFetched;
+import de.thedevstack.conversationsplus.services.muc.OnConferenceJoined;
+import de.thedevstack.conversationsplus.services.muc.listener.ConferenceConfigurationFetched;
import de.thedevstack.conversationsplus.utils.AccountUtil;
import de.thedevstack.conversationsplus.utils.ImageUtil;
import de.thedevstack.conversationsplus.utils.MessageUtil;
import de.thedevstack.conversationsplus.utils.UiUpdateHelper;
import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor;
import de.thedevstack.conversationsplus.utils.XmppSendUtil;
+import de.thedevstack.conversationsplus.services.muc.listener.ConferenceServiceDiscoveryReceived;
+import de.thedevstack.conversationsplus.xmpp.XepRegistry;
+import de.thedevstack.conversationsplus.xmpp.disco.ServiceDiscoveryIqPacketGenerator;
+import de.thedevstack.conversationsplus.xmpp.disco.ServiceDiscoveryIqPacketParser;
+import de.thedevstack.conversationsplus.xmpp.disco.ServiceDiscoveryXep;
+import de.thedevstack.conversationsplus.xmpp.iqversion.SoftwareVersionXep;
import de.thedevstack.conversationsplus.xmpp.mam.Mam;
+import de.thedevstack.conversationsplus.xmpp.openpgp.OpenPgpXep;
+import de.thedevstack.conversationsplus.xmpp.ping.PingXep;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacketReceiver;
+import de.thedevstack.conversationsplus.xmpp.time.EntityTimeXep;
import de.tzur.conversations.Settings;
import de.thedevstack.conversationsplus.Config;
import de.thedevstack.conversationsplus.R;
@@ -87,7 +102,6 @@ import de.thedevstack.conversationsplus.entities.TransferablePlaceholder;
import de.thedevstack.conversationsplus.generator.IqGenerator;
import de.thedevstack.conversationsplus.generator.MessageGenerator;
import de.thedevstack.conversationsplus.generator.PresenceGenerator;
-import de.thedevstack.conversationsplus.parser.IqParser;
import de.thedevstack.conversationsplus.parser.MessageParser;
import de.thedevstack.conversationsplus.parser.PresenceParser;
import de.thedevstack.conversationsplus.persistance.DatabaseBackend;
@@ -150,7 +164,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
this);
private OnMessagePacketReceived mMessageParser = new MessageParser(this);
private OnPresencePacketReceived mPresenceParser = new PresenceParser(this);
- private IqParser mIqParser = new IqParser(this);
+
private OnIqPacketReceived mDefaultIqHandler = new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket packet) {
@@ -695,11 +709,20 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public XmppConnection createConnection(final Account account) {
account.setResource(ConversationsPlusPreferences.resource().toLowerCase(Locale.getDefault()));
+
+ // TODO Find better place for initialization
+ IqPacketReceiver iqPacketReceiver = IqPacketReceiver.getInstance();
+ XepRegistry.add(new EntityTimeXep(ConversationsPlusPreferences.sendEntityTime()));
+ XepRegistry.add(new SoftwareVersionXep(ConversationsPlusPreferences.sendSoftwareVersion()));
+ XepRegistry.add(new ServiceDiscoveryXep());
+ XepRegistry.add(new PingXep());
+ XepRegistry.add(new OpenPgpXep());
+
final XmppConnection connection = new XmppConnection(account, this);
connection.setOnMessagePacketReceivedListener(this.mMessageParser);
connection.setOnStatusChangedListener(this.statusListener);
connection.setOnPresencePacketReceivedListener(this.mPresenceParser);
- connection.setOnUnregisteredIqPacketReceivedListener(this.mIqParser);
+ connection.setOnUnregisteredIqPacketReceivedListener(iqPacketReceiver);
connection.setOnJinglePacketReceivedListener(this.jingleListener);
connection.setOnBindListener(this.mOnBindListener);
connection.setOnMessageAcknowledgeListener(this.mOnMessageAcknowledgedListener);
@@ -875,7 +898,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
- private void sendUnsentMessages(final Conversation conversation) {
+ public void sendUnsentMessages(final Conversation conversation) {
conversation.findWaitingMessages(new Conversation.OnMessageFound() {
@Override
@@ -898,7 +921,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
Logging.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetching roster");
}
iqPacket.query(Xmlns.ROSTER).setAttribute("ver", account.getRosterVersion());
- sendIqPacket(account, iqPacket, mIqParser);
+ XmppSendUtil.sendIqPacket(account, iqPacket, IqPacketReceiver.getInstance().getLegacyIqParser()); //TODO -> replace with RosterIqHandler
}
public void fetchBookmarks(final Account account) {
@@ -1657,44 +1680,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
if (account.getStatus() == Account.State.ONLINE) {
conversation.resetMucOptions();
conversation.setHasMessagesLeftOnServer(false);
- fetchConferenceConfiguration(conversation, new OnConferenceConfigurationFetched() {
-
- private void join(Conversation conversation) {
- Account account = conversation.getAccount();
- final MucOptions mucOptions = conversation.getMucOptions();
- final Jid joinJid = mucOptions.getSelf().getFullJid();
- Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": joining conversation " + joinJid.toString());
- long lastMessageTransmitted = conversation.getLastMessageTransmitted();
- PresencePacket packet = PresenceGenerator.generateMucJoin(account, joinJid, mucOptions, lastMessageTransmitted);
- XmppSendUtil.sendPresencePacket(account, packet);
- if (onConferenceJoined != null) {
- onConferenceJoined.onConferenceJoined(conversation);
- }
- if (!joinJid.equals(conversation.getJid())) {
- conversation.setContactJid(joinJid);
- databaseBackend.updateConversation(conversation);
- }
-
- if (mucOptions.mamSupport()) {
- getMessageArchiveService().catchupMUC(conversation);
- }
- if (mucOptions.membersOnly() && mucOptions.nonanonymous()) {
- fetchConferenceMembers(conversation);
- }
- sendUnsentMessages(conversation);
- }
-
- @Override
- public void onConferenceConfigurationFetched(Conversation conversation) {
- join(conversation);
- }
-
- @Override
- public void onFetchFailed(final Conversation conversation, Element error) {
- join(conversation);
- fetchConferenceConfiguration(conversation);
- }
- });
+ fetchConferenceConfiguration(conversation, new ConferenceConfigurationFetched(onConferenceJoined));
updateConversationUi();
} else {
account.pendingConferenceJoins.add(conversation);
@@ -1704,37 +1690,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
}
- private void fetchConferenceMembers(final Conversation conversation) {
- final Account account = conversation.getAccount();
- final String[] affiliations = {"member","admin","owner"};
- OnIqPacketReceived callback = new OnIqPacketReceived() {
-
- private int i = 0;
-
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- Element query = packet.query("http://jabber.org/protocol/muc#admin");
- if (packet.getType() == IqPacket.TYPE.RESULT && query != null) {
- for(Element child : query.getChildren()) {
- if ("item".equals(child.getName())) {
- conversation.getMucOptions().putMember(child.getAttributeAsJid("jid"));
- }
- }
- } else {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not request affiliation "+affiliations[i]+" in "+conversation.getJid().toBareJid());
- }
- ++i;
- if (i >= affiliations.length) {
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": retrieved members for "+conversation.getJid().toBareJid()+": "+conversation.getMucOptions().getMembers());
- }
- }
- };
- for(String affiliation : affiliations) {
- sendIqPacket(account, mIqGenerator.queryAffiliation(conversation, affiliation), callback);
- }
- Log.d(Config.LOGTAG,account.getJid().toBareJid()+": fetching members for "+conversation.getName());
- }
-
public void providePasswordForMuc(Conversation conversation, String password) {
if (conversation.getMode() == Conversation.MODE_MULTI) {
conversation.getMucOptions().setPassword(password);
@@ -1889,40 +1844,8 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public void fetchConferenceConfiguration(final Conversation conversation, final OnConferenceConfigurationFetched callback) {
- IqPacket request = new IqPacket(IqPacket.TYPE.GET);
- request.setTo(conversation.getJid().toBareJid());
- request.query("http://jabber.org/protocol/disco#info");
- sendIqPacket(conversation.getAccount(), request, new OnIqPacketReceived() {
- @Override
- public void onIqPacketReceived(Account account, IqPacket packet) {
- Element query = packet.findChild("query","http://jabber.org/protocol/disco#info");
- if (packet.getType() == IqPacket.TYPE.RESULT && query != null) {
- ArrayList<String> features = new ArrayList<>();
- for (Element child : query.getChildren()) {
- if (child != null && child.getName().equals("feature")) {
- String var = child.getAttribute("var");
- if (var != null) {
- features.add(var);
- }
- }
- }
- Element form = query.findChild("x", "jabber:x:data");
- if (form != null) {
- conversation.getMucOptions().updateFormData(Data.parse(form));
- }
- conversation.getMucOptions().updateFeatures(features);
- if (callback != null) {
- callback.onConferenceConfigurationFetched(conversation);
- }
- Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetched muc configuration for " + conversation.getJid().toBareJid() + " - " + features.toString());
- updateConversationUi();
- } else if (packet.getType() == IqPacket.TYPE.ERROR) {
- if (callback != null) {
- callback.onFetchFailed(conversation, packet.getError());
- }
- }
- }
- });
+ IqPacket request = ServiceDiscoveryIqPacketGenerator.generateRequest(conversation.getJid().toBareJid());
+ XmppSendUtil.sendIqPacket(conversation.getAccount(), request, new ConferenceServiceDiscoveryReceived(conversation, callback));
}
public void pushConferenceConfiguration(final Conversation conversation, final Bundle options, final OnConferenceOptionsPushed callback) {
@@ -2466,10 +2389,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
return this.mIqGenerator;
}
- public IqParser getIqParser() {
- return this.mIqParser;
- }
-
public JingleConnectionManager getJingleConnectionManager() {
return this.mJingleConnectionManager;
}
@@ -2529,7 +2448,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void onIqPacketReceived(final Account account, final IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) {
account.getBlocklist().add(jid);
- updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
+ UiUpdateHelper.updateBlocklistUi(OnUpdateBlocklist.Status.BLOCKED);
}
}
});
@@ -2544,7 +2463,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
public void onIqPacketReceived(final Account account, final IqPacket packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) {
account.getBlocklist().remove(jid);
- updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
+ UiUpdateHelper.updateBlocklistUi(OnUpdateBlocklist.Status.UNBLOCKED);
}
}
});
@@ -2587,15 +2506,13 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
} else {
if (!account.inProgressDiscoFetches.contains(key)) {
account.inProgressDiscoFetches.add(key);
- IqPacket request = new IqPacket(IqPacket.TYPE.GET);
- request.setTo(jid);
- request.query("http://jabber.org/protocol/disco#info");
+ IqPacket request = ServiceDiscoveryIqPacketGenerator.generateRequest(jid);
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": making disco request for "+key.second+" to "+jid);
sendIqPacket(account, request, new OnIqPacketReceived() {
@Override
public void onIqPacketReceived(Account account, IqPacket discoPacket) {
- if (discoPacket.getType() == IqPacket.TYPE.RESULT) {
- ServiceDiscoveryResult disco = new ServiceDiscoveryResult(discoPacket);
+ ServiceDiscoveryResult disco = ServiceDiscoveryIqPacketParser.parse(discoPacket);
+ if (null != disco) {
if (presence.getVer().equals(disco.getVer())) {
databaseBackend.insertDiscoveryResult(disco);
injectServiceDiscorveryResult(account.getRoster(), presence.getHash(), presence.getVer(), disco);
@@ -2657,15 +2574,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
void informUser(int r);
}
- public interface OnMoreMessagesLoaded {
- void onMoreMessagesLoaded(int count, Conversation conversation);
-
- void informUser(int r);
-
- void setLoadingInProgress();
- boolean isLoadingInProgress();
- }
public interface OnAccountPasswordChanged {
void onPasswordChangeSucceeded();
@@ -2694,10 +2603,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
}
public interface OnCaptchaRequested {
- void onCaptchaRequested(Account account,
- String id,
- Data data,
- Bitmap captcha);
+ void onCaptchaRequested(Account account, String id, Data data, Bitmap captcha);
}
public interface OnRosterUpdate {
@@ -2708,16 +2614,6 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
void onMucRosterUpdate();
}
- public interface OnConferenceConfigurationFetched {
- void onConferenceConfigurationFetched(Conversation conversation);
-
- void onFetchFailed(Conversation conversation, Element error);
- }
-
- public interface OnConferenceJoined {
- void onConferenceJoined(Conversation conversation);
- }
-
public interface OnConferenceOptionsPushed {
void onPushSucceeded();
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/MessageArchiveService.java b/src/main/java/de/thedevstack/conversationsplus/services/mam/MessageArchiveService.java
index 6402f4ed..6ad413b4 100644
--- a/src/main/java/de/thedevstack/conversationsplus/services/MessageArchiveService.java
+++ b/src/main/java/de/thedevstack/conversationsplus/services/mam/MessageArchiveService.java
@@ -1,4 +1,4 @@
-package de.thedevstack.conversationsplus.services;
+package de.thedevstack.conversationsplus.services.mam;
import android.util.Pair;
@@ -15,6 +15,7 @@ import de.thedevstack.conversationsplus.R;
import de.thedevstack.conversationsplus.entities.Account;
import de.thedevstack.conversationsplus.entities.Conversation;
import de.thedevstack.conversationsplus.generator.AbstractGenerator;
+import de.thedevstack.conversationsplus.services.XmppConnectionService;
import de.thedevstack.conversationsplus.xml.Element;
import de.thedevstack.conversationsplus.xmpp.OnAdvancedStreamFeaturesLoaded;
import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived;
@@ -101,7 +102,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
return this.query(conversation, start, end, null);
}
- public Query query(Conversation conversation, long start, long end, XmppConnectionService.OnMoreMessagesLoaded callback) {
+ public Query query(Conversation conversation, long start, long end, OnMoreMessagesLoaded callback) {
synchronized (this.queries) {
if (start > end) {
return null;
@@ -186,7 +187,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
}
}
- public boolean queryInProgress(Conversation conversation, XmppConnectionService.OnMoreMessagesLoaded callback) {
+ public boolean queryInProgress(Conversation conversation, OnMoreMessagesLoaded callback) {
synchronized (this.queries) {
for(Query query : queries) {
if (query.conversation == conversation) {
@@ -274,7 +275,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
private Account account;
private Conversation conversation;
private PagingOrder pagingOrder = PagingOrder.NORMAL;
- private XmppConnectionService.OnMoreMessagesLoaded callback = null;
+ private OnMoreMessagesLoaded callback = null;
public Query(Conversation conversation, long start, long end) {
@@ -339,7 +340,7 @@ public class MessageArchiveService implements OnAdvancedStreamFeaturesLoaded {
return start;
}
- public void setCallback(XmppConnectionService.OnMoreMessagesLoaded callback) {
+ public void setCallback(OnMoreMessagesLoaded callback) {
this.callback = callback;
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/mam/OnMoreMessagesLoaded.java b/src/main/java/de/thedevstack/conversationsplus/services/mam/OnMoreMessagesLoaded.java
new file mode 100644
index 00000000..72e4b516
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/services/mam/OnMoreMessagesLoaded.java
@@ -0,0 +1,13 @@
+package de.thedevstack.conversationsplus.services.mam;
+
+import de.thedevstack.conversationsplus.entities.Conversation;
+
+public interface OnMoreMessagesLoaded {
+ void onMoreMessagesLoaded(int count, Conversation conversation);
+
+ void informUser(int r);
+
+ void setLoadingInProgress();
+
+ boolean isLoadingInProgress();
+} \ No newline at end of file
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/muc/OnConferenceConfigurationFetched.java b/src/main/java/de/thedevstack/conversationsplus/services/muc/OnConferenceConfigurationFetched.java
new file mode 100644
index 00000000..7e7222fd
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/services/muc/OnConferenceConfigurationFetched.java
@@ -0,0 +1,10 @@
+package de.thedevstack.conversationsplus.services.muc;
+
+import de.thedevstack.conversationsplus.entities.Conversation;
+import de.thedevstack.conversationsplus.xml.Element;
+
+public interface OnConferenceConfigurationFetched {
+ void onConferenceConfigurationFetched(Conversation conversation);
+
+ void onFetchFailed(Conversation conversation, Element error);
+} \ No newline at end of file
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/muc/OnConferenceJoined.java b/src/main/java/de/thedevstack/conversationsplus/services/muc/OnConferenceJoined.java
new file mode 100644
index 00000000..5e9a0af5
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/services/muc/OnConferenceJoined.java
@@ -0,0 +1,7 @@
+package de.thedevstack.conversationsplus.services.muc;
+
+import de.thedevstack.conversationsplus.entities.Conversation;
+
+public interface OnConferenceJoined {
+ void onConferenceJoined(Conversation conversation);
+} \ No newline at end of file
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/muc/listener/ConferenceConfigurationFetched.java b/src/main/java/de/thedevstack/conversationsplus/services/muc/listener/ConferenceConfigurationFetched.java
new file mode 100644
index 00000000..a386db88
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/services/muc/listener/ConferenceConfigurationFetched.java
@@ -0,0 +1,96 @@
+package de.thedevstack.conversationsplus.services.muc.listener;
+
+import android.util.Log;
+
+import de.thedevstack.conversationsplus.Config;
+import de.thedevstack.conversationsplus.entities.Account;
+import de.thedevstack.conversationsplus.entities.Conversation;
+import de.thedevstack.conversationsplus.entities.MucOptions;
+import de.thedevstack.conversationsplus.generator.PresenceGenerator;
+import de.thedevstack.conversationsplus.persistance.DatabaseBackend;
+import de.thedevstack.conversationsplus.services.muc.OnConferenceConfigurationFetched;
+import de.thedevstack.conversationsplus.services.muc.OnConferenceJoined;
+import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor;
+import de.thedevstack.conversationsplus.utils.XmppSendUtil;
+import de.thedevstack.conversationsplus.xml.Element;
+import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived;
+import de.thedevstack.conversationsplus.xmpp.jid.Jid;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
+import de.thedevstack.conversationsplus.xmpp.stanzas.PresencePacket;
+
+/**
+ */
+public class ConferenceConfigurationFetched implements OnConferenceConfigurationFetched {
+ private OnConferenceJoined onConferenceJoined;
+
+ public ConferenceConfigurationFetched(OnConferenceJoined onConferenceJoined) {
+ this.onConferenceJoined = onConferenceJoined;
+ }
+
+ private void join(Conversation conversation) {
+ Account account = conversation.getAccount();
+ final MucOptions mucOptions = conversation.getMucOptions();
+ final Jid joinJid = mucOptions.getSelf().getFullJid();
+ Log.d(Config.LOGTAG, account.getJid().toBareJid().toString() + ": joining conversation " + joinJid.toString());
+ long lastMessageTransmitted = conversation.getLastMessageTransmitted();
+ PresencePacket packet = PresenceGenerator.generateMucJoin(account, joinJid, mucOptions, lastMessageTransmitted);
+ XmppSendUtil.sendPresencePacket(account, packet);
+ if (onConferenceJoined != null) {
+ onConferenceJoined.onConferenceJoined(conversation);
+ }
+ if (!joinJid.equals(conversation.getJid())) {
+ conversation.setContactJid(joinJid);
+ DatabaseBackend.getInstance().updateConversation(conversation);
+ }
+
+ if (mucOptions.mamSupport()) {
+ XmppConnectionServiceAccessor.xmppConnectionService.getMessageArchiveService().catchupMUC(conversation);
+ }
+ if (mucOptions.membersOnly() && mucOptions.nonanonymous()) {
+ fetchConferenceMembers(conversation);
+ }
+ XmppConnectionServiceAccessor.xmppConnectionService.sendUnsentMessages(conversation);
+ }
+
+ @Override
+ public void onConferenceConfigurationFetched(Conversation conversation) {
+ join(conversation);
+ }
+
+ @Override
+ public void onFetchFailed(final Conversation conversation, Element error) {
+ join(conversation);
+ XmppConnectionServiceAccessor.xmppConnectionService.fetchConferenceConfiguration(conversation);
+ }
+
+ private void fetchConferenceMembers(final Conversation conversation) {
+ final Account account = conversation.getAccount();
+ final String[] affiliations = {"member","admin","owner"};
+ OnIqPacketReceived callback = new OnIqPacketReceived() {
+
+ private int i = 0;
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Element query = packet.query("http://jabber.org/protocol/muc#admin");
+ if (packet.getType() == IqPacket.TYPE.RESULT && query != null) {
+ for(Element child : query.getChildren()) {
+ if ("item".equals(child.getName())) {
+ conversation.getMucOptions().putMember(child.getAttributeAsJid("jid"));
+ }
+ }
+ } else {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": could not request affiliation "+affiliations[i]+" in "+conversation.getJid().toBareJid());
+ }
+ ++i;
+ if (i >= affiliations.length) {
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": retrieved members for "+conversation.getJid().toBareJid()+": "+conversation.getMucOptions().getMembers());
+ }
+ }
+ };
+ for(String affiliation : affiliations) {
+ XmppSendUtil.sendIqPacket(account, XmppConnectionServiceAccessor.xmppConnectionService.getIqGenerator().queryAffiliation(conversation, affiliation), callback);
+ }
+ Log.d(Config.LOGTAG,account.getJid().toBareJid()+": fetching members for "+conversation.getName());
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/services/muc/listener/ConferenceServiceDiscoveryReceived.java b/src/main/java/de/thedevstack/conversationsplus/services/muc/listener/ConferenceServiceDiscoveryReceived.java
new file mode 100644
index 00000000..5d1b8c8d
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/services/muc/listener/ConferenceServiceDiscoveryReceived.java
@@ -0,0 +1,59 @@
+package de.thedevstack.conversationsplus.services.muc.listener;
+
+import android.util.Log;
+
+import java.util.ArrayList;
+
+import de.thedevstack.conversationsplus.Config;
+import de.thedevstack.conversationsplus.entities.Account;
+import de.thedevstack.conversationsplus.entities.Conversation;
+import de.thedevstack.conversationsplus.services.XmppConnectionService;
+import de.thedevstack.conversationsplus.services.muc.OnConferenceConfigurationFetched;
+import de.thedevstack.conversationsplus.utils.UiUpdateHelper;
+import de.thedevstack.conversationsplus.xml.Element;
+import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived;
+import de.thedevstack.conversationsplus.xmpp.disco.ServiceDiscovery;
+import de.thedevstack.conversationsplus.xmpp.forms.Data;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
+
+/**
+ */
+public class ConferenceServiceDiscoveryReceived implements OnIqPacketReceived {
+ private Conversation conversation;
+ private OnConferenceConfigurationFetched callback;
+
+ public ConferenceServiceDiscoveryReceived(Conversation conversation, OnConferenceConfigurationFetched callback) {
+ this.conversation = conversation;
+ this.callback = callback;
+ }
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ Element query = packet.findChild(ServiceDiscovery.ELEMENT, ServiceDiscovery.NAMESPACE);
+ if (packet.getType() == IqPacket.TYPE.RESULT && query != null) {
+ ArrayList<String> features = new ArrayList<>();
+ for (Element child : query.getChildren()) {
+ if (child != null && child.getName().equals("feature")) {
+ String var = child.getAttribute("var");
+ if (var != null) {
+ features.add(var);
+ }
+ }
+ }
+ Element form = query.findChild("x", "jabber:x:data");
+ if (form != null) {
+ conversation.getMucOptions().updateFormData(Data.parse(form));
+ }
+ conversation.getMucOptions().updateFeatures(features);
+ if (callback != null) {
+ callback.onConferenceConfigurationFetched(conversation);
+ }
+ Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": fetched muc configuration for " + conversation.getJid().toBareJid() + " - " + features.toString());
+ UiUpdateHelper.updateConversationUi();
+ } else if (packet.getType() == IqPacket.TYPE.ERROR) {
+ if (callback != null) {
+ callback.onFetchFailed(conversation, packet.getError());
+ }
+ }
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/SettingsActivity.java b/src/main/java/de/thedevstack/conversationsplus/ui/SettingsActivity.java
index 296f8f4f..53c3892d 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/SettingsActivity.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/SettingsActivity.java
@@ -24,7 +24,9 @@ import java.util.Locale;
import de.duenndns.ssl.MemorizingTrustManager;
import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
import de.thedevstack.conversationsplus.services.ExportLogsService;
+import de.thedevstack.conversationsplus.xmpp.XepRegistry;
import de.tzur.conversations.Settings;
import de.thedevstack.conversationsplus.R;
import de.thedevstack.conversationsplus.entities.Account;
@@ -187,6 +189,18 @@ public class SettingsActivity extends XmppActivity implements
FileBackend.onFileTransferFolderChanged();
} else if ("img_transfer_folder".equals(name)) {
FileBackend.onImageTransferFolderChanged();
+ } else if ("send_entity_time".equals(name)) {
+ if (ConversationsPlusPreferences.sendEntityTime()) {
+ XepRegistry.enable("time");
+ } else {
+ XepRegistry.disable("time");
+ }
+ } else if ("send_software_info".equals(name)) {
+ if (ConversationsPlusPreferences.sendSoftwareVersion()) {
+ XepRegistry.enable("iq-version");
+ } else {
+ XepRegistry.disable("iq-version");
+ }
}
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ConversationMoreMessagesLoadedListener.java b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ConversationMoreMessagesLoadedListener.java
index 8e2909ad..fd8b5782 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ConversationMoreMessagesLoadedListener.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ConversationMoreMessagesLoadedListener.java
@@ -11,6 +11,7 @@ import java.util.List;
import de.thedevstack.conversationsplus.entities.Conversation;
import de.thedevstack.conversationsplus.entities.Message;
import de.thedevstack.conversationsplus.services.XmppConnectionService;
+import de.thedevstack.conversationsplus.services.mam.OnMoreMessagesLoaded;
import de.thedevstack.conversationsplus.ui.ConversationActivity;
import de.thedevstack.conversationsplus.ui.ConversationFragment;
import de.thedevstack.conversationsplus.ui.adapter.MessageAdapter;
@@ -18,7 +19,7 @@ import de.thedevstack.conversationsplus.ui.adapter.MessageAdapter;
/**
* This listener updates the UI when messages are loaded from the server.
*/
-public class ConversationMoreMessagesLoadedListener implements XmppConnectionService.OnMoreMessagesLoaded {
+public class ConversationMoreMessagesLoadedListener implements OnMoreMessagesLoaded {
private SwipyRefreshLayout swipeLayout;
private List<Message> messageList;
private ConversationFragment fragment;
diff --git a/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ConversationSwipeRefreshListener.java b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ConversationSwipeRefreshListener.java
index 6dc9f4a4..d401251d 100644
--- a/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ConversationSwipeRefreshListener.java
+++ b/src/main/java/de/thedevstack/conversationsplus/ui/listeners/ConversationSwipeRefreshListener.java
@@ -13,7 +13,7 @@ import de.thedevstack.conversationsplus.R;
import de.thedevstack.conversationsplus.entities.Account;
import de.thedevstack.conversationsplus.entities.Conversation;
import de.thedevstack.conversationsplus.entities.Message;
-import de.thedevstack.conversationsplus.services.MessageArchiveService;
+import de.thedevstack.conversationsplus.services.mam.MessageArchiveService;
import de.thedevstack.conversationsplus.ui.ConversationActivity;
import de.thedevstack.conversationsplus.ui.ConversationFragment;
import de.thedevstack.conversationsplus.ui.adapter.MessageAdapter;
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/UiUpdateHelper.java b/src/main/java/de/thedevstack/conversationsplus/utils/UiUpdateHelper.java
index 7638caad..4477f074 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/UiUpdateHelper.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/UiUpdateHelper.java
@@ -2,6 +2,7 @@ package de.thedevstack.conversationsplus.utils;
import de.thedevstack.android.logcat.Logging;
import de.thedevstack.conversationsplus.services.XmppConnectionService;
+import de.thedevstack.conversationsplus.xmpp.OnUpdateBlocklist;
/**
* Helper class to avoid passing the xmppConnectionService to everywhere just to update the UI.
@@ -49,4 +50,12 @@ public class UiUpdateHelper {
Logging.e("UiUpdateHelper", "XMPP Connection Service not initialized. MUC Roster Ui not updated.");
}
}
+
+ public static void updateBlocklistUi(final OnUpdateBlocklist.Status status) {
+ if (null != UiUpdateHelper.xmppConnectionService) {
+ UiUpdateHelper.xmppConnectionService.updateBlocklistUi(status);
+ } else {
+ Logging.e("UiUpdateHelper", "XMPP Connection Service not initialized.Blocklist Ui not updated.");
+ }
+ }
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java b/src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java
index 11d05832..67b70ef6 100644
--- a/src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java
+++ b/src/main/java/de/thedevstack/conversationsplus/utils/XmppSendUtil.java
@@ -8,9 +8,13 @@ import de.thedevstack.conversationsplus.xmpp.stanzas.MessagePacket;
import de.thedevstack.conversationsplus.xmpp.stanzas.PresencePacket;
/**
- * Created by tzur on 09.01.2016.
+ *
*/
public class XmppSendUtil {
+ public static void sendIqPacket(Account account, IqPacket packet) {
+ sendIqPacket(account, packet, null);
+ }
+
public static void sendIqPacket(Account account, IqPacket packet, OnIqPacketReceived callback) {
final XmppConnection connection = account.getXmppConnection();
if (connection != null) {
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/AbstractXep.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/AbstractXep.java
new file mode 100644
index 00000000..96cbeb3e
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/AbstractXep.java
@@ -0,0 +1,30 @@
+package de.thedevstack.conversationsplus.xmpp;
+
+/**
+ */
+public abstract class AbstractXep implements Xep {
+ protected boolean enabled = true;
+
+ public AbstractXep() {
+ this(true);
+ }
+
+ public AbstractXep(boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public final void enable() {
+ this.enabled = true;
+ }
+
+ @Override
+ public final void disable() {
+ this.enabled = false;
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/IqPacketHandler.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/IqPacketHandler.java
new file mode 100644
index 00000000..cfd5172e
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/IqPacketHandler.java
@@ -0,0 +1,12 @@
+package de.thedevstack.conversationsplus.xmpp;
+
+import de.thedevstack.conversationsplus.entities.Account;
+import de.thedevstack.conversationsplus.xmpp.exceptions.IqPacketErrorException;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
+
+/**
+ *
+ */
+public interface IqPacketHandler {
+ void handleIqPacket(Account account, IqPacket packet) throws IqPacketErrorException;
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/Xep.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/Xep.java
new file mode 100644
index 00000000..058f345e
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/Xep.java
@@ -0,0 +1,16 @@
+package de.thedevstack.conversationsplus.xmpp;
+
+/**
+ */
+public interface Xep {
+ boolean isEnabled();
+ void enable();
+ void disable();
+ String xepNumber();
+ String shortName();
+ String name();
+ String namespace();
+ String featureNamespace();
+ String elementName();
+ IqPacketHandler handler();
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/XepRegistry.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/XepRegistry.java
new file mode 100644
index 00000000..89174c70
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/XepRegistry.java
@@ -0,0 +1,51 @@
+package de.thedevstack.conversationsplus.xmpp;
+
+import java.util.Hashtable;
+
+import de.thedevstack.conversationsplus.xmpp.disco.FeatureRegistry;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacketReceiver;
+
+/**
+ */
+public class XepRegistry {
+ private final Hashtable<String, Xep> xeps = new Hashtable<>();
+ private static final XepRegistry INSTANCE = new XepRegistry();
+
+ public static void enable(String shortName) {
+ if (INSTANCE.xeps.containsKey(shortName)) {
+ Xep xep = INSTANCE.xeps.get(shortName);
+ xep.enable();
+ FeatureRegistry.remove(xep);
+ IqPacketReceiver.getInstance().registerIqPacketHandler(xep);
+ }
+ }
+
+ public static void disable(String shortName) {
+ if (INSTANCE.xeps.containsKey(shortName)) {
+ Xep xep = INSTANCE.xeps.get(shortName);
+ xep.disable();
+ FeatureRegistry.remove(xep);
+ IqPacketReceiver.getInstance().unregisterIqPacketHandler(xep);
+ }
+ }
+
+ public static void add(Xep xep) {
+ INSTANCE.xeps.put(xep.shortName(), xep);
+ if (xep.isEnabled()) {
+ FeatureRegistry.add(xep);
+ IqPacketReceiver.getInstance().registerIqPacketHandler(xep);
+ }
+ }
+
+ public static void remove(Xep xep) {
+ INSTANCE.xeps.remove(xep.shortName());
+ FeatureRegistry.remove(xep);
+ IqPacketReceiver.getInstance().unregisterIqPacketHandler(xep);
+ }
+
+ public static XepRegistry getInstance() {
+ return INSTANCE;
+ }
+
+ private XepRegistry() {}
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java
index 54db5346..2b366dbb 100644
--- a/src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/XmppConnection.java
@@ -84,9 +84,11 @@ import de.thedevstack.conversationsplus.xmpp.mam.Mam;
import de.thedevstack.conversationsplus.xmpp.stanzas.AbstractAcknowledgeableStanza;
import de.thedevstack.conversationsplus.xmpp.stanzas.AbstractStanza;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacketReceiver;
import de.thedevstack.conversationsplus.xmpp.stanzas.MessagePacket;
import de.thedevstack.conversationsplus.xmpp.stanzas.PresencePacket;
import de.thedevstack.conversationsplus.xmpp.stanzas.csi.ActivePacket;
+import de.thedevstack.conversationsplus.xmpp.stanzas.csi.Csi;
import de.thedevstack.conversationsplus.xmpp.stanzas.csi.InactivePacket;
import de.thedevstack.conversationsplus.xmpp.stanzas.streammgmt.AckPacket;
import de.thedevstack.conversationsplus.xmpp.stanzas.streammgmt.EnablePacket;
@@ -1092,7 +1094,7 @@ public class XmppConnection implements Runnable {
}
if (getFeatures().blocking() && !features.blockListRequested) {
Log.d(Config.LOGTAG, account.getJid().toBareJid() + ": Requesting block list");
- this.sendIqPacket(getIqGenerator().generateGetBlockList(), mXmppConnectionService.getIqParser());
+ this.sendIqPacket(getIqGenerator().generateGetBlockList(), IqPacketReceiver.getInstance().getLegacyIqParser());
}
for (final OnAdvancedStreamFeaturesLoaded listener : advancedStreamFeaturesLoadedListeners) {
listener.onAdvancedStreamFeaturesAvailable(account);
@@ -1509,7 +1511,7 @@ public class XmppConnection implements Runnable {
}
public boolean csi() {
- return connection.streamFeatures != null && connection.streamFeatures.hasChild("csi", "urn:xmpp:csi:0");
+ return connection.streamFeatures != null && connection.streamFeatures.hasChild("csi", Csi.NAMESPACE);
}
public boolean pep() {
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();
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/exceptions/NotAllowedIqException.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/exceptions/NotAllowedIqException.java
new file mode 100644
index 00000000..45bf231a
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/exceptions/NotAllowedIqException.java
@@ -0,0 +1,11 @@
+package de.thedevstack.conversationsplus.xmpp.exceptions;
+
+import de.thedevstack.conversationsplus.xml.Element;
+
+/**
+ */
+public class NotAllowedIqException extends IqPacketErrorException {
+ public NotAllowedIqException(Element context, String errorMessage) {
+ super(context, errorMessage);
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersion.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersion.java
new file mode 100644
index 00000000..e90ccb5a
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersion.java
@@ -0,0 +1,9 @@
+package de.thedevstack.conversationsplus.xmpp.iqversion;
+
+/**
+ *
+ */
+public interface IqVersion {
+ String NAMESPACE = "jabber:iq:version";
+ String ELEMENT = "query";
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersionPacket.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersionPacket.java
deleted file mode 100644
index a3cecdac..00000000
--- a/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersionPacket.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package de.thedevstack.conversationsplus.xmpp.iqversion;
-
-import android.os.Build;
-
-import de.thedevstack.conversationsplus.ConversationsPlusApplication;
-import de.thedevstack.conversationsplus.xml.Element;
-import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
-
-/**
- * Representation of an software version packet as defined in XEP-0092.
- * @see <a href="http://xmpp.org/extensions/xep-0092.html">http://xmpp.org/extensions/xep-0092.html</a>
- */
-public class IqVersionPacket extends IqPacket {
- public static final String NAMESPACE = "jabber:iq:version";
-
- IqVersionPacket() {
- super(IqPacket.TYPE.RESULT);
- Element query = this.addChild("query", NAMESPACE);
- query.addChild("name").setContent(ConversationsPlusApplication.getName());
- query.addChild("version").setContent(ConversationsPlusApplication.getVersion());
- query.addChild("os").setContent("Android " + Build.VERSION.RELEASE);
- }
-}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersionPacketGenerator.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersionPacketGenerator.java
index 97a7e90d..c4961c1d 100644
--- a/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersionPacketGenerator.java
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersionPacketGenerator.java
@@ -1,6 +1,12 @@
package de.thedevstack.conversationsplus.xmpp.iqversion;
+import android.os.Build;
+
+import de.thedevstack.conversationsplus.ConversationsPlusApplication;
+import de.thedevstack.conversationsplus.ConversationsPlusPreferences;
+import de.thedevstack.conversationsplus.xml.Element;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacketGenerator;
/**
* Generates the IQ Packets for Software Version
@@ -26,12 +32,17 @@ public final class IqVersionPacketGenerator {
* @param packet the packet to respond to
* @return
*/
- public static IqVersionPacket generateResponse(IqPacket packet) {
- IqVersionPacket iqVersionPacket = new IqVersionPacket();
- iqVersionPacket.setTo(packet.getFrom());
- iqVersionPacket.setId(packet.getId());
+ public static IqPacket generateResponse(IqPacket packet) {
+ IqPacket responsePacket = IqPacketGenerator.generateIqResultResponse(packet);
+
+ Element query = responsePacket.addChild(IqVersion.ELEMENT, IqVersion.NAMESPACE);
+ query.addChild("name").setContent(ConversationsPlusApplication.getName());
+ query.addChild("version").setContent(ConversationsPlusApplication.getVersion());
+ if (ConversationsPlusPreferences.sendOsInformation()) {
+ query.addChild("os").setContent("Android " + Build.VERSION.RELEASE);
+ }
- return iqVersionPacket;
+ return responsePacket;
}
private IqVersionPacketGenerator() {}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersionPacketHandler.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersionPacketHandler.java
new file mode 100644
index 00000000..7462d0cb
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/IqVersionPacketHandler.java
@@ -0,0 +1,21 @@
+package de.thedevstack.conversationsplus.xmpp.iqversion;
+
+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 IqVersionPacketHandler implements IqPacketHandler {
+ @Override
+ public void handleIqPacket(Account account, IqPacket packet) throws IqPacketErrorException {
+ if (packet.getType() == IqPacket.TYPE.GET) {
+ XmppSendUtil.sendIqPacket(account, IqVersionPacketGenerator.generateResponse(packet));
+ } else {
+ throw new NotAllowedIqException(packet, "only type=get allowed for processing an Software Version (XEP-0092)");
+ }
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/SoftwareVersionXep.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/SoftwareVersionXep.java
new file mode 100644
index 00000000..9cd69bb2
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/iqversion/SoftwareVersionXep.java
@@ -0,0 +1,51 @@
+package de.thedevstack.conversationsplus.xmpp.iqversion;
+
+import de.thedevstack.conversationsplus.xmpp.AbstractXep;
+import de.thedevstack.conversationsplus.xmpp.IqPacketHandler;
+
+/**
+ */
+public class SoftwareVersionXep extends AbstractXep {
+ public SoftwareVersionXep() {
+ super();
+ }
+
+ public SoftwareVersionXep(boolean enabled) {
+ super(enabled);
+ }
+
+ @Override
+ public String xepNumber() {
+ return "0092";
+ }
+
+ @Override
+ public String shortName() {
+ return "iq-version";
+ }
+
+ @Override
+ public String name() {
+ return "Software Version";
+ }
+
+ @Override
+ public String namespace() {
+ return IqVersion.NAMESPACE;
+ }
+
+ @Override
+ public String featureNamespace() {
+ return IqVersion.NAMESPACE;
+ }
+
+ @Override
+ public String elementName() {
+ return IqVersion.ELEMENT;
+ }
+
+ @Override
+ public IqPacketHandler handler() {
+ return new IqVersionPacketHandler();
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/openpgp/OpenPgpXep.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/openpgp/OpenPgpXep.java
new file mode 100644
index 00000000..15229bf9
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/openpgp/OpenPgpXep.java
@@ -0,0 +1,48 @@
+package de.thedevstack.conversationsplus.xmpp.openpgp;
+
+import de.thedevstack.conversationsplus.xmpp.AbstractXep;
+import de.thedevstack.conversationsplus.xmpp.IqPacketHandler;
+
+/**
+ */
+public class OpenPgpXep extends AbstractXep {
+ public static final String ENCRYPTED_NAMESPACE = "jabber:x:encrypted";
+ public static final String ENCRYPTED_ELEMENT = "x";
+ public static final String SIGNED_NAMESPACE = "jabber:x:signed";
+ public static final String SIGNED_ELEMENT = "x";
+
+ @Override
+ public String xepNumber() {
+ return "0027";
+ }
+
+ @Override
+ public String shortName() {
+ return "openpgp";
+ }
+
+ @Override
+ public String name() {
+ return "Current Jabber OpenPGP Usage";
+ }
+
+ @Override
+ public String namespace() {
+ return null;
+ }
+
+ @Override
+ public String featureNamespace() {
+ return null;
+ }
+
+ @Override
+ public String elementName() {
+ return null;
+ }
+
+ @Override
+ public IqPacketHandler handler() {
+ return null;
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/ping/Ping.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/ping/Ping.java
new file mode 100644
index 00000000..ac201c01
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/ping/Ping.java
@@ -0,0 +1,8 @@
+package de.thedevstack.conversationsplus.xmpp.ping;
+
+/**
+ */
+public interface Ping {
+ String NAMESPACE = "urn:xmpp:ping";
+ String ELEMENT = "ping";
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/ping/PingPacketHandler.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/ping/PingPacketHandler.java
new file mode 100644
index 00000000..14e8f57c
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/ping/PingPacketHandler.java
@@ -0,0 +1,22 @@
+package de.thedevstack.conversationsplus.xmpp.ping;
+
+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;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacketGenerator;
+
+/**
+ */
+public class PingPacketHandler implements IqPacketHandler {
+ @Override
+ public void handleIqPacket(Account account, IqPacket packet) throws IqPacketErrorException {
+ if (packet.getType() == IqPacket.TYPE.GET) {
+ XmppSendUtil.sendIqPacket(account, IqPacketGenerator.generateIqResultResponse(packet));
+ } else {
+ throw new NotAllowedIqException(packet, "only type=get allowed for processing an XMPP Ping (XEP-0199)");
+ }
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/ping/PingXep.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/ping/PingXep.java
new file mode 100644
index 00000000..6036a64f
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/ping/PingXep.java
@@ -0,0 +1,51 @@
+package de.thedevstack.conversationsplus.xmpp.ping;
+
+import de.thedevstack.conversationsplus.xmpp.AbstractXep;
+import de.thedevstack.conversationsplus.xmpp.IqPacketHandler;
+
+/**
+ */
+public class PingXep extends AbstractXep {
+ public PingXep() {
+ super();
+ }
+
+ public PingXep(boolean enabled) {
+ super(enabled);
+ }
+
+ @Override
+ public String xepNumber() {
+ return "0199";
+ }
+
+ @Override
+ public String shortName() {
+ return "ping";
+ }
+
+ @Override
+ public String name() {
+ return "XMPP Ping";
+ }
+
+ @Override
+ public String namespace() {
+ return Ping.NAMESPACE;
+ }
+
+ @Override
+ public String featureNamespace() {
+ return Ping.NAMESPACE;
+ }
+
+ @Override
+ public String elementName() {
+ return Ping.ELEMENT;
+ }
+
+ @Override
+ public IqPacketHandler handler() {
+ return new PingPacketHandler();
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/ErrorIqPacket.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/ErrorIqPacket.java
new file mode 100644
index 00000000..3f87202e
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/ErrorIqPacket.java
@@ -0,0 +1,16 @@
+package de.thedevstack.conversationsplus.xmpp.stanzas;
+
+import de.thedevstack.conversationsplus.xml.Element;
+
+/**
+ */
+public class ErrorIqPacket extends IqPacket {
+ public final static String NAMESPACE = "urn:ietf:params:xml:ns:xmpp-stanzas";
+
+ public ErrorIqPacket(IqErrorCondition condition) {
+ super(IqPacket.TYPE.ERROR);
+ final Element error = this.addChild("error");
+ error.setAttribute("type", condition.getType().toString());
+ error.addChild(condition.toString(), NAMESPACE);
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqErrorCondition.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqErrorCondition.java
new file mode 100644
index 00000000..688ea007
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqErrorCondition.java
@@ -0,0 +1,48 @@
+package de.thedevstack.conversationsplus.xmpp.stanzas;
+
+/**
+ */
+public enum IqErrorCondition {
+ BAD_REQUEST(IqErrorType.MODIFY),
+ CONFLICT(IqErrorType.CANCEL),
+ FEATURE_NOT_IMPLEMENTED(IqErrorType.CANCEL),
+ FORBIDDEN(IqErrorType.AUTH),
+ GONE(IqErrorType.MODIFY),
+ INTERNAL_SERVER_ERROR(IqErrorType.WAIT),
+ ITEM_NOT_FOUND(IqErrorType.CANCEL),
+ JID_MALFORMED(IqErrorType.MODIFY),
+ NOT_ACCEPTABLE(IqErrorType.MODIFY),
+ NOT_ALLOWED(IqErrorType.CANCEL),
+ NOT_AUTHORIZED(IqErrorType.AUTH),
+ PAYMENT_REQUIRED(IqErrorType.AUTH),
+ RECIPIENT_UNAVAILABLE(IqErrorType.WAIT),
+ REDIRECT(IqErrorType.MODIFY),
+ REGISTRATION_REQUIRED(IqErrorType.AUTH),
+ REMOTE_SERVER_NOT_FOUND(IqErrorType.CANCEL),
+ REMOTE_SERVER_TIMEOUT(IqErrorType.WAIT),
+ RESOURCE_CONSTRAINT(IqErrorType.WAIT),
+ SERVICE_UNAVAILABLE(IqErrorType.CANCEL),
+ SUBSCRIPTION_REQUIRED(IqErrorType.AUTH),
+ UNDEFINED_CONDITION(IqErrorType.CANCEL),
+ UNEXPECTED_REQUEST(IqErrorType.WAIT)
+ ;
+
+ private IqErrorType type;
+
+ IqErrorCondition(IqErrorType type) {
+ this.type = type;
+ }
+
+ public IqErrorType getType() {
+ return type;
+ }
+
+ public static IqErrorCondition fromString(String condition) {
+ return (null != condition) ? Enum.valueOf(IqErrorCondition.class, condition.toUpperCase().replaceAll("-", "_")) : null;
+ }
+
+ @Override
+ public String toString() {
+ return super.toString().toLowerCase().replaceAll("_", "-");
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqErrorType.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqErrorType.java
new file mode 100644
index 00000000..4ebe0f41
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqErrorType.java
@@ -0,0 +1,12 @@
+package de.thedevstack.conversationsplus.xmpp.stanzas;
+
+/**
+ */
+public enum IqErrorType {
+ CANCEL, MODIFY, AUTH, WAIT;
+
+ @Override
+ public String toString() {
+ return super.toString().toLowerCase();
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacket.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacket.java
index d86831e0..415d5de3 100644
--- a/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacket.java
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacket.java
@@ -59,6 +59,10 @@ public class IqPacket extends AbstractAcknowledgeableStanza {
}
}
+ public Element getChildElement() {
+ return this.children.get(0);
+ }
+
public IqPacket generateResponse(final TYPE type) {
final IqPacket packet = new IqPacket(type);
packet.setTo(this.getFrom());
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketGenerator.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketGenerator.java
index 27e6607f..01204a96 100644
--- a/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketGenerator.java
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketGenerator.java
@@ -1,5 +1,7 @@
package de.thedevstack.conversationsplus.xmpp.stanzas;
+import de.thedevstack.conversationsplus.xml.Element;
+
/**
* Created by tzur on 15.01.2016.
*/
@@ -21,8 +23,19 @@ public final class IqPacketGenerator {
return generateIqPacket(IqPacket.TYPE.RESULT);
}
- public static IqPacket generateIqErrorPacket() {
- return generateIqPacket(IqPacket.TYPE.ERROR);
+ public static IqPacket generateIqResultResponse(IqPacket packet) {
+ IqPacket responsePacket = generateIqResultPacket();
+ responsePacket.setTo(packet.getFrom());
+ responsePacket.setId(packet.getId());
+
+ return responsePacket;
+ }
+
+ public static ErrorIqPacket generateIqErrorPacketResponse(IqErrorCondition condition, IqPacket packet) {
+ ErrorIqPacket errorPacket = new ErrorIqPacket(condition);
+ errorPacket.setTo(packet.getFrom());
+ errorPacket.setId(packet.getId());
+ return errorPacket;
}
private IqPacketGenerator() {
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketReceiver.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketReceiver.java
new file mode 100644
index 00000000..bfae0355
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/IqPacketReceiver.java
@@ -0,0 +1,96 @@
+package de.thedevstack.conversationsplus.xmpp.stanzas;
+
+import java.util.Hashtable;
+
+import de.thedevstack.conversationsplus.entities.Account;
+import de.thedevstack.conversationsplus.parser.IqParser;
+import de.thedevstack.conversationsplus.utils.XmppConnectionServiceAccessor;
+import de.thedevstack.conversationsplus.utils.XmppSendUtil;
+import de.thedevstack.conversationsplus.xml.Element;
+import de.thedevstack.conversationsplus.xmpp.IqPacketHandler;
+import de.thedevstack.conversationsplus.xmpp.OnIqPacketReceived;
+import de.thedevstack.conversationsplus.xmpp.Xep;
+import de.thedevstack.conversationsplus.xmpp.exceptions.IqPacketErrorException;
+import de.thedevstack.conversationsplus.xmpp.exceptions.NotAllowedIqException;
+
+/**
+ */
+public final class IqPacketReceiver implements OnIqPacketReceived {
+ private final Hashtable<String, IqPacketHandler> iqPacketHandlers = new Hashtable<>();
+ private final IqParser legacyIqParser;
+ private static IqPacketReceiver INSTANCE;
+
+ public synchronized static IqPacketReceiver getInstance() {
+ if (null == INSTANCE) {
+ IqPacketReceiver.INSTANCE = new IqPacketReceiver();
+ }
+ return INSTANCE;
+ }
+
+ private IqPacketReceiver() {
+ this.legacyIqParser = new IqParser(XmppConnectionServiceAccessor.xmppConnectionService);
+ }
+
+ @Override
+ public void onIqPacketReceived(Account account, IqPacket packet) {
+ String key = generateKey(packet);
+ if (null != key && this.iqPacketHandlers.containsKey(key)) {
+ try {
+ IqPacketHandler iqPacketHandler = this.iqPacketHandlers.get(key);
+ iqPacketHandler.handleIqPacket(account, packet);
+ } catch (NotAllowedIqException e) {
+ ErrorIqPacket errorIqPacket = IqPacketGenerator.generateIqErrorPacketResponse(IqErrorCondition.NOT_ALLOWED, packet);
+ XmppSendUtil.sendIqPacket(account, errorIqPacket);
+ } catch (IqPacketErrorException e) {
+
+ }
+ } else {
+ this.legacyIqParser.onIqPacketReceived(account, packet);
+ }
+ }
+
+ public static String generateKey(IqPacket packet) {
+ Element childElement = packet.getChildElement();
+ return generateKey(childElement.getName(), childElement.getNamespace());
+ }
+
+ public static String generateKey(String elementName, String xmlns) {
+ if (null == elementName && null == xmlns) {
+ return null;
+ }
+ return ((null != elementName) ? elementName : "") + ((null != xmlns) ? ("-" + xmlns) : "");
+ }
+
+ public void registerIqPacketHandler(String elementName, String xmlns, IqPacketHandler iqPacketHandler) {
+ String key = generateKey(elementName, xmlns);
+ if (null != key) {
+ this.iqPacketHandlers.put(key, iqPacketHandler);
+ }
+ }
+
+ public void unregisterIqPacketHandler(String elementName, String xmlns) {
+ String key = generateKey(elementName, xmlns);
+ if (null != key && this.iqPacketHandlers.containsKey(key)) {
+ this.iqPacketHandlers.remove(key);
+ }
+ }
+
+ public void registerIqPacketHandler(Xep xep) {
+ String key = generateKey(xep.elementName(), xep.namespace());
+ if (null != key && null != xep.handler()) {
+ this.iqPacketHandlers.put(key, xep.handler());
+ }
+ }
+
+ public void unregisterIqPacketHandler(Xep xep) {
+ String key = generateKey(xep.elementName(), xep.namespace());
+ if (null != key && this.iqPacketHandlers.containsKey(key)) {
+ this.iqPacketHandlers.remove(key);
+ }
+ }
+
+ @Deprecated
+ public IqParser getLegacyIqParser() {
+ return this.legacyIqParser;
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/csi/ActivePacket.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/csi/ActivePacket.java
index 47538a64..52b2e04d 100644
--- a/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/csi/ActivePacket.java
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/csi/ActivePacket.java
@@ -5,6 +5,6 @@ import de.thedevstack.conversationsplus.xmpp.stanzas.AbstractStanza;
public class ActivePacket extends AbstractStanza {
public ActivePacket() {
super("active");
- setAttribute("xmlns", "urn:xmpp:csi:0");
+ setAttribute("xmlns", Csi.NAMESPACE);
}
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/csi/Csi.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/csi/Csi.java
new file mode 100644
index 00000000..e002b093
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/csi/Csi.java
@@ -0,0 +1,7 @@
+package de.thedevstack.conversationsplus.xmpp.stanzas.csi;
+
+/**
+ */
+public interface Csi {
+ String NAMESPACE = "urn:xmpp:csi:0";
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/csi/InactivePacket.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/csi/InactivePacket.java
index ca5904a5..35486858 100644
--- a/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/csi/InactivePacket.java
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/stanzas/csi/InactivePacket.java
@@ -5,6 +5,6 @@ import de.thedevstack.conversationsplus.xmpp.stanzas.AbstractStanza;
public class InactivePacket extends AbstractStanza {
public InactivePacket() {
super("inactive");
- setAttribute("xmlns", "urn:xmpp:csi:0");
+ setAttribute("xmlns", Csi.NAMESPACE);
}
}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/time/EntityTime.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/time/EntityTime.java
new file mode 100644
index 00000000..7382f37a
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/time/EntityTime.java
@@ -0,0 +1,8 @@
+package de.thedevstack.conversationsplus.xmpp.time;
+
+/**
+ */
+public interface EntityTime {
+ String NAMESPACE = "urn:xmpp:time";
+ String ELEMENT = "time";
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/time/EntityTimeXep.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/time/EntityTimeXep.java
new file mode 100644
index 00000000..fb721bce
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/time/EntityTimeXep.java
@@ -0,0 +1,51 @@
+package de.thedevstack.conversationsplus.xmpp.time;
+
+import de.thedevstack.conversationsplus.xmpp.AbstractXep;
+import de.thedevstack.conversationsplus.xmpp.IqPacketHandler;
+
+/**
+ */
+public class EntityTimeXep extends AbstractXep {
+ public EntityTimeXep() {
+ super();
+ }
+
+ public EntityTimeXep(boolean enabled) {
+ super(enabled);
+ }
+
+ @Override
+ public String xepNumber() {
+ return "0202";
+ }
+
+ @Override
+ public String shortName() {
+ return "time";
+ }
+
+ @Override
+ public String name() {
+ return "Entity Time";
+ }
+
+ @Override
+ public String namespace() {
+ return EntityTime.NAMESPACE;
+ }
+
+ @Override
+ public String featureNamespace() {
+ return EntityTime.NAMESPACE;
+ }
+
+ @Override
+ public String elementName() {
+ return EntityTime.ELEMENT;
+ }
+
+ @Override
+ public IqPacketHandler handler() {
+ return new TimeIqPacketHandler();
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/time/TimeIqPacketHandler.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/time/TimeIqPacketHandler.java
new file mode 100644
index 00000000..961c649b
--- /dev/null
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/time/TimeIqPacketHandler.java
@@ -0,0 +1,21 @@
+package de.thedevstack.conversationsplus.xmpp.time;
+
+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 TimeIqPacketHandler implements IqPacketHandler {
+ @Override
+ public void handleIqPacket(Account account, IqPacket packet) throws IqPacketErrorException {
+ if (packet.getType() == IqPacket.TYPE.GET) {
+ XmppSendUtil.sendIqPacket(account, TimePacketGenerator.generateResponse(packet));
+ } else {
+ throw new NotAllowedIqException(packet, "only type=get allowed for processing an Entity Time (XEP-0202)");
+ }
+ }
+}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/time/TimePacket.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/time/TimePacket.java
deleted file mode 100644
index d8756d41..00000000
--- a/src/main/java/de/thedevstack/conversationsplus/xmpp/time/TimePacket.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package de.thedevstack.conversationsplus.xmpp.time;
-
-import java.util.Locale;
-import java.util.TimeZone;
-
-import de.thedevstack.conversationsplus.generator.AbstractGenerator;
-import de.thedevstack.conversationsplus.xml.Element;
-import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
-
-/**
- * Representation of an software version packet as defined in XEP-0202.
- * @see <a href="http://xmpp.org/extensions/xep-0202.html">http://xmpp.org/extensions/xep-0202.html</a>
- */
-public class TimePacket extends IqPacket {
- public static final String NAMESPACE = "urn:xmpp:time";
-
- TimePacket() {
- super(IqPacket.TYPE.RESULT);
- Element time = this.addChild("time", NAMESPACE);
- final long now = System.currentTimeMillis();
- time.addChild("utc").setContent(AbstractGenerator.getTimestamp(now));
- TimeZone ourTimezone = TimeZone.getDefault();
- long offsetSeconds = ourTimezone.getOffset(now) / 1000;
- long offsetMinutes = Math.abs((offsetSeconds % 3600) / 60);
- long offsetHours = offsetSeconds / 3600;
- String hours;
- if (offsetHours < 0) {
- hours = String.format(Locale.US, "%03d", offsetHours);
- } else {
- hours = String.format(Locale.US, "%02d", offsetHours);
- }
- String minutes = String.format(Locale.US, "%02d", offsetMinutes);
- time.addChild("tzo").setContent(hours + ":" + minutes);
- }
-}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/time/TimePacketGenerator.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/time/TimePacketGenerator.java
index 344beb9e..ef381d4d 100644
--- a/src/main/java/de/thedevstack/conversationsplus/xmpp/time/TimePacketGenerator.java
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/time/TimePacketGenerator.java
@@ -1,7 +1,12 @@
package de.thedevstack.conversationsplus.xmpp.time;
-import de.thedevstack.conversationsplus.xmpp.iqversion.IqVersionPacket;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import de.thedevstack.conversationsplus.generator.AbstractGenerator;
+import de.thedevstack.conversationsplus.xml.Element;
import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacket;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqPacketGenerator;
/**
* Generates the IQ Packets for Entity Time
@@ -26,12 +31,26 @@ public final class TimePacketGenerator {
* @param packet
* @return
*/
- public static TimePacket generateResponse(IqPacket packet) {
- TimePacket timePacket = new TimePacket();
- timePacket.setTo(packet.getFrom());
- timePacket.setId(packet.getId());
+ public static IqPacket generateResponse(IqPacket packet) {
+ IqPacket responsePacket = IqPacketGenerator.generateIqResultResponse(packet);
+
+ Element time = responsePacket.addChild(EntityTime.ELEMENT, EntityTime.NAMESPACE);
+ final long now = System.currentTimeMillis();
+ time.addChild("utc").setContent(AbstractGenerator.getTimestamp(now));
+ TimeZone ourTimezone = TimeZone.getDefault();
+ long offsetSeconds = ourTimezone.getOffset(now) / 1000;
+ long offsetMinutes = Math.abs((offsetSeconds % 3600) / 60);
+ long offsetHours = offsetSeconds / 3600;
+ String hours;
+ if (offsetHours < 0) {
+ hours = String.format(Locale.US, "%03d", offsetHours);
+ } else {
+ hours = String.format(Locale.US, "%02d", offsetHours);
+ }
+ String minutes = String.format(Locale.US, "%02d", offsetMinutes);
+ time.addChild("tzo").setContent(hours + ":" + minutes);
- return timePacket;
+ return responsePacket;
}
private TimePacketGenerator() {}
diff --git a/src/main/java/de/thedevstack/conversationsplus/xmpp/utils/ErrorIqPacketExceptionHelper.java b/src/main/java/de/thedevstack/conversationsplus/xmpp/utils/ErrorIqPacketExceptionHelper.java
index bcc5e9dd..f3ac92dc 100644
--- a/src/main/java/de/thedevstack/conversationsplus/xmpp/utils/ErrorIqPacketExceptionHelper.java
+++ b/src/main/java/de/thedevstack/conversationsplus/xmpp/utils/ErrorIqPacketExceptionHelper.java
@@ -7,26 +7,26 @@ import de.thedevstack.conversationsplus.xmpp.exceptions.InternalServerErrorExcep
import de.thedevstack.conversationsplus.xmpp.exceptions.IqPacketErrorException;
import de.thedevstack.conversationsplus.xmpp.exceptions.ServiceUnavailableException;
import de.thedevstack.conversationsplus.xmpp.exceptions.UndefinedConditionException;
+import de.thedevstack.conversationsplus.xmpp.stanzas.ErrorIqPacket;
+import de.thedevstack.conversationsplus.xmpp.stanzas.IqErrorCondition;
/**
- * Created by steckbrief on 22.08.2016.
*/
public final class ErrorIqPacketExceptionHelper {
- private final static String ERROR_NAMESPACE = "urn:ietf:params:xml:ns:xmpp-stanzas";
public static void throwIqErrorException(Element errorIqPacket) throws IqPacketErrorException {
Element packet = IqPacketParser.findChild(errorIqPacket, "error", "jabber:client");
if (null != packet) {
- if (hasErrorElement(packet, "bad-request")) {
+ if (hasErrorElement(packet, IqErrorCondition.BAD_REQUEST.toString())) {
throw new BadRequestIqErrorException(errorIqPacket, getErrorText(packet));
}
- if (hasErrorElement(packet, "service-unavailable")) {
+ if (hasErrorElement(packet, IqErrorCondition.SERVICE_UNAVAILABLE.toString())) {
throw new ServiceUnavailableException(errorIqPacket, getErrorText(packet));
}
- if (hasErrorElement(packet, "internal-server-error")) {
+ if (hasErrorElement(packet, IqErrorCondition.INTERNAL_SERVER_ERROR.toString())) {
throw new InternalServerErrorException(errorIqPacket, getErrorText(packet));
}
- if (hasErrorElement(packet, "undefined-condition")) {
+ if (hasErrorElement(packet, IqErrorCondition.UNDEFINED_CONDITION.toString())) {
throw new UndefinedConditionException(errorIqPacket, getErrorText(packet));
}
}
@@ -34,11 +34,11 @@ public final class ErrorIqPacketExceptionHelper {
}
private static boolean hasErrorElement(Element packet, String elementName) {
- return null != IqPacketParser.findChild(packet, elementName, ERROR_NAMESPACE);
+ return null != IqPacketParser.findChild(packet, elementName, ErrorIqPacket.NAMESPACE);
}
private static String getErrorText(Element packet) {
- return IqPacketParser.findChildContent(packet, "text", ERROR_NAMESPACE);
+ return IqPacketParser.findChildContent(packet, "text", ErrorIqPacket.NAMESPACE);
}
private ErrorIqPacketExceptionHelper() {