From dcb66eef592bf3959a3aaafae0802e0b35500e2d Mon Sep 17 00:00:00 2001 From: Aditya Borikar Date: Fri, 19 Jun 2020 05:12:03 +0530 Subject: [PATCH 1/7] Add support for HTTP lookup method through xep-0156 --- documentation/extensions/index.md | 1 + .../altconnections/HttpLookupMethod.java | 176 ++++++++++++++++++ .../smack/altconnections/package-info.java | 22 +++ .../altconnections/HttpLookupMethodTest.java | 57 ++++++ 4 files changed, 256 insertions(+) create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/altconnections/HttpLookupMethod.java create mode 100644 smack-core/src/main/java/org/jivesoftware/smack/altconnections/package-info.java create mode 100644 smack-core/src/test/java/org/jivesoftware/smack/altconnections/HttpLookupMethodTest.java diff --git a/documentation/extensions/index.md b/documentation/extensions/index.md index 0223d1554..78a2bbc57 100644 --- a/documentation/extensions/index.md +++ b/documentation/extensions/index.md @@ -14,6 +14,7 @@ Currently supported XEPs of Smack (all sub-projects) | Name | XEP | Version | Description | |---------------------------------------------|--------------------------------------------------------|-----------|----------------------------------------------------------------------------------------------------------| +| Discovering Alternative XMPP Connection Methods | [XEP-0156](https://xmpp.org/extensions/xep-0156.html) | 1.3.0 | Defines ways to discover alternative connection methods. | | Nonzas | [XEP-0360](https://xmpp.org/extensions/xep-0360.html) | n/a | Defines the term "Nonza", describing every top level stream element that is not a Stanza. | Currently supported XEPs of smack-tcp diff --git a/smack-core/src/main/java/org/jivesoftware/smack/altconnections/HttpLookupMethod.java b/smack-core/src/main/java/org/jivesoftware/smack/altconnections/HttpLookupMethod.java new file mode 100644 index 000000000..75a4cf263 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/altconnections/HttpLookupMethod.java @@ -0,0 +1,176 @@ +/** + * + * Copyright 2020 Aditya Borikar. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.altconnections; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smack.xml.XmlPullParserException; + +import org.jxmpp.jid.DomainBareJid; + +/** + * The HTTP lookup method uses web host metadata to list the URIs of alternative connection methods. + * + *

In order to obtain host-meta XRD element from the host in the form of an InputStream, + * use {@link #getXrdStream(DomainBareJid)} method. To obtain endpoints for Bosh or Websocket + * connection endpoints from host, use {@link LinkRelation#BOSH} and {@link LinkRelation#WEBSOCKET} + * respectively with the {@link #lookup(DomainBareJid, LinkRelation)} method. In case one is looking + * for endpoints described by other than BOSH or Websocket LinkRelation, use the more flexible + * {@link #lookup(DomainBareJid, String)} method.

+ * Example:
+ *
+ * {@code
+ * DomainBareJid xmppServerAddress = JidCreate.domainBareFrom("example.org");
+ * List endpoints = HttpLookupMethod.lookup(xmppServiceAddress, LinkRelation.WEBSOCKET);
+ * }
+ * 
+ * @see + * HTTP Lookup Method from XEP-0156. + * + */ +public final class HttpLookupMethod { + private static final String XRD_NAMESPACE = "http://docs.oasis-open.org/ns/xri/xrd-1.0"; + + /** + * Specifies a link relation for the selected type of connection. + */ + public enum LinkRelation { + /** + * Selects link relation attribute as "urn:xmpp:alt-connections:xbosh". + */ + BOSH("urn:xmpp:alt-connections:xbosh"), + /** + * Selects link relation attribute as "urn:xmpp:alt-connections:websocket". + */ + WEBSOCKET("urn:xmpp:alt-connections:websocket"); + private final String attribute; + LinkRelation(String relAttribute) { + this.attribute = relAttribute; + } + } + + /** + * Get remote endpoints for the given LinkRelation from host. + * + * @param xmppServiceAddress address of host + * @param relation LinkRelation as a string specifying type of connection + * @return list of endpoints + * @throws IOException exception due to input/output operations + * @throws XmlPullParserException exception encountered during XML parsing + * @throws URISyntaxException exception to indicate that a string could not be parsed as a URI reference + */ + public static List lookup(DomainBareJid xmppServiceAddress, String relation) throws IOException, XmlPullParserException, URISyntaxException { + try (InputStream inputStream = getXrdStream(xmppServiceAddress)) { + XmlPullParser xmlPullParser = PacketParserUtils.getParserFor(inputStream); + List endpoints = parseXrdLinkReferencesFor(xmlPullParser, relation); + return endpoints; + } + } + + /** + * Get remote endpoints for the given LinkRelation from host. + * + * @param xmppServiceAddress address of host + * @param relation {@link LinkRelation} specifying type of connection + * @return list of endpoints + * @throws IOException exception due to input/output operations + * @throws XmlPullParserException exception encountered during XML parsing + * @throws URISyntaxException exception to indicate that a string could not be parsed as a URI reference + */ + public static List lookup(DomainBareJid xmppServiceAddress, LinkRelation relation) throws IOException, XmlPullParserException, URISyntaxException { + return lookup(xmppServiceAddress, relation.attribute); + } + + /** + * Constructs a HTTP connection with the host specified by the DomainBareJid + * and retrieves XRD element in the form of an InputStream. The method will + * throw a {@link FileNotFoundException} if host-meta isn't published. + * + * @param xmppServiceAddress address of host + * @return InputStream containing XRD element + * @throws IOException exception due to input/output operations + */ + public static InputStream getXrdStream(DomainBareJid xmppServiceAddress) throws IOException { + final String metadataUrl = "https://" + xmppServiceAddress + "/.well-known/host-meta"; + final URL putUrl = new URL(metadataUrl); + final URLConnection urlConnection = putUrl.openConnection(); + return urlConnection.getInputStream(); + } + + /** + * Get remote endpoints for the provided LinkRelation from provided XmlPullParser. + * + * @param parser XmlPullParser that contains LinkRelations + * @param relation type of endpoints specified by the given LinkRelation + * @return list of endpoints + * @throws IOException exception due to input/output operations + * @throws XmlPullParserException exception encountered during XML parsing + * @throws URISyntaxException exception to indicate that a string could not be parsed as a URI reference + */ + public static List parseXrdLinkReferencesFor(XmlPullParser parser, String relation) throws IOException, XmlPullParserException, URISyntaxException { + List uriList = new ArrayList<>(); + int initialDepth = parser.getDepth(); + + loop: while (true) { + XmlPullParser.TagEvent tag = parser.nextTag(); + switch (tag) { + case START_ELEMENT: + String name = parser.getName(); + String namespace = parser.getNamespace(); + String rel = parser.getAttributeValue("rel"); + + if (!namespace.equals(XRD_NAMESPACE) || !name.equals("Link") || !rel.equals(relation)) { + continue loop; + } + String endpointUri = parser.getAttributeValue("href"); + URI uri = new URI(endpointUri); + uriList.add(uri); + break; + case END_ELEMENT: + if (parser.getDepth() == initialDepth) { + break loop; + } + break; + } + } + return uriList; + } + + /** + * Get remote endpoints for the provided LinkRelation from provided XmlPullParser. + * + * @param parser XmlPullParser that contains LinkRelations + * @param relation type of endpoints specified by the given LinkRelation + * @return list of endpoints + * @throws IOException exception due to input/output operations + * @throws XmlPullParserException exception encountered during XML parsing + * @throws URISyntaxException exception to indicate that a string could not be parsed as a URI reference + */ + public static List parseXrdLinkReferencesFor(XmlPullParser parser, LinkRelation relation) throws IOException, XmlPullParserException, URISyntaxException { + return parseXrdLinkReferencesFor(parser, relation.attribute); + } +} diff --git a/smack-core/src/main/java/org/jivesoftware/smack/altconnections/package-info.java b/smack-core/src/main/java/org/jivesoftware/smack/altconnections/package-info.java new file mode 100644 index 000000000..5d9955232 --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/altconnections/package-info.java @@ -0,0 +1,22 @@ +/** + * + * Copyright 2020 Aditya Borikar. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * Smack's API for XEP-0156: Discovering Alternative XMPP Connection Methods. + *
+ * XEP is partially supported as HTTP lookup is supported but DNS lookup isn't. + */ +package org.jivesoftware.smack.altconnections; diff --git a/smack-core/src/test/java/org/jivesoftware/smack/altconnections/HttpLookupMethodTest.java b/smack-core/src/test/java/org/jivesoftware/smack/altconnections/HttpLookupMethodTest.java new file mode 100644 index 000000000..efa503ac1 --- /dev/null +++ b/smack-core/src/test/java/org/jivesoftware/smack/altconnections/HttpLookupMethodTest.java @@ -0,0 +1,57 @@ +/** + * + * Copyright 2020 Aditya Borikar. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.altconnections; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; + +import org.jivesoftware.smack.altconnections.HttpLookupMethod.LinkRelation; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smack.xml.XmlPullParserException; + +import org.junit.jupiter.api.Test; +import org.jxmpp.stringprep.XmppStringprepException; + +public class HttpLookupMethodTest { + private static final String XRD_XML = "" + + "" + + "" + + "" + + ""; + + @Test + public void parseXrdLinkReferencesForWebsockets() throws XmppStringprepException, IOException, XmlPullParserException, URISyntaxException { + List endpoints = new ArrayList<>(); + endpoints.add(new URI("wss://xmpp.igniterealtime.org:7483/ws/")); + endpoints.add(new URI("ws://xmpp.igniterealtime.org:7070/ws/")); + List expectedEndpoints = HttpLookupMethod.parseXrdLinkReferencesFor(PacketParserUtils.getParserFor(XRD_XML), LinkRelation.WEBSOCKET); + assertEquals(expectedEndpoints, endpoints); + } + + @Test + public void parseXrdLinkReferencesForBosh() throws URISyntaxException, IOException, XmlPullParserException { + List endpoints = new ArrayList<>(); + endpoints.add(new URI("https://igniterealtime.org:443/http-bind/")); + List expectedEndpoints = HttpLookupMethod.parseXrdLinkReferencesFor(PacketParserUtils.getParserFor(XRD_XML), LinkRelation.BOSH); + assertEquals(expectedEndpoints, endpoints); + } +} From 0eeb89409a0de7714b05655801978f9acea5d438 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 28 Jun 2020 17:42:01 +0200 Subject: [PATCH 2/7] [gradle] Add fix for javadoc search --- build.gradle | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/build.gradle b/build.gradle index 2e1270a4c..b962c9105 100644 --- a/build.gradle +++ b/build.gradle @@ -259,6 +259,13 @@ allprojects { if (JavaVersion.current().isJava9Compatible()) { tasks.withType(Javadoc) { options.addStringOption('-release', javaMajor) + + // Fix for javadoc search. If not set, the search result would direct to + // javadoc/undefined/org/jivesoftware/smack/altconnections/HttpLookupMethod.html + // instead of + // javadoc/org/jivesoftware/smack/altconnections/HttpLookupMethod.html + // https://stackoverflow.com/a/53732633/194894 + options.addBooleanOption("-no-module-directories", true) } tasks.withType(JavaCompile) { options.compilerArgs.addAll([ From 45f75d5ce0dcb4e8c68ea59109211dda8799565f Mon Sep 17 00:00:00 2001 From: Aditya Borikar Date: Sat, 27 Jun 2020 02:07:42 +0530 Subject: [PATCH 3/7] Remove unrequired assignment of value to connectionEndpoint variable The current code would work just fine for a connection having multiple endpoints. However, when there is only one endpoint ConnectionAttemptState.nextAddress() would return null, since connectionEndpointIterator has already iterated over the only possible value in the contructor leading to a NullPointerException. This means that during establishment of a connection having multiple endpoints, the first value inside connectionEndpointIterator would always be overlooked. --- .../java/org/jivesoftware/smack/tcp/ConnectionAttemptState.java | 1 - 1 file changed, 1 deletion(-) diff --git a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/ConnectionAttemptState.java b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/ConnectionAttemptState.java index d8a3890e0..1a092edcd 100644 --- a/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/ConnectionAttemptState.java +++ b/smack-tcp/src/main/java/org/jivesoftware/smack/tcp/ConnectionAttemptState.java @@ -69,7 +69,6 @@ public final class ConnectionAttemptState { List endpoints = discoveredEndpoints.result.discoveredRemoteConnectionEndpoints; connectionEndpointIterator = endpoints.iterator(); - connectionEndpoint = connectionEndpointIterator.next(); connectionExceptions = new ArrayList<>(endpoints.size()); } From 049d68777868cb530eec6d51553a14cbfed0e100 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Sun, 28 Jun 2020 17:42:01 +0200 Subject: [PATCH 4/7] [gradle] Add fix for javadoc search --- build.gradle | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/build.gradle b/build.gradle index 2e1270a4c..b962c9105 100644 --- a/build.gradle +++ b/build.gradle @@ -259,6 +259,13 @@ allprojects { if (JavaVersion.current().isJava9Compatible()) { tasks.withType(Javadoc) { options.addStringOption('-release', javaMajor) + + // Fix for javadoc search. If not set, the search result would direct to + // javadoc/undefined/org/jivesoftware/smack/altconnections/HttpLookupMethod.html + // instead of + // javadoc/org/jivesoftware/smack/altconnections/HttpLookupMethod.html + // https://stackoverflow.com/a/53732633/194894 + options.addBooleanOption("-no-module-directories", true) } tasks.withType(JavaCompile) { options.compilerArgs.addAll([ From ed02bcf0d4a6e67fb496cb30bf15a6977825c37b Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Fri, 3 Jul 2020 21:55:36 +0200 Subject: [PATCH 5/7] [ibb] Ensure InBandBytestreamManager is a singleton InBandBytestreamManager followed an unusual pattern: Within the connectionTermianted() callback, it would remove itself from the 'managers' map. This allowed for multiple instances of an InBandBytestreamManager to exist for the same connection, causing all kinds of issues. This fixes the issue by changing InBandBytestreamManager to use the Smack-idiomatic pattern used by managers. We also do no longer reset the listeners if the connection is termianted, as listeners (and handlers) typically persist until they are explicitly removed by the user. As positive side-effect, the number of indeterministic unit-tests, caused by using Thread.sleep(), is reduced. The executor service in InitiationListener was also removed, because the IQ handler is already called asynchronously to the connections main loop. Thanks to Anno van Vliet for reporting this. --- .../ibb/InBandBytestreamManager.java | 64 +++--------------- .../bytestreams/ibb/InitiationListener.java | 67 ++++++------------- .../ibb/InitiationListenerTest.java | 48 ++++--------- 3 files changed, 41 insertions(+), 138 deletions(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java index c54d02e17..9eda2a49c 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InBandBytestreamManager.java @@ -110,22 +110,6 @@ public final class InBandBytestreamManager extends Manager implements Bytestream public void connectionCreated(final XMPPConnection connection) { // create the manager for this connection InBandBytestreamManager.getByteStreamManager(connection); - - // register shutdown listener - connection.addConnectionListener(new AbstractConnectionClosedListener() { - - @Override - public void connectionTerminated() { - InBandBytestreamManager.getByteStreamManager(connection).disableService(); - } - - @Override - public void connected(XMPPConnection connection) { - InBandBytestreamManager.getByteStreamManager(connection); - } - - }); - } }); } @@ -206,6 +190,15 @@ public final class InBandBytestreamManager extends Manager implements Bytestream private InBandBytestreamManager(XMPPConnection connection) { super(connection); + connection.addConnectionListener(new AbstractConnectionClosedListener() { + @Override + public void connectionTerminated() { + // reset internal status + InBandBytestreamManager.this.sessions.clear(); + InBandBytestreamManager.this.ignoredBytestreamRequests.clear(); + } + }); + // register bytestream open packet listener this.initiationListener = new InitiationListener(this); connection.registerIQRequestHandler(initiationListener); @@ -453,19 +446,6 @@ public final class InBandBytestreamManager extends Manager implements Bytestream connection().sendStanza(error); } - /** - * Responses to the given IQ packet's sender with an XMPP error that an In-Band Bytestream open - * request is rejected because its block size is greater than the maximum allowed block size. - * - * @param request IQ stanza that should be answered with a resource-constraint error - * @throws NotConnectedException if the XMPP connection is not connected. - * @throws InterruptedException if the calling thread was interrupted. - */ - protected void replyResourceConstraintPacket(IQ request) throws NotConnectedException, InterruptedException { - IQ error = IQ.createErrorResponse(request, StanzaError.Condition.resource_constraint); - connection().sendStanza(error); - } - /** * Responses to the given IQ packet's sender with an XMPP error that an In-Band Bytestream * session could not be found. @@ -539,30 +519,4 @@ public final class InBandBytestreamManager extends Manager implements Bytestream return ignoredBytestreamRequests; } - /** - * Disables the InBandBytestreamManager by removing its stanza listeners and resetting its - * internal status, which includes removing this instance from the managers map. - */ - private void disableService() { - final XMPPConnection connection = connection(); - - // remove manager from static managers map - managers.remove(connection); - - // remove all listeners registered by this manager - connection.unregisterIQRequestHandler(initiationListener); - connection.unregisterIQRequestHandler(dataListener); - connection.unregisterIQRequestHandler(closeListener); - - // shutdown threads - this.initiationListener.shutdown(); - - // reset internal status - this.userListeners.clear(); - this.allRequestListeners.clear(); - this.sessions.clear(); - this.ignoredBytestreamRequests.clear(); - - } - } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListener.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListener.java index e5de08106..c03d1ce6a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListener.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListener.java @@ -16,15 +16,9 @@ */ package org.jivesoftware.smackx.bytestreams.ibb; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.iqrequest.AbstractIqRequestHandler; import org.jivesoftware.smack.packet.IQ; -import org.jivesoftware.smack.packet.Stanza; +import org.jivesoftware.smack.packet.StanzaError; import org.jivesoftware.smackx.bytestreams.BytestreamListener; import org.jivesoftware.smackx.bytestreams.ibb.packet.Open; @@ -44,14 +38,10 @@ import org.jivesoftware.smackx.filetransfer.StreamNegotiator; * @author Henning Staib */ class InitiationListener extends AbstractIqRequestHandler { - private static final Logger LOGGER = Logger.getLogger(InitiationListener.class.getName()); /* manager containing the listeners and the XMPP connection */ private final InBandBytestreamManager manager; - /* executor service to process incoming requests concurrently */ - private final ExecutorService initiationListenerExecutor; - /** * Constructor. * @@ -60,40 +50,29 @@ class InitiationListener extends AbstractIqRequestHandler { protected InitiationListener(InBandBytestreamManager manager) { super(Open.ELEMENT, Open.NAMESPACE, IQ.Type.set, Mode.async); this.manager = manager; - initiationListenerExecutor = Executors.newCachedThreadPool(); } @Override - public IQ handleIQRequest(final IQ packet) { - initiationListenerExecutor.execute(new Runnable() { - - @Override - public void run() { - try { - processRequest(packet); - } - catch (InterruptedException | NotConnectedException e) { - LOGGER.log(Level.WARNING, "proccessRequest", e); - } - } - }); - return null; - } - - private void processRequest(Stanza packet) throws NotConnectedException, InterruptedException { - Open ibbRequest = (Open) packet; + public IQ handleIQRequest(final IQ iqRequest) { + Open ibbRequest = (Open) iqRequest; + int blockSize = ibbRequest.getBlockSize(); + int maximumBlockSize = manager.getMaximumBlockSize(); // validate that block size is within allowed range - if (ibbRequest.getBlockSize() > this.manager.getMaximumBlockSize()) { - this.manager.replyResourceConstraintPacket(ibbRequest); - return; + if (blockSize > maximumBlockSize) { + StanzaError error = StanzaError.getBuilder().setCondition(StanzaError.Condition.resource_constraint) + .setDescriptiveEnText("Requests block size of " + blockSize + " exceeds maximum block size of " + + maximumBlockSize) + .build(); + return IQ.createErrorResponse(iqRequest, error); } StreamNegotiator.signal(ibbRequest.getFrom().toString() + '\t' + ibbRequest.getSessionID(), ibbRequest); // ignore request if in ignore list - if (this.manager.getIgnoredBytestreamRequests().remove(ibbRequest.getSessionID())) - return; + if (this.manager.getIgnoredBytestreamRequests().remove(ibbRequest.getSessionID())) { + return null; + } // build bytestream request from packet InBandBytestreamRequest request = new InBandBytestreamRequest(this.manager, ibbRequest); @@ -102,7 +81,6 @@ class InitiationListener extends AbstractIqRequestHandler { BytestreamListener userListener = this.manager.getUserListener(ibbRequest.getFrom()); if (userListener != null) { userListener.incomingBytestreamRequest(request); - } else if (!this.manager.getAllRequestListeners().isEmpty()) { /* @@ -111,21 +89,16 @@ class InitiationListener extends AbstractIqRequestHandler { for (BytestreamListener listener : this.manager.getAllRequestListeners()) { listener.incomingBytestreamRequest(request); } - } else { - /* - * if there is no listener for this initiation request, reply with reject message - */ - this.manager.replyRejectPacket(ibbRequest); + StanzaError error = StanzaError.getBuilder() + .setCondition(StanzaError.Condition.not_acceptable) + .setDescriptiveEnText("No file-transfer listeners registered") + .build(); + return IQ.createErrorResponse(iqRequest, error); } - } - /** - * Shuts down the listeners executor service. - */ - protected void shutdown() { - this.initiationListenerExecutor.shutdownNow(); + return null; } } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListenerTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListenerTest.java index 3f4591212..a3136e4d6 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListenerTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/ibb/InitiationListenerTest.java @@ -83,23 +83,14 @@ public class InitiationListenerTest extends SmackTestSuite { */ @Test public void shouldRespondWithError() throws Exception { - // run the listener with the initiation packet - initiationListener.handleIQRequest(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // capture reply to the In-Band Bytestream open request - ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); - verify(connection).sendStanza(argument.capture()); + IQ response = initiationListener.handleIQRequest(initBytestream); // assert that reply is the correct error packet - assertEquals(initiatorJID, argument.getValue().getTo()); - assertEquals(IQ.Type.error, argument.getValue().getType()); + assertEquals(initiatorJID, response.getTo()); + assertEquals(IQ.Type.error, response.getType()); assertEquals(StanzaError.Condition.not_acceptable, - argument.getValue().getError().getCondition()); - + response.getError().getCondition()); } /** @@ -113,21 +104,13 @@ public class InitiationListenerTest extends SmackTestSuite { byteStreamManager.setMaximumBlockSize(1024); // run the listener with the initiation packet - initiationListener.handleIQRequest(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); - - // capture reply to the In-Band Bytestream open request - ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); - verify(connection).sendStanza(argument.capture()); + IQ response = initiationListener.handleIQRequest(initBytestream); // assert that reply is the correct error packet - assertEquals(initiatorJID, argument.getValue().getTo()); - assertEquals(IQ.Type.error, argument.getValue().getType()); + assertEquals(initiatorJID, response.getTo()); + assertEquals(IQ.Type.error, response.getType()); assertEquals(StanzaError.Condition.resource_constraint, - argument.getValue().getError().getCondition()); - + response.getError().getCondition()); } /** @@ -199,24 +182,17 @@ public class InitiationListenerTest extends SmackTestSuite { byteStreamManager.addIncomingBytestreamListener(listener, JidCreate.from("other_" + initiatorJID)); // run the listener with the initiation packet - initiationListener.handleIQRequest(initBytestream); - - // wait because packet is processed in an extra thread - Thread.sleep(200); + IQ response = initiationListener.handleIQRequest(initBytestream); // assert listener is not called ArgumentCaptor byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); verify(listener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); - // capture reply to the In-Band Bytestream open request - ArgumentCaptor argument = ArgumentCaptor.forClass(IQ.class); - verify(connection).sendStanza(argument.capture()); - // assert that reply is the correct error packet - assertEquals(initiatorJID, argument.getValue().getTo()); - assertEquals(IQ.Type.error, argument.getValue().getType()); + assertEquals(initiatorJID, response.getTo()); + assertEquals(IQ.Type.error, response.getType()); assertEquals(StanzaError.Condition.not_acceptable, - argument.getValue().getError().getCondition()); + response.getError().getCondition()); } /** From 3e8666cd9197f94ccbfe0eaf184de707fb263496 Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 6 Jul 2020 11:24:13 +0200 Subject: [PATCH 6/7] [xdata] Fix case in FillableForm --- .../java/org/jivesoftware/smackx/xdata/form/FillableForm.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FillableForm.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FillableForm.java index 62fd32540..e3daf7e1a 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FillableForm.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FillableForm.java @@ -193,7 +193,7 @@ public class FillableForm extends FilledForm { return FormField.textSingleBuilder(fieldName); case hidden: return FormField.hiddenBuilder(fieldName); - case list_multi: + case list_single: return FormField.listSingleBuilder(fieldName); default: throw new IllegalArgumentException(); From b7fe56fb9ba3bf4547999f0a18759e5ad1f8b16e Mon Sep 17 00:00:00 2001 From: Florian Schmaus Date: Mon, 6 Jul 2020 11:24:52 +0200 Subject: [PATCH 7/7] [xdata] Add message to IllegalArgumentException --- .../java/org/jivesoftware/smackx/xdata/form/FillableForm.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FillableForm.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FillableForm.java index e3daf7e1a..b407864cc 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FillableForm.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/xdata/form/FillableForm.java @@ -196,7 +196,7 @@ public class FillableForm extends FilledForm { case list_single: return FormField.listSingleBuilder(fieldName); default: - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Unsupported type: " + type); } }