From d2794ccf321113100c39c0ef3461d8f6fa6ef869 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sun, 22 Jan 2023 20:25:47 +0100 Subject: [PATCH] create new models for IQ, Message & Presence --- .../eu/siacs/conversations/xml/Namespace.java | 1 + .../siacs/conversations/xmpp/InvalidJid.java | 196 ++++----- .../conversations/android/xml/TagWriter.java | 126 ++++++ .../android/xmpp/XmppConnection.java | 409 +++++++----------- .../android/xmpp/manager/BlockingManager.java | 8 +- .../android/xmpp/manager/DiscoManager.java | 6 +- .../android/xmpp/manager/RosterManager.java | 8 +- .../android/xmpp/model/StreamElement.java | 8 + .../android/xmpp/model/csi/Active.java | 12 + .../android/xmpp/model/csi/Inactive.java | 12 + .../android/xmpp/model/csi/package-info.java | 5 + .../android/xmpp/model/register/Register.java | 12 + .../xmpp/model/register/package-info.java | 5 + .../android/xmpp/model/sm/Ack.java | 17 + .../android/xmpp/model/sm/Enable.java | 13 + .../android/xmpp/model/sm/Request.java | 12 + .../android/xmpp/model/sm/Resume.java | 18 + .../android/xmpp/model/sm/package-info.java | 5 + .../android/xmpp/model/stanza/IQ.java | 32 ++ .../android/xmpp/model/stanza/Message.java | 11 + .../android/xmpp/model/stanza/Presence.java | 12 + .../android/xmpp/model/stanza/Stanza.java | 36 ++ .../xmpp/model/stanza/package-info.java | 5 + .../android/xmpp/processor/IqProcessor.java | 17 +- .../xmpp/processor/JingleProcessor.java | 14 - .../xmpp/processor/MessageProcessor.java | 6 +- .../xmpp/processor/PresenceProcessor.java | 8 +- 27 files changed, 621 insertions(+), 393 deletions(-) create mode 100644 src/main/java/im/conversations/android/xml/TagWriter.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/StreamElement.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/csi/Active.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/csi/Inactive.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/csi/package-info.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/register/Register.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/register/package-info.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/sm/Ack.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/sm/Enable.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/sm/Request.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/sm/Resume.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/sm/package-info.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/stanza/IQ.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/stanza/Message.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/stanza/Presence.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/stanza/Stanza.java create mode 100644 src/main/java/im/conversations/android/xmpp/model/stanza/package-info.java delete mode 100644 src/main/java/im/conversations/android/xmpp/processor/JingleProcessor.java diff --git a/src/main/java/eu/siacs/conversations/xml/Namespace.java b/src/main/java/eu/siacs/conversations/xml/Namespace.java index 2c5eaaac0..2791ff827 100644 --- a/src/main/java/eu/siacs/conversations/xml/Namespace.java +++ b/src/main/java/eu/siacs/conversations/xml/Namespace.java @@ -78,4 +78,5 @@ public final class Namespace { public static final String OMEMO_DTLS_SRTP_VERIFICATION = "http://gultsch.de/xmpp/drafts/omemo/dlts-srtp-verification"; public static final String UNIFIED_PUSH = "http://gultsch.de/xmpp/drafts/unified-push"; + public static final String JABBER_CLIENT = "jabber:client"; } diff --git a/src/main/java/eu/siacs/conversations/xmpp/InvalidJid.java b/src/main/java/eu/siacs/conversations/xmpp/InvalidJid.java index f3a21c36f..aae86ad96 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/InvalidJid.java +++ b/src/main/java/eu/siacs/conversations/xmpp/InvalidJid.java @@ -30,126 +30,128 @@ package eu.siacs.conversations.xmpp; import androidx.annotation.NonNull; - import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; public class InvalidJid implements Jid { - private final String value; + private final String value; - private InvalidJid(String jid) { - this.value = jid; - } + private InvalidJid(String jid) { + this.value = jid; + } - public static Jid of(String jid, boolean fallback) { - final int pos = jid.indexOf('/'); - if (fallback && pos >= 0 && jid.length() >= pos + 1) { - if (jid.substring(pos+1).trim().isEmpty()) { - return Jid.ofEscaped(jid.substring(0,pos)); - } - } - return new InvalidJid(jid); - } + public static Jid of(String jid, boolean fallback) { + final int pos = jid.indexOf('/'); + if (fallback && pos >= 0 && jid.length() >= pos + 1) { + if (jid.substring(pos + 1).trim().isEmpty()) { + return Jid.ofEscaped(jid.substring(0, pos)); + } + } + return new InvalidJid(jid); + } - @Override - @NonNull - public String toString() { - return value; - } + @Override + @NonNull + public String toString() { + return value; + } - @Override - public boolean isFullJid() { - throw new AssertionError("Not implemented"); - } + @Override + public boolean isFullJid() { + throw new AssertionError("Not implemented"); + } - @Override - public boolean isBareJid() { - throw new AssertionError("Not implemented"); - } + @Override + public boolean isBareJid() { + throw new AssertionError("Not implemented"); + } - @Override - public boolean isDomainJid() { - throw new AssertionError("Not implemented"); - } + @Override + public boolean isDomainJid() { + throw new AssertionError("Not implemented"); + } - @Override - public Jid asBareJid() { - throw new AssertionError("Not implemented"); - } + @Override + public Jid asBareJid() { + throw new AssertionError("Not implemented"); + } + @Override + public Jid withResource(CharSequence charSequence) { + throw new AssertionError("Not implemented"); + } - @Override - public Jid withResource(CharSequence charSequence) { - throw new AssertionError("Not implemented"); - } + @Override + public String getLocal() { + throw new AssertionError("Not implemented"); + } - @Override - public String getLocal() { - throw new AssertionError("Not implemented"); - } + @Override + public String getEscapedLocal() { + throw new AssertionError("Not implemented"); + } - @Override - public String getEscapedLocal() { - throw new AssertionError("Not implemented"); - } + @Override + public Jid getDomain() { + throw new AssertionError("Not implemented"); + } - @Override - public Jid getDomain() { - throw new AssertionError("Not implemented"); - } + @Override + public String getResource() { + throw new AssertionError("Not implemented"); + } - @Override - public String getResource() { - throw new AssertionError("Not implemented"); - } + @Override + public String toEscapedString() { + throw new AssertionError("Not implemented"); + } - @Override - public String toEscapedString() { - throw new AssertionError("Not implemented"); - } + @Override + public int length() { + return value.length(); + } - @Override - public int length() { - return value.length(); - } + @Override + public char charAt(int index) { + return value.charAt(index); + } - @Override - public char charAt(int index) { - return value.charAt(index); - } + @Override + public CharSequence subSequence(int start, int end) { + return value.subSequence(start, end); + } - @Override - public CharSequence subSequence(int start, int end) { - return value.subSequence(start, end); - } + @Override + public int compareTo(@NonNull Jid o) { + throw new AssertionError("Not implemented"); + } - @Override - public int compareTo(@NonNull Jid o) { - throw new AssertionError("Not implemented"); - } + public static Jid getNullForInvalid(Jid jid) { + if (jid instanceof InvalidJid) { + return null; + } else { + return jid; + } + } - public static Jid getNullForInvalid(Jid jid) { - if (jid instanceof InvalidJid) { - return null; - } else { - return jid; - } - } + public static boolean isValid(Jid jid) { + return !(jid != null && jid instanceof InvalidJid); + } - public static boolean isValid(Jid jid) { - return !(jid != null && jid instanceof InvalidJid); - } + public static boolean invalid(final Jid jid) { + return jid instanceof InvalidJid; + } - public static boolean hasValidFrom(AbstractStanza stanza) { - final String from = stanza.getAttribute("from"); - if (from == null) { - return false; - } - try { - Jid.ofEscaped(from); - return true; - } catch (IllegalArgumentException e) { - return false; - } - } + public static boolean hasValidFrom(AbstractStanza stanza) { + final String from = stanza.getAttribute("from"); + if (from == null) { + return false; + } + try { + Jid.ofEscaped(from); + return true; + } catch (IllegalArgumentException e) { + return false; + } + } } diff --git a/src/main/java/im/conversations/android/xml/TagWriter.java b/src/main/java/im/conversations/android/xml/TagWriter.java new file mode 100644 index 000000000..221805eb7 --- /dev/null +++ b/src/main/java/im/conversations/android/xml/TagWriter.java @@ -0,0 +1,126 @@ +package im.conversations.android.xml; + +import android.util.Log; +import eu.siacs.conversations.Config; +import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xml.Tag; +import im.conversations.android.xmpp.model.StreamElement; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +public class TagWriter { + + private OutputStreamWriter outputStream; + private boolean finished = false; + private final LinkedBlockingQueue writeQueue = new LinkedBlockingQueue<>(); + private CountDownLatch stanzaWriterCountDownLatch = null; + + private final Thread asyncStanzaWriter = + new Thread() { + + @Override + public void run() { + stanzaWriterCountDownLatch = new CountDownLatch(1); + while (!isInterrupted()) { + if (finished && writeQueue.size() == 0) { + break; + } + try { + final StreamElement output = writeQueue.take(); + outputStream.write(output.toString()); + if (writeQueue.size() == 0) { + outputStream.flush(); + } + } catch (Exception e) { + break; + } + } + stanzaWriterCountDownLatch.countDown(); + } + }; + + public TagWriter() {} + + public synchronized void setOutputStream(OutputStream out) throws IOException { + if (out == null) { + throw new IOException(); + } + this.outputStream = new OutputStreamWriter(out); + } + + public void beginDocument() throws IOException { + if (outputStream == null) { + throw new IOException("output stream was null"); + } + outputStream.write(""); + } + + public void writeTag(final Tag tag) throws IOException { + writeTag(tag, true); + } + + public synchronized void writeTag(final Tag tag, final boolean flush) throws IOException { + if (outputStream == null) { + throw new IOException("output stream was null"); + } + outputStream.write(tag.toString()); + if (flush) { + outputStream.flush(); + } + } + + public synchronized void writeElement(Element element) throws IOException { + if (outputStream == null) { + throw new IOException("output stream was null"); + } + outputStream.write(element.toString()); + outputStream.flush(); + } + + public void writeStanzaAsync(StreamElement stanza) { + if (finished) { + Log.d(Config.LOGTAG, "attempting to write stanza to finished TagWriter"); + } else { + if (!asyncStanzaWriter.isAlive()) { + try { + asyncStanzaWriter.start(); + } catch (IllegalThreadStateException e) { + // already started + } + } + writeQueue.add(stanza); + } + } + + public void finish() { + this.finished = true; + } + + public boolean await(long timeout, TimeUnit timeunit) throws InterruptedException { + if (stanzaWriterCountDownLatch == null) { + return true; + } else { + return stanzaWriterCountDownLatch.await(timeout, timeunit); + } + } + + public boolean isActive() { + return outputStream != null; + } + + public synchronized void forceClose() { + asyncStanzaWriter.interrupt(); + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + // ignoring + } + } + outputStream = null; + } +} diff --git a/src/main/java/im/conversations/android/xmpp/XmppConnection.java b/src/main/java/im/conversations/android/xmpp/XmppConnection.java index 14f9523be..458ee1020 100644 --- a/src/main/java/im/conversations/android/xmpp/XmppConnection.java +++ b/src/main/java/im/conversations/android/xmpp/XmppConnection.java @@ -28,7 +28,6 @@ import eu.siacs.conversations.crypto.XmppDomainVerifier; import eu.siacs.conversations.http.HttpConnectionManager; import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.services.MemorizingTrustManager; -import eu.siacs.conversations.services.MessageArchiveService; import eu.siacs.conversations.services.NotificationService; import eu.siacs.conversations.ui.util.PendingItem; import eu.siacs.conversations.utils.Patterns; @@ -41,22 +40,12 @@ import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xml.LocalizedContent; import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xml.Tag; -import eu.siacs.conversations.xml.TagWriter; import eu.siacs.conversations.xml.XmlReader; +import eu.siacs.conversations.xmpp.InvalidJid; import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.bind.Bind2; import eu.siacs.conversations.xmpp.forms.Data; -import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; -import eu.siacs.conversations.xmpp.stanzas.AbstractAcknowledgeableStanza; -import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; -import eu.siacs.conversations.xmpp.stanzas.IqPacket; -import eu.siacs.conversations.xmpp.stanzas.MessagePacket; -import eu.siacs.conversations.xmpp.stanzas.PresencePacket; -import eu.siacs.conversations.xmpp.stanzas.csi.ActivePacket; -import eu.siacs.conversations.xmpp.stanzas.csi.InactivePacket; -import eu.siacs.conversations.xmpp.stanzas.streammgmt.AckPacket; import eu.siacs.conversations.xmpp.stanzas.streammgmt.EnablePacket; -import eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket; import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket; import im.conversations.android.IDs; import im.conversations.android.database.ConversationsDatabase; @@ -64,12 +53,24 @@ import im.conversations.android.database.CredentialStore; import im.conversations.android.database.model.Account; import im.conversations.android.database.model.Connection; import im.conversations.android.database.model.Credential; +import im.conversations.android.xml.TagWriter; import im.conversations.android.xmpp.manager.AbstractManager; import im.conversations.android.xmpp.manager.DiscoManager; +import im.conversations.android.xmpp.model.StreamElement; +import im.conversations.android.xmpp.model.csi.Active; +import im.conversations.android.xmpp.model.csi.Inactive; +import im.conversations.android.xmpp.model.register.Register; +import im.conversations.android.xmpp.model.sm.Ack; +import im.conversations.android.xmpp.model.sm.Enable; +import im.conversations.android.xmpp.model.sm.Request; +import im.conversations.android.xmpp.model.sm.Resume; +import im.conversations.android.xmpp.model.stanza.IQ; +import im.conversations.android.xmpp.model.stanza.Message; +import im.conversations.android.xmpp.model.stanza.Presence; +import im.conversations.android.xmpp.model.stanza.Stanza; import im.conversations.android.xmpp.model.streams.Features; import im.conversations.android.xmpp.processor.BindProcessor; import im.conversations.android.xmpp.processor.IqProcessor; -import im.conversations.android.xmpp.processor.JingleProcessor; import im.conversations.android.xmpp.processor.MessageAcknowledgeProcessor; import im.conversations.android.xmpp.processor.MessageProcessor; import im.conversations.android.xmpp.processor.PresenceProcessor; @@ -93,7 +94,6 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; @@ -117,14 +117,9 @@ import org.xmlpull.v1.XmlPullParserException; public class XmppConnection implements Runnable { - private static final int PACKET_IQ = 0; - private static final int PACKET_MESSAGE = 1; - private static final int PACKET_PRESENCE = 2; protected final Account account; - private final HashMap commands = new HashMap<>(); - private final SparseArray mStanzaQueue = new SparseArray<>(); - private final Hashtable>> packetCallbacks = - new Hashtable<>(); + private final SparseArray mStanzaQueue = new SparseArray<>(); + private final Hashtable>> packetCallbacks = new Hashtable<>(); private final Context context; private Socket socket; private XmlReader tagReader; @@ -149,19 +144,17 @@ public class XmppConnection implements Runnable { private long lastPingSent = 0; private long lastConnect = 0; private long lastSessionStarted = 0; - private boolean isMamPreferenceAlways = false; private final AtomicBoolean mWaitingForSmCatchup = new AtomicBoolean(false); private final AtomicInteger mSmCatchupMessageCounter = new AtomicInteger(0); private int attempt = 0; - private final Consumer presencePacketConsumer; - private final Consumer jinglePacketConsumer; - private final Consumer iqPacketConsumer; - private final Consumer messagePacketConsumer; + private final Consumer presencePacketConsumer; + private final Consumer iqPacketConsumer; + private final Consumer messagePacketConsumer; private final BiFunction messageAcknowledgeProcessor; private final Consumer bindConsumer; private final ClassToInstanceMap managers; private Consumer statusListener = null; - private PendingItem> connectedFuture = new PendingItem<>(); + private final PendingItem> connectedFuture = new PendingItem<>(); private SaslMechanism saslMechanism; private HashedToken.Mechanism hashTokenRequest; private HttpUrl redirectionUrl = null; @@ -181,7 +174,6 @@ public class XmppConnection implements Runnable { this.messagePacketConsumer = new MessageProcessor(context, this); this.presencePacketConsumer = new PresenceProcessor(context, this); this.iqPacketConsumer = new IqProcessor(context, this); - this.jinglePacketConsumer = new JingleProcessor(context, this); this.messageAcknowledgeProcessor = new MessageAcknowledgeProcessor(context, this); this.bindConsumer = new BindProcessor(context, this); this.managers = Managers.initialize(context, this); @@ -262,12 +254,6 @@ public class XmppConnection implements Runnable { } } - public Jid getJidForCommand(final String node) { - synchronized (this.commands) { - return this.commands.get(node); - } - } - public void prepareNewConnection() { this.lastConnect = SystemClock.elapsedRealtime(); this.lastPingSent = SystemClock.elapsedRealtime(); @@ -616,7 +602,7 @@ public class XmppConnection implements Runnable { Config.LOGTAG, account.address + ": acknowledging stanza #" + this.stanzasReceived); } - final AckPacket ack = new AckPacket(this.stanzasReceived); + final Ack ack = new Ack(this.stanzasReceived); tagWriter.writeStanzaAsync(ack); } else if (nextTag.isStart("a")) { synchronized (NotificationService.CATCHUP_LOCK) { @@ -849,7 +835,7 @@ public class XmppConnection implements Runnable { private void resetOutboundStanzaQueue() { synchronized (this.mStanzaQueue) { - final List intermediateStanzas = new ArrayList<>(); + final List intermediateStanzas = new ArrayList<>(); if (Config.EXTENDED_SM_LOGGING) { Log.d( Config.LOGTAG, @@ -858,7 +844,7 @@ public class XmppConnection implements Runnable { + this.stanzasSentBeforeAuthentication); } for (int i = this.stanzasSentBeforeAuthentication + 1; i <= this.stanzasSent; ++i) { - final AbstractAcknowledgeableStanza stanza = this.mStanzaQueue.get(i); + final Stanza stanza = this.mStanzaQueue.get(i); if (stanza != null) { intermediateStanzas.add(stanza); } @@ -966,14 +952,14 @@ public class XmppConnection implements Runnable { this.streamId = streamId; this.stanzasReceived = 0; this.inSmacksSession = true; - final RequestPacket r = new RequestPacket(); + final Request r = new Request(); tagWriter.writeStanzaAsync(r); } private void processResumed(final Element resumed) throws StateChangingException { this.inSmacksSession = true; this.isBound = true; - this.tagWriter.writeStanzaAsync(new RequestPacket()); + this.tagWriter.writeStanzaAsync(new Request()); lastPacketReceived = SystemClock.elapsedRealtime(); final Optional h = resumed.getOptionalIntAttribute("h"); final int serverCount; @@ -983,7 +969,7 @@ public class XmppConnection implements Runnable { resetStreamId(); throw new StateChangingException(ConnectionState.INCOMPATIBLE_SERVER); } - final ArrayList failedStanzas = new ArrayList<>(); + final ArrayList failedStanzas = new ArrayList<>(); final boolean acknowledgedMessages; synchronized (this.mStanzaQueue) { if (serverCount < stanzasSent) { @@ -999,9 +985,9 @@ public class XmppConnection implements Runnable { mStanzaQueue.clear(); } Log.d(Config.LOGTAG, account.address + ": resending " + failedStanzas.size() + " stanzas"); - for (final AbstractAcknowledgeableStanza packet : failedStanzas) { - if (packet instanceof MessagePacket) { - MessagePacket message = (MessagePacket) packet; + for (final Stanza packet : failedStanzas) { + if (packet instanceof Message) { + Message message = (Message) packet; // TODO set ack = false in message table // context.markMessage(account, message.getTo().asBareJid(), message.getId(), // Message.STATUS_UNSEND); @@ -1058,9 +1044,9 @@ public class XmppConnection implements Runnable { + ": server acknowledged stanza #" + mStanzaQueue.keyAt(i)); } - final AbstractAcknowledgeableStanza stanza = mStanzaQueue.valueAt(i); - if (stanza instanceof MessagePacket && messageAcknowledgeProcessor != null) { - final MessagePacket packet = (MessagePacket) stanza; + final Stanza stanza = mStanzaQueue.valueAt(i); + if (stanza instanceof Message) { + final Message packet = (Message) stanza; final String id = packet.getId(); final Jid to = packet.getTo(); if (id != null && to != null) { @@ -1074,36 +1060,9 @@ public class XmppConnection implements Runnable { return acknowledgedMessages; } - private @NonNull Element processPacket(final Tag currentTag, final int packetType) + private S processStanza(final Tag currentTag, final Class clazz) throws IOException { - final Element element; - switch (packetType) { - case PACKET_IQ: - element = new IqPacket(); - break; - case PACKET_MESSAGE: - element = new MessagePacket(); - break; - case PACKET_PRESENCE: - element = new PresencePacket(); - break; - default: - throw new AssertionError("Should never encounter invalid type"); - } - element.setAttributes(currentTag.getAttributes()); - Tag nextTag = tagReader.readTag(); - if (nextTag == null) { - throw new IOException("interrupted mid tag"); - } - while (!nextTag.isEnd(element.getName())) { - if (!nextTag.isNo()) { - element.addChild(tagReader.readElement(nextTag)); - } - nextTag = tagReader.readTag(); - if (nextTag == null) { - throw new IOException("interrupted mid tag"); - } - } + final S stanza = tagReader.readElement(currentTag, clazz); if (stanzasReceived == Integer.MAX_VALUE) { resetStreamId(); throw new IOException("time to restart the session. cant handle >2 billion pcks"); @@ -1115,103 +1074,93 @@ public class XmppConnection implements Runnable { Config.LOGTAG, account.address + ": not counting stanza(" - + element.getClass().getSimpleName() + + stanza.getClass().getSimpleName() + "). Not in smacks session."); } lastPacketReceived = SystemClock.elapsedRealtime(); - if (element instanceof IqPacket - && (((IqPacket) element).getType() == IqPacket.TYPE.SET) - && element.hasChild("jingle", Namespace.JINGLE)) { - return JinglePacket.upgrade((IqPacket) element); - } else { - return element; + if (InvalidJid.invalid(stanza.getTo()) || InvalidJid.invalid(stanza.getFrom())) { + Log.e( + Config.LOGTAG, + "encountered invalid stanza from " + + stanza.getFrom() + + " to " + + stanza.getTo()); } + return stanza; } private void processIq(final Tag currentTag) throws IOException { - final IqPacket packet = (IqPacket) processPacket(currentTag, PACKET_IQ); - if (!packet.valid()) { + final IQ packet = processStanza(currentTag, IQ.class); + if (InvalidJid.invalid(packet.getTo()) || InvalidJid.invalid(packet.getFrom())) { Log.e( Config.LOGTAG, - "encountered invalid iq from='" - + packet.getFrom() - + "' to='" - + packet.getTo() - + "'"); + "encountered invalid IQ from " + packet.getFrom() + " to " + packet.getTo()); return; } - if (packet instanceof JinglePacket) { - this.jinglePacketConsumer.accept((JinglePacket) packet); - } else { - final Consumer callback; - synchronized (this.packetCallbacks) { - final Pair> packetCallbackDuple = - packetCallbacks.get(packet.getId()); - if (packetCallbackDuple != null) { - // Packets to the server should have responses from the server - if (toServer(packetCallbackDuple.first)) { - if (fromServer(packet)) { - callback = packetCallbackDuple.second; - packetCallbacks.remove(packet.getId()); - } else { - callback = null; - Log.e(Config.LOGTAG, account.address + ": ignoring spoofed iq packet"); - } + final Consumer callback; + synchronized (this.packetCallbacks) { + final Pair> packetCallbackDuple = packetCallbacks.get(packet.getId()); + if (packetCallbackDuple != null) { + // Packets to the server should have responses from the server + if (toServer(packetCallbackDuple.first)) { + if (fromServer(packet)) { + callback = packetCallbackDuple.second; + packetCallbacks.remove(packet.getId()); } else { - if (packet.getFrom() != null - && packet.getFrom().equals(packetCallbackDuple.first.getTo())) { - callback = packetCallbackDuple.second; - packetCallbacks.remove(packet.getId()); - } else { - callback = null; - Log.e(Config.LOGTAG, account.address + ": ignoring spoofed iq packet"); - } + callback = null; + Log.e(Config.LOGTAG, account.address + ": ignoring spoofed iq packet"); } - } else if (packet.getType() == IqPacket.TYPE.GET - || packet.getType() == IqPacket.TYPE.SET) { - callback = this.iqPacketConsumer; } else { - callback = null; + if (packet.getFrom() != null + && packet.getFrom().equals(packetCallbackDuple.first.getTo())) { + callback = packetCallbackDuple.second; + packetCallbacks.remove(packet.getId()); + } else { + callback = null; + Log.e(Config.LOGTAG, account.address + ": ignoring spoofed iq packet"); + } } + } else if (packet.getType() == IQ.Type.GET || packet.getType() == IQ.Type.SET) { + callback = this.iqPacketConsumer; + } else { + callback = null; } - if (callback != null) { - try { - callback.accept(packet); - } catch (StateChangingError error) { - throw new StateChangingException(error.state); - } + } + if (callback != null) { + try { + callback.accept(packet); + } catch (StateChangingError error) { + throw new StateChangingException(error.state); } } } private void processMessage(final Tag currentTag) throws IOException { - final MessagePacket packet = (MessagePacket) processPacket(currentTag, PACKET_MESSAGE); - if (!packet.valid()) { + final var message = processStanza(currentTag, Message.class); + if (InvalidJid.invalid(message.getTo()) || InvalidJid.invalid(message.getFrom())) { Log.e( Config.LOGTAG, - "encountered invalid message from='" - + packet.getFrom() - + "' to='" - + packet.getTo() - + "'"); + "encountered invalid Message from " + + message.getFrom() + + " to " + + message.getTo()); return; } - this.messagePacketConsumer.accept(packet); + this.messagePacketConsumer.accept(message); } private void processPresence(final Tag currentTag) throws IOException { - PresencePacket packet = (PresencePacket) processPacket(currentTag, PACKET_PRESENCE); - if (!packet.valid()) { + final var presence = processStanza(currentTag, Presence.class); + if (InvalidJid.invalid(presence.getTo()) || InvalidJid.invalid(presence.getFrom())) { Log.e( Config.LOGTAG, - "encountered invalid presence from='" - + packet.getFrom() - + "' to='" - + packet.getTo() - + "'"); + "encountered invalid Presence from " + + presence.getFrom() + + " to " + + presence.getTo()); return; } - this.presencePacketConsumer.accept(packet); + this.presencePacketConsumer.accept(presence); } private void sendStartTLS() throws IOException { @@ -1350,7 +1299,7 @@ public class XmppConnection implements Runnable { Config.LOGTAG, account.address + ": resuming after stanza #" + stanzasReceived); } - final ResumePacket resume = new ResumePacket(this.streamId, stanzasReceived); + final Resume resume = new Resume(this.streamId, stanzasReceived); this.mSmCatchupMessageCounter.set(0); this.mWaitingForSmCatchup.set(true); this.tagWriter.writeStanzaAsync(resume); @@ -1563,15 +1512,15 @@ public class XmppConnection implements Runnable { sendRegistryRequest(); return; } - final IqPacket preAuthRequest = new IqPacket(IqPacket.TYPE.SET); + final IQ preAuthRequest = new IQ(IQ.Type.SET); preAuthRequest.addChild("preauth", Namespace.PARS).setAttribute("token", preAuth); sendUnmodifiedIqPacket( preAuthRequest, (response) -> { - if (response.getType() == IqPacket.TYPE.RESULT) { + if (response.getType() == IQ.Type.RESULT) { sendRegistryRequest(); } else { - final String error = response.getErrorCondition(); + final String error = ""; // response.getErrorCondition(); Log.d(Config.LOGTAG, account.address + ": failed to pre auth. " + error); throw new StateChangingError(ConnectionState.REGISTRATION_INVALID_TOKEN); } @@ -1580,32 +1529,39 @@ public class XmppConnection implements Runnable { } private void sendRegistryRequest() { - final IqPacket register = new IqPacket(IqPacket.TYPE.GET); - register.query(Namespace.REGISTER); - register.setTo(account.address.getDomain()); + final IQ retrieveRegistration = new IQ(IQ.Type.GET); + retrieveRegistration.addExtension(new Register()); + retrieveRegistration.setTo(account.address.getDomain()); sendUnmodifiedIqPacket( - register, + retrieveRegistration, (packet) -> { - if (packet.getType() == IqPacket.TYPE.TIMEOUT) { + if (packet.getType() == IQ.Type.TIMEOUT) { return; } - if (packet.getType() == IqPacket.TYPE.ERROR) { + if (packet.getType() == IQ.Type.ERROR) { + throw new StateChangingError(ConnectionState.REGISTRATION_FAILED); + } + final Register query = packet.getExtension(Register.class); + if (query == null) { throw new StateChangingError(ConnectionState.REGISTRATION_FAILED); } - final Element query = packet.query(Namespace.REGISTER); if (query.hasChild("username") && (query.hasChild("password"))) { final Credential credential = CredentialStore.getInstance(context).get(account); - final IqPacket register1 = new IqPacket(IqPacket.TYPE.SET); + final IQ registrationRequest = new IQ(IQ.Type.SET); final Element username = new Element("username") .setContent(account.address.getEscapedLocal()); final Element password = new Element("password").setContent(credential.password); - register1.query(Namespace.REGISTER).addChild(username); - register1.query().addChild(password); - register1.setFrom(account.address); - sendUnmodifiedIqPacket(register1, this::handleRegistrationResponse, true); + + final var register = registrationRequest.addExtension(new Register()); + + register.addChild(username); + register.addChild(password); + registrationRequest.setFrom(account.address); + sendUnmodifiedIqPacket( + registrationRequest, this::handleRegistrationResponse, true); } else if (query.hasChild("x", Namespace.DATA)) { final Data data = Data.parse(query.findChild("x", Namespace.DATA)); final Element blob = query.findChild("data", "urn:xmpp:bob"); @@ -1667,8 +1623,8 @@ public class XmppConnection implements Runnable { true); } - private void handleRegistrationResponse(final IqPacket packet) { - if (packet.getType() == IqPacket.TYPE.RESULT) { + private void handleRegistrationResponse(final IQ packet) { + if (packet.getType() == IQ.Type.RESULT) { ConversationsDatabase.getInstance(context) .accountDao() .setPendingRegistration(account.id, false); @@ -1716,9 +1672,6 @@ public class XmppConnection implements Runnable { this.stanzasSent = 0; mStanzaQueue.clear(); this.redirectionUrl = null; - synchronized (this.commands) { - this.commands.clear(); - } this.saslMechanism = null; } @@ -1736,16 +1689,16 @@ public class XmppConnection implements Runnable { } else { resource = this.createNewResource(IDs.tiny(account.randomSeed)); } - final IqPacket iq = new IqPacket(IqPacket.TYPE.SET); + final IQ iq = new IQ(IQ.Type.SET); iq.addChild("bind", Namespace.BIND).addChild("resource").setContent(resource); this.sendUnmodifiedIqPacket( iq, (packet) -> { - if (packet.getType() == IqPacket.TYPE.TIMEOUT) { + if (packet.getType() == IQ.Type.TIMEOUT) { return; } final Element bind = packet.findChild("bind"); - if (bind != null && packet.getType() == IqPacket.TYPE.RESULT) { + if (bind != null && packet.getType() == IQ.Type.RESULT) { isBound = true; final String jid = bind.findChildContent("jid"); if (Strings.isNullOrEmpty(jid)) { @@ -1789,7 +1742,7 @@ public class XmppConnection implements Runnable { + packet); } final Element error = packet.findChild("error"); - if (packet.getType() == IqPacket.TYPE.ERROR + if (packet.getType() == IQ.Type.ERROR && error != null && error.hasChild("conflict")) { final String alternativeResource = createNewResource(IDs.tiny()); @@ -1813,8 +1766,8 @@ public class XmppConnection implements Runnable { } private void clearIqCallbacks() { - final IqPacket failurePacket = new IqPacket(IqPacket.TYPE.TIMEOUT); - final ArrayList> callbacks = new ArrayList<>(); + final IQ failurePacket = new IQ(IQ.Type.TIMEOUT); + final ArrayList> callbacks = new ArrayList<>(); synchronized (this.packetCallbacks) { if (this.packetCallbacks.size() == 0) { return; @@ -1825,15 +1778,15 @@ public class XmppConnection implements Runnable { + ": clearing " + this.packetCallbacks.size() + " iq callbacks"); - final Iterator>> iterator = + final Iterator>> iterator = this.packetCallbacks.values().iterator(); while (iterator.hasNext()) { - Pair> entry = iterator.next(); + Pair> entry = iterator.next(); callbacks.add(entry.second); iterator.remove(); } } - for (final Consumer callback : callbacks) { + for (final Consumer callback : callbacks) { try { callback.accept(failurePacket); } catch (StateChangingError error) { @@ -1856,15 +1809,15 @@ public class XmppConnection implements Runnable { private void sendStartSession() { Log.d(Config.LOGTAG, account.address + ": sending legacy session to outdated server"); - final IqPacket startSession = new IqPacket(IqPacket.TYPE.SET); + final IQ startSession = new IQ(IQ.Type.SET); startSession.addChild("session", "urn:ietf:params:xml:ns:xmpp-session"); this.sendUnmodifiedIqPacket( startSession, (packet) -> { - if (packet.getType() == IqPacket.TYPE.RESULT) { + if (packet.getType() == IQ.Type.RESULT) { enableStreamManagement(); sendPostBindInitialization(false); - } else if (packet.getType() != IqPacket.TYPE.TIMEOUT) { + } else if (packet.getType() != IQ.Type.TIMEOUT) { throw new StateChangingError(ConnectionState.SESSION_FAILURE); } }, @@ -1876,7 +1829,7 @@ public class XmppConnection implements Runnable { final boolean streamManagement = this.streamFeatures.streamManagement(); if (streamManagement) { synchronized (this.mStanzaQueue) { - final EnablePacket enable = new EnablePacket(); + final Enable enable = new Enable(); tagWriter.writeStanzaAsync(enable); stanzasSent = 0; mStanzaQueue.clear(); @@ -1936,60 +1889,6 @@ public class XmppConnection implements Runnable { return this.connectionState; } - private void discoverMamPreferences() { - IqPacket request = new IqPacket(IqPacket.TYPE.GET); - request.addChild("prefs", MessageArchiveService.Version.MAM_2.namespace); - sendIqPacket( - request, - (response) -> { - if (response.getType() == IqPacket.TYPE.RESULT) { - Element prefs = - response.findChild( - "prefs", MessageArchiveService.Version.MAM_2.namespace); - isMamPreferenceAlways = - "always" - .equals( - prefs == null - ? null - : prefs.getAttribute("default")); - } - }); - } - - private void discoverCommands() { - final IqPacket request = new IqPacket(IqPacket.TYPE.GET); - request.setTo(account.address.getDomain()); - request.addChild("query", Namespace.DISCO_ITEMS).setAttribute("node", Namespace.COMMANDS); - sendIqPacket( - request, - (response) -> { - if (response.getType() == IqPacket.TYPE.RESULT) { - final Element query = response.findChild("query", Namespace.DISCO_ITEMS); - if (query == null) { - return; - } - final HashMap commands = new HashMap<>(); - for (final Element child : query.getChildren()) { - if ("item".equals(child.getName())) { - final String node = child.getAttribute("node"); - final Jid jid = child.getAttributeAsJid("jid"); - if (node != null && jid != null) { - commands.put(node, jid); - } - } - } - synchronized (this.commands) { - this.commands.clear(); - this.commands.putAll(commands); - } - } - }); - } - - public boolean isMamPreferenceAlways() { - return isMamPreferenceAlways; - } - private void finalizeBind() { this.enableAdvancedStreamFeatures(); this.bindConsumer.accept(this.connectionAddress); @@ -2005,12 +1904,12 @@ public class XmppConnection implements Runnable { } private void sendEnableCarbons() { - final IqPacket iq = new IqPacket(IqPacket.TYPE.SET); + final IQ iq = new IQ(IQ.Type.SET); iq.addChild("enable", Namespace.CARBONS); this.sendIqPacket( iq, (packet) -> { - if (packet.getType() == IqPacket.TYPE.RESULT) { + if (packet.getType() == IQ.Type.RESULT) { Log.d(Config.LOGTAG, account.address + ": successfully enabled carbons"); this.carbonsEnabled = true; } else { @@ -2055,9 +1954,9 @@ public class XmppConnection implements Runnable { private void failPendingMessages(final String error) { synchronized (this.mStanzaQueue) { for (int i = 0; i < mStanzaQueue.size(); ++i) { - final AbstractAcknowledgeableStanza stanza = mStanzaQueue.valueAt(i); - if (stanza instanceof MessagePacket) { - final MessagePacket packet = (MessagePacket) stanza; + final Stanza stanza = mStanzaQueue.valueAt(i); + if (stanza instanceof Message) { + final Message packet = (Message) stanza; final String id = packet.getId(); final Jid to = packet.getTo(); // TODO set ack=true but add error? @@ -2123,15 +2022,15 @@ public class XmppConnection implements Runnable { return String.format("%s.%s", context.getString(R.string.app_name), postfixId); } - public ListenableFuture sendIqPacket(final IqPacket packet) { - final SettableFuture future = SettableFuture.create(); + public ListenableFuture sendIqPacket(final IQ packet) { + final SettableFuture future = SettableFuture.create(); sendIqPacket( packet, result -> { final var type = result.getType(); - if (type == IqPacket.TYPE.RESULT) { + if (type == IQ.Type.RESULT) { future.set(result); - } else if (type == IqPacket.TYPE.TIMEOUT) { + } else if (type == IQ.Type.TIMEOUT) { future.setException(new TimeoutException()); } else { // TODO some sort of IqErrorException @@ -2141,15 +2040,15 @@ public class XmppConnection implements Runnable { return future; } - public String sendIqPacket(final IqPacket packet, final Consumer callback) { + public String sendIqPacket(final IQ packet, final Consumer callback) { packet.setFrom(account.address); return this.sendUnmodifiedIqPacket(packet, callback, false); } public synchronized String sendUnmodifiedIqPacket( - final IqPacket packet, final Consumer callback, boolean force) { + final IQ packet, final Consumer callback, boolean force) { if (Strings.isNullOrEmpty(packet.getId())) { - packet.setAttribute("id", IDs.medium()); + packet.setId(IDs.medium()); } if (callback != null) { synchronized (this.packetCallbacks) { @@ -2160,19 +2059,19 @@ public class XmppConnection implements Runnable { return packet.getId(); } - public void sendMessagePacket(final MessagePacket packet) { + public void sendMessagePacket(final Message packet) { this.sendPacket(packet); } - public void sendPresencePacket(final PresencePacket packet) { + public void sendPresencePacket(final Presence packet) { this.sendPacket(packet); } - private synchronized void sendPacket(final AbstractStanza packet) { + private synchronized void sendPacket(final StreamElement packet) { sendPacket(packet, false); } - private synchronized void sendPacket(final AbstractStanza packet, final boolean force) { + private synchronized void sendPacket(final StreamElement packet, final boolean force) { if (stanzasSent == Integer.MAX_VALUE) { resetStreamId(); disconnect(true); @@ -2188,8 +2087,8 @@ public class XmppConnection implements Runnable { + " do not write stanza to unbound stream " + packet.toString()); } - if (packet instanceof AbstractAcknowledgeableStanza) { - AbstractAcknowledgeableStanza stanza = (AbstractAcknowledgeableStanza) packet; + if (packet instanceof Stanza) { + final Stanza stanza = (Stanza) packet; if (this.mStanzaQueue.size() != 0) { int currentHighestKey = this.mStanzaQueue.keyAt(this.mStanzaQueue.size() - 1); @@ -2209,7 +2108,7 @@ public class XmppConnection implements Runnable { + stanzasSent); } this.mStanzaQueue.append(stanzasSent, stanza); - if (stanza instanceof MessagePacket && stanza.getId() != null && inSmacksSession) { + if (stanza instanceof Message && stanza.getId() != null && inSmacksSession) { if (Config.EXTENDED_SM_LOGGING) { Log.d( Config.LOGTAG, @@ -2217,7 +2116,7 @@ public class XmppConnection implements Runnable { + ": requesting ack for message stanza #" + stanzasSent); } - tagWriter.writeStanzaAsync(new RequestPacket()); + tagWriter.writeStanzaAsync(new Request()); } } } @@ -2225,7 +2124,7 @@ public class XmppConnection implements Runnable { public void sendPing() { if (!r()) { - final IqPacket iq = new IqPacket(IqPacket.TYPE.GET); + final IQ iq = new IQ(IQ.Type.GET); iq.setFrom(account.address); iq.addChild("ping", Namespace.PING); this.sendIqPacket(iq, null); @@ -2308,7 +2207,7 @@ public class XmppConnection implements Runnable { public boolean r() { if (this.inSmacksSession) { - this.tagWriter.writeStanzaAsync(new RequestPacket()); + this.tagWriter.writeStanzaAsync(new Request()); return true; } else { return false; @@ -2346,11 +2245,11 @@ public class XmppConnection implements Runnable { } public void sendActive() { - this.sendPacket(new ActivePacket()); + this.sendPacket(new Active()); } public void sendInactive() { - this.sendPacket(new InactivePacket()); + this.sendPacket(new Inactive()); } public void resetAttemptCount(boolean resetConnectTime) { @@ -2360,7 +2259,7 @@ public class XmppConnection implements Runnable { } } - public boolean fromServer(final AbstractStanza stanza) { + public boolean fromServer(final Stanza stanza) { final Jid from = stanza.getFrom(); return from == null || from.equals(connectionAddress.getDomain()) @@ -2368,7 +2267,7 @@ public class XmppConnection implements Runnable { || from.equals(connectionAddress); } - public boolean toServer(final AbstractStanza stanza) { + public boolean toServer(final Stanza stanza) { final Jid to = stanza.getTo(); return to == null || to.equals(connectionAddress.getDomain()) @@ -2376,7 +2275,7 @@ public class XmppConnection implements Runnable { || to.equals(connectionAddress); } - public boolean fromAccount(final AbstractStanza stanza) { + public boolean fromAccount(final Stanza stanza) { final Jid from = stanza.getFrom(); return from != null && from.asBareJid().equals(connectionAddress.asBareJid()); } diff --git a/src/main/java/im/conversations/android/xmpp/manager/BlockingManager.java b/src/main/java/im/conversations/android/xmpp/manager/BlockingManager.java index dcf30ef42..db6ea55cf 100644 --- a/src/main/java/im/conversations/android/xmpp/manager/BlockingManager.java +++ b/src/main/java/im/conversations/android/xmpp/manager/BlockingManager.java @@ -2,11 +2,11 @@ package im.conversations.android.xmpp.manager; import android.content.Context; import com.google.common.collect.Collections2; -import eu.siacs.conversations.xmpp.stanzas.IqPacket; import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.model.blocking.Block; import im.conversations.android.xmpp.model.blocking.Blocklist; import im.conversations.android.xmpp.model.blocking.Unblock; +import im.conversations.android.xmpp.model.stanza.IQ; import java.util.Objects; public class BlockingManager extends AbstractManager { @@ -20,13 +20,13 @@ public class BlockingManager extends AbstractManager { public void handlePush(final Unblock unblock) {} public void fetch() { - final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET); + final IQ iqPacket = new IQ(IQ.Type.GET); iqPacket.addChild(new Blocklist()); connection.sendIqPacket(iqPacket, this::handleFetchResult); } - private void handleFetchResult(final IqPacket result) { - if (result.getType() != IqPacket.TYPE.RESULT) { + private void handleFetchResult(final IQ result) { + if (result.getType() != IQ.Type.RESULT) { return; } final var blocklist = result.getExtension(Blocklist.class); diff --git a/src/main/java/im/conversations/android/xmpp/manager/DiscoManager.java b/src/main/java/im/conversations/android/xmpp/manager/DiscoManager.java index c7e234af8..c2f9aa7aa 100644 --- a/src/main/java/im/conversations/android/xmpp/manager/DiscoManager.java +++ b/src/main/java/im/conversations/android/xmpp/manager/DiscoManager.java @@ -10,7 +10,6 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import eu.siacs.conversations.xmpp.Jid; -import eu.siacs.conversations.xmpp.stanzas.IqPacket; import im.conversations.android.xmpp.Entity; import im.conversations.android.xmpp.EntityCapabilities; import im.conversations.android.xmpp.EntityCapabilities2; @@ -18,6 +17,7 @@ import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.model.disco.info.InfoQuery; import im.conversations.android.xmpp.model.disco.items.Item; import im.conversations.android.xmpp.model.disco.items.ItemsQuery; +import im.conversations.android.xmpp.model.stanza.IQ; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -52,7 +52,7 @@ public class DiscoManager extends AbstractManager { @Nullable final String node, @Nullable final EntityCapabilities.Hash hash) { final var requestNode = hash != null && node != null ? hash.capabilityNode(node) : node; - final var iqRequest = new IqPacket(IqPacket.TYPE.GET); + final var iqRequest = new IQ(IQ.Type.GET); iqRequest.setTo(entity.address); final InfoQuery infoQueryRequest = iqRequest.addExtension(new InfoQuery()); if (requestNode != null) { @@ -114,7 +114,7 @@ public class DiscoManager extends AbstractManager { public ListenableFuture> items( @NonNull final Entity.DiscoItem entity, @Nullable final String node) { final var requestNode = Strings.emptyToNull(node); - final var iqPacket = new IqPacket(IqPacket.TYPE.GET); + final var iqPacket = new IQ(IQ.Type.GET); iqPacket.setTo(entity.address); final ItemsQuery itemsQueryRequest = iqPacket.addExtension(new ItemsQuery()); if (requestNode != null) { diff --git a/src/main/java/im/conversations/android/xmpp/manager/RosterManager.java b/src/main/java/im/conversations/android/xmpp/manager/RosterManager.java index dd3389ba8..ebe4f64a4 100644 --- a/src/main/java/im/conversations/android/xmpp/manager/RosterManager.java +++ b/src/main/java/im/conversations/android/xmpp/manager/RosterManager.java @@ -5,10 +5,10 @@ import android.util.Log; import com.google.common.base.Strings; import com.google.common.collect.Collections2; import eu.siacs.conversations.Config; -import eu.siacs.conversations.xmpp.stanzas.IqPacket; import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.model.roster.Item; import im.conversations.android.xmpp.model.roster.Query; +import im.conversations.android.xmpp.model.stanza.IQ; import java.util.Objects; public class RosterManager extends AbstractManager { @@ -27,7 +27,7 @@ public class RosterManager extends AbstractManager { final var account = getAccount(); final var database = getDatabase(); final String rosterVersion = database.accountDao().getRosterVersion(account.id); - final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET); + final IQ iqPacket = new IQ(IQ.Type.GET); final Query rosterQuery = new Query(); iqPacket.addChild(rosterQuery); if (Strings.isNullOrEmpty(rosterVersion)) { @@ -39,8 +39,8 @@ public class RosterManager extends AbstractManager { connection.sendIqPacket(iqPacket, this::handleFetchResult); } - private void handleFetchResult(final IqPacket result) { - if (result.getType() != IqPacket.TYPE.RESULT) { + private void handleFetchResult(final IQ result) { + if (result.getType() != IQ.Type.RESULT) { return; } final var query = result.getExtension(Query.class); diff --git a/src/main/java/im/conversations/android/xmpp/model/StreamElement.java b/src/main/java/im/conversations/android/xmpp/model/StreamElement.java new file mode 100644 index 000000000..46f92612f --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/StreamElement.java @@ -0,0 +1,8 @@ +package im.conversations.android.xmpp.model; + +public abstract class StreamElement extends Extension { + + protected StreamElement(Class clazz) { + super(clazz); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/model/csi/Active.java b/src/main/java/im/conversations/android/xmpp/model/csi/Active.java new file mode 100644 index 000000000..21fb65bb4 --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/csi/Active.java @@ -0,0 +1,12 @@ +package im.conversations.android.xmpp.model.csi; + +import im.conversations.android.annotation.XmlElement; +import im.conversations.android.xmpp.model.StreamElement; + +@XmlElement +public class Active extends StreamElement { + + public Active() { + super(Active.class); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/model/csi/Inactive.java b/src/main/java/im/conversations/android/xmpp/model/csi/Inactive.java new file mode 100644 index 000000000..7c36b593d --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/csi/Inactive.java @@ -0,0 +1,12 @@ +package im.conversations.android.xmpp.model.csi; + +import im.conversations.android.annotation.XmlElement; +import im.conversations.android.xmpp.model.StreamElement; + +@XmlElement +public class Inactive extends StreamElement { + + public Inactive() { + super(Inactive.class); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/model/csi/package-info.java b/src/main/java/im/conversations/android/xmpp/model/csi/package-info.java new file mode 100644 index 000000000..58d26b1f1 --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/csi/package-info.java @@ -0,0 +1,5 @@ +@XmlPackage(namespace = Namespace.CSI) +package im.conversations.android.xmpp.model.csi; + +import eu.siacs.conversations.xml.Namespace; +import im.conversations.android.annotation.XmlPackage; diff --git a/src/main/java/im/conversations/android/xmpp/model/register/Register.java b/src/main/java/im/conversations/android/xmpp/model/register/Register.java new file mode 100644 index 000000000..d16d0ab72 --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/register/Register.java @@ -0,0 +1,12 @@ +package im.conversations.android.xmpp.model.register; + +import im.conversations.android.annotation.XmlElement; +import im.conversations.android.xmpp.model.Extension; + +@XmlElement(name = "query") +public class Register extends Extension { + + public Register() { + super(Register.class); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/model/register/package-info.java b/src/main/java/im/conversations/android/xmpp/model/register/package-info.java new file mode 100644 index 000000000..c7cc6db5b --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/register/package-info.java @@ -0,0 +1,5 @@ +@XmlPackage(namespace = Namespace.REGISTER) +package im.conversations.android.xmpp.model.register; + +import eu.siacs.conversations.xml.Namespace; +import im.conversations.android.annotation.XmlPackage; diff --git a/src/main/java/im/conversations/android/xmpp/model/sm/Ack.java b/src/main/java/im/conversations/android/xmpp/model/sm/Ack.java new file mode 100644 index 000000000..f980489d6 --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/sm/Ack.java @@ -0,0 +1,17 @@ +package im.conversations.android.xmpp.model.sm; + +import im.conversations.android.annotation.XmlElement; +import im.conversations.android.xmpp.model.StreamElement; + +@XmlElement(name = "a") +public class Ack extends StreamElement { + + public Ack() { + super(Ack.class); + } + + public Ack(final int sequence) { + super(Ack.class); + this.setAttribute("h", sequence); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/model/sm/Enable.java b/src/main/java/im/conversations/android/xmpp/model/sm/Enable.java new file mode 100644 index 000000000..9b80a93ba --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/sm/Enable.java @@ -0,0 +1,13 @@ +package im.conversations.android.xmpp.model.sm; + +import im.conversations.android.annotation.XmlElement; +import im.conversations.android.xmpp.model.StreamElement; + +@XmlElement +public class Enable extends StreamElement { + + public Enable() { + super(Enable.class); + this.setAttribute("resume", "true"); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/model/sm/Request.java b/src/main/java/im/conversations/android/xmpp/model/sm/Request.java new file mode 100644 index 000000000..ad1de61bc --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/sm/Request.java @@ -0,0 +1,12 @@ +package im.conversations.android.xmpp.model.sm; + +import im.conversations.android.annotation.XmlElement; +import im.conversations.android.xmpp.model.StreamElement; + +@XmlElement(name = "r") +public class Request extends StreamElement { + + public Request() { + super(Request.class); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/model/sm/Resume.java b/src/main/java/im/conversations/android/xmpp/model/sm/Resume.java new file mode 100644 index 000000000..e47b19966 --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/sm/Resume.java @@ -0,0 +1,18 @@ +package im.conversations.android.xmpp.model.sm; + +import im.conversations.android.annotation.XmlElement; +import im.conversations.android.xmpp.model.StreamElement; + +@XmlElement +public class Resume extends StreamElement { + + public Resume() { + super(Resume.class); + } + + public Resume(final String id, final int sequence) { + super(Resume.class); + this.setAttribute("previd", id); + this.setAttribute("h", sequence); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/model/sm/package-info.java b/src/main/java/im/conversations/android/xmpp/model/sm/package-info.java new file mode 100644 index 000000000..dd2e036fc --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/sm/package-info.java @@ -0,0 +1,5 @@ +@XmlPackage(namespace = Namespace.STREAM_MANAGEMENT) +package im.conversations.android.xmpp.model.sm; + +import eu.siacs.conversations.xml.Namespace; +import im.conversations.android.annotation.XmlPackage; diff --git a/src/main/java/im/conversations/android/xmpp/model/stanza/IQ.java b/src/main/java/im/conversations/android/xmpp/model/stanza/IQ.java new file mode 100644 index 000000000..18430a19f --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/stanza/IQ.java @@ -0,0 +1,32 @@ +package im.conversations.android.xmpp.model.stanza; + +import com.google.common.base.Strings; +import im.conversations.android.annotation.XmlElement; +import java.util.Locale; + +@XmlElement +public class IQ extends Stanza { + + public IQ() { + super(IQ.class); + } + + public IQ(final Type type) { + super(IQ.class); + this.setAttribute("type", type.toString().toLowerCase(Locale.ROOT)); + } + + // TODO get rid of timeout + public enum Type { + SET, + GET, + ERROR, + RESULT, + TIMEOUT + } + + public Type getType() { + return Type.valueOf( + Strings.nullToEmpty(this.getAttribute("type")).toUpperCase(Locale.ROOT)); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/model/stanza/Message.java b/src/main/java/im/conversations/android/xmpp/model/stanza/Message.java new file mode 100644 index 000000000..5340833ae --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/stanza/Message.java @@ -0,0 +1,11 @@ +package im.conversations.android.xmpp.model.stanza; + +import im.conversations.android.annotation.XmlElement; + +@XmlElement +public class Message extends Stanza { + + public Message() { + super(Message.class); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/model/stanza/Presence.java b/src/main/java/im/conversations/android/xmpp/model/stanza/Presence.java new file mode 100644 index 000000000..129660b00 --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/stanza/Presence.java @@ -0,0 +1,12 @@ +package im.conversations.android.xmpp.model.stanza; + +import im.conversations.android.annotation.XmlElement; +import im.conversations.android.xmpp.model.capabilties.EntityCapabilities; + +@XmlElement +public class Presence extends Stanza implements EntityCapabilities { + + public Presence() { + super(Presence.class); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/model/stanza/Stanza.java b/src/main/java/im/conversations/android/xmpp/model/stanza/Stanza.java new file mode 100644 index 000000000..1e2296bba --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/stanza/Stanza.java @@ -0,0 +1,36 @@ +package im.conversations.android.xmpp.model.stanza; + +import eu.siacs.conversations.xmpp.Jid; +import im.conversations.android.xmpp.model.Extension; +import im.conversations.android.xmpp.model.StreamElement; + +public abstract class Stanza extends StreamElement { + + protected Stanza(Class clazz) { + super(clazz); + } + + public Jid getTo() { + return this.getAttributeAsJid("to"); + } + + public Jid getFrom() { + return this.getAttributeAsJid("from"); + } + + public String getId() { + return this.getAttribute("id"); + } + + public void setId(final String id) { + this.setAttribute("id", id); + } + + public void setFrom(final Jid from) { + this.setAttribute("from", from); + } + + public void setTo(final Jid to) { + this.setAttribute("to", to); + } +} diff --git a/src/main/java/im/conversations/android/xmpp/model/stanza/package-info.java b/src/main/java/im/conversations/android/xmpp/model/stanza/package-info.java new file mode 100644 index 000000000..d12fe56db --- /dev/null +++ b/src/main/java/im/conversations/android/xmpp/model/stanza/package-info.java @@ -0,0 +1,5 @@ +@XmlPackage(namespace = Namespace.JABBER_CLIENT) +package im.conversations.android.xmpp.model.stanza; + +import eu.siacs.conversations.xml.Namespace; +import im.conversations.android.annotation.XmlPackage; diff --git a/src/main/java/im/conversations/android/xmpp/processor/IqProcessor.java b/src/main/java/im/conversations/android/xmpp/processor/IqProcessor.java index 2db4bd074..173e4db7c 100644 --- a/src/main/java/im/conversations/android/xmpp/processor/IqProcessor.java +++ b/src/main/java/im/conversations/android/xmpp/processor/IqProcessor.java @@ -2,40 +2,39 @@ package im.conversations.android.xmpp.processor; import android.content.Context; import com.google.common.base.Preconditions; -import eu.siacs.conversations.xmpp.stanzas.IqPacket; import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.manager.BlockingManager; import im.conversations.android.xmpp.manager.RosterManager; import im.conversations.android.xmpp.model.blocking.Block; import im.conversations.android.xmpp.model.blocking.Unblock; import im.conversations.android.xmpp.model.roster.Query; +import im.conversations.android.xmpp.model.stanza.IQ; import java.util.Arrays; import java.util.function.Consumer; -public class IqProcessor extends XmppConnection.Delegate implements Consumer { +public class IqProcessor extends XmppConnection.Delegate implements Consumer { public IqProcessor(final Context context, final XmppConnection connection) { super(context, connection); } @Override - public void accept(final IqPacket packet) { - final IqPacket.TYPE type = packet.getType(); - Preconditions.checkArgument( - Arrays.asList(IqPacket.TYPE.GET, IqPacket.TYPE.SET).contains(type)); - if (type == IqPacket.TYPE.SET + public void accept(final IQ packet) { + final IQ.Type type = packet.getType(); + Preconditions.checkArgument(Arrays.asList(IQ.Type.GET, IQ.Type.SET).contains(type)); + if (type == IQ.Type.SET && connection.fromAccount(packet) && packet.hasExtension(Query.class)) { getManager(RosterManager.class).handlePush(packet.getExtension(Query.class)); return; } - if (type == IqPacket.TYPE.SET + if (type == IQ.Type.SET && connection.fromAccount(packet) && packet.hasExtension(Block.class)) { getManager(BlockingManager.class).handlePush(packet.getExtension(Block.class)); return; } - if (type == IqPacket.TYPE.SET + if (type == IQ.Type.SET && connection.fromAccount(packet) && packet.hasExtension(Unblock.class)) { getManager(BlockingManager.class).handlePush(packet.getExtension(Unblock.class)); diff --git a/src/main/java/im/conversations/android/xmpp/processor/JingleProcessor.java b/src/main/java/im/conversations/android/xmpp/processor/JingleProcessor.java deleted file mode 100644 index 493b398bb..000000000 --- a/src/main/java/im/conversations/android/xmpp/processor/JingleProcessor.java +++ /dev/null @@ -1,14 +0,0 @@ -package im.conversations.android.xmpp.processor; - -import android.content.Context; -import eu.siacs.conversations.xmpp.jingle.stanzas.JinglePacket; -import im.conversations.android.xmpp.XmppConnection; -import java.util.function.Consumer; - -public class JingleProcessor implements Consumer { - - public JingleProcessor(final Context context, final XmppConnection connection) {} - - @Override - public void accept(JinglePacket jinglePacket) {} -} diff --git a/src/main/java/im/conversations/android/xmpp/processor/MessageProcessor.java b/src/main/java/im/conversations/android/xmpp/processor/MessageProcessor.java index 594b7d7ca..13ca6a88c 100644 --- a/src/main/java/im/conversations/android/xmpp/processor/MessageProcessor.java +++ b/src/main/java/im/conversations/android/xmpp/processor/MessageProcessor.java @@ -1,14 +1,14 @@ package im.conversations.android.xmpp.processor; import android.content.Context; -import eu.siacs.conversations.xmpp.stanzas.MessagePacket; import im.conversations.android.xmpp.XmppConnection; +import im.conversations.android.xmpp.model.stanza.Message; import java.util.function.Consumer; -public class MessageProcessor implements Consumer { +public class MessageProcessor implements Consumer { public MessageProcessor(final Context context, final XmppConnection connection) {} @Override - public void accept(final MessagePacket messagePacket) {} + public void accept(final Message messagePacket) {} } diff --git a/src/main/java/im/conversations/android/xmpp/processor/PresenceProcessor.java b/src/main/java/im/conversations/android/xmpp/processor/PresenceProcessor.java index 68143add7..62a5961b0 100644 --- a/src/main/java/im/conversations/android/xmpp/processor/PresenceProcessor.java +++ b/src/main/java/im/conversations/android/xmpp/processor/PresenceProcessor.java @@ -1,22 +1,22 @@ package im.conversations.android.xmpp.processor; import android.content.Context; -import eu.siacs.conversations.xmpp.stanzas.PresencePacket; import im.conversations.android.database.model.PresenceShow; import im.conversations.android.database.model.PresenceType; import im.conversations.android.xmpp.Entity; import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.manager.DiscoManager; +import im.conversations.android.xmpp.model.stanza.Presence; import java.util.function.Consumer; -public class PresenceProcessor extends XmppConnection.Delegate implements Consumer { +public class PresenceProcessor extends XmppConnection.Delegate implements Consumer { public PresenceProcessor(final Context context, final XmppConnection connection) { super(context, connection); } @Override - public void accept(final PresencePacket presencePacket) { + public void accept(final Presence presencePacket) { final var from = presencePacket.getFrom(); final var address = from == null ? null : from.asBareJid(); final var resource = from == null ? null : from.getResource(); @@ -38,7 +38,7 @@ public class PresenceProcessor extends XmppConnection.Delegate implements Consum fetchCapabilities(presencePacket); } - private void fetchCapabilities(final PresencePacket presencePacket) { + private void fetchCapabilities(final Presence presencePacket) { final var entity = presencePacket.getFrom(); final var nodeHash = presencePacket.getCapabilities(); if (nodeHash != null) {