add models for Error conditions
This commit is contained in:
parent
ddcab5fb58
commit
9a855a57ac
|
@ -1,6 +1,7 @@
|
|||
package im.conversations.android.annotation.processor;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.google.common.base.CaseFormat;
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
@ -8,7 +9,6 @@ import im.conversations.android.annotation.XmlElement;
|
|||
import im.conversations.android.annotation.XmlPackage;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
|
@ -88,8 +88,9 @@ public class XmlElementProcessor extends AbstractProcessor {
|
|||
|
||||
private static Id of(final TypeElement typeElement) {
|
||||
final XmlElement xmlElement = typeElement.getAnnotation(XmlElement.class);
|
||||
PackageElement packageElement = (PackageElement) typeElement.getEnclosingElement();
|
||||
XmlPackage xmlPackage = packageElement.getAnnotation(XmlPackage.class);
|
||||
PackageElement packageElement = getPackageElement(typeElement);
|
||||
XmlPackage xmlPackage =
|
||||
packageElement == null ? null : packageElement.getAnnotation(XmlPackage.class);
|
||||
if (xmlElement == null) {
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
|
@ -112,13 +113,29 @@ public class XmlElementProcessor extends AbstractProcessor {
|
|||
}
|
||||
final String name;
|
||||
if (Strings.isNullOrEmpty(elementName)) {
|
||||
name = typeElement.getSimpleName().toString().toLowerCase(Locale.ROOT);
|
||||
name =
|
||||
CaseFormat.UPPER_CAMEL.to(
|
||||
CaseFormat.LOWER_HYPHEN, typeElement.getSimpleName().toString());
|
||||
} else {
|
||||
name = elementName;
|
||||
}
|
||||
return new Id(name, namespace);
|
||||
}
|
||||
|
||||
private static PackageElement getPackageElement(final TypeElement typeElement) {
|
||||
final Element parent = typeElement.getEnclosingElement();
|
||||
if (parent instanceof PackageElement) {
|
||||
return (PackageElement) parent;
|
||||
} else {
|
||||
final Element nextParent = parent.getEnclosingElement();
|
||||
if (nextParent instanceof PackageElement) {
|
||||
return (PackageElement) nextParent;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Id {
|
||||
public final String name;
|
||||
public final String namespace;
|
||||
|
|
|
@ -9,6 +9,7 @@ import eu.siacs.conversations.utils.XmlHelper;
|
|||
import eu.siacs.conversations.xmpp.InvalidJid;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
|
||||
import im.conversations.android.xmpp.ExtensionFactory;
|
||||
import im.conversations.android.xmpp.model.Extension;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
@ -89,6 +90,11 @@ public class Element {
|
|||
Collections2.filter(this.children, clazz::isInstance), clazz::cast);
|
||||
}
|
||||
|
||||
public Collection<ExtensionFactory.Id> getExtensionIds() {
|
||||
return Collections2.transform(
|
||||
this.children, c -> new ExtensionFactory.Id(c.getName(), c.getNamespace()));
|
||||
}
|
||||
|
||||
public String findChildContent(String name) {
|
||||
Element element = findChild(name);
|
||||
return element == null ? null : element.getContent();
|
||||
|
|
|
@ -80,4 +80,6 @@ public final class Namespace {
|
|||
public static final String UNIFIED_PUSH = "http://gultsch.de/xmpp/drafts/unified-push";
|
||||
public static final String JABBER_CLIENT = "jabber:client";
|
||||
public static final String FORWARD = "urn:xmpp:forward:0";
|
||||
|
||||
public static final String STANZAS = "urn:ietf:params:xml:ns:xmpp-stanzas";
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package eu.siacs.conversations.xml;
|
||||
|
||||
import com.google.common.io.ByteSource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
|
@ -11,10 +10,10 @@ public class XmlElementReader {
|
|||
return read(ByteSource.wrap(bytes).openStream());
|
||||
}
|
||||
|
||||
public static Element read(InputStream inputStream) throws IOException {
|
||||
final XmlReader xmlReader = new XmlReader();
|
||||
public static Element read(final InputStream inputStream) throws IOException {
|
||||
try (final XmlReader xmlReader = new XmlReader()) {
|
||||
xmlReader.setInputStream(inputStream);
|
||||
return xmlReader.readElement(xmlReader.readTag());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package im.conversations.android.xmpp;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Objects;
|
||||
import eu.siacs.conversations.xml.Element;
|
||||
import im.conversations.android.xmpp.model.Extension;
|
||||
|
@ -61,5 +62,13 @@ public final class ExtensionFactory {
|
|||
public int hashCode() {
|
||||
return Objects.hashCode(name, namespace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("name", name)
|
||||
.add("namespace", namespace)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,12 +60,13 @@ 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.ping.Ping;
|
||||
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.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;
|
||||
|
@ -120,7 +121,7 @@ public class XmppConnection implements Runnable {
|
|||
|
||||
protected final Account account;
|
||||
private final SparseArray<Stanza> mStanzaQueue = new SparseArray<>();
|
||||
private final Hashtable<String, Pair<IQ, Consumer<IQ>>> packetCallbacks = new Hashtable<>();
|
||||
private final Hashtable<String, Pair<Iq, Consumer<Iq>>> packetCallbacks = new Hashtable<>();
|
||||
private final Context context;
|
||||
private Socket socket;
|
||||
private XmlReader tagReader;
|
||||
|
@ -147,7 +148,7 @@ public class XmppConnection implements Runnable {
|
|||
private final AtomicInteger mSmCatchupMessageCounter = new AtomicInteger(0);
|
||||
private int attempt = 0;
|
||||
private final Consumer<Presence> presencePacketConsumer;
|
||||
private final Consumer<IQ> iqPacketConsumer;
|
||||
private final Consumer<Iq> iqPacketConsumer;
|
||||
private final Consumer<Message> messagePacketConsumer;
|
||||
private final BiFunction<Jid, String, Boolean> messageAcknowledgeProcessor;
|
||||
private final Consumer<Jid> bindConsumer;
|
||||
|
@ -1088,16 +1089,16 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
|
||||
private void processIq(final Tag currentTag) throws IOException {
|
||||
final IQ packet = processStanza(currentTag, IQ.class);
|
||||
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());
|
||||
return;
|
||||
}
|
||||
final Consumer<IQ> callback;
|
||||
final Consumer<Iq> callback;
|
||||
synchronized (this.packetCallbacks) {
|
||||
final Pair<IQ, Consumer<IQ>> packetCallbackDuple = packetCallbacks.get(packet.getId());
|
||||
final Pair<Iq, Consumer<Iq>> packetCallbackDuple = packetCallbacks.get(packet.getId());
|
||||
if (packetCallbackDuple != null) {
|
||||
// Packets to the server should have responses from the server
|
||||
if (toServer(packetCallbackDuple.first)) {
|
||||
|
@ -1118,7 +1119,7 @@ public class XmppConnection implements Runnable {
|
|||
Log.e(Config.LOGTAG, account.address + ": ignoring spoofed iq packet");
|
||||
}
|
||||
}
|
||||
} else if (packet.getType() == IQ.Type.GET || packet.getType() == IQ.Type.SET) {
|
||||
} else if (packet.getType() == Iq.Type.GET || packet.getType() == Iq.Type.SET) {
|
||||
callback = this.iqPacketConsumer;
|
||||
} else {
|
||||
callback = null;
|
||||
|
@ -1510,12 +1511,12 @@ public class XmppConnection implements Runnable {
|
|||
sendRegistryRequest();
|
||||
return;
|
||||
}
|
||||
final IQ preAuthRequest = new IQ(IQ.Type.SET);
|
||||
final Iq preAuthRequest = new Iq(Iq.Type.SET);
|
||||
preAuthRequest.addChild("preauth", Namespace.PARS).setAttribute("token", preAuth);
|
||||
sendUnmodifiedIqPacket(
|
||||
preAuthRequest,
|
||||
(response) -> {
|
||||
if (response.getType() == IQ.Type.RESULT) {
|
||||
if (response.getType() == Iq.Type.RESULT) {
|
||||
sendRegistryRequest();
|
||||
} else {
|
||||
final String error = ""; // response.getErrorCondition();
|
||||
|
@ -1527,16 +1528,16 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
|
||||
private void sendRegistryRequest() {
|
||||
final IQ retrieveRegistration = new IQ(IQ.Type.GET);
|
||||
final Iq retrieveRegistration = new Iq(Iq.Type.GET);
|
||||
retrieveRegistration.addExtension(new Register());
|
||||
retrieveRegistration.setTo(account.address.getDomain());
|
||||
sendUnmodifiedIqPacket(
|
||||
retrieveRegistration,
|
||||
(packet) -> {
|
||||
if (packet.getType() == IQ.Type.TIMEOUT) {
|
||||
if (packet.getType() == Iq.Type.TIMEOUT) {
|
||||
return;
|
||||
}
|
||||
if (packet.getType() == IQ.Type.ERROR) {
|
||||
if (packet.getType() == Iq.Type.ERROR) {
|
||||
throw new StateChangingError(ConnectionState.REGISTRATION_FAILED);
|
||||
}
|
||||
final Register query = packet.getExtension(Register.class);
|
||||
|
@ -1546,7 +1547,7 @@ public class XmppConnection implements Runnable {
|
|||
if (query.hasChild("username") && (query.hasChild("password"))) {
|
||||
final Credential credential =
|
||||
CredentialStore.getInstance(context).get(account);
|
||||
final IQ registrationRequest = new IQ(IQ.Type.SET);
|
||||
final Iq registrationRequest = new Iq(Iq.Type.SET);
|
||||
final Element username =
|
||||
new Element("username")
|
||||
.setContent(account.address.getEscapedLocal());
|
||||
|
@ -1621,8 +1622,8 @@ public class XmppConnection implements Runnable {
|
|||
true);
|
||||
}
|
||||
|
||||
private void handleRegistrationResponse(final IQ packet) {
|
||||
if (packet.getType() == IQ.Type.RESULT) {
|
||||
private void handleRegistrationResponse(final Iq packet) {
|
||||
if (packet.getType() == Iq.Type.RESULT) {
|
||||
ConversationsDatabase.getInstance(context)
|
||||
.accountDao()
|
||||
.setPendingRegistration(account.id, false);
|
||||
|
@ -1687,16 +1688,16 @@ public class XmppConnection implements Runnable {
|
|||
} else {
|
||||
resource = this.createNewResource(IDs.tiny(account.randomSeed));
|
||||
}
|
||||
final IQ iq = new IQ(IQ.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() == IQ.Type.TIMEOUT) {
|
||||
if (packet.getType() == Iq.Type.TIMEOUT) {
|
||||
return;
|
||||
}
|
||||
final Element bind = packet.findChild("bind");
|
||||
if (bind != null && packet.getType() == IQ.Type.RESULT) {
|
||||
if (bind != null && packet.getType() == Iq.Type.RESULT) {
|
||||
isBound = true;
|
||||
final String jid = bind.findChildContent("jid");
|
||||
if (Strings.isNullOrEmpty(jid)) {
|
||||
|
@ -1740,7 +1741,7 @@ public class XmppConnection implements Runnable {
|
|||
+ packet);
|
||||
}
|
||||
final Element error = packet.findChild("error");
|
||||
if (packet.getType() == IQ.Type.ERROR
|
||||
if (packet.getType() == Iq.Type.ERROR
|
||||
&& error != null
|
||||
&& error.hasChild("conflict")) {
|
||||
final String alternativeResource = createNewResource(IDs.tiny());
|
||||
|
@ -1764,8 +1765,8 @@ public class XmppConnection implements Runnable {
|
|||
}
|
||||
|
||||
private void clearIqCallbacks() {
|
||||
final IQ failurePacket = new IQ(IQ.Type.TIMEOUT);
|
||||
final ArrayList<Consumer<IQ>> callbacks = new ArrayList<>();
|
||||
final Iq failurePacket = new Iq(Iq.Type.TIMEOUT);
|
||||
final ArrayList<Consumer<Iq>> callbacks = new ArrayList<>();
|
||||
synchronized (this.packetCallbacks) {
|
||||
if (this.packetCallbacks.size() == 0) {
|
||||
return;
|
||||
|
@ -1776,15 +1777,15 @@ public class XmppConnection implements Runnable {
|
|||
+ ": clearing "
|
||||
+ this.packetCallbacks.size()
|
||||
+ " iq callbacks");
|
||||
final Iterator<Pair<IQ, Consumer<IQ>>> iterator =
|
||||
final Iterator<Pair<Iq, Consumer<Iq>>> iterator =
|
||||
this.packetCallbacks.values().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Pair<IQ, Consumer<IQ>> entry = iterator.next();
|
||||
Pair<Iq, Consumer<Iq>> entry = iterator.next();
|
||||
callbacks.add(entry.second);
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
for (final Consumer<IQ> callback : callbacks) {
|
||||
for (final Consumer<Iq> callback : callbacks) {
|
||||
try {
|
||||
callback.accept(failurePacket);
|
||||
} catch (StateChangingError error) {
|
||||
|
@ -1807,15 +1808,15 @@ public class XmppConnection implements Runnable {
|
|||
|
||||
private void sendStartSession() {
|
||||
Log.d(Config.LOGTAG, account.address + ": sending legacy session to outdated server");
|
||||
final IQ startSession = new IQ(IQ.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() == IQ.Type.RESULT) {
|
||||
if (packet.getType() == Iq.Type.RESULT) {
|
||||
enableStreamManagement();
|
||||
sendPostBindInitialization(false);
|
||||
} else if (packet.getType() != IQ.Type.TIMEOUT) {
|
||||
} else if (packet.getType() != Iq.Type.TIMEOUT) {
|
||||
throw new StateChangingError(ConnectionState.SESSION_FAILURE);
|
||||
}
|
||||
},
|
||||
|
@ -2004,15 +2005,15 @@ public class XmppConnection implements Runnable {
|
|||
return String.format("%s.%s", context.getString(R.string.app_name), postfixId);
|
||||
}
|
||||
|
||||
public ListenableFuture<IQ> sendIqPacket(final IQ packet) {
|
||||
final SettableFuture<IQ> future = SettableFuture.create();
|
||||
public ListenableFuture<Iq> sendIqPacket(final Iq packet) {
|
||||
final SettableFuture<Iq> future = SettableFuture.create();
|
||||
sendIqPacket(
|
||||
packet,
|
||||
result -> {
|
||||
final var type = result.getType();
|
||||
if (type == IQ.Type.RESULT) {
|
||||
if (type == Iq.Type.RESULT) {
|
||||
future.set(result);
|
||||
} else if (type == IQ.Type.TIMEOUT) {
|
||||
} else if (type == Iq.Type.TIMEOUT) {
|
||||
future.setException(new TimeoutException());
|
||||
} else {
|
||||
// TODO some sort of IqErrorException
|
||||
|
@ -2022,13 +2023,13 @@ public class XmppConnection implements Runnable {
|
|||
return future;
|
||||
}
|
||||
|
||||
public String sendIqPacket(final IQ packet, final Consumer<IQ> callback) {
|
||||
public String sendIqPacket(final Iq packet, final Consumer<Iq> callback) {
|
||||
packet.setFrom(account.address);
|
||||
return this.sendUnmodifiedIqPacket(packet, callback, false);
|
||||
}
|
||||
|
||||
public synchronized String sendUnmodifiedIqPacket(
|
||||
final IQ packet, final Consumer<IQ> callback, boolean force) {
|
||||
final Iq packet, final Consumer<Iq> callback, boolean force) {
|
||||
if (Strings.isNullOrEmpty(packet.getId())) {
|
||||
packet.setId(IDs.medium());
|
||||
}
|
||||
|
@ -2106,9 +2107,9 @@ public class XmppConnection implements Runnable {
|
|||
|
||||
public void sendPing() {
|
||||
if (!r()) {
|
||||
final IQ iq = new IQ(IQ.Type.GET);
|
||||
final Iq iq = new Iq(Iq.Type.GET);
|
||||
iq.setFrom(account.address);
|
||||
iq.addChild("ping", Namespace.PING);
|
||||
iq.addExtension(new Ping());
|
||||
this.sendIqPacket(iq, null);
|
||||
}
|
||||
this.lastPingSent = SystemClock.elapsedRealtime();
|
||||
|
|
|
@ -7,7 +7,7 @@ import im.conversations.android.xmpp.model.blocking.Block;
|
|||
import im.conversations.android.xmpp.model.blocking.Blocklist;
|
||||
import im.conversations.android.xmpp.model.blocking.Item;
|
||||
import im.conversations.android.xmpp.model.blocking.Unblock;
|
||||
import im.conversations.android.xmpp.model.stanza.IQ;
|
||||
import im.conversations.android.xmpp.model.stanza.Iq;
|
||||
import java.util.Objects;
|
||||
|
||||
public class BlockingManager extends AbstractManager {
|
||||
|
@ -38,13 +38,13 @@ public class BlockingManager extends AbstractManager {
|
|||
}
|
||||
|
||||
public void fetch() {
|
||||
final IQ iqPacket = new IQ(IQ.Type.GET);
|
||||
final Iq iqPacket = new Iq(Iq.Type.GET);
|
||||
iqPacket.addChild(new Blocklist());
|
||||
connection.sendIqPacket(iqPacket, this::handleFetchResult);
|
||||
}
|
||||
|
||||
private void handleFetchResult(final IQ result) {
|
||||
if (result.getType() != IQ.Type.RESULT) {
|
||||
private void handleFetchResult(final Iq result) {
|
||||
if (result.getType() != Iq.Type.RESULT) {
|
||||
return;
|
||||
}
|
||||
final var blocklist = result.getExtension(Blocklist.class);
|
||||
|
|
|
@ -5,7 +5,7 @@ import im.conversations.android.xmpp.XmppConnection;
|
|||
import im.conversations.android.xmpp.model.carbons.Enable;
|
||||
import im.conversations.android.xmpp.model.carbons.Received;
|
||||
import im.conversations.android.xmpp.model.carbons.Sent;
|
||||
import im.conversations.android.xmpp.model.stanza.IQ;
|
||||
import im.conversations.android.xmpp.model.stanza.Iq;
|
||||
import im.conversations.android.xmpp.processor.MessageProcessor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -24,12 +24,12 @@ public class CarbonsManager extends AbstractManager {
|
|||
}
|
||||
|
||||
public void enable() {
|
||||
final var iq = new IQ(IQ.Type.SET);
|
||||
final var iq = new Iq(Iq.Type.SET);
|
||||
iq.addExtension(new Enable());
|
||||
connection.sendIqPacket(
|
||||
iq,
|
||||
result -> {
|
||||
if (result.getType() == IQ.Type.RESULT) {
|
||||
if (result.getType() == Iq.Type.RESULT) {
|
||||
LOGGER.info("{}: successfully enabled carbons", getAccount().address);
|
||||
this.enabled = true;
|
||||
} else {
|
||||
|
|
|
@ -17,7 +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 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 IQ(IQ.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<Collection<Item>> items(
|
||||
@NonNull final Entity.DiscoItem entity, @Nullable final String node) {
|
||||
final var requestNode = Strings.emptyToNull(node);
|
||||
final var iqPacket = new IQ(IQ.Type.GET);
|
||||
final var iqPacket = new Iq(Iq.Type.GET);
|
||||
iqPacket.setTo(entity.address);
|
||||
final ItemsQuery itemsQueryRequest = iqPacket.addExtension(new ItemsQuery());
|
||||
if (requestNode != null) {
|
||||
|
|
|
@ -6,7 +6,7 @@ import com.google.common.collect.Collections2;
|
|||
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 im.conversations.android.xmpp.model.stanza.Iq;
|
||||
import java.util.Objects;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -29,7 +29,7 @@ public class RosterManager extends AbstractManager {
|
|||
final var account = getAccount();
|
||||
final var database = getDatabase();
|
||||
final String rosterVersion = database.accountDao().getRosterVersion(account.id);
|
||||
final IQ iqPacket = new IQ(IQ.Type.GET);
|
||||
final Iq iqPacket = new Iq(Iq.Type.GET);
|
||||
final Query rosterQuery = new Query();
|
||||
iqPacket.addChild(rosterQuery);
|
||||
if (Strings.isNullOrEmpty(rosterVersion)) {
|
||||
|
@ -41,8 +41,8 @@ public class RosterManager extends AbstractManager {
|
|||
connection.sendIqPacket(iqPacket, this::handleFetchResult);
|
||||
}
|
||||
|
||||
private void handleFetchResult(final IQ result) {
|
||||
if (result.getType() != IQ.Type.RESULT) {
|
||||
private void handleFetchResult(final Iq result) {
|
||||
if (result.getType() != Iq.Type.RESULT) {
|
||||
return;
|
||||
}
|
||||
final var query = result.getExtension(Query.class);
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
package im.conversations.android.xmpp.model.error;
|
||||
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
import im.conversations.android.annotation.XmlElement;
|
||||
import im.conversations.android.xmpp.model.Extension;
|
||||
|
||||
public abstract class Condition extends Extension {
|
||||
|
||||
public Condition(Class<? extends Extension> clazz) {
|
||||
super(clazz);
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class BadRequest extends Condition {
|
||||
|
||||
public BadRequest() {
|
||||
super(BadRequest.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class Conflict extends Condition {
|
||||
|
||||
public Conflict() {
|
||||
super(Conflict.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class FeatureNotImplemented extends Condition {
|
||||
|
||||
public FeatureNotImplemented() {
|
||||
super(FeatureNotImplemented.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class Forbidden extends Condition {
|
||||
|
||||
public Forbidden() {
|
||||
super(Forbidden.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class Gone extends Condition {
|
||||
|
||||
public Gone() {
|
||||
super(Gone.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class InternalServerError extends Condition {
|
||||
|
||||
public InternalServerError() {
|
||||
super(InternalServerError.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class ItemNotFound extends Condition {
|
||||
|
||||
public ItemNotFound() {
|
||||
super(ItemNotFound.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class JidMalformed extends Condition {
|
||||
|
||||
public JidMalformed() {
|
||||
super(JidMalformed.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class NotAcceptable extends Condition {
|
||||
|
||||
public NotAcceptable() {
|
||||
super(NotAcceptable.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class NotAllowed extends Condition {
|
||||
|
||||
public NotAllowed() {
|
||||
super(NotAllowed.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class NotAuthorized extends Condition {
|
||||
|
||||
public NotAuthorized() {
|
||||
super(NotAuthorized.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class PaymentRequired extends Condition {
|
||||
|
||||
public PaymentRequired() {
|
||||
super(PaymentRequired.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class RecipientUnavailable extends Condition {
|
||||
|
||||
public RecipientUnavailable() {
|
||||
super(RecipientUnavailable.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class Redirect extends Condition {
|
||||
|
||||
public Redirect() {
|
||||
super(Redirect.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class RegistrationRequired extends Condition {
|
||||
|
||||
public RegistrationRequired() {
|
||||
super(RegistrationRequired.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class RemoteServerNotFound extends Condition {
|
||||
|
||||
public RemoteServerNotFound() {
|
||||
super(RemoteServerNotFound.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class RemoteServerTimeout extends Condition {
|
||||
|
||||
public RemoteServerTimeout() {
|
||||
super(RemoteServerTimeout.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class ResourceConstraint extends Condition {
|
||||
|
||||
public ResourceConstraint() {
|
||||
super(ResourceConstraint.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class ServiceUnavailable extends Condition {
|
||||
|
||||
public ServiceUnavailable() {
|
||||
super(ServiceUnavailable.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class SubscriptionRequired extends Condition {
|
||||
|
||||
public SubscriptionRequired() {
|
||||
super(SubscriptionRequired.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class UndefinedCondition extends Condition {
|
||||
|
||||
public UndefinedCondition() {
|
||||
super(UndefinedCondition.class);
|
||||
}
|
||||
}
|
||||
|
||||
@XmlElement(namespace = Namespace.STANZAS)
|
||||
public static class UnexpectedRequest extends Condition {
|
||||
|
||||
public UnexpectedRequest() {
|
||||
super(UnexpectedRequest.class);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package im.conversations.android.xmpp.model.error;
|
||||
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
import im.conversations.android.annotation.XmlElement;
|
||||
import im.conversations.android.xmpp.model.Extension;
|
||||
|
||||
@XmlElement(namespace = Namespace.JABBER_CLIENT)
|
||||
public class Error extends Extension {
|
||||
|
||||
public Error() {
|
||||
super(Error.class);
|
||||
}
|
||||
|
||||
public Condition getCondition() {
|
||||
return this.getExtension(Condition.class);
|
||||
}
|
||||
|
||||
public void setCondition(final Condition condition) {
|
||||
this.addExtension(condition);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package im.conversations.android.xmpp.model.ping;
|
||||
|
||||
import eu.siacs.conversations.xml.Namespace;
|
||||
import im.conversations.android.annotation.XmlElement;
|
||||
import im.conversations.android.xmpp.model.Extension;
|
||||
|
||||
@XmlElement(namespace = Namespace.PING)
|
||||
public class Ping extends Extension {
|
||||
|
||||
public Ping() {
|
||||
super(Ping.class);
|
||||
}
|
||||
}
|
|
@ -5,14 +5,14 @@ import im.conversations.android.annotation.XmlElement;
|
|||
import java.util.Locale;
|
||||
|
||||
@XmlElement
|
||||
public class IQ extends Stanza {
|
||||
public class Iq extends Stanza {
|
||||
|
||||
public IQ() {
|
||||
super(IQ.class);
|
||||
public Iq() {
|
||||
super(Iq.class);
|
||||
}
|
||||
|
||||
public IQ(final Type type) {
|
||||
super(IQ.class);
|
||||
public Iq(final Type type) {
|
||||
super(Iq.class);
|
||||
this.setAttribute("type", type.toString().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ 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;
|
||||
import im.conversations.android.xmpp.model.error.Error;
|
||||
|
||||
public abstract class Stanza extends StreamElement {
|
||||
|
||||
|
@ -33,4 +34,8 @@ public abstract class Stanza extends StreamElement {
|
|||
public void setTo(final Jid to) {
|
||||
this.setAttribute("to", to);
|
||||
}
|
||||
|
||||
public Error getError() {
|
||||
return this.getExtension(Error.class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import im.conversations.android.xmpp.manager.BlockingManager;
|
|||
import im.conversations.android.xmpp.manager.BookmarkManager;
|
||||
import im.conversations.android.xmpp.manager.DiscoManager;
|
||||
import im.conversations.android.xmpp.manager.RosterManager;
|
||||
import im.conversations.android.xmpp.model.stanza.Presence;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class BindProcessor extends XmppConnection.Delegate implements Consumer<Jid> {
|
||||
|
@ -47,7 +46,7 @@ public class BindProcessor extends XmppConnection.Delegate implements Consumer<J
|
|||
|
||||
getManager(BookmarkManager.class).fetch();
|
||||
|
||||
connection.sendPresencePacket(new Presence());
|
||||
// connection.sendPresencePacket(new Presence());
|
||||
|
||||
// TODO send initial presence
|
||||
}
|
||||
|
|
|
@ -5,41 +5,83 @@ import com.google.common.base.Preconditions;
|
|||
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.Extension;
|
||||
import im.conversations.android.xmpp.model.blocking.Block;
|
||||
import im.conversations.android.xmpp.model.blocking.Unblock;
|
||||
import im.conversations.android.xmpp.model.error.Condition;
|
||||
import im.conversations.android.xmpp.model.error.Error;
|
||||
import im.conversations.android.xmpp.model.ping.Ping;
|
||||
import im.conversations.android.xmpp.model.roster.Query;
|
||||
import im.conversations.android.xmpp.model.stanza.IQ;
|
||||
import im.conversations.android.xmpp.model.stanza.Iq;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Consumer;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class IqProcessor extends XmppConnection.Delegate implements Consumer<IQ> {
|
||||
public class IqProcessor extends XmppConnection.Delegate implements Consumer<Iq> {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(IqProcessor.class);
|
||||
|
||||
public IqProcessor(final Context context, final XmppConnection connection) {
|
||||
super(context, connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
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));
|
||||
sendResultFor(packet);
|
||||
return;
|
||||
}
|
||||
if (type == IQ.Type.SET
|
||||
if (type == Iq.Type.SET
|
||||
&& connection.fromAccount(packet)
|
||||
&& packet.hasExtension(Block.class)) {
|
||||
getManager(BlockingManager.class).handlePush(packet.getExtension(Block.class));
|
||||
sendResultFor(packet);
|
||||
return;
|
||||
}
|
||||
if (type == IQ.Type.SET
|
||||
if (type == Iq.Type.SET
|
||||
&& connection.fromAccount(packet)
|
||||
&& packet.hasExtension(Unblock.class)) {
|
||||
getManager(BlockingManager.class).handlePush(packet.getExtension(Unblock.class));
|
||||
sendResultFor(packet);
|
||||
return;
|
||||
}
|
||||
// TODO return feature not implemented
|
||||
if (type == Iq.Type.GET && packet.hasExtension(Ping.class)) {
|
||||
LOGGER.debug("Responding to ping from {}", packet.getFrom());
|
||||
sendResultFor(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
final var extensionIds = packet.getExtensionIds();
|
||||
LOGGER.info("Could not handle {}. Sending feature-not-implemented", extensionIds);
|
||||
sendErrorFor(packet, new Condition.FeatureNotImplemented());
|
||||
}
|
||||
|
||||
public void sendResultFor(final Iq request, final Extension... extensions) {
|
||||
final var from = request.getFrom();
|
||||
final var id = request.getId();
|
||||
final var response = new Iq(Iq.Type.RESULT);
|
||||
response.setTo(from);
|
||||
response.setId(id);
|
||||
for (final Extension extension : extensions) {
|
||||
response.addExtension(extension);
|
||||
}
|
||||
connection.sendIqPacket(response);
|
||||
}
|
||||
|
||||
public void sendErrorFor(final Iq request, final Condition condition) {
|
||||
final var from = request.getFrom();
|
||||
final var id = request.getId();
|
||||
final var response = new Iq(Iq.Type.ERROR);
|
||||
response.setTo(from);
|
||||
response.setId(id);
|
||||
final Error error = response.addExtension(new Error());
|
||||
error.setCondition(condition);
|
||||
connection.sendIqPacket(response);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,5 +42,14 @@ public class MessageProcessor extends XmppConnection.Delegate implements Consume
|
|||
if (!Strings.isNullOrEmpty(body)) {
|
||||
LOGGER.info("'{}' from {}", body, message.getFrom());
|
||||
}
|
||||
|
||||
// TODO process receipt requests (184 + 333)
|
||||
|
||||
// TODO collect Extensions that require transformation (everything that will end up in the
|
||||
// message tables)
|
||||
|
||||
// TODO pass pubsub events to pubsub manager
|
||||
|
||||
// TODO pass JMI to JingleManager
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,11 @@ import static org.junit.Assert.assertEquals;
|
|||
|
||||
import eu.siacs.conversations.xml.Element;
|
||||
import eu.siacs.conversations.xml.XmlElementReader;
|
||||
import im.conversations.android.xmpp.model.error.Condition;
|
||||
import im.conversations.android.xmpp.model.error.Error;
|
||||
import im.conversations.android.xmpp.model.roster.Item;
|
||||
import im.conversations.android.xmpp.model.roster.Query;
|
||||
import im.conversations.android.xmpp.model.stanza.Message;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
|
@ -31,4 +34,23 @@ public class XmlElementReaderTest {
|
|||
final Collection<Item> items = query.getExtensions(Item.class);
|
||||
assertEquals(2, items.size());
|
||||
}
|
||||
|
||||
public void readMessageError() throws IOException {
|
||||
final String xml =
|
||||
"<message\n"
|
||||
+ " to='juliet@capulet.com/balcony'\n"
|
||||
+ " from='romeo@montague.net/garden'\n"
|
||||
+ " xmlns='jabber:client'\n"
|
||||
+ " type='error'>\n"
|
||||
+ " <body>Wherefore art thou, Romeo?</body>\n"
|
||||
+ " <error code='404' type='cancel'>\n"
|
||||
+ " <item-not-found xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>\n"
|
||||
+ " </error>\n"
|
||||
+ "</message>";
|
||||
final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
|
||||
assertThat(element, instanceOf(Message.class));
|
||||
final Message message = (Message) element;
|
||||
final Error error = message.getError();
|
||||
assertThat(error.getCondition(), instanceOf(Condition.ItemNotFound.class));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue