create new models for IQ, Message & Presence

This commit is contained in:
Daniel Gultsch 2023-01-22 20:25:47 +01:00
parent f16603742f
commit d2794ccf32
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
27 changed files with 621 additions and 393 deletions

View file

@ -78,4 +78,5 @@ public final class Namespace {
public static final String OMEMO_DTLS_SRTP_VERIFICATION = public static final String OMEMO_DTLS_SRTP_VERIFICATION =
"http://gultsch.de/xmpp/drafts/omemo/dlts-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 UNIFIED_PUSH = "http://gultsch.de/xmpp/drafts/unified-push";
public static final String JABBER_CLIENT = "jabber:client";
} }

View file

@ -30,126 +30,128 @@
package eu.siacs.conversations.xmpp; package eu.siacs.conversations.xmpp;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import eu.siacs.conversations.xmpp.stanzas.AbstractStanza; import eu.siacs.conversations.xmpp.stanzas.AbstractStanza;
public class InvalidJid implements Jid { public class InvalidJid implements Jid {
private final String value; private final String value;
private InvalidJid(String jid) { private InvalidJid(String jid) {
this.value = jid; this.value = jid;
} }
public static Jid of(String jid, boolean fallback) { public static Jid of(String jid, boolean fallback) {
final int pos = jid.indexOf('/'); final int pos = jid.indexOf('/');
if (fallback && pos >= 0 && jid.length() >= pos + 1) { if (fallback && pos >= 0 && jid.length() >= pos + 1) {
if (jid.substring(pos+1).trim().isEmpty()) { if (jid.substring(pos + 1).trim().isEmpty()) {
return Jid.ofEscaped(jid.substring(0,pos)); return Jid.ofEscaped(jid.substring(0, pos));
} }
} }
return new InvalidJid(jid); return new InvalidJid(jid);
} }
@Override @Override
@NonNull @NonNull
public String toString() { public String toString() {
return value; return value;
} }
@Override @Override
public boolean isFullJid() { public boolean isFullJid() {
throw new AssertionError("Not implemented"); throw new AssertionError("Not implemented");
} }
@Override @Override
public boolean isBareJid() { public boolean isBareJid() {
throw new AssertionError("Not implemented"); throw new AssertionError("Not implemented");
} }
@Override @Override
public boolean isDomainJid() { public boolean isDomainJid() {
throw new AssertionError("Not implemented"); throw new AssertionError("Not implemented");
} }
@Override @Override
public Jid asBareJid() { public Jid asBareJid() {
throw new AssertionError("Not implemented"); throw new AssertionError("Not implemented");
} }
@Override
public Jid withResource(CharSequence charSequence) {
throw new AssertionError("Not implemented");
}
@Override @Override
public Jid withResource(CharSequence charSequence) { public String getLocal() {
throw new AssertionError("Not implemented"); throw new AssertionError("Not implemented");
} }
@Override @Override
public String getLocal() { public String getEscapedLocal() {
throw new AssertionError("Not implemented"); throw new AssertionError("Not implemented");
} }
@Override @Override
public String getEscapedLocal() { public Jid getDomain() {
throw new AssertionError("Not implemented"); throw new AssertionError("Not implemented");
} }
@Override @Override
public Jid getDomain() { public String getResource() {
throw new AssertionError("Not implemented"); throw new AssertionError("Not implemented");
} }
@Override @Override
public String getResource() { public String toEscapedString() {
throw new AssertionError("Not implemented"); throw new AssertionError("Not implemented");
} }
@Override @Override
public String toEscapedString() { public int length() {
throw new AssertionError("Not implemented"); return value.length();
} }
@Override @Override
public int length() { public char charAt(int index) {
return value.length(); return value.charAt(index);
} }
@Override @Override
public char charAt(int index) { public CharSequence subSequence(int start, int end) {
return value.charAt(index); return value.subSequence(start, end);
} }
@Override @Override
public CharSequence subSequence(int start, int end) { public int compareTo(@NonNull Jid o) {
return value.subSequence(start, end); throw new AssertionError("Not implemented");
} }
@Override public static Jid getNullForInvalid(Jid jid) {
public int compareTo(@NonNull Jid o) { if (jid instanceof InvalidJid) {
throw new AssertionError("Not implemented"); return null;
} } else {
return jid;
}
}
public static Jid getNullForInvalid(Jid jid) { public static boolean isValid(Jid jid) {
if (jid instanceof InvalidJid) { return !(jid != null && jid instanceof InvalidJid);
return null; }
} else {
return jid;
}
}
public static boolean isValid(Jid jid) { public static boolean invalid(final Jid jid) {
return !(jid != null && jid instanceof InvalidJid); return jid instanceof InvalidJid;
} }
public static boolean hasValidFrom(AbstractStanza stanza) { public static boolean hasValidFrom(AbstractStanza stanza) {
final String from = stanza.getAttribute("from"); final String from = stanza.getAttribute("from");
if (from == null) { if (from == null) {
return false; return false;
} }
try { try {
Jid.ofEscaped(from); Jid.ofEscaped(from);
return true; return true;
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
return false; return false;
} }
} }
} }

View file

@ -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<StreamElement> 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("<?xml version='1.0'?>");
}
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;
}
}

View file

@ -28,7 +28,6 @@ import eu.siacs.conversations.crypto.XmppDomainVerifier;
import eu.siacs.conversations.http.HttpConnectionManager; import eu.siacs.conversations.http.HttpConnectionManager;
import eu.siacs.conversations.persistance.FileBackend; import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.services.MemorizingTrustManager; import eu.siacs.conversations.services.MemorizingTrustManager;
import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.services.NotificationService; import eu.siacs.conversations.services.NotificationService;
import eu.siacs.conversations.ui.util.PendingItem; import eu.siacs.conversations.ui.util.PendingItem;
import eu.siacs.conversations.utils.Patterns; 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.LocalizedContent;
import eu.siacs.conversations.xml.Namespace; import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xml.Tag; import eu.siacs.conversations.xml.Tag;
import eu.siacs.conversations.xml.TagWriter;
import eu.siacs.conversations.xml.XmlReader; import eu.siacs.conversations.xml.XmlReader;
import eu.siacs.conversations.xmpp.InvalidJid;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.bind.Bind2; import eu.siacs.conversations.xmpp.bind.Bind2;
import eu.siacs.conversations.xmpp.forms.Data; 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.EnablePacket;
import eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket;
import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket; import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket;
import im.conversations.android.IDs; import im.conversations.android.IDs;
import im.conversations.android.database.ConversationsDatabase; 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.Account;
import im.conversations.android.database.model.Connection; import im.conversations.android.database.model.Connection;
import im.conversations.android.database.model.Credential; 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.AbstractManager;
import im.conversations.android.xmpp.manager.DiscoManager; 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.model.streams.Features;
import im.conversations.android.xmpp.processor.BindProcessor; import im.conversations.android.xmpp.processor.BindProcessor;
import im.conversations.android.xmpp.processor.IqProcessor; 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.MessageAcknowledgeProcessor;
import im.conversations.android.xmpp.processor.MessageProcessor; import im.conversations.android.xmpp.processor.MessageProcessor;
import im.conversations.android.xmpp.processor.PresenceProcessor; import im.conversations.android.xmpp.processor.PresenceProcessor;
@ -93,7 +94,6 @@ import java.security.cert.X509Certificate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -117,14 +117,9 @@ import org.xmlpull.v1.XmlPullParserException;
public class XmppConnection implements Runnable { 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; protected final Account account;
private final HashMap<String, Jid> commands = new HashMap<>(); private final SparseArray<Stanza> mStanzaQueue = new SparseArray<>();
private final SparseArray<AbstractAcknowledgeableStanza> mStanzaQueue = new SparseArray<>(); private final Hashtable<String, Pair<IQ, Consumer<IQ>>> packetCallbacks = new Hashtable<>();
private final Hashtable<String, Pair<IqPacket, Consumer<IqPacket>>> packetCallbacks =
new Hashtable<>();
private final Context context; private final Context context;
private Socket socket; private Socket socket;
private XmlReader tagReader; private XmlReader tagReader;
@ -149,19 +144,17 @@ public class XmppConnection implements Runnable {
private long lastPingSent = 0; private long lastPingSent = 0;
private long lastConnect = 0; private long lastConnect = 0;
private long lastSessionStarted = 0; private long lastSessionStarted = 0;
private boolean isMamPreferenceAlways = false;
private final AtomicBoolean mWaitingForSmCatchup = new AtomicBoolean(false); private final AtomicBoolean mWaitingForSmCatchup = new AtomicBoolean(false);
private final AtomicInteger mSmCatchupMessageCounter = new AtomicInteger(0); private final AtomicInteger mSmCatchupMessageCounter = new AtomicInteger(0);
private int attempt = 0; private int attempt = 0;
private final Consumer<PresencePacket> presencePacketConsumer; private final Consumer<Presence> presencePacketConsumer;
private final Consumer<JinglePacket> jinglePacketConsumer; private final Consumer<IQ> iqPacketConsumer;
private final Consumer<IqPacket> iqPacketConsumer; private final Consumer<Message> messagePacketConsumer;
private final Consumer<MessagePacket> messagePacketConsumer;
private final BiFunction<Jid, String, Boolean> messageAcknowledgeProcessor; private final BiFunction<Jid, String, Boolean> messageAcknowledgeProcessor;
private final Consumer<Jid> bindConsumer; private final Consumer<Jid> bindConsumer;
private final ClassToInstanceMap<AbstractManager> managers; private final ClassToInstanceMap<AbstractManager> managers;
private Consumer<XmppConnection> statusListener = null; private Consumer<XmppConnection> statusListener = null;
private PendingItem<SettableFuture<XmppConnection>> connectedFuture = new PendingItem<>(); private final PendingItem<SettableFuture<XmppConnection>> connectedFuture = new PendingItem<>();
private SaslMechanism saslMechanism; private SaslMechanism saslMechanism;
private HashedToken.Mechanism hashTokenRequest; private HashedToken.Mechanism hashTokenRequest;
private HttpUrl redirectionUrl = null; private HttpUrl redirectionUrl = null;
@ -181,7 +174,6 @@ public class XmppConnection implements Runnable {
this.messagePacketConsumer = new MessageProcessor(context, this); this.messagePacketConsumer = new MessageProcessor(context, this);
this.presencePacketConsumer = new PresenceProcessor(context, this); this.presencePacketConsumer = new PresenceProcessor(context, this);
this.iqPacketConsumer = new IqProcessor(context, this); this.iqPacketConsumer = new IqProcessor(context, this);
this.jinglePacketConsumer = new JingleProcessor(context, this);
this.messageAcknowledgeProcessor = new MessageAcknowledgeProcessor(context, this); this.messageAcknowledgeProcessor = new MessageAcknowledgeProcessor(context, this);
this.bindConsumer = new BindProcessor(context, this); this.bindConsumer = new BindProcessor(context, this);
this.managers = Managers.initialize(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() { public void prepareNewConnection() {
this.lastConnect = SystemClock.elapsedRealtime(); this.lastConnect = SystemClock.elapsedRealtime();
this.lastPingSent = SystemClock.elapsedRealtime(); this.lastPingSent = SystemClock.elapsedRealtime();
@ -616,7 +602,7 @@ public class XmppConnection implements Runnable {
Config.LOGTAG, Config.LOGTAG,
account.address + ": acknowledging stanza #" + this.stanzasReceived); account.address + ": acknowledging stanza #" + this.stanzasReceived);
} }
final AckPacket ack = new AckPacket(this.stanzasReceived); final Ack ack = new Ack(this.stanzasReceived);
tagWriter.writeStanzaAsync(ack); tagWriter.writeStanzaAsync(ack);
} else if (nextTag.isStart("a")) { } else if (nextTag.isStart("a")) {
synchronized (NotificationService.CATCHUP_LOCK) { synchronized (NotificationService.CATCHUP_LOCK) {
@ -849,7 +835,7 @@ public class XmppConnection implements Runnable {
private void resetOutboundStanzaQueue() { private void resetOutboundStanzaQueue() {
synchronized (this.mStanzaQueue) { synchronized (this.mStanzaQueue) {
final List<AbstractAcknowledgeableStanza> intermediateStanzas = new ArrayList<>(); final List<Stanza> intermediateStanzas = new ArrayList<>();
if (Config.EXTENDED_SM_LOGGING) { if (Config.EXTENDED_SM_LOGGING) {
Log.d( Log.d(
Config.LOGTAG, Config.LOGTAG,
@ -858,7 +844,7 @@ public class XmppConnection implements Runnable {
+ this.stanzasSentBeforeAuthentication); + this.stanzasSentBeforeAuthentication);
} }
for (int i = this.stanzasSentBeforeAuthentication + 1; i <= this.stanzasSent; ++i) { 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) { if (stanza != null) {
intermediateStanzas.add(stanza); intermediateStanzas.add(stanza);
} }
@ -966,14 +952,14 @@ public class XmppConnection implements Runnable {
this.streamId = streamId; this.streamId = streamId;
this.stanzasReceived = 0; this.stanzasReceived = 0;
this.inSmacksSession = true; this.inSmacksSession = true;
final RequestPacket r = new RequestPacket(); final Request r = new Request();
tagWriter.writeStanzaAsync(r); tagWriter.writeStanzaAsync(r);
} }
private void processResumed(final Element resumed) throws StateChangingException { private void processResumed(final Element resumed) throws StateChangingException {
this.inSmacksSession = true; this.inSmacksSession = true;
this.isBound = true; this.isBound = true;
this.tagWriter.writeStanzaAsync(new RequestPacket()); this.tagWriter.writeStanzaAsync(new Request());
lastPacketReceived = SystemClock.elapsedRealtime(); lastPacketReceived = SystemClock.elapsedRealtime();
final Optional<Integer> h = resumed.getOptionalIntAttribute("h"); final Optional<Integer> h = resumed.getOptionalIntAttribute("h");
final int serverCount; final int serverCount;
@ -983,7 +969,7 @@ public class XmppConnection implements Runnable {
resetStreamId(); resetStreamId();
throw new StateChangingException(ConnectionState.INCOMPATIBLE_SERVER); throw new StateChangingException(ConnectionState.INCOMPATIBLE_SERVER);
} }
final ArrayList<AbstractAcknowledgeableStanza> failedStanzas = new ArrayList<>(); final ArrayList<Stanza> failedStanzas = new ArrayList<>();
final boolean acknowledgedMessages; final boolean acknowledgedMessages;
synchronized (this.mStanzaQueue) { synchronized (this.mStanzaQueue) {
if (serverCount < stanzasSent) { if (serverCount < stanzasSent) {
@ -999,9 +985,9 @@ public class XmppConnection implements Runnable {
mStanzaQueue.clear(); mStanzaQueue.clear();
} }
Log.d(Config.LOGTAG, account.address + ": resending " + failedStanzas.size() + " stanzas"); Log.d(Config.LOGTAG, account.address + ": resending " + failedStanzas.size() + " stanzas");
for (final AbstractAcknowledgeableStanza packet : failedStanzas) { for (final Stanza packet : failedStanzas) {
if (packet instanceof MessagePacket) { if (packet instanceof Message) {
MessagePacket message = (MessagePacket) packet; Message message = (Message) packet;
// TODO set ack = false in message table // TODO set ack = false in message table
// context.markMessage(account, message.getTo().asBareJid(), message.getId(), // context.markMessage(account, message.getTo().asBareJid(), message.getId(),
// Message.STATUS_UNSEND); // Message.STATUS_UNSEND);
@ -1058,9 +1044,9 @@ public class XmppConnection implements Runnable {
+ ": server acknowledged stanza #" + ": server acknowledged stanza #"
+ mStanzaQueue.keyAt(i)); + mStanzaQueue.keyAt(i));
} }
final AbstractAcknowledgeableStanza stanza = mStanzaQueue.valueAt(i); final Stanza stanza = mStanzaQueue.valueAt(i);
if (stanza instanceof MessagePacket && messageAcknowledgeProcessor != null) { if (stanza instanceof Message) {
final MessagePacket packet = (MessagePacket) stanza; final Message packet = (Message) stanza;
final String id = packet.getId(); final String id = packet.getId();
final Jid to = packet.getTo(); final Jid to = packet.getTo();
if (id != null && to != null) { if (id != null && to != null) {
@ -1074,36 +1060,9 @@ public class XmppConnection implements Runnable {
return acknowledgedMessages; return acknowledgedMessages;
} }
private @NonNull Element processPacket(final Tag currentTag, final int packetType) private <S extends Stanza> S processStanza(final Tag currentTag, final Class<S> clazz)
throws IOException { throws IOException {
final Element element; final S stanza = tagReader.readElement(currentTag, clazz);
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");
}
}
if (stanzasReceived == Integer.MAX_VALUE) { if (stanzasReceived == Integer.MAX_VALUE) {
resetStreamId(); resetStreamId();
throw new IOException("time to restart the session. cant handle >2 billion pcks"); throw new IOException("time to restart the session. cant handle >2 billion pcks");
@ -1115,103 +1074,93 @@ public class XmppConnection implements Runnable {
Config.LOGTAG, Config.LOGTAG,
account.address account.address
+ ": not counting stanza(" + ": not counting stanza("
+ element.getClass().getSimpleName() + stanza.getClass().getSimpleName()
+ "). Not in smacks session."); + "). Not in smacks session.");
} }
lastPacketReceived = SystemClock.elapsedRealtime(); lastPacketReceived = SystemClock.elapsedRealtime();
if (element instanceof IqPacket if (InvalidJid.invalid(stanza.getTo()) || InvalidJid.invalid(stanza.getFrom())) {
&& (((IqPacket) element).getType() == IqPacket.TYPE.SET) Log.e(
&& element.hasChild("jingle", Namespace.JINGLE)) { Config.LOGTAG,
return JinglePacket.upgrade((IqPacket) element); "encountered invalid stanza from "
} else { + stanza.getFrom()
return element; + " to "
+ stanza.getTo());
} }
return stanza;
} }
private void processIq(final Tag currentTag) throws IOException { private void processIq(final Tag currentTag) throws IOException {
final IqPacket packet = (IqPacket) processPacket(currentTag, PACKET_IQ); final IQ packet = processStanza(currentTag, IQ.class);
if (!packet.valid()) { if (InvalidJid.invalid(packet.getTo()) || InvalidJid.invalid(packet.getFrom())) {
Log.e( Log.e(
Config.LOGTAG, Config.LOGTAG,
"encountered invalid iq from='" "encountered invalid IQ from " + packet.getFrom() + " to " + packet.getTo());
+ packet.getFrom()
+ "' to='"
+ packet.getTo()
+ "'");
return; return;
} }
if (packet instanceof JinglePacket) { final Consumer<IQ> callback;
this.jinglePacketConsumer.accept((JinglePacket) packet); synchronized (this.packetCallbacks) {
} else { final Pair<IQ, Consumer<IQ>> packetCallbackDuple = packetCallbacks.get(packet.getId());
final Consumer<IqPacket> callback; if (packetCallbackDuple != null) {
synchronized (this.packetCallbacks) { // Packets to the server should have responses from the server
final Pair<IqPacket, Consumer<IqPacket>> packetCallbackDuple = if (toServer(packetCallbackDuple.first)) {
packetCallbacks.get(packet.getId()); if (fromServer(packet)) {
if (packetCallbackDuple != null) { callback = packetCallbackDuple.second;
// Packets to the server should have responses from the server packetCallbacks.remove(packet.getId());
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");
}
} else { } else {
if (packet.getFrom() != null callback = null;
&& packet.getFrom().equals(packetCallbackDuple.first.getTo())) { Log.e(Config.LOGTAG, account.address + ": ignoring spoofed iq packet");
callback = packetCallbackDuple.second;
packetCallbacks.remove(packet.getId());
} else {
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 { } 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 { if (callback != null) {
callback.accept(packet); try {
} catch (StateChangingError error) { callback.accept(packet);
throw new StateChangingException(error.state); } catch (StateChangingError error) {
} throw new StateChangingException(error.state);
} }
} }
} }
private void processMessage(final Tag currentTag) throws IOException { private void processMessage(final Tag currentTag) throws IOException {
final MessagePacket packet = (MessagePacket) processPacket(currentTag, PACKET_MESSAGE); final var message = processStanza(currentTag, Message.class);
if (!packet.valid()) { if (InvalidJid.invalid(message.getTo()) || InvalidJid.invalid(message.getFrom())) {
Log.e( Log.e(
Config.LOGTAG, Config.LOGTAG,
"encountered invalid message from='" "encountered invalid Message from "
+ packet.getFrom() + message.getFrom()
+ "' to='" + " to "
+ packet.getTo() + message.getTo());
+ "'");
return; return;
} }
this.messagePacketConsumer.accept(packet); this.messagePacketConsumer.accept(message);
} }
private void processPresence(final Tag currentTag) throws IOException { private void processPresence(final Tag currentTag) throws IOException {
PresencePacket packet = (PresencePacket) processPacket(currentTag, PACKET_PRESENCE); final var presence = processStanza(currentTag, Presence.class);
if (!packet.valid()) { if (InvalidJid.invalid(presence.getTo()) || InvalidJid.invalid(presence.getFrom())) {
Log.e( Log.e(
Config.LOGTAG, Config.LOGTAG,
"encountered invalid presence from='" "encountered invalid Presence from "
+ packet.getFrom() + presence.getFrom()
+ "' to='" + " to "
+ packet.getTo() + presence.getTo());
+ "'");
return; return;
} }
this.presencePacketConsumer.accept(packet); this.presencePacketConsumer.accept(presence);
} }
private void sendStartTLS() throws IOException { private void sendStartTLS() throws IOException {
@ -1350,7 +1299,7 @@ public class XmppConnection implements Runnable {
Config.LOGTAG, Config.LOGTAG,
account.address + ": resuming after stanza #" + stanzasReceived); 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.mSmCatchupMessageCounter.set(0);
this.mWaitingForSmCatchup.set(true); this.mWaitingForSmCatchup.set(true);
this.tagWriter.writeStanzaAsync(resume); this.tagWriter.writeStanzaAsync(resume);
@ -1563,15 +1512,15 @@ public class XmppConnection implements Runnable {
sendRegistryRequest(); sendRegistryRequest();
return; 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); preAuthRequest.addChild("preauth", Namespace.PARS).setAttribute("token", preAuth);
sendUnmodifiedIqPacket( sendUnmodifiedIqPacket(
preAuthRequest, preAuthRequest,
(response) -> { (response) -> {
if (response.getType() == IqPacket.TYPE.RESULT) { if (response.getType() == IQ.Type.RESULT) {
sendRegistryRequest(); sendRegistryRequest();
} else { } else {
final String error = response.getErrorCondition(); final String error = ""; // response.getErrorCondition();
Log.d(Config.LOGTAG, account.address + ": failed to pre auth. " + error); Log.d(Config.LOGTAG, account.address + ": failed to pre auth. " + error);
throw new StateChangingError(ConnectionState.REGISTRATION_INVALID_TOKEN); throw new StateChangingError(ConnectionState.REGISTRATION_INVALID_TOKEN);
} }
@ -1580,32 +1529,39 @@ public class XmppConnection implements Runnable {
} }
private void sendRegistryRequest() { private void sendRegistryRequest() {
final IqPacket register = new IqPacket(IqPacket.TYPE.GET); final IQ retrieveRegistration = new IQ(IQ.Type.GET);
register.query(Namespace.REGISTER); retrieveRegistration.addExtension(new Register());
register.setTo(account.address.getDomain()); retrieveRegistration.setTo(account.address.getDomain());
sendUnmodifiedIqPacket( sendUnmodifiedIqPacket(
register, retrieveRegistration,
(packet) -> { (packet) -> {
if (packet.getType() == IqPacket.TYPE.TIMEOUT) { if (packet.getType() == IQ.Type.TIMEOUT) {
return; 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); throw new StateChangingError(ConnectionState.REGISTRATION_FAILED);
} }
final Element query = packet.query(Namespace.REGISTER);
if (query.hasChild("username") && (query.hasChild("password"))) { if (query.hasChild("username") && (query.hasChild("password"))) {
final Credential credential = final Credential credential =
CredentialStore.getInstance(context).get(account); CredentialStore.getInstance(context).get(account);
final IqPacket register1 = new IqPacket(IqPacket.TYPE.SET); final IQ registrationRequest = new IQ(IQ.Type.SET);
final Element username = final Element username =
new Element("username") new Element("username")
.setContent(account.address.getEscapedLocal()); .setContent(account.address.getEscapedLocal());
final Element password = final Element password =
new Element("password").setContent(credential.password); new Element("password").setContent(credential.password);
register1.query(Namespace.REGISTER).addChild(username);
register1.query().addChild(password); final var register = registrationRequest.addExtension(new Register());
register1.setFrom(account.address);
sendUnmodifiedIqPacket(register1, this::handleRegistrationResponse, true); register.addChild(username);
register.addChild(password);
registrationRequest.setFrom(account.address);
sendUnmodifiedIqPacket(
registrationRequest, this::handleRegistrationResponse, true);
} else if (query.hasChild("x", Namespace.DATA)) { } else if (query.hasChild("x", Namespace.DATA)) {
final Data data = Data.parse(query.findChild("x", Namespace.DATA)); final Data data = Data.parse(query.findChild("x", Namespace.DATA));
final Element blob = query.findChild("data", "urn:xmpp:bob"); final Element blob = query.findChild("data", "urn:xmpp:bob");
@ -1667,8 +1623,8 @@ public class XmppConnection implements Runnable {
true); true);
} }
private void handleRegistrationResponse(final IqPacket packet) { private void handleRegistrationResponse(final IQ packet) {
if (packet.getType() == IqPacket.TYPE.RESULT) { if (packet.getType() == IQ.Type.RESULT) {
ConversationsDatabase.getInstance(context) ConversationsDatabase.getInstance(context)
.accountDao() .accountDao()
.setPendingRegistration(account.id, false); .setPendingRegistration(account.id, false);
@ -1716,9 +1672,6 @@ public class XmppConnection implements Runnable {
this.stanzasSent = 0; this.stanzasSent = 0;
mStanzaQueue.clear(); mStanzaQueue.clear();
this.redirectionUrl = null; this.redirectionUrl = null;
synchronized (this.commands) {
this.commands.clear();
}
this.saslMechanism = null; this.saslMechanism = null;
} }
@ -1736,16 +1689,16 @@ public class XmppConnection implements Runnable {
} else { } else {
resource = this.createNewResource(IDs.tiny(account.randomSeed)); 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); iq.addChild("bind", Namespace.BIND).addChild("resource").setContent(resource);
this.sendUnmodifiedIqPacket( this.sendUnmodifiedIqPacket(
iq, iq,
(packet) -> { (packet) -> {
if (packet.getType() == IqPacket.TYPE.TIMEOUT) { if (packet.getType() == IQ.Type.TIMEOUT) {
return; return;
} }
final Element bind = packet.findChild("bind"); final Element bind = packet.findChild("bind");
if (bind != null && packet.getType() == IqPacket.TYPE.RESULT) { if (bind != null && packet.getType() == IQ.Type.RESULT) {
isBound = true; isBound = true;
final String jid = bind.findChildContent("jid"); final String jid = bind.findChildContent("jid");
if (Strings.isNullOrEmpty(jid)) { if (Strings.isNullOrEmpty(jid)) {
@ -1789,7 +1742,7 @@ public class XmppConnection implements Runnable {
+ packet); + packet);
} }
final Element error = packet.findChild("error"); final Element error = packet.findChild("error");
if (packet.getType() == IqPacket.TYPE.ERROR if (packet.getType() == IQ.Type.ERROR
&& error != null && error != null
&& error.hasChild("conflict")) { && error.hasChild("conflict")) {
final String alternativeResource = createNewResource(IDs.tiny()); final String alternativeResource = createNewResource(IDs.tiny());
@ -1813,8 +1766,8 @@ public class XmppConnection implements Runnable {
} }
private void clearIqCallbacks() { private void clearIqCallbacks() {
final IqPacket failurePacket = new IqPacket(IqPacket.TYPE.TIMEOUT); final IQ failurePacket = new IQ(IQ.Type.TIMEOUT);
final ArrayList<Consumer<IqPacket>> callbacks = new ArrayList<>(); final ArrayList<Consumer<IQ>> callbacks = new ArrayList<>();
synchronized (this.packetCallbacks) { synchronized (this.packetCallbacks) {
if (this.packetCallbacks.size() == 0) { if (this.packetCallbacks.size() == 0) {
return; return;
@ -1825,15 +1778,15 @@ public class XmppConnection implements Runnable {
+ ": clearing " + ": clearing "
+ this.packetCallbacks.size() + this.packetCallbacks.size()
+ " iq callbacks"); + " iq callbacks");
final Iterator<Pair<IqPacket, Consumer<IqPacket>>> iterator = final Iterator<Pair<IQ, Consumer<IQ>>> iterator =
this.packetCallbacks.values().iterator(); this.packetCallbacks.values().iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
Pair<IqPacket, Consumer<IqPacket>> entry = iterator.next(); Pair<IQ, Consumer<IQ>> entry = iterator.next();
callbacks.add(entry.second); callbacks.add(entry.second);
iterator.remove(); iterator.remove();
} }
} }
for (final Consumer<IqPacket> callback : callbacks) { for (final Consumer<IQ> callback : callbacks) {
try { try {
callback.accept(failurePacket); callback.accept(failurePacket);
} catch (StateChangingError error) { } catch (StateChangingError error) {
@ -1856,15 +1809,15 @@ public class XmppConnection implements Runnable {
private void sendStartSession() { private void sendStartSession() {
Log.d(Config.LOGTAG, account.address + ": sending legacy session to outdated server"); 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"); startSession.addChild("session", "urn:ietf:params:xml:ns:xmpp-session");
this.sendUnmodifiedIqPacket( this.sendUnmodifiedIqPacket(
startSession, startSession,
(packet) -> { (packet) -> {
if (packet.getType() == IqPacket.TYPE.RESULT) { if (packet.getType() == IQ.Type.RESULT) {
enableStreamManagement(); enableStreamManagement();
sendPostBindInitialization(false); sendPostBindInitialization(false);
} else if (packet.getType() != IqPacket.TYPE.TIMEOUT) { } else if (packet.getType() != IQ.Type.TIMEOUT) {
throw new StateChangingError(ConnectionState.SESSION_FAILURE); throw new StateChangingError(ConnectionState.SESSION_FAILURE);
} }
}, },
@ -1876,7 +1829,7 @@ public class XmppConnection implements Runnable {
final boolean streamManagement = this.streamFeatures.streamManagement(); final boolean streamManagement = this.streamFeatures.streamManagement();
if (streamManagement) { if (streamManagement) {
synchronized (this.mStanzaQueue) { synchronized (this.mStanzaQueue) {
final EnablePacket enable = new EnablePacket(); final Enable enable = new Enable();
tagWriter.writeStanzaAsync(enable); tagWriter.writeStanzaAsync(enable);
stanzasSent = 0; stanzasSent = 0;
mStanzaQueue.clear(); mStanzaQueue.clear();
@ -1936,60 +1889,6 @@ public class XmppConnection implements Runnable {
return this.connectionState; 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<String, Jid> 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() { private void finalizeBind() {
this.enableAdvancedStreamFeatures(); this.enableAdvancedStreamFeatures();
this.bindConsumer.accept(this.connectionAddress); this.bindConsumer.accept(this.connectionAddress);
@ -2005,12 +1904,12 @@ public class XmppConnection implements Runnable {
} }
private void sendEnableCarbons() { private void sendEnableCarbons() {
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET); final IQ iq = new IQ(IQ.Type.SET);
iq.addChild("enable", Namespace.CARBONS); iq.addChild("enable", Namespace.CARBONS);
this.sendIqPacket( this.sendIqPacket(
iq, iq,
(packet) -> { (packet) -> {
if (packet.getType() == IqPacket.TYPE.RESULT) { if (packet.getType() == IQ.Type.RESULT) {
Log.d(Config.LOGTAG, account.address + ": successfully enabled carbons"); Log.d(Config.LOGTAG, account.address + ": successfully enabled carbons");
this.carbonsEnabled = true; this.carbonsEnabled = true;
} else { } else {
@ -2055,9 +1954,9 @@ public class XmppConnection implements Runnable {
private void failPendingMessages(final String error) { private void failPendingMessages(final String error) {
synchronized (this.mStanzaQueue) { synchronized (this.mStanzaQueue) {
for (int i = 0; i < mStanzaQueue.size(); ++i) { for (int i = 0; i < mStanzaQueue.size(); ++i) {
final AbstractAcknowledgeableStanza stanza = mStanzaQueue.valueAt(i); final Stanza stanza = mStanzaQueue.valueAt(i);
if (stanza instanceof MessagePacket) { if (stanza instanceof Message) {
final MessagePacket packet = (MessagePacket) stanza; final Message packet = (Message) stanza;
final String id = packet.getId(); final String id = packet.getId();
final Jid to = packet.getTo(); final Jid to = packet.getTo();
// TODO set ack=true but add error? // 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); return String.format("%s.%s", context.getString(R.string.app_name), postfixId);
} }
public ListenableFuture<IqPacket> sendIqPacket(final IqPacket packet) { public ListenableFuture<IQ> sendIqPacket(final IQ packet) {
final SettableFuture<IqPacket> future = SettableFuture.create(); final SettableFuture<IQ> future = SettableFuture.create();
sendIqPacket( sendIqPacket(
packet, packet,
result -> { result -> {
final var type = result.getType(); final var type = result.getType();
if (type == IqPacket.TYPE.RESULT) { if (type == IQ.Type.RESULT) {
future.set(result); future.set(result);
} else if (type == IqPacket.TYPE.TIMEOUT) { } else if (type == IQ.Type.TIMEOUT) {
future.setException(new TimeoutException()); future.setException(new TimeoutException());
} else { } else {
// TODO some sort of IqErrorException // TODO some sort of IqErrorException
@ -2141,15 +2040,15 @@ public class XmppConnection implements Runnable {
return future; return future;
} }
public String sendIqPacket(final IqPacket packet, final Consumer<IqPacket> callback) { public String sendIqPacket(final IQ packet, final Consumer<IQ> callback) {
packet.setFrom(account.address); packet.setFrom(account.address);
return this.sendUnmodifiedIqPacket(packet, callback, false); return this.sendUnmodifiedIqPacket(packet, callback, false);
} }
public synchronized String sendUnmodifiedIqPacket( public synchronized String sendUnmodifiedIqPacket(
final IqPacket packet, final Consumer<IqPacket> callback, boolean force) { final IQ packet, final Consumer<IQ> callback, boolean force) {
if (Strings.isNullOrEmpty(packet.getId())) { if (Strings.isNullOrEmpty(packet.getId())) {
packet.setAttribute("id", IDs.medium()); packet.setId(IDs.medium());
} }
if (callback != null) { if (callback != null) {
synchronized (this.packetCallbacks) { synchronized (this.packetCallbacks) {
@ -2160,19 +2059,19 @@ public class XmppConnection implements Runnable {
return packet.getId(); return packet.getId();
} }
public void sendMessagePacket(final MessagePacket packet) { public void sendMessagePacket(final Message packet) {
this.sendPacket(packet); this.sendPacket(packet);
} }
public void sendPresencePacket(final PresencePacket packet) { public void sendPresencePacket(final Presence packet) {
this.sendPacket(packet); this.sendPacket(packet);
} }
private synchronized void sendPacket(final AbstractStanza packet) { private synchronized void sendPacket(final StreamElement packet) {
sendPacket(packet, false); 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) { if (stanzasSent == Integer.MAX_VALUE) {
resetStreamId(); resetStreamId();
disconnect(true); disconnect(true);
@ -2188,8 +2087,8 @@ public class XmppConnection implements Runnable {
+ " do not write stanza to unbound stream " + " do not write stanza to unbound stream "
+ packet.toString()); + packet.toString());
} }
if (packet instanceof AbstractAcknowledgeableStanza) { if (packet instanceof Stanza) {
AbstractAcknowledgeableStanza stanza = (AbstractAcknowledgeableStanza) packet; final Stanza stanza = (Stanza) packet;
if (this.mStanzaQueue.size() != 0) { if (this.mStanzaQueue.size() != 0) {
int currentHighestKey = this.mStanzaQueue.keyAt(this.mStanzaQueue.size() - 1); int currentHighestKey = this.mStanzaQueue.keyAt(this.mStanzaQueue.size() - 1);
@ -2209,7 +2108,7 @@ public class XmppConnection implements Runnable {
+ stanzasSent); + stanzasSent);
} }
this.mStanzaQueue.append(stanzasSent, stanza); 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) { if (Config.EXTENDED_SM_LOGGING) {
Log.d( Log.d(
Config.LOGTAG, Config.LOGTAG,
@ -2217,7 +2116,7 @@ public class XmppConnection implements Runnable {
+ ": requesting ack for message stanza #" + ": requesting ack for message stanza #"
+ stanzasSent); + stanzasSent);
} }
tagWriter.writeStanzaAsync(new RequestPacket()); tagWriter.writeStanzaAsync(new Request());
} }
} }
} }
@ -2225,7 +2124,7 @@ public class XmppConnection implements Runnable {
public void sendPing() { public void sendPing() {
if (!r()) { if (!r()) {
final IqPacket iq = new IqPacket(IqPacket.TYPE.GET); final IQ iq = new IQ(IQ.Type.GET);
iq.setFrom(account.address); iq.setFrom(account.address);
iq.addChild("ping", Namespace.PING); iq.addChild("ping", Namespace.PING);
this.sendIqPacket(iq, null); this.sendIqPacket(iq, null);
@ -2308,7 +2207,7 @@ public class XmppConnection implements Runnable {
public boolean r() { public boolean r() {
if (this.inSmacksSession) { if (this.inSmacksSession) {
this.tagWriter.writeStanzaAsync(new RequestPacket()); this.tagWriter.writeStanzaAsync(new Request());
return true; return true;
} else { } else {
return false; return false;
@ -2346,11 +2245,11 @@ public class XmppConnection implements Runnable {
} }
public void sendActive() { public void sendActive() {
this.sendPacket(new ActivePacket()); this.sendPacket(new Active());
} }
public void sendInactive() { public void sendInactive() {
this.sendPacket(new InactivePacket()); this.sendPacket(new Inactive());
} }
public void resetAttemptCount(boolean resetConnectTime) { 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(); final Jid from = stanza.getFrom();
return from == null return from == null
|| from.equals(connectionAddress.getDomain()) || from.equals(connectionAddress.getDomain())
@ -2368,7 +2267,7 @@ public class XmppConnection implements Runnable {
|| from.equals(connectionAddress); || from.equals(connectionAddress);
} }
public boolean toServer(final AbstractStanza stanza) { public boolean toServer(final Stanza stanza) {
final Jid to = stanza.getTo(); final Jid to = stanza.getTo();
return to == null return to == null
|| to.equals(connectionAddress.getDomain()) || to.equals(connectionAddress.getDomain())
@ -2376,7 +2275,7 @@ public class XmppConnection implements Runnable {
|| to.equals(connectionAddress); || to.equals(connectionAddress);
} }
public boolean fromAccount(final AbstractStanza stanza) { public boolean fromAccount(final Stanza stanza) {
final Jid from = stanza.getFrom(); final Jid from = stanza.getFrom();
return from != null && from.asBareJid().equals(connectionAddress.asBareJid()); return from != null && from.asBareJid().equals(connectionAddress.asBareJid());
} }

View file

@ -2,11 +2,11 @@ package im.conversations.android.xmpp.manager;
import android.content.Context; import android.content.Context;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.blocking.Block; import im.conversations.android.xmpp.model.blocking.Block;
import im.conversations.android.xmpp.model.blocking.Blocklist; import im.conversations.android.xmpp.model.blocking.Blocklist;
import im.conversations.android.xmpp.model.blocking.Unblock; import im.conversations.android.xmpp.model.blocking.Unblock;
import im.conversations.android.xmpp.model.stanza.IQ;
import java.util.Objects; import java.util.Objects;
public class BlockingManager extends AbstractManager { public class BlockingManager extends AbstractManager {
@ -20,13 +20,13 @@ public class BlockingManager extends AbstractManager {
public void handlePush(final Unblock unblock) {} public void handlePush(final Unblock unblock) {}
public void fetch() { public void fetch() {
final IqPacket iqPacket = new IqPacket(IqPacket.TYPE.GET); final IQ iqPacket = new IQ(IQ.Type.GET);
iqPacket.addChild(new Blocklist()); iqPacket.addChild(new Blocklist());
connection.sendIqPacket(iqPacket, this::handleFetchResult); connection.sendIqPacket(iqPacket, this::handleFetchResult);
} }
private void handleFetchResult(final IqPacket result) { private void handleFetchResult(final IQ result) {
if (result.getType() != IqPacket.TYPE.RESULT) { if (result.getType() != IQ.Type.RESULT) {
return; return;
} }
final var blocklist = result.getExtension(Blocklist.class); final var blocklist = result.getExtension(Blocklist.class);

View file

@ -10,7 +10,6 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import im.conversations.android.xmpp.Entity; import im.conversations.android.xmpp.Entity;
import im.conversations.android.xmpp.EntityCapabilities; import im.conversations.android.xmpp.EntityCapabilities;
import im.conversations.android.xmpp.EntityCapabilities2; import im.conversations.android.xmpp.EntityCapabilities2;
@ -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.info.InfoQuery;
import im.conversations.android.xmpp.model.disco.items.Item; import im.conversations.android.xmpp.model.disco.items.Item;
import im.conversations.android.xmpp.model.disco.items.ItemsQuery; import im.conversations.android.xmpp.model.disco.items.ItemsQuery;
import im.conversations.android.xmpp.model.stanza.IQ;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -52,7 +52,7 @@ public class DiscoManager extends AbstractManager {
@Nullable final String node, @Nullable final String node,
@Nullable final EntityCapabilities.Hash hash) { @Nullable final EntityCapabilities.Hash hash) {
final var requestNode = hash != null && node != null ? hash.capabilityNode(node) : node; 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); iqRequest.setTo(entity.address);
final InfoQuery infoQueryRequest = iqRequest.addExtension(new InfoQuery()); final InfoQuery infoQueryRequest = iqRequest.addExtension(new InfoQuery());
if (requestNode != null) { if (requestNode != null) {
@ -114,7 +114,7 @@ public class DiscoManager extends AbstractManager {
public ListenableFuture<Collection<Item>> items( public ListenableFuture<Collection<Item>> items(
@NonNull final Entity.DiscoItem entity, @Nullable final String node) { @NonNull final Entity.DiscoItem entity, @Nullable final String node) {
final var requestNode = Strings.emptyToNull(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); iqPacket.setTo(entity.address);
final ItemsQuery itemsQueryRequest = iqPacket.addExtension(new ItemsQuery()); final ItemsQuery itemsQueryRequest = iqPacket.addExtension(new ItemsQuery());
if (requestNode != null) { if (requestNode != null) {

View file

@ -5,10 +5,10 @@ import android.util.Log;
import com.google.common.base.Strings; import com.google.common.base.Strings;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import eu.siacs.conversations.Config; import eu.siacs.conversations.Config;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.roster.Item; import im.conversations.android.xmpp.model.roster.Item;
import im.conversations.android.xmpp.model.roster.Query; import im.conversations.android.xmpp.model.roster.Query;
import im.conversations.android.xmpp.model.stanza.IQ;
import java.util.Objects; import java.util.Objects;
public class RosterManager extends AbstractManager { public class RosterManager extends AbstractManager {
@ -27,7 +27,7 @@ public class RosterManager extends AbstractManager {
final var account = getAccount(); final var account = getAccount();
final var database = getDatabase(); final var database = getDatabase();
final String rosterVersion = database.accountDao().getRosterVersion(account.id); 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(); final Query rosterQuery = new Query();
iqPacket.addChild(rosterQuery); iqPacket.addChild(rosterQuery);
if (Strings.isNullOrEmpty(rosterVersion)) { if (Strings.isNullOrEmpty(rosterVersion)) {
@ -39,8 +39,8 @@ public class RosterManager extends AbstractManager {
connection.sendIqPacket(iqPacket, this::handleFetchResult); connection.sendIqPacket(iqPacket, this::handleFetchResult);
} }
private void handleFetchResult(final IqPacket result) { private void handleFetchResult(final IQ result) {
if (result.getType() != IqPacket.TYPE.RESULT) { if (result.getType() != IQ.Type.RESULT) {
return; return;
} }
final var query = result.getExtension(Query.class); final var query = result.getExtension(Query.class);

View file

@ -0,0 +1,8 @@
package im.conversations.android.xmpp.model;
public abstract class StreamElement extends Extension {
protected StreamElement(Class<? extends Extension> clazz) {
super(clazz);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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");
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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;

View file

@ -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));
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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<? extends Extension> 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);
}
}

View file

@ -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;

View file

@ -2,40 +2,39 @@ package im.conversations.android.xmpp.processor;
import android.content.Context; import android.content.Context;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import eu.siacs.conversations.xmpp.stanzas.IqPacket;
import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.manager.BlockingManager; import im.conversations.android.xmpp.manager.BlockingManager;
import im.conversations.android.xmpp.manager.RosterManager; import im.conversations.android.xmpp.manager.RosterManager;
import im.conversations.android.xmpp.model.blocking.Block; import im.conversations.android.xmpp.model.blocking.Block;
import im.conversations.android.xmpp.model.blocking.Unblock; import im.conversations.android.xmpp.model.blocking.Unblock;
import im.conversations.android.xmpp.model.roster.Query; import im.conversations.android.xmpp.model.roster.Query;
import im.conversations.android.xmpp.model.stanza.IQ;
import java.util.Arrays; import java.util.Arrays;
import java.util.function.Consumer; import java.util.function.Consumer;
public class IqProcessor extends XmppConnection.Delegate implements Consumer<IqPacket> { public class IqProcessor extends XmppConnection.Delegate implements Consumer<IQ> {
public IqProcessor(final Context context, final XmppConnection connection) { public IqProcessor(final Context context, final XmppConnection connection) {
super(context, connection); super(context, connection);
} }
@Override @Override
public void accept(final IqPacket packet) { public void accept(final IQ packet) {
final IqPacket.TYPE type = packet.getType(); final IQ.Type type = packet.getType();
Preconditions.checkArgument( Preconditions.checkArgument(Arrays.asList(IQ.Type.GET, IQ.Type.SET).contains(type));
Arrays.asList(IqPacket.TYPE.GET, IqPacket.TYPE.SET).contains(type)); if (type == IQ.Type.SET
if (type == IqPacket.TYPE.SET
&& connection.fromAccount(packet) && connection.fromAccount(packet)
&& packet.hasExtension(Query.class)) { && packet.hasExtension(Query.class)) {
getManager(RosterManager.class).handlePush(packet.getExtension(Query.class)); getManager(RosterManager.class).handlePush(packet.getExtension(Query.class));
return; return;
} }
if (type == IqPacket.TYPE.SET if (type == IQ.Type.SET
&& connection.fromAccount(packet) && connection.fromAccount(packet)
&& packet.hasExtension(Block.class)) { && packet.hasExtension(Block.class)) {
getManager(BlockingManager.class).handlePush(packet.getExtension(Block.class)); getManager(BlockingManager.class).handlePush(packet.getExtension(Block.class));
return; return;
} }
if (type == IqPacket.TYPE.SET if (type == IQ.Type.SET
&& connection.fromAccount(packet) && connection.fromAccount(packet)
&& packet.hasExtension(Unblock.class)) { && packet.hasExtension(Unblock.class)) {
getManager(BlockingManager.class).handlePush(packet.getExtension(Unblock.class)); getManager(BlockingManager.class).handlePush(packet.getExtension(Unblock.class));

View file

@ -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<JinglePacket> {
public JingleProcessor(final Context context, final XmppConnection connection) {}
@Override
public void accept(JinglePacket jinglePacket) {}
}

View file

@ -1,14 +1,14 @@
package im.conversations.android.xmpp.processor; package im.conversations.android.xmpp.processor;
import android.content.Context; import android.content.Context;
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.model.stanza.Message;
import java.util.function.Consumer; import java.util.function.Consumer;
public class MessageProcessor implements Consumer<MessagePacket> { public class MessageProcessor implements Consumer<Message> {
public MessageProcessor(final Context context, final XmppConnection connection) {} public MessageProcessor(final Context context, final XmppConnection connection) {}
@Override @Override
public void accept(final MessagePacket messagePacket) {} public void accept(final Message messagePacket) {}
} }

View file

@ -1,22 +1,22 @@
package im.conversations.android.xmpp.processor; package im.conversations.android.xmpp.processor;
import android.content.Context; import android.content.Context;
import eu.siacs.conversations.xmpp.stanzas.PresencePacket;
import im.conversations.android.database.model.PresenceShow; import im.conversations.android.database.model.PresenceShow;
import im.conversations.android.database.model.PresenceType; import im.conversations.android.database.model.PresenceType;
import im.conversations.android.xmpp.Entity; import im.conversations.android.xmpp.Entity;
import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.XmppConnection;
import im.conversations.android.xmpp.manager.DiscoManager; import im.conversations.android.xmpp.manager.DiscoManager;
import im.conversations.android.xmpp.model.stanza.Presence;
import java.util.function.Consumer; import java.util.function.Consumer;
public class PresenceProcessor extends XmppConnection.Delegate implements Consumer<PresencePacket> { public class PresenceProcessor extends XmppConnection.Delegate implements Consumer<Presence> {
public PresenceProcessor(final Context context, final XmppConnection connection) { public PresenceProcessor(final Context context, final XmppConnection connection) {
super(context, connection); super(context, connection);
} }
@Override @Override
public void accept(final PresencePacket presencePacket) { public void accept(final Presence presencePacket) {
final var from = presencePacket.getFrom(); final var from = presencePacket.getFrom();
final var address = from == null ? null : from.asBareJid(); final var address = from == null ? null : from.asBareJid();
final var resource = from == null ? null : from.getResource(); final var resource = from == null ? null : from.getResource();
@ -38,7 +38,7 @@ public class PresenceProcessor extends XmppConnection.Delegate implements Consum
fetchCapabilities(presencePacket); fetchCapabilities(presencePacket);
} }
private void fetchCapabilities(final PresencePacket presencePacket) { private void fetchCapabilities(final Presence presencePacket) {
final var entity = presencePacket.getFrom(); final var entity = presencePacket.getFrom();
final var nodeHash = presencePacket.getCapabilities(); final var nodeHash = presencePacket.getCapabilities();
if (nodeHash != null) { if (nodeHash != null) {