1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-11-26 05:52:06 +01:00

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 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. | | [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 - 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 - 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. | | [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; package org.jivesoftware.smack;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -50,6 +52,18 @@ import org.jivesoftware.smack.util.Objects;
*/ */
public final class SmackConfiguration { 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 defaultPacketReplyTimeout = 5000;
private static int packetCollectorSize = 5000; private static int packetCollectorSize = 5000;

View file

@ -261,6 +261,8 @@ public abstract class IQ extends Stanza {
} }
protected final void initializeAsResultFor(IQ request) { protected final void initializeAsResultFor(IQ request) {
assert this != request;
if (!(request.getType() == Type.get || request.getType() == Type.set)) { if (!(request.getType() == Type.get || request.getType() == Type.set)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"IQ must be of type 'set' or 'get'. Original IQ: " + request.toXML()); "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 class ExceptionUtil {
public static String getStackTrace(Throwable throwable) { public static String getStackTrace(Throwable throwable) {
if (throwable == null) {
return null;
}
StringWriter stringWriter = new StringWriter(); StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter); PrintWriter printWriter = new PrintWriter(stringWriter);

View file

@ -16,13 +16,30 @@
*/ */
package org.jivesoftware.smack.util; package org.jivesoftware.smack.util;
import java.util.logging.Logger;
import javax.xml.namespace.QName; import javax.xml.namespace.QName;
import org.jivesoftware.smack.packet.FullyQualifiedElement; import org.jivesoftware.smack.packet.FullyQualifiedElement;
public class XmppElementUtil { public class XmppElementUtil {
public static final Logger LOGGER = Logger.getLogger(XmppElementUtil.class.getName());
public static QName getQNameFor(Class<? extends FullyQualifiedElement> fullyQualifiedElement) { 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; String element, namespace;
try { try {
element = (String) fullyQualifiedElement.getField("ELEMENT").get(null); element = (String) fullyQualifiedElement.getField("ELEMENT").get(null);

View file

@ -16,10 +16,12 @@
*/ */
package org.jivesoftware.smack.packet; package org.jivesoftware.smack.packet;
import org.jivesoftware.smack.SmackConfiguration;
public class TestIQ extends SimpleIQ { public class TestIQ extends SimpleIQ {
public TestIQ() { public TestIQ() {
this("https://igniterealtime.org/projects/smack", "test-iq"); this(SmackConfiguration.SMACK_URL_STRING, "test-iq");
} }
public TestIQ(String element, String namespace) { 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.assertFalse;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; 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.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.function.Supplier;
import java.util.stream.Stream;
import javax.xml.parsers.FactoryConfigurationError; import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
@ -50,8 +48,9 @@ import org.jivesoftware.smack.xml.XmlPullParser;
import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smack.xml.XmlPullParserException;
import com.jamesmurty.utils.XMLBuilder; 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.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.EnumSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
@ -428,7 +427,7 @@ public class PacketParserUtilsTest {
} }
// TODO: Re-enable once we throw an exception on duplicate body elements. // TODO: Re-enable once we throw an exception on duplicate body elements.
@Ignore @Disabled
@Test @Test
public void duplicateMessageBodiesTest() public void duplicateMessageBodiesTest()
throws FactoryConfigurationError, XmlPullParserException, IOException, Exception { throws FactoryConfigurationError, XmlPullParserException, IOException, Exception {
@ -460,7 +459,7 @@ public class PacketParserUtilsTest {
assertXmlNotSimilar(control, message.toXML().toString()); assertXmlNotSimilar(control, message.toXML().toString());
} }
@Ignore @Disabled
@Test @Test
public void duplicateMessageBodiesTest2() public void duplicateMessageBodiesTest2()
throws FactoryConfigurationError, XmlPullParserException, IOException, Exception { throws FactoryConfigurationError, XmlPullParserException, IOException, Exception {
@ -679,7 +678,7 @@ public class PacketParserUtilsTest {
* *
* @throws Exception * @throws Exception
*/ */
@Test(expected = XmlPullParserException.class) @Test
public void invalidMessageBodyContainingTagTest() throws Exception { public void invalidMessageBodyContainingTagTest() throws Exception {
String control = XMLBuilder.create("message") String control = XMLBuilder.create("message")
.namespace(StreamOpen.CLIENT_NAMESPACE) .namespace(StreamOpen.CLIENT_NAMESPACE)
@ -695,14 +694,14 @@ public class PacketParserUtilsTest {
.t("Bad Message Body") .t("Bad Message Body")
.asString(outputProperties); .asString(outputProperties);
Message message = PacketParserUtils.parseMessage(TestUtils.getMessageParser(control)); assertThrows(XmlPullParserException.class, () ->
PacketParserUtils.parseMessage(TestUtils.getMessageParser(control))
fail("Should throw exception. Instead got message: " + message.toXML().toString()); );
} }
@Test @Test
public void invalidXMLInMessageBody() throws Exception { public void invalidXMLInMessageBody() throws Exception {
String validControl = XMLBuilder.create("message") final String validControl = XMLBuilder.create("message")
.namespace(StreamOpen.CLIENT_NAMESPACE) .namespace(StreamOpen.CLIENT_NAMESPACE)
.a("from", "romeo@montague.lit/orchard") .a("from", "romeo@montague.lit/orchard")
.a("to", "juliet@capulet.lit/balcony") .a("to", "juliet@capulet.lit/balcony")
@ -713,41 +712,20 @@ public class PacketParserUtilsTest {
.t("Good Message Body") .t("Good Message Body")
.asString(outputProperties); .asString(outputProperties);
// XPP3 writes "end tag", StAX writes "end-tag". assertThrows(XmlPullParserException.class, () -> {
Supplier<Stream<String>> expectedContentOfExceptionMessage = () -> Stream.of("end tag", "end-tag"); String invalidControl = validControl.replace("Good Message Body", "Bad </span> Body");
String invalidControl = validControl.replace("Good Message Body", "Bad </span> Body");
try {
PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(invalidControl)); 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"); assertThrows(XmlPullParserException.class, () -> {
String invalidControl = validControl.replace("Good Message Body", "Bad </body> Body");
try {
PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(invalidControl)); 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"); assertThrows(XmlPullParserException.class, () -> {
String invalidControl = validControl.replace("Good Message Body", "Bad </message> Body");
try {
PacketParserUtils.parseMessage(PacketParserUtils.getParserFor(invalidControl)); 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 @Test
@ -891,15 +869,18 @@ public class PacketParserUtilsTest {
return otherLanguage; return otherLanguage;
} }
@Test(expected = IllegalArgumentException.class) @Test
public void descriptiveTextNullLangPassedMap() throws Exception { public void descriptiveTextNullLangPassedMap() throws Exception {
final String text = "Dummy descriptive text"; final String text = "Dummy descriptive text";
Map<String, String> texts = new HashMap<>(); Map<String, String> texts = new HashMap<>();
texts.put(null, text); texts.put(null, text);
StanzaError
.getBuilder(StanzaError.Condition.internal_server_error) assertThrows(IllegalArgumentException.class, () ->
.setDescriptiveTexts(texts) StanzaError
.build(); .getBuilder(StanzaError.Condition.internal_server_error)
.setDescriptiveTexts(texts)
.build()
);
} }
@Test @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.httpfileupload.HttpFileUploadManager</className>
<className>org.jivesoftware.smackx.eme.ExplicitMessageEncryptionManager</className> <className>org.jivesoftware.smackx.eme.ExplicitMessageEncryptionManager</className>
<className>org.jivesoftware.smackx.sid.StableUniqueStanzaIdManager</className> <className>org.jivesoftware.smackx.sid.StableUniqueStanzaIdManager</className>
<className>org.jivesoftware.smackx.xmlelement.DataFormsXmlElementManager</className>
</startupClasses> </startupClasses>
</smack> </smack>

View file

@ -36,6 +36,7 @@ import java.util.logging.Logger;
import org.jivesoftware.smack.AbstractConnectionListener; import org.jivesoftware.smack.AbstractConnectionListener;
import org.jivesoftware.smack.ConnectionCreationListener; import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.StanzaListener; 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 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; 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 * @param node the node attribute that supplements the 'jid' attribute
*/ */
public void setNode(String node) { 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 javax.xml.namespace.QName;
import org.jivesoftware.smack.Manager; import org.jivesoftware.smack.Manager;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
@ -193,7 +194,7 @@ public final class PrivateDataManager extends Manager {
@Override @Override
public String getNamespace() { public String getNamespace() {
return "https://igniterealtime.org/projects/smack/"; return SmackConfiguration.SMACK_URL_STRING;
} }
@Override @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.shim.packet.HeadersExtension;
import org.jivesoftware.smackx.xdata.Form; 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 { public abstract class Node {
protected final PubSubManager pubSubManager; protected final PubSubManager pubSubManager;
protected final String id; protected final String id;
@ -386,12 +390,45 @@ public abstract class Node {
* @throws NotConnectedException * @throws NotConnectedException
* @throws InterruptedException * @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 pubSub = createPubsubPacket(Type.set, new SubscribeExtension(jid, getId()));
PubSub reply = sendPubsubPacket(pubSub); PubSub reply = sendPubsubPacket(pubSub);
return reply.getExtension(PubSubElementType.SUBSCRIPTION); 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 * 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 * 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 NotConnectedException
* @throws InterruptedException * @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())); PubSub request = createPubsubPacket(Type.set, new SubscribeExtension(jid, getId()));
request.addExtension(new FormNode(FormNodeType.OPTIONS, subForm)); request.addExtension(new FormNode(FormNodeType.OPTIONS, subForm));
PubSub reply = sendPubsubPacket(request); PubSub reply = sendPubsubPacket(request);
return reply.getExtension(PubSubElementType.SUBSCRIPTION); 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 * Remove the subscription related to the specified JID. This will only
* work if there is only 1 subscription. If there are multiple subscriptions, * 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.Stanza;
import org.jivesoftware.smack.packet.StanzaError; import org.jivesoftware.smack.packet.StanzaError;
import org.jivesoftware.smack.packet.StanzaError.Condition; import org.jivesoftware.smack.packet.StanzaError.Condition;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo; import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
@ -268,6 +269,7 @@ public final class PubSubManager extends Manager {
* @throws NotAPubSubNodeException * @throws NotAPubSubNodeException
*/ */
public Node getNode(String id) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException, 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); Node node = nodeMap.get(id);
if (node == null) { if (node == null) {

View file

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

View file

@ -28,7 +28,6 @@ import static org.mockito.Mockito.mock;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.ConnectException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.util.List; import java.util.List;
@ -521,17 +520,24 @@ public class Socks5ByteStreamManagerTest {
* @throws InterruptedException * @throws InterruptedException
* @throws SmackException * @throws SmackException
* @throws XMPPException * @throws XMPPException
* @throws XmppStringprepException
*/ */
@Test @Test
public void shouldFailIfInitiatorCannotConnectToSocks5Proxy() public void shouldFailIfInitiatorCannotConnectToSocks5Proxy()
throws SmackException, InterruptedException, XMPPException { throws SmackException, InterruptedException, XMPPException, XmppStringprepException {
final Protocol protocol = new Protocol(); final Protocol protocol = new Protocol();
final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID);
final String sessionID = "session_id_shouldFailIfInitiatorCannotConnectToSocks5Proxy"; 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 // get Socks5ByteStreamManager for connection
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
byteStreamManager.setAnnounceLocalStreamHost(false); byteStreamManager.setAnnounceLocalStreamHost(false);
byteStreamManager.setProxyConnectionTimeout(3000);
/** /**
* create responses in the order they should be queried specified by the XEP-0065 * 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 // initiator can't connect to proxy because it is not running
protocol.verifyAll(); protocol.verifyAll();
Throwable actualCause = e.getCause().getCause(); Throwable actualCause = e.getCause();
assertEquals("Unexpected throwable: " + actualCause + '.' + ExceptionUtil.getStackTrace(actualCause), ConnectException.class, actualCause.getClass()); assertEquals("Unexpected throwable: " + actualCause + '.' + ExceptionUtil.getStackTrace(actualCause),
TimeoutException.class, actualCause.getClass());
} }
/** /**

View file

@ -23,6 +23,8 @@ dependencies {
// (ab)uses @Before from org.junit // (ab)uses @Before from org.junit
compile "org.junit.vintage:junit-vintage-engine:$junitVersion" compile "org.junit.vintage:junit-vintage-engine:$junitVersion"
compile 'junit:junit:4.12' 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" 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.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.Assert.fail;
import java.util.List;
import org.jivesoftware.smack.SmackConfiguration;
import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.StandardExtensionElement;
import org.jivesoftware.smack.packet.StanzaError; import org.jivesoftware.smack.packet.StanzaError;
import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest; import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
@ -69,8 +68,6 @@ public class PubSubIntegrationTest extends AbstractSmackIntegrationTest {
try { try {
LeafNode leafNode = (LeafNode) node; LeafNode leafNode = (LeafNode) node;
leafNode.publish(); leafNode.publish();
List<Item> items = leafNode.getItems();
assertTrue(items.isEmpty());
} }
finally { finally {
pubSubManagerOne.deleteNode(nodename); pubSubManagerOne.deleteNode(nodename);
@ -95,6 +92,7 @@ public class PubSubIntegrationTest extends AbstractSmackIntegrationTest {
public void transientNotificationOnlyNodeWithItemTest() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException { public void transientNotificationOnlyNodeWithItemTest() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
final String nodename = "sinttest-transient-notificationonly-withitem-nodename-" + testRunId; final String nodename = "sinttest-transient-notificationonly-withitem-nodename-" + testRunId;
final String itemId = "sinttest-transient-notificationonly-withitem-itemid-" + testRunId; final String itemId = "sinttest-transient-notificationonly-withitem-itemid-" + testRunId;
ConfigureForm defaultConfiguration = pubSubManagerOne.getDefaultConfiguration(); ConfigureForm defaultConfiguration = pubSubManagerOne.getDefaultConfiguration();
ConfigureForm config = new ConfigureForm(defaultConfiguration.createAnswerForm()); ConfigureForm config = new ConfigureForm(defaultConfiguration.createAnswerForm());
// Configure the node as "Notification-Only Node". // 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') // Configure the node as "transient" (set persistent_items to 'false')
config.setPersistentItems(false); config.setPersistentItems(false);
Node node = pubSubManagerOne.createNode(nodename, config); 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 { try {
LeafNode leafNode = (LeafNode) node; XMPPErrorException e = assertThrows(XMPPErrorException.class, () -> {
leafNode.publish(new Item(itemId)); LeafNode leafNode = (LeafNode) node;
fail("An exception should have been thrown.");
} Item item = new PayloadItem<>(itemId, dummyPayload);
catch (XMPPErrorException e) { leafNode.publish(item);
});
assertEquals(StanzaError.Type.MODIFY, e.getStanzaError().getType()); assertEquals(StanzaError.Type.MODIFY, e.getStanzaError().getType());
assertNotNull(e.getStanzaError().getExtension("item-forbidden", "http://jabber.org/protocol/pubsub#errors")); 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 // 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 // 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 // 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 = smackTlsContext.sslContext.createSSLEngine(config.getXMPPServiceDomain().toString(), remoteAddress.getPort());
engine.setUseClientMode(true); engine.setUseClientMode(true);