Compare commits

...

15 Commits

Author SHA1 Message Date
Florian Schmaus 658905e1ba Do not check exception message in PacketParserUtilsTest
as it may be localized.

This also makes PacketParserUtilsTest to use Junit5.

Fixes SMACK-874.
2019-07-05 17:40:45 +02:00
Florian Schmaus fda9408cf3 Use XmlStringBuilder in PubSub's SubscribeExtension 2019-07-05 11:31:12 +02:00
Florian Schmaus 9c0da3ed07 Change SubscribeExtension's jid field type from String to Jid
and make it final.
2019-07-05 11:24:02 +02:00
Florian Schmaus 89b64fbf0c Add TODO comment to XmppNioTcpConnection 2019-07-05 09:17:14 +02:00
Florian Schmaus 60c7e0ab76 Fix typo in XmppNioTcpConnection: s/Therfore/Therefore/ 2019-07-05 09:17:14 +02:00
Florian Schmaus 178ae8abef Merge branch 'master' of github.com:igniterealtime/Smack 2019-07-04 16:57:07 +02:00
Florian Schmaus aec648c34b Improve PubSubIntegrationTest
Ensuring that the node has no items in
transientNotificationOnlyNodeWithoutItemTest() is not right. An
implementation is free to create an item with an ID and return it. The
item is just not guaranteed to be persistent.

Also add a dummy payload to
transientNotificationOnlyNodeWithItemTest().
2019-07-04 16:48:35 +02:00
Florian Schmaus df5899e72b Introduce SmackConfiguration.SMACK_URL(_STRING) 2019-07-04 15:51:28 +02:00
Florian Schmaus f3b856c80b Improve shouldFailIfInitiatorCannotConnectToSocks5Proxy() unit test
If run in parallel with other unit tests, especially onces that open
up a proxy, this test could fail, because another unit test actually
had an proxy running on the very address this unit test assumes to be
no proxy running.

We now use an IP address from RFC 5737's TEST-NET-1 address block,
which should never be available.
2019-07-04 15:49:47 +02:00
Florian Schmaus 1b5a264d52 Add assert to IQ.initializeAsResultFor(IQ)
This method is not meant to be used to be invoked with the identity.
2019-07-04 15:48:07 +02:00
Florian Schmaus 870e6c674a Make ExceptionUtil.getStackTrace(Throwable) null safe 2019-07-04 15:47:46 +02:00
Florian Schmaus 01289e9682 Add support for XEP-0315: Data Forms XML Element
Fixes SMACK-872.
2019-06-12 22:32:24 +02:00
Florian Schmaus a3f6fa65a4 Disallow null or empty string in PubSubManager.getNode(String) 2019-06-12 22:31:55 +02:00
Florian Schmaus ef88bb17d0 Disallow empty string as node in DiscoverInfo 2019-06-12 22:31:29 +02:00
Florian Schmaus e3ec422071 Try to lookup QNAME first in XmppElementUtil.getQNameFor() 2019-06-12 22:30:56 +02:00
24 changed files with 422 additions and 87 deletions

View File

@ -86,6 +86,7 @@ Experimental Smack Extensions and currently supported XEPs of smack-experimental
|-----------------------------------------------------------|--------------------------------------------------------|-----------|-------------------------------------------------------------------------------------------------------------------------|
| Message Carbons | [XEP-0280](https://xmpp.org/extensions/xep-0280.html) | n/a | Keep all IM clients for a user engaged in a conversation, by carbon-copy outbound messages to all interested resources. |
| [Message Archive Management](mam.md) | [XEP-0313](https://xmpp.org/extensions/xep-0313.html) | n/a | Query and control an archive of messages stored on a server. |
| Data Forms XML Element | [XEP-0315](https://xmpp.org/extensions/xep-0315.html) | n/a | Allows to include XML-data in XEP-0004 data forms. |
| [Internet of Things - Sensor Data](iot.md) | [XEP-0323](https://xmpp.org/extensions/xep-0323.html) | n/a | Sensor data interchange over XMPP. |
| [Internet of Things - Provisioning](iot.md) | [XEP-0324](https://xmpp.org/extensions/xep-0324.html) | n/a | Provisioning, access rights and user privileges for the Internet of Things. |
| [Internet of Things - Control](iot.md) | [XEP-0325](https://xmpp.org/extensions/xep-0325.html) | n/a | Describes how to control devices or actuators in an XMPP-based sensor network. |

View File

@ -17,6 +17,8 @@
package org.jivesoftware.smack;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -50,6 +52,18 @@ import org.jivesoftware.smack.util.Objects;
*/
public final class SmackConfiguration {
public static final String SMACK_URL_STRING = "https://igniterealtime.org/projects/smack";
public static final URL SMACK_URL;
static {
try {
SMACK_URL = new URL(SMACK_URL_STRING);
} catch (MalformedURLException e) {
throw new IllegalStateException(e);
}
}
private static int defaultPacketReplyTimeout = 5000;
private static int packetCollectorSize = 5000;

View File

@ -261,6 +261,8 @@ public abstract class IQ extends Stanza {
}
protected final void initializeAsResultFor(IQ request) {
assert this != request;
if (!(request.getType() == Type.get || request.getType() == Type.set)) {
throw new IllegalArgumentException(
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML());

View File

@ -22,6 +22,10 @@ import java.io.StringWriter;
public class ExceptionUtil {
public static String getStackTrace(Throwable throwable) {
if (throwable == null) {
return null;
}
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);

View File

@ -16,13 +16,30 @@
*/
package org.jivesoftware.smack.util;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.FullyQualifiedElement;
public class XmppElementUtil {
public static final Logger LOGGER = Logger.getLogger(XmppElementUtil.class.getName());
public static QName getQNameFor(Class<? extends FullyQualifiedElement> fullyQualifiedElement) {
try {
Object qnameObject = fullyQualifiedElement.getField("QNAME").get(null);
if (QName.class.isAssignableFrom(qnameObject.getClass())) {
return (QName) qnameObject;
}
LOGGER.warning("The QNAME field of " + fullyQualifiedElement + " is not of type QNAME.");
} catch (NoSuchFieldException e) {
LOGGER.finer("The class " + fullyQualifiedElement + " has no static QNAME field. Consider adding one.");
// Proceed to fallback strategy.
} catch (IllegalArgumentException | IllegalAccessException | SecurityException e) {
throw new IllegalArgumentException(e);
}
String element, namespace;
try {
element = (String) fullyQualifiedElement.getField("ELEMENT").get(null);

View File

@ -16,10 +16,12 @@
*/
package org.jivesoftware.smack.packet;
import org.jivesoftware.smack.SmackConfiguration;
public class TestIQ extends SimpleIQ {
public TestIQ() {
this("https://igniterealtime.org/projects/smack", "test-iq");
this(SmackConfiguration.SMACK_URL_STRING, "test-iq");
}
public TestIQ(String element, String namespace) {

View File

@ -22,15 +22,13 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.function.Supplier;
import java.util.stream.Stream;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
@ -50,8 +48,9 @@ import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import com.jamesmurty.utils.XMLBuilder;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.xml.sax.SAXException;
@ -428,7 +427,7 @@ public class PacketParserUtilsTest {
}
// TODO: Re-enable once we throw an exception on duplicate body elements.
@Ignore
@Disabled
@Test
public void duplicateMessageBodiesTest()
throws FactoryConfigurationError, XmlPullParserException, IOException, Exception {
@ -460,7 +459,7 @@ public class PacketParserUtilsTest {
assertXmlNotSimilar(control, message.toXML().toString());
}
@Ignore
@Disabled
@Test
public void duplicateMessageBodiesTest2()
throws FactoryConfigurationError, XmlPullParserException, IOException, Exception {
@ -679,7 +678,7 @@ public class PacketParserUtilsTest {
*
* @throws Exception
*/
@Test(expected = XmlPullParserException.class)
@Test
public void invalidMessageBodyContainingTagTest() throws Exception {
String control = XMLBuilder.create("message")
.namespace(StreamOpen.CLIENT_NAMESPACE)
@ -695,14 +694,14 @@ public class PacketParserUtilsTest {
.t("Bad Message Body")
.asString(outputProperties);
Message message = PacketParserUtils.parseMessage(TestUtils.getMessageParser(control));
fail("Should throw exception. Instead got message: " + message.toXML().toString());
assertThrows(XmlPullParserException.class, () ->
PacketParserUtils.parseMessage(TestUtils.getMessageParser(control))
);
}
@Test
public void invalidXMLInMessageBody() throws Exception {
String validControl = XMLBuilder.create("message")
final String validControl = XMLBuilder.create("message")
.namespace(StreamOpen.CLIENT_NAMESPACE)
.a("from", "romeo@montague.lit/orchard")
.a("to", "juliet@capulet.lit/balcony")
@ -713,41 +712,20 @@ public class PacketParserUtilsTest {
.t("Good Message Body")
.asString(outputProperties);
// XPP3 writes "end tag", StAX writes "end-tag".
Supplier<Stream<String>> expectedContentOfExceptionMessage = () -> Stream.of("end tag", "end-tag");
String invalidControl = validControl.replace("Good Message Body", "Bad </span> Body");
try {
assertThrows(XmlPullParserException.class, () -> {
String invalidControl = validControl.replace("Good Message Body", "Bad </span> Body");
PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(invalidControl));
fail("Exception should be thrown");
} catch (XmlPullParserException e) {
String exceptionMessage = e.getMessage();
boolean expectedContentFound = expectedContentOfExceptionMessage.get().anyMatch((expected) -> exceptionMessage.contains(expected));
assertTrue(expectedContentFound);
}
});
invalidControl = validControl.replace("Good Message Body", "Bad </body> Body");
try {
assertThrows(XmlPullParserException.class, () -> {
String invalidControl = validControl.replace("Good Message Body", "Bad </body> Body");
PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(invalidControl));
fail("Exception should be thrown");
} catch (XmlPullParserException e) {
String exceptionMessage = e.getMessage();
boolean expectedContentFound = expectedContentOfExceptionMessage.get().anyMatch((expected) -> exceptionMessage.contains(expected));
assertTrue(expectedContentFound);
}
});
invalidControl = validControl.replace("Good Message Body", "Bad </message> Body");
try {
assertThrows(XmlPullParserException.class, () -> {
String invalidControl = validControl.replace("Good Message Body", "Bad </message> Body");
PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(invalidControl));
fail("Exception should be thrown");
} catch (XmlPullParserException e) {
String exceptionMessage = e.getMessage();
boolean expectedContentFound = expectedContentOfExceptionMessage.get().anyMatch((expected) -> exceptionMessage.contains(expected));
assertTrue(expectedContentFound);
}
});
}
@Test
@ -891,15 +869,18 @@ public class PacketParserUtilsTest {
return otherLanguage;
}
@Test(expected = IllegalArgumentException.class)
@Test
public void descriptiveTextNullLangPassedMap() throws Exception {
final String text = "Dummy descriptive text";
Map<String, String> texts = new HashMap<>();
texts.put(null, text);
StanzaError
.getBuilder(StanzaError.Condition.internal_server_error)
.setDescriptiveTexts(texts)
.build();
assertThrows(IllegalArgumentException.class, () ->
StanzaError
.getBuilder(StanzaError.Condition.internal_server_error)
.setDescriptiveTexts(texts)
.build()
);
}
@Test

View File

@ -0,0 +1,28 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* 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.smackx.xmlelement;
import org.jivesoftware.smackx.xdata.provider.FormFieldChildElementProviderManager;
import org.jivesoftware.smackx.xmlelement.provider.DataFormsXmlElementProvider;
public class DataFormsXmlElementManager {
static {
FormFieldChildElementProviderManager.addFormFieldChildElementProvider(new DataFormsXmlElementProvider());
}
}

View File

@ -0,0 +1,74 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* 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.smackx.xmlelement.element;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.StandardExtensionElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.xdata.FormField;
import org.jivesoftware.smackx.xdata.FormFieldChildElement;
public class DataFormsXmlElement implements FormFieldChildElement {
public static final String ELEMENT = "wrapper";
public static final String NAMESPACE = "urn:xmpp:xml-element";
public static final QName QNAME = new QName(NAMESPACE, ELEMENT);
private final StandardExtensionElement payload;
public DataFormsXmlElement(StandardExtensionElement payload) {
this.payload = payload;
}
@Override
public QName getQName() {
return QNAME;
}
@Override
public String getElementName() {
return ELEMENT;
}
@Override
public String getNamespace() {
return NAMESPACE;
}
@Override
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
if (payload == null) {
return xml.closeEmptyElement();
}
xml.rightAngleBracket();
xml.append(payload.toXML());
xml.closeElement(this);
return xml;
}
public static DataFormsXmlElement from(FormField formField) {
return (DataFormsXmlElement) formField.getFormFieldChildElement(QNAME);
}
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* 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.
*/
/**
* Element classes for XEP-0315: Data Forms XML Element.
*/
package org.jivesoftware.smackx.xmlelement.element;

View File

@ -0,0 +1,21 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* 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.
*/
/**
* Smacks implementation of XEP-0315: Data Forms XML Element.
*/
package org.jivesoftware.smackx.xmlelement;

View File

@ -0,0 +1,54 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* 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.smackx.xmlelement.provider;
import java.io.IOException;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.StandardExtensionElement;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.parsing.SmackParsingException;
import org.jivesoftware.smack.parsing.StandardExtensionElementProvider;
import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException;
import org.jivesoftware.smackx.xdata.provider.FormFieldChildElementProvider;
import org.jivesoftware.smackx.xmlelement.element.DataFormsXmlElement;
public class DataFormsXmlElementProvider extends FormFieldChildElementProvider<DataFormsXmlElement> {
@Override
public QName getQName() {
return DataFormsXmlElement.QNAME;
}
@Override
public DataFormsXmlElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment)
throws IOException, XmlPullParserException, SmackParsingException {
XmlPullParser.TagEvent tagEvent = parser.nextTag();
final StandardExtensionElement standardExtensionElement;
if (tagEvent == XmlPullParser.TagEvent.START_ELEMENT) {
standardExtensionElement = StandardExtensionElementProvider.INSTANCE.parse(parser);
} else {
standardExtensionElement = null;
}
return new DataFormsXmlElement(standardExtensionElement);
}
}

View File

@ -0,0 +1,21 @@
/**
*
* Copyright 2019 Florian Schmaus
*
* 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.
*/
/**
* Provider classes for XEP-0315: Data Forms XML Element.
*/
package org.jivesoftware.smackx.xmlelement.provider;

View File

@ -7,5 +7,6 @@
<className>org.jivesoftware.smackx.httpfileupload.HttpFileUploadManager</className>
<className>org.jivesoftware.smackx.eme.ExplicitMessageEncryptionManager</className>
<className>org.jivesoftware.smackx.sid.StableUniqueStanzaIdManager</className>
<className>org.jivesoftware.smackx.xmlelement.DataFormsXmlElementManager</className>
</startupClasses>
</smack>

View File

@ -36,6 +36,7 @@ import java.util.logging.Logger;
import org.jivesoftware.smack.AbstractConnectionListener;
import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.StanzaListener;
@ -92,7 +93,7 @@ public final class EntityCapsManager extends Manager {
*/
private static final String DEFAULT_HASH = StringUtils.SHA1;
private static String DEFAULT_ENTITY_NODE = "http://www.igniterealtime.org/projects/smack";
private static String DEFAULT_ENTITY_NODE = SmackConfiguration.SMACK_URL_STRING;
protected static EntityCapsPersistentCache persistentCache;

View File

@ -203,7 +203,7 @@ public class DiscoverInfo extends IQ implements TypedCloneable<DiscoverInfo> {
* @param node the node attribute that supplements the 'jid' attribute
*/
public void setNode(String node) {
this.node = node;
this.node = StringUtils.requireNullOrNotEmpty(node, "The node can not be the empty string");
}
/**

View File

@ -25,6 +25,7 @@ import java.util.WeakHashMap;
import javax.xml.namespace.QName;
import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection;
@ -193,7 +194,7 @@ public final class PrivateDataManager extends Manager {
@Override
public String getNamespace() {
return "https://igniterealtime.org/projects/smack/";
return SmackConfiguration.SMACK_URL_STRING;
}
@Override

View File

@ -46,6 +46,10 @@ import org.jivesoftware.smackx.shim.packet.Header;
import org.jivesoftware.smackx.shim.packet.HeadersExtension;
import org.jivesoftware.smackx.xdata.Form;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
public abstract class Node {
protected final PubSubManager pubSubManager;
protected final String id;
@ -386,12 +390,45 @@ public abstract class Node {
* @throws NotConnectedException
* @throws InterruptedException
*/
public Subscription subscribe(String jid) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
public Subscription subscribe(Jid jid) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
PubSub pubSub = createPubsubPacket(Type.set, new SubscribeExtension(jid, getId()));
PubSub reply = sendPubsubPacket(pubSub);
return reply.getExtension(PubSubElementType.SUBSCRIPTION);
}
/**
* The user subscribes to the node using the supplied jid. The
* bare jid portion of this one must match the jid for the connection.
*
* Please note that the {@link Subscription.State} should be checked
* on return since more actions may be required by the caller.
* {@link Subscription.State#pending} - The owner must approve the subscription
* request before messages will be received.
* {@link Subscription.State#unconfigured} - If the {@link Subscription#isConfigRequired()} is true,
* the caller must configure the subscription before messages will be received. If it is false
* the caller can configure it but is not required to do so.
*
* @param jidString The jid to subscribe as.
* @return The subscription
* @throws XMPPErrorException
* @throws NoResponseException
* @throws NotConnectedException
* @throws InterruptedException
* @throws IllegalArgumentException if the provided string is not a valid JID.
* @deprecated use {@link #subscribe(Jid)} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public Subscription subscribe(String jidString) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
Jid jid;
try {
jid = JidCreate.from(jidString);
} catch (XmppStringprepException e) {
throw new IllegalArgumentException(e);
}
return subscribe(jid);
}
/**
* The user subscribes to the node using the supplied jid and subscription
* options. The bare jid portion of this one must match the jid for the
@ -414,13 +451,49 @@ public abstract class Node {
* @throws NotConnectedException
* @throws InterruptedException
*/
public Subscription subscribe(String jid, SubscribeForm subForm) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
public Subscription subscribe(Jid jid, SubscribeForm subForm) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
PubSub request = createPubsubPacket(Type.set, new SubscribeExtension(jid, getId()));
request.addExtension(new FormNode(FormNodeType.OPTIONS, subForm));
PubSub reply = sendPubsubPacket(request);
return reply.getExtension(PubSubElementType.SUBSCRIPTION);
}
/**
* The user subscribes to the node using the supplied jid and subscription
* options. The bare jid portion of this one must match the jid for the
* connection.
*
* Please note that the {@link Subscription.State} should be checked
* on return since more actions may be required by the caller.
* {@link Subscription.State#pending} - The owner must approve the subscription
* request before messages will be received.
* {@link Subscription.State#unconfigured} - If the {@link Subscription#isConfigRequired()} is true,
* the caller must configure the subscription before messages will be received. If it is false
* the caller can configure it but is not required to do so.
*
* @param jidString The jid to subscribe as.
* @param subForm
*
* @return The subscription
* @throws XMPPErrorException
* @throws NoResponseException
* @throws NotConnectedException
* @throws InterruptedException
* @throws IllegalArgumentException if the provided string is not a valid JID.
* @deprecated use {@link #subscribe(Jid, SubscribeForm)} instead.
*/
@Deprecated
// TODO: Remove in Smack 4.5.
public Subscription subscribe(String jidString, SubscribeForm subForm) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
Jid jid;
try {
jid = JidCreate.from(jidString);
} catch (XmppStringprepException e) {
throw new IllegalArgumentException(e);
}
return subscribe(jid, subForm);
}
/**
* Remove the subscription related to the specified JID. This will only
* work if there is only 1 subscription. If there are multiple subscriptions,

View File

@ -37,6 +37,7 @@ import org.jivesoftware.smack.packet.IQ.Type;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.StanzaError;
import org.jivesoftware.smack.packet.StanzaError.Condition;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
@ -268,6 +269,7 @@ public final class PubSubManager extends Manager {
* @throws NotAPubSubNodeException
*/
public Node getNode(String id) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, NotAPubSubNodeException {
StringUtils.requireNotNullNorEmpty(id, "The node ID can not be null or the empty string");
Node node = nodeMap.get(id);
if (node == null) {

View File

@ -16,42 +16,39 @@
*/
package org.jivesoftware.smackx.pubsub;
import org.jivesoftware.smack.packet.XmlEnvironment;
import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jxmpp.jid.Jid;
/**
* Represents a request to subscribe to a node.
*
* @author Robin Collier
*/
public class SubscribeExtension extends NodeExtension {
protected String jid;
protected final Jid jid;
public SubscribeExtension(String subscribeJid) {
public SubscribeExtension(Jid subscribeJid) {
super(PubSubElementType.SUBSCRIBE);
jid = subscribeJid;
}
public SubscribeExtension(String subscribeJid, String nodeId) {
public SubscribeExtension(Jid subscribeJid, String nodeId) {
super(PubSubElementType.SUBSCRIBE, nodeId);
jid = subscribeJid;
}
public String getJid() {
public Jid getJid() {
return jid;
}
@Override
public String toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) {
StringBuilder builder = new StringBuilder("<");
builder.append(getElementName());
if (getNode() != null) {
builder.append(" node='");
builder.append(getNode());
builder.append('\'');
}
builder.append(" jid='");
builder.append(getJid());
builder.append("'/>");
return builder.toString();
public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) {
XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment);
xml.optAttribute("node", getNode());
xml.attribute("jid", getJid());
xml.closeEmptyElement();
return xml;
}
}

View File

@ -28,7 +28,6 @@ import static org.mockito.Mockito.mock;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.List;
@ -521,17 +520,24 @@ public class Socks5ByteStreamManagerTest {
* @throws InterruptedException
* @throws SmackException
* @throws XMPPException
* @throws XmppStringprepException
*/
@Test
public void shouldFailIfInitiatorCannotConnectToSocks5Proxy()
throws SmackException, InterruptedException, XMPPException {
throws SmackException, InterruptedException, XMPPException, XmppStringprepException {
final Protocol protocol = new Protocol();
final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID);
final String sessionID = "session_id_shouldFailIfInitiatorCannotConnectToSocks5Proxy";
// TODO: The following two variables should be named initatorProxyJid and initiatorProxyAddress.
final DomainBareJid proxyJID = JidCreate.domainBareFrom("s5b-proxy.initiator.org");
// Use an TEST-NET-1 address from RFC 5737 to act as black hole.
final String proxyAddress = "192.0.2.1";
// get Socks5ByteStreamManager for connection
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
byteStreamManager.setAnnounceLocalStreamHost(false);
byteStreamManager.setProxyConnectionTimeout(3000);
/**
* create responses in the order they should be queried specified by the XEP-0065
@ -602,8 +608,9 @@ public class Socks5ByteStreamManagerTest {
// initiator can't connect to proxy because it is not running
protocol.verifyAll();
Throwable actualCause = e.getCause().getCause();
assertEquals("Unexpected throwable: " + actualCause + '.' + ExceptionUtil.getStackTrace(actualCause), ConnectException.class, actualCause.getClass());
Throwable actualCause = e.getCause();
assertEquals("Unexpected throwable: " + actualCause + '.' + ExceptionUtil.getStackTrace(actualCause),
TimeoutException.class, actualCause.getClass());
}
/**

View File

@ -23,6 +23,8 @@ dependencies {
// (ab)uses @Before from org.junit
compile "org.junit.vintage:junit-vintage-engine:$junitVersion"
compile 'junit:junit:4.12'
// Add Junit 5 API for e.g. assertThrows()
implementation "org.junit.jupiter:junit-jupiter-api:$junitVersion"
testCompile "org.jxmpp:jxmpp-jid:$jxmppVersion:tests"
}

View File

@ -18,14 +18,13 @@ package org.jivesoftware.smackx.pubsub;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.StandardExtensionElement;
import org.jivesoftware.smack.packet.StanzaError;
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
@ -69,8 +68,6 @@ public class PubSubIntegrationTest extends AbstractSmackIntegrationTest {
try {
LeafNode leafNode = (LeafNode) node;
leafNode.publish();
List<Item> items = leafNode.getItems();
assertTrue(items.isEmpty());
}
finally {
pubSubManagerOne.deleteNode(nodename);
@ -95,6 +92,7 @@ public class PubSubIntegrationTest extends AbstractSmackIntegrationTest {
public void transientNotificationOnlyNodeWithItemTest() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
final String nodename = "sinttest-transient-notificationonly-withitem-nodename-" + testRunId;
final String itemId = "sinttest-transient-notificationonly-withitem-itemid-" + testRunId;
ConfigureForm defaultConfiguration = pubSubManagerOne.getDefaultConfiguration();
ConfigureForm config = new ConfigureForm(defaultConfiguration.createAnswerForm());
// Configure the node as "Notification-Only Node".
@ -102,12 +100,23 @@ public class PubSubIntegrationTest extends AbstractSmackIntegrationTest {
// Configure the node as "transient" (set persistent_items to 'false')
config.setPersistentItems(false);
Node node = pubSubManagerOne.createNode(nodename, config);
// Add a dummy payload. If there is no payload, but just an item ID, then ejabberd will *not* return an error,
// which I believe to be non-compliant behavior (although, granted, the XEP is not very clear about this). A user
// which sends an empty item with ID to an node that is configured to be notification-only and transient probably
// does something wrong, as the item's ID will never appear anywhere. Hence it would be nice if the user would be
// made aware of this issue by returning an error. Sadly ejabberd does not do so.
// See also https://github.com/processone/ejabberd/issues/2864#issuecomment-500741915
final StandardExtensionElement dummyPayload = StandardExtensionElement.builder("dummy-payload",
SmackConfiguration.SMACK_URL_STRING).setText(testRunId).build();
try {
LeafNode leafNode = (LeafNode) node;
leafNode.publish(new Item(itemId));
fail("An exception should have been thrown.");
}
catch (XMPPErrorException e) {
XMPPErrorException e = assertThrows(XMPPErrorException.class, () -> {
LeafNode leafNode = (LeafNode) node;
Item item = new PayloadItem<>(itemId, dummyPayload);
leafNode.publish(item);
});
assertEquals(StanzaError.Type.MODIFY, e.getStanzaError().getType());
assertNotNull(e.getStanzaError().getExtension("item-forbidden", "http://jabber.org/protocol/pubsub#errors"));
}

View File

@ -1116,7 +1116,9 @@ public class XmppNioTcpConnection extends AbstractXmppNioConnection {
// remote hostname information, in which case peerHost needs to be specified." that A should be used. TLS
// session resumption may would need or at least benefit from B. Variant A would also be required if the
// String is used for certificate verification. And it appears at least likely that TLS session resumption
// would not be hurt by using variant A. Therfore we currently use variant A.
// would not be hurt by using variant A. Therefore we currently use variant A.
// TODO: Should we use the ACE representation of the XMPP service domain? Compare with f60e4055ec529f0b8160acedf13275592ab10a4b
// If yes, then we should probably introduce getXmppServiceDomainAceEncodedIfPossible().
engine = smackTlsContext.sslContext.createSSLEngine(config.getXMPPServiceDomain().toString(), remoteAddress.getPort());
engine.setUseClientMode(true);