diff --git a/.travis.yml b/.travis.yml
index 5b6c55b30..41f447858 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,7 +10,6 @@ cache:
- $HOME/.m2
before_install:
- - export JAVA_OPTS="-XX:MaxPermSize=512M"
- export GRADLE_VERSION=2.12
- wget https://services.gradle.org/distributions/gradle-${GRADLE_VERSION}-all.zip
- unzip -q gradle-${GRADLE_VERSION}-all.zip
diff --git a/README.md b/README.md
index f4026b3e1..f9ec1f48b 100644
--- a/README.md
+++ b/README.md
@@ -3,20 +3,23 @@ Smack
[![Build Status](https://travis-ci.org/igniterealtime/Smack.svg?branch=master)](https://travis-ci.org/igniterealtime/Smack) [![Coverage Status](https://coveralls.io/repos/igniterealtime/Smack/badge.svg)](https://coveralls.io/r/igniterealtime/Smack) [![Project Stats](https://www.openhub.net/p/smackxmpp/widgets/project_thin_badge.gif)](https://www.openhub.net/p/smackxmpp) [![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/thing/3480125) [![Visit our IRC channel](https://kiwiirc.com/buttons/irc.freenode.net/smack.png)](https://kiwiirc.com/client/irc.freenode.net/smack)
-Instructions how to use Smack in your Java or Android project are provided in the [Smack 4.1 Readme and Upgrade Guide](https://github.com/igniterealtime/Smack/wiki/Smack-4.1-Readme-and-Upgrade-Guide).
-
About
-----
-[Smack] is an open source, highly modular, easy to use, XMPP client library written in Java for JVMs and Android.
+[Smack] is an open source, highly modular, easy to use, XMPP client library written in Java for Java SE compatible JVMs and Android.
-A pure Java library, it can be embedded into your applications to create anything from a full XMPP client to simple XMPP integrations such as sending notification messages and presence-enabling devices.
+A pure Java library, it can be embedded into your applications to create anything from a full XMPP instant messaging client to simple XMPP integrations such as sending notification messages and presence-enabling devices.
Smack and XMPP allows you to easily exchange data, in various ways e.g. fire-and-forget, publish-subscribe, between human and non-human endpoints (M2M, IoT, …).
Confused? Have a look at the [Overview](documentation/overview.md).
[Smack] - an [Ignite Realtime] community project.
+Getting started
+---------------
+
+Instructions how to use Smack in your Java or Android project are provided in the [Smack 4.2 Readme and Upgrade Guide](https://github.com/igniterealtime/Smack/wiki/Smack-4.2-Readme-and-Upgrade-Guide).
+
Bug Reporting
-------------
@@ -48,7 +51,7 @@ The guidelines also contain development quickstart instructions.
Resources
---------
-- Bug Tracker: http://issues.igniterealtime.org/browse/SMACK
+- Bug Tracker: https://issues.igniterealtime.org/browse/SMACK
- JaCoCo Coverage Reports: https://www.igniterealtime.org/builds/smack/dailybuilds/jacoco/html/
- Nightly Builds: http://www.igniterealtime.org/downloads/nightly_smack.jsp
- Nightly Javadoc: http://www.igniterealtime.org/builds/smack/dailybuilds/javadoc/
@@ -57,6 +60,7 @@ Resources
- Dev Forum: http://community.igniterealtime.org/community/developers/smack
- Maven Releases: https://oss.sonatype.org/content/repositories/releases/org/igniterealtime/smack/
- Maven Snapshots: https://oss.sonatype.org/content/repositories/snapshots/org/igniterealtime/smack/
+- Nightly Unique Maven Snapshots: https://igniterealtime.org/repo/
Donate
------
diff --git a/build.gradle b/build.gradle
index 4922ceff0..92a62e572 100644
--- a/build.gradle
+++ b/build.gradle
@@ -118,7 +118,7 @@ allprojects {
// Some systems may not have set their platform default
// converter to 'utf8', but we use unicode in our source
// files. Therefore ensure that javac uses unicode
- options.encoding = "utf8"
+ options.encoding = 'UTF-8'
options.compilerArgs = [
'-Xlint:all',
// Set '-options' because a non-java7 javac will emit a
@@ -164,6 +164,7 @@ allprojects {
}
tasks.withType(Javadoc) {
options.charSet = "UTF-8"
+ options.encoding = 'UTF-8'
}
// Pin the errorprone version to prevent "unsupported major.minor
@@ -301,6 +302,8 @@ subprojects {
archives sourcesJar
archives javadocJar
archives testJar
+ // See http://stackoverflow.com/a/21946676/194894
+ testRuntime testJar
}
uploadArchives {
diff --git a/documentation/extensions/index.md b/documentation/extensions/index.md
index 481229840..325fcf30a 100644
--- a/documentation/extensions/index.md
+++ b/documentation/extensions/index.md
@@ -91,6 +91,7 @@ Experimental Smack Extensions and currently supported XEPs of smack-experimental
| [Internet of Things - Discovery](iot.md) | [XEP-0347](http://xmpp.org/extensions/xep-0347.html) | Describes how Things can be installed and discovered by their owners. |
| Client State Indication | [XEP-0352](http://xmpp.org/extensions/xep-0352.html) | A way for the client to indicate its active/inactive state. |
| [Push Notifications](pushnotifications.md) | [XEP-0357](http://xmpp.org/extensions/xep-0357.html) | Defines a way to manage push notifications from an XMPP Server. |
+| HTTP File Upload | [XEP-0363](http://xmpp.org/extensions/xep-0363.html) | Protocol to request permissions to upload a file to an HTTP server and get a shareable URL. |
| [Multi-User Chat Light](muclight.md) | [XEP-xxxx](http://mongooseim.readthedocs.io/en/latest/open-extensions/xeps/xep-muc-light.html) | Multi-User Chats for mobile XMPP applications and specific enviroment. |
| Google GCM JSON payload | n/a | Semantically the same as XEP-0335: JSON Containers |
diff --git a/resources/releasedocs/changelog.html b/resources/releasedocs/changelog.html
index cb513e360..b8909c036 100644
--- a/resources/releasedocs/changelog.html
+++ b/resources/releasedocs/changelog.html
@@ -141,6 +141,140 @@ hr {
+
4.2.0 -- 2017-03-10
+
+
Sub-task
+
+
+- [SMACK-639] - Add support for pre-approved subscription requests (RFC 6121 § 3.4)
+
+
+
+
Bug
+
+
+- [SMACK-306] - loadRosterOnLogin has non-trivial side effect on getRoster
+
+- [SMACK-416] - Refactor PEP to make it use the existing pubsub API.
+
+- [SMACK-674] - PubSub Affiliation extension element is missing 'jid' attribute, and is using wrong element name 'subscription'
+
+- [SMACK-682] - Add support for "XEP-0360: Nonzas (are not Stanzas)"
+
+- [SMACK-683] - Using a Proxy with XMPPTCPConnection failes with "SocketException: Unconnected sockets not implemented"
+
+- [SMACK-691] - Add support for MUCItem's Actor 'nick'
+
+- [SMACK-705] - PubSub's Affiliation.getElementName() returns wrong name
+
+- [SMACK-722] - SASL X-OAUTH2 implementation incorrectly performs Base64 encoding twice
+
+- [SMACK-723] - Support "Caps Optimizations" (XEP-0115 § 8.4)
+
+- [SMACK-724] - Do not re-use the Socket after connect() failed.
+
+- [SMACK-725] - ReconnectionManager should handle AlreadyConnectedException and AlreadyLoggedInException not as failure
+
+- [SMACK-741] - Ad-hoc command 'note' element 'type' attribute should be treated as optional
+
+- [SMACK-745] - Memory leak in MultiUserChat
+
+
+
+
New Feature
+
+
+- [SMACK-366] - Add support for DNSSEC.
+
+- [SMACK-610] - Add support for XEP-0080: User Location
+
+- [SMACK-619] - Add roomDestroyed to MUC UserStatusListener
+
+- [SMACK-625] - Add support for XEP-313: Message Archive Management
+
+- [SMACK-675] - Add support for PubSub affiliation actions as owner
+
+- [SMACK-677] - Add support for SASL 'authzid' (Authorization Identity)
+
+- [SMACK-690] - Add support for DNS-Based Authentication of Named Entities (DANE, RFC 6698)
+
+- [SMACK-731] - Add support for XEP-0191: Blocking Command
+
+- [SMACK-732] - Smack should be able to handle "single equals sign" SASL responses
+
+- [SMACK-740] - Add support for Multi-User Chat Light
+
+- [SMACK-742] - Add support for XEP-0133: Service Administration
+
+- [SMACK-747] - Add support for XEP-0363: HTTP File Upload
+
+
+
+
Task
+
+
+- [SMACK-638] - Call connection creation listeners from within AbstractXMPPConnection's constructor
+
+- [SMACK-644] - Throw exception if account creation or password change is performed over insecure connections
+
+- [SMACK-655] - Enable StreamManagement by default
+
+
+
+
Improvement
+
+
+- [SMACK-372] - Make package protected methods in PEPItem public
+
+- [SMACK-572] - Rejoin MUC rooms after reconnect
+
+- [SMACK-628] - Rework Roster handling with anonymous connections
+
+- [SMACK-629] - Rework how Smack handles anonymous connections
+
+- [SMACK-631] - Improve ParsingExceptionCallback, allow it to be a functional interface
+
+- [SMACK-632] - Make Smack interruptible
+
+- [SMACK-633] - Allow clean and graceful disconnects (stream closing)
+
+- [SMACK-634] - Use jxmpp-jid, add Jid class to replace String's being used as JIDs
+
+- [SMACK-646] - Add support for MUC roomnick rewrite
+
+- [SMACK-647] - Don't automatically call login() on connect() if the connection was authenticated before
+
+- [SMACK-648] - Improve MultiUserChat API
+
+- [SMACK-657] - Rename RosterEntry.getStatus and RosterPacket.ItemStatus to ItemAskStatus
+
+- [SMACK-663] - Roster should be fully loaded when Roster.getInstanceFor(XMPPConnection) is called with a authenticated connection
+
+- [SMACK-665] - Rename 'serviceName' to 'xmppServiceDomain'
+
+- [SMACK-666] - Typo in 'RosterEntries.rosterEntires()', change to 'RosterEntries.rosterEntries()'
+
+- [SMACK-703] - Limit the stored presences of entities not in Roster
+
+- [SMACK-704] - Pass down Message stanza in ChatStateListener
+
+- [SMACK-711] - Improve the logging of TCP connection attempts.
+
+- [SMACK-720] - Improve support for Tor and Hidden Services.
+
+- [SMACK-721] - Report illegal Stream Management states to avoid OOM Exception
+
+- [SMACK-727] - Add partial support for the IoT XEPs (XEP-0323, -0324, -0325, -0347)
+
+- [SMACK-733] - Handle outgoing 'unavailable' Presences in Roster
+
+- [SMACK-736] - Add support for Chat Markers (XEP-0333)
+
+- [SMACK-737] - Add support for Bits of Binary (XEP-0231)
+
+- [SMACK-738] - Add support for Push Notifications (XEP-0357)
+
+
4.1.9 -- 2016-11-19
diff --git a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java
index abf9105f6..b91bb5a40 100644
--- a/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java
+++ b/smack-bosh/src/main/java/org/jivesoftware/smack/bosh/XMPPBOSHConnection.java
@@ -38,6 +38,7 @@ import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smack.packet.Nonza;
import org.jivesoftware.smack.packet.Presence;
+import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.SASLFailure;
import org.jivesoftware.smack.sasl.packet.SaslStreamElements.Success;
import org.jivesoftware.smack.util.PacketParserUtils;
@@ -529,7 +530,13 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
}
break;
case "error":
- throw new StreamErrorException(PacketParserUtils.parseStreamError(parser));
+ //Some bosh error isn't stream error.
+ if ("urn:ietf:params:xml:ns:xmpp-streams".equals(parser.getNamespace(null))) {
+ throw new StreamErrorException(PacketParserUtils.parseStreamError(parser));
+ } else {
+ XMPPError.Builder builder = PacketParserUtils.parseError(parser);
+ throw new XMPPException.XMPPErrorException(null, builder.build());
+ }
}
break;
}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/ScramMechanism.java b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/ScramMechanism.java
index 08a1f6aab..edeedb665 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/ScramMechanism.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/sasl/core/ScramMechanism.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2014-2016 Florian Schmaus
+ * Copyright 2014-2017 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,6 +22,7 @@ import java.security.SecureRandom;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
+import java.util.Random;
import javax.security.auth.callback.CallbackHandler;
@@ -41,7 +42,12 @@ public abstract class ScramMechanism extends SASLMechanism {
private static final byte[] SERVER_KEY_BYTES = toBytes("Server Key");
private static final byte[] ONE = new byte[] { 0, 0, 0, 1 };
- private static final SecureRandom RANDOM = new SecureRandom();
+ private static final ThreadLocal
SECURE_RANDOM = new ThreadLocal() {
+ @Override
+ protected SecureRandom initialValue() {
+ return new SecureRandom();
+ }
+ };
private static final Cache CACHE = new LruCache(10);
@@ -292,8 +298,9 @@ public abstract class ScramMechanism extends SASLMechanism {
String getRandomAscii() {
int count = 0;
char[] randomAscii = new char[RANDOM_ASCII_BYTE_COUNT];
+ final Random random = SECURE_RANDOM.get();
while (count < RANDOM_ASCII_BYTE_COUNT) {
- int r = RANDOM.nextInt(128);
+ int r = random.nextInt(128);
char c = (char) r;
// RFC 5802 § 5.1 specifies 'r:' to exclude the ',' character and to be only printable ASCII characters
if (!isPrintableNonCommaAsciiChar(c)) {
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/ParserUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/ParserUtils.java
index 5a67c720e..9a5b68153 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/util/ParserUtils.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/util/ParserUtils.java
@@ -235,4 +235,19 @@ public class ParserUtils {
return uri;
}
+ public static String getRequiredAttribute(XmlPullParser parser, String name) throws IOException {
+ String value = parser.getAttributeValue("", name);
+ if (StringUtils.isNullOrEmpty(value)) {
+ throw new IOException("Attribute " + name + " is null or empty (" + value + ')');
+ }
+ return value;
+ }
+
+ public static String getRequiredNextText(XmlPullParser parser) throws XmlPullParserException, IOException {
+ String text = parser.nextText();
+ if (StringUtils.isNullOrEmpty(text)) {
+ throw new IOException("Next text is null or empty (" + text + ')');
+ }
+ return text;
+ }
}
diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java
index e61b15650..94e528099 100644
--- a/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java
+++ b/smack-core/src/main/java/org/jivesoftware/smack/util/StringUtils.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2003-2007 Jive Software, 2016 Florian Schmaus.
+ * Copyright 2003-2007 Jive Software, 2016-2017 Florian Schmaus.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -271,7 +271,12 @@ public class StringUtils {
* The Random class is not considered to be cryptographically secure, so
* only use these random Strings for low to medium security applications.
*/
- private static final Random randGen = new Random();
+ private static final ThreadLocal randGen = new ThreadLocal() {
+ @Override
+ protected Random initialValue() {
+ return new Random();
+ }
+ };
/**
* Array of numbers and letters of mixed case. Numbers appear in the list
@@ -299,15 +304,22 @@ public class StringUtils {
if (length < 1) {
return null;
}
+
+ final Random random = randGen.get();
// Create a char buffer to put random letters and numbers in.
char [] randBuffer = new char[length];
for (int i=0; i SECURE_RANDOM = new ThreadLocal() {
+ @Override
+ protected SecureRandom initialValue() {
+ return new SecureRandom();
+ }
+ };
public static String randomString(final int length) {
if (length < 1) {
@@ -315,7 +327,7 @@ public class StringUtils {
}
byte[] randomBytes = new byte[length];
- SECURE_RANDOM.nextBytes(randomBytes);
+ SECURE_RANDOM.get().nextBytes(randomBytes);
char[] randomChars = new char[length];
for (int i = 0; i < length; i++) {
randomChars[i] = getPrintableChar(randomBytes[i]);
diff --git a/smack-experimental/build.gradle b/smack-experimental/build.gradle
index d6b8c2582..51315f23a 100644
--- a/smack-experimental/build.gradle
+++ b/smack-experimental/build.gradle
@@ -9,4 +9,5 @@ dependencies {
compile project(':smack-extensions')
testCompile project(path: ":smack-core", configuration: "testRuntime")
testCompile project(path: ":smack-core", configuration: "archives")
+ testCompile project(path: ":smack-extensions", configuration: "testRuntime")
}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java
new file mode 100644
index 000000000..ddf67f054
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadManager.java
@@ -0,0 +1,463 @@
+/**
+ *
+ * Copyright © 2017 Grigory Fedorov
+ *
+ * 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.httpfileupload;
+
+import org.jivesoftware.smack.AbstractConnectionListener;
+import org.jivesoftware.smack.ConnectionConfiguration;
+import org.jivesoftware.smack.ConnectionCreationListener;
+import org.jivesoftware.smack.Manager;
+import org.jivesoftware.smack.SmackException;
+import org.jivesoftware.smack.XMPPConnection;
+import org.jivesoftware.smack.XMPPConnectionRegistry;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
+import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
+import org.jivesoftware.smackx.httpfileupload.UploadService.Version;
+import org.jivesoftware.smackx.httpfileupload.element.Slot;
+import org.jivesoftware.smackx.httpfileupload.element.SlotRequest;
+import org.jivesoftware.smackx.httpfileupload.element.SlotRequest_V0_2;
+import org.jivesoftware.smackx.xdata.FormField;
+import org.jivesoftware.smackx.xdata.packet.DataForm;
+import org.jxmpp.jid.DomainBareJid;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.WeakHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * A manager for XEP-0363: HTTP File Upload.
+ *
+ * @author Grigory Fedorov
+ * @author Florian Schmaus
+ * @see XEP-0363: HTTP File Upload
+ */
+public final class HttpFileUploadManager extends Manager {
+
+ public static final String NAMESPACE = "urn:xmpp:http:upload:0";
+ public static final String NAMESPACE_0_2 = "urn:xmpp:http:upload";
+
+ private static final Logger LOGGER = Logger.getLogger(HttpFileUploadManager.class.getName());
+
+ static {
+ XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
+ @Override
+ public void connectionCreated(XMPPConnection connection) {
+ getInstanceFor(connection);
+ }
+ });
+ }
+
+ private static final Map INSTANCES = new WeakHashMap<>();
+
+ private UploadService defaultUploadService;
+
+ private SSLSocketFactory tlsSocketFactory;
+
+ /**
+ * Obtain the HttpFileUploadManager responsible for a connection.
+ *
+ * @param connection the connection object.
+ * @return a HttpFileUploadManager instance
+ */
+ public static synchronized HttpFileUploadManager getInstanceFor(XMPPConnection connection) {
+ HttpFileUploadManager httpFileUploadManager = INSTANCES.get(connection);
+
+ if (httpFileUploadManager == null) {
+ httpFileUploadManager = new HttpFileUploadManager(connection);
+ INSTANCES.put(connection, httpFileUploadManager);
+ }
+
+ return httpFileUploadManager;
+ }
+
+ private HttpFileUploadManager(XMPPConnection connection) {
+ super(connection);
+
+ connection.addConnectionListener(new AbstractConnectionListener() {
+ @Override
+ public void authenticated(XMPPConnection connection, boolean resumed) {
+ // No need to reset the cache if the connection got resumed.
+ if (resumed) {
+ return;
+ }
+
+ try {
+ discoverUploadService();
+ } catch (XMPPException.XMPPErrorException | SmackException.NotConnectedException
+ | SmackException.NoResponseException | InterruptedException e) {
+ LOGGER.log(Level.WARNING, "Error during discovering HTTP File Upload service", e);
+ }
+ }
+ });
+ }
+
+ private static UploadService uploadServiceFrom(DiscoverInfo discoverInfo) {
+ assert(containsHttpFileUploadNamespace(discoverInfo));
+
+ UploadService.Version version;
+ if (discoverInfo.containsFeature(NAMESPACE)) {
+ version = Version.v0_3;
+ } else if (discoverInfo.containsFeature(NAMESPACE_0_2)) {
+ version = Version.v0_2;
+ } else {
+ throw new AssertionError();
+ }
+
+ DomainBareJid address = discoverInfo.getFrom().asDomainBareJid();
+
+ DataForm dataForm = DataForm.from(discoverInfo);
+ if (dataForm == null) {
+ return new UploadService(address, version);
+ }
+
+ FormField field = dataForm.getField("max-file-size");
+ if (field == null) {
+ return new UploadService(address, version);
+ }
+
+ List values = field.getValues();
+ if (values.isEmpty()) {
+ return new UploadService(address, version);
+
+ }
+
+ Long maxFileSize = Long.valueOf(values.get(0));
+ return new UploadService(address, version, maxFileSize);
+ }
+
+ /**
+ * Discover upload service.
+ *
+ * Called automatically when connection is authenticated.
+ *
+ * Note that this is a synchronous call -- Smack must wait for the server response.
+ *
+ * @return true if upload service was discovered
+
+ * @throws XMPPException.XMPPErrorException
+ * @throws SmackException.NotConnectedException
+ * @throws InterruptedException
+ * @throws SmackException.NoResponseException
+ */
+ public boolean discoverUploadService() throws XMPPException.XMPPErrorException, SmackException.NotConnectedException,
+ InterruptedException, SmackException.NoResponseException {
+ ServiceDiscoveryManager sdm = ServiceDiscoveryManager.getInstanceFor(connection());
+ List servicesDiscoverInfo = sdm
+ .findServicesDiscoverInfo(NAMESPACE, true, true);
+
+ if (servicesDiscoverInfo.isEmpty()) {
+ servicesDiscoverInfo = sdm.findServicesDiscoverInfo(NAMESPACE_0_2, true, true);
+ if (servicesDiscoverInfo.isEmpty()) {
+ return false;
+ }
+ }
+
+ DiscoverInfo discoverInfo = servicesDiscoverInfo.get(0);
+
+ defaultUploadService = uploadServiceFrom(discoverInfo);
+ return true;
+ }
+
+ /**
+ * Check if upload service was discovered.
+ *
+ * @return true if upload service was discovered
+ */
+ public boolean isUploadServiceDiscovered() {
+ return defaultUploadService != null;
+ }
+
+ /**
+ * Get default upload service if it was discovered.
+ *
+ * @return upload service JID or null if not available
+ */
+ public UploadService getDefaultUploadService() {
+ return defaultUploadService;
+ }
+
+ /**
+ * Request slot and uploaded file to HTTP file upload service.
+ *
+ * You don't need to request slot and upload file separately, this method will do both.
+ * Note that this is a synchronous call -- Smack must wait for the server response.
+ *
+ * @param file file to be uploaded
+ * @return public URL for sharing uploaded file
+ * @throws InterruptedException
+ * @throws XMPPException.XMPPErrorException
+ * @throws SmackException
+ * @throws IOException in case of HTTP upload errors
+ */
+ public URL uploadFile(File file) throws InterruptedException, XMPPException.XMPPErrorException,
+ SmackException, IOException {
+ return uploadFile(file, null);
+ }
+
+ /**
+ * Request slot and uploaded file to HTTP file upload service with progress callback.
+ *
+ * You don't need to request slot and upload file separately, this method will do both.
+ * Note that this is a synchronous call -- Smack must wait for the server response.
+ *
+ * @param file file to be uploaded
+ * @param listener upload progress listener of null
+ * @return public URL for sharing uploaded file
+ *
+ * @throws InterruptedException
+ * @throws XMPPException.XMPPErrorException
+ * @throws SmackException
+ * @throws IOException
+ */
+ public URL uploadFile(File file, UploadProgressListener listener) throws InterruptedException,
+ XMPPException.XMPPErrorException, SmackException, IOException {
+ if (!file.isFile()) {
+ throw new FileNotFoundException("The path " + file.getAbsolutePath() + " is not a file");
+ }
+ final Slot slot = requestSlot(file.getName(), file.length(), "application/octet-stream");
+
+ uploadFile(file, slot, listener);
+
+ return slot.getGetUrl();
+ }
+
+
+ /**
+ * Request a new upload slot from default upload service (if discovered). When you get slot you should upload file
+ * to PUT URL and share GET URL. Note that this is a synchronous call -- Smack must wait for the server response.
+ *
+ * @param filename name of file to be uploaded
+ * @param fileSize file size in bytes.
+ * @return file upload Slot in case of success
+ * @throws IllegalArgumentException if fileSize is less than or equal to zero or greater than the maximum size
+ * supported by the service.
+ * @throws InterruptedException
+ * @throws XMPPException.XMPPErrorException
+ * @throws SmackException.NotConnectedException
+ * @throws SmackException.NoResponseException
+ */
+ public Slot requestSlot(String filename, long fileSize) throws InterruptedException,
+ XMPPException.XMPPErrorException, SmackException {
+ return requestSlot(filename, fileSize, null, null);
+ }
+
+ /**
+ * Request a new upload slot with optional content type from default upload service (if discovered).
+ *
+ * When you get slot you should upload file to PUT URL and share GET URL.
+ * Note that this is a synchronous call -- Smack must wait for the server response.
+ *
+ * @param filename name of file to be uploaded
+ * @param fileSize file size in bytes.
+ * @param contentType file content-type or null
+ * @return file upload Slot in case of success
+
+ * @throws IllegalArgumentException if fileSize is less than or equal to zero or greater than the maximum size
+ * supported by the service.
+ * @throws SmackException.NotConnectedException
+ * @throws InterruptedException
+ * @throws XMPPException.XMPPErrorException
+ * @throws SmackException.NoResponseException
+ */
+ public Slot requestSlot(String filename, long fileSize, String contentType) throws SmackException,
+ InterruptedException, XMPPException.XMPPErrorException {
+ return requestSlot(filename, fileSize, contentType, null);
+ }
+
+ /**
+ * Request a new upload slot with optional content type from custom upload service.
+ *
+ * When you get slot you should upload file to PUT URL and share GET URL.
+ * Note that this is a synchronous call -- Smack must wait for the server response.
+ *
+ * @param filename name of file to be uploaded
+ * @param fileSize file size in bytes.
+ * @param contentType file content-type or null
+ * @param uploadServiceAddress the address of the upload service to use or null for default one
+ * @return file upload Slot in case of success
+ * @throws IllegalArgumentException if fileSize is less than or equal to zero or greater than the maximum size
+ * supported by the service.
+ * @throws SmackException
+ * @throws InterruptedException
+ * @throws XMPPException.XMPPErrorException
+ */
+ public Slot requestSlot(String filename, long fileSize, String contentType, DomainBareJid uploadServiceAddress)
+ throws SmackException, InterruptedException, XMPPException.XMPPErrorException {
+ final XMPPConnection connection = connection();
+ final UploadService defaultUploadService = this.defaultUploadService;
+
+ // The upload service we are going to use.
+ UploadService uploadService;
+
+ if (uploadServiceAddress == null) {
+ uploadService = defaultUploadService;
+ } else {
+ if (defaultUploadService != null && defaultUploadService.getAddress().equals(uploadServiceAddress)) {
+ // Avoid performing a service discovery if we already know about the given service.
+ uploadService = defaultUploadService;
+ } else {
+ DiscoverInfo discoverInfo = ServiceDiscoveryManager.getInstanceFor(connection).discoverInfo(uploadServiceAddress);
+ if (!containsHttpFileUploadNamespace(discoverInfo)) {
+ throw new IllegalArgumentException("There is no HTTP upload service running at the given address '"
+ + uploadServiceAddress + '\'');
+ }
+ uploadService = uploadServiceFrom(discoverInfo);
+ }
+ }
+
+ if (uploadService == null) {
+ throw new SmackException("No upload service specified and also none discovered.");
+ }
+
+ if (!uploadService.acceptsFileOfSize(fileSize)) {
+ throw new IllegalArgumentException(
+ "Requested file size " + fileSize + " is greater than max allowed size " + uploadService.getMaxFileSize());
+ }
+
+ SlotRequest slotRequest;
+ switch (uploadService.getVersion()) {
+ case v0_3:
+ slotRequest = new SlotRequest(uploadService.getAddress(), filename, fileSize, contentType);
+ break;
+ case v0_2:
+ slotRequest = new SlotRequest_V0_2(uploadService.getAddress(), filename, fileSize, contentType);
+ break;
+ default:
+ throw new AssertionError();
+ }
+
+ return connection.createStanzaCollectorAndSend(slotRequest).nextResultOrThrow();
+ }
+
+ public void setTlsContext(SSLContext tlsContext) {
+ if (tlsContext == null) {
+ return;
+ }
+ this.tlsSocketFactory = tlsContext.getSocketFactory();
+ }
+
+ public void useTlsSettingsFrom(ConnectionConfiguration connectionConfiguration) {
+ SSLContext sslContext = connectionConfiguration.getCustomSSLContext();
+ setTlsContext(sslContext);
+ }
+
+ private void uploadFile(final File file, final Slot slot, UploadProgressListener listener) throws IOException {
+ final long fileSize = file.length();
+ // TODO Remove once Smack's minimum Android API level is 19 or higher. See also comment below.
+ if (fileSize >= Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("File size " + fileSize + " must be less than " + Integer.MAX_VALUE);
+ }
+ final int fileSizeInt = (int) fileSize;
+
+ // Construct the FileInputStream first to make sure we can actually read the file.
+ final FileInputStream fis = new FileInputStream(file);
+
+ final URL putUrl = slot.getPutUrl();
+
+ final HttpURLConnection urlConnection = (HttpURLConnection) putUrl.openConnection();
+
+ urlConnection.setRequestMethod("PUT");
+ urlConnection.setUseCaches(false);
+ urlConnection.setDoOutput(true);
+ // TODO Change to using fileSize once Smack's minimum Android API level is 19 or higher.
+ urlConnection.setFixedLengthStreamingMode(fileSizeInt);
+ urlConnection.setRequestProperty("Content-Type", "application/octet-stream;");
+ for (Entry header : slot.getHeaders().entrySet()) {
+ urlConnection.setRequestProperty(header.getKey(), header.getValue());
+ }
+
+ final SSLSocketFactory tlsSocketFactory = this.tlsSocketFactory;
+ if (tlsSocketFactory != null && urlConnection instanceof HttpsURLConnection) {
+ HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) urlConnection;
+ httpsUrlConnection.setSSLSocketFactory(tlsSocketFactory);
+ }
+
+ try {
+ OutputStream outputStream = urlConnection.getOutputStream();
+
+ long bytesSend = 0;
+
+ if (listener != null) {
+ listener.onUploadProgress(0, fileSize);
+ }
+
+ BufferedInputStream inputStream = new BufferedInputStream(fis);
+
+ // TODO Factor in extra static method (and re-use e.g. in bytestream code).
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ try {
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
+ outputStream.write(buffer, 0, bytesRead);
+ bytesSend += bytesRead;
+
+ if (listener != null) {
+ listener.onUploadProgress(bytesSend, fileSize);
+ }
+ }
+ }
+ finally {
+ try {
+ inputStream.close();
+ }
+ catch (IOException e) {
+ LOGGER.log(Level.WARNING, "Exception while closing input stream", e);
+ }
+ try {
+ outputStream.close();
+ }
+ catch (IOException e) {
+ LOGGER.log(Level.WARNING, "Exception while closing output stream", e);
+ }
+ }
+
+ int status = urlConnection.getResponseCode();
+ switch (status) {
+ case HttpURLConnection.HTTP_OK:
+ case HttpURLConnection.HTTP_CREATED:
+ case HttpURLConnection.HTTP_NO_CONTENT:
+ break;
+ default:
+ throw new IOException("Error response " + status + " from server during file upload: "
+ + urlConnection.getResponseMessage() + ", file size: " + fileSize + ", put URL: "
+ + putUrl);
+ }
+ }
+ finally {
+ urlConnection.disconnect();
+ }
+ }
+
+ private static boolean containsHttpFileUploadNamespace(DiscoverInfo discoverInfo) {
+ return discoverInfo.containsFeature(NAMESPACE) || discoverInfo.containsFeature(NAMESPACE_0_2);
+ }
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/UploadProgressListener.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/UploadProgressListener.java
new file mode 100644
index 000000000..085734795
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/UploadProgressListener.java
@@ -0,0 +1,32 @@
+/**
+ *
+ * Copyright © 2017 Grigory Fedorov
+ *
+ * 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.httpfileupload;
+
+/**
+ * Callback interface to get upload progress.
+ */
+public interface UploadProgressListener {
+
+ /**
+ * Callback for displaying upload progress.
+ *
+ * @param uploadedBytes the number of bytes uploaded at the moment
+ * @param totalBytes the total number of bytes to be uploaded
+ */
+ void onUploadProgress(long uploadedBytes, long totalBytes);
+
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/UploadService.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/UploadService.java
new file mode 100644
index 000000000..e9d81a447
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/UploadService.java
@@ -0,0 +1,66 @@
+/**
+ *
+ * Copyright © 2017 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.httpfileupload;
+
+import org.jivesoftware.smack.util.Objects;
+import org.jxmpp.jid.DomainBareJid;
+
+public class UploadService {
+
+ enum Version {
+ v0_2,
+ v0_3,
+ };
+
+ private final DomainBareJid address;
+ private final Version version;
+ private final Long maxFileSize;
+
+ UploadService(DomainBareJid address, Version version) {
+ this(address, version, null);
+ }
+
+ UploadService(DomainBareJid address, Version version, Long maxFileSize) {
+ this.address = Objects.requireNonNull(address);
+ this.version = version;
+ this.maxFileSize = maxFileSize;
+ }
+
+ public DomainBareJid getAddress() {
+ return address;
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+
+ public boolean hasMaxFileSizeLimit() {
+ return maxFileSize != null;
+ }
+
+ public Long getMaxFileSize() {
+ return maxFileSize;
+ }
+
+ public boolean acceptsFileOfSize(long size) {
+ if (!hasMaxFileSizeLimit()) {
+ return true;
+ }
+
+ return size <= maxFileSize;
+ }
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/FileTooLargeError.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/FileTooLargeError.java
new file mode 100644
index 000000000..a0fcb7d04
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/FileTooLargeError.java
@@ -0,0 +1,76 @@
+/**
+ *
+ * Copyright © 2017 Grigory Fedorov
+ *
+ * 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.httpfileupload.element;
+
+import org.jivesoftware.smack.packet.ExtensionElement;
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.packet.XMPPError;
+import org.jivesoftware.smack.util.XmlStringBuilder;
+
+/**
+ * File Too Large error extension.
+ *
+ * @author Grigory Fedorov
+ * @see XEP-0363: HTTP File Upload
+ */
+public class FileTooLargeError implements ExtensionElement {
+ public static final String ELEMENT = "file-too-large";
+ public static final String NAMESPACE = SlotRequest.NAMESPACE;
+
+ private final long maxFileSize;
+ private final String namespace;
+
+ public FileTooLargeError(long maxFileSize) {
+ this(maxFileSize, NAMESPACE);
+ }
+
+ protected FileTooLargeError(long maxFileSize, String namespace) {
+ this.maxFileSize = maxFileSize;
+ this.namespace = namespace;
+ }
+
+ public long getMaxFileSize() {
+ return maxFileSize;
+ }
+
+ @Override
+ public String getElementName() {
+ return ELEMENT;
+ }
+
+ @Override
+ public String getNamespace() {
+ return namespace;
+ }
+
+ @Override
+ public XmlStringBuilder toXML() {
+ XmlStringBuilder xml = new XmlStringBuilder(this);
+ xml.rightAngleBracket();
+ xml.element("max-file-size", String.valueOf(maxFileSize));
+ xml.closeElement(this);
+ return xml;
+ }
+
+ public static FileTooLargeError from(IQ iq) {
+ XMPPError error = iq.getError();
+ if (error == null) {
+ return null;
+ }
+ return error.getExtension(ELEMENT, NAMESPACE);
+ }
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/FileTooLargeError_V0_2.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/FileTooLargeError_V0_2.java
new file mode 100644
index 000000000..fedf8d118
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/FileTooLargeError_V0_2.java
@@ -0,0 +1,30 @@
+/**
+ *
+ * Copyright 2017 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.httpfileupload.element;
+
+import org.jivesoftware.smackx.httpfileupload.HttpFileUploadManager;
+
+public class FileTooLargeError_V0_2 extends FileTooLargeError {
+
+ public static final String NAMESPACE = HttpFileUploadManager.NAMESPACE_0_2;
+
+ public FileTooLargeError_V0_2(long maxFileSize) {
+ super(maxFileSize, NAMESPACE);
+ }
+
+
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/Slot.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/Slot.java
new file mode 100644
index 000000000..e341bca46
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/Slot.java
@@ -0,0 +1,84 @@
+/**
+ *
+ * Copyright © 2017 Grigory Fedorov
+ *
+ * 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.httpfileupload.element;
+
+import org.jivesoftware.smack.packet.IQ;
+
+import java.net.URL;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Slot responded by upload service.
+ *
+ * @author Grigory Fedorov
+ * @see XEP-0363: HTTP File Upload
+ */
+public class Slot extends IQ {
+
+ public static final String ELEMENT = "slot";
+ public static final String NAMESPACE = SlotRequest.NAMESPACE;
+
+ private final URL putUrl;
+ private final URL getUrl;
+ private final Map headers;
+
+ public Slot(URL putUrl, URL getUrl) {
+ this(putUrl, getUrl, null);
+ }
+
+ public Slot(URL putUrl, URL getUrl, Map headers) {
+ this(putUrl, getUrl, headers, NAMESPACE);
+ }
+
+ protected Slot(URL putUrl, URL getUrl, Map headers, String namespace) {
+ super(ELEMENT, namespace);
+ setType(Type.result);
+ this.putUrl = putUrl;
+ this.getUrl = getUrl;
+ if (headers == null) {
+ this.headers = Collections.emptyMap();
+ } else {
+ this.headers = Collections.unmodifiableMap(headers);
+ }
+ }
+
+ public URL getPutUrl() {
+ return putUrl;
+ }
+
+ public URL getGetUrl() {
+ return getUrl;
+ }
+
+ public Map getHeaders() {
+ return headers;
+ }
+
+ @Override
+ protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
+ xml.rightAngleBracket();
+
+ xml.element("put", putUrl.toString());
+ xml.element("get", getUrl.toString());
+ for (Map.Entry entry : getHeaders().entrySet()) {
+ xml.openElement("header").attribute(entry.getKey(), entry.getValue());
+ }
+
+ return xml;
+ }
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/SlotRequest.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/SlotRequest.java
new file mode 100644
index 000000000..a776ccf32
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/SlotRequest.java
@@ -0,0 +1,89 @@
+/**
+ *
+ * Copyright © 2017 Grigory Fedorov
+ *
+ * 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.httpfileupload.element;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smackx.httpfileupload.HttpFileUploadManager;
+import org.jxmpp.jid.DomainBareJid;
+
+/**
+ * Upload slot request.
+
+ * @author Grigory Fedorov
+ * @see XEP-0363: HTTP File Upload
+ */
+public class SlotRequest extends IQ {
+ public static final String ELEMENT = "request";
+ public static final String NAMESPACE = HttpFileUploadManager.NAMESPACE;
+
+ private final String filename;
+ private final long size;
+ private final String contentType;
+
+ public SlotRequest(DomainBareJid uploadServiceAddress, String filename, long size) {
+ this(uploadServiceAddress, filename, size, null);
+ }
+
+ /**
+ * Create new slot request.
+ *
+ * @param uploadServiceAddress the XMPP address of the service to request the slot from.
+ * @param filename name of file
+ * @param size size of file in bytes
+ * @param contentType file content type or null
+ * @throws IllegalArgumentException if size is less than or equal to zero
+ */
+ public SlotRequest(DomainBareJid uploadServiceAddress, String filename, long size, String contentType) {
+ this(uploadServiceAddress, filename, size, contentType, NAMESPACE);
+ }
+
+ protected SlotRequest(DomainBareJid uploadServiceAddress, String filename, long size, String contentType, String namespace) {
+ super(ELEMENT, namespace);
+
+ if (size <= 0) {
+ throw new IllegalArgumentException("File fileSize must be greater than zero.");
+ }
+
+ this.filename = filename;
+ this.size = size;
+ this.contentType = contentType;
+
+ setType(Type.get);
+ setTo(uploadServiceAddress);
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public String getContentType() {
+ return contentType;
+ }
+
+ @Override
+ protected IQChildElementXmlStringBuilder getIQChildElementBuilder(IQChildElementXmlStringBuilder xml) {
+ xml.rightAngleBracket();
+ xml.element("filename", filename);
+ xml.element("size", String.valueOf(size));
+ xml.optElement("content-type", contentType);
+ return xml;
+ }
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/SlotRequest_V0_2.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/SlotRequest_V0_2.java
new file mode 100644
index 000000000..eb87bc923
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/SlotRequest_V0_2.java
@@ -0,0 +1,42 @@
+/**
+ *
+ * Copyright 2017 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.httpfileupload.element;
+
+import org.jivesoftware.smackx.httpfileupload.HttpFileUploadManager;
+import org.jxmpp.jid.DomainBareJid;
+
+public class SlotRequest_V0_2 extends SlotRequest {
+
+ public static final String NAMESPACE = HttpFileUploadManager.NAMESPACE_0_2;
+
+ public SlotRequest_V0_2(DomainBareJid uploadServiceAddress, String filename, long size) {
+ this(uploadServiceAddress, filename, size, null);
+ }
+
+ /**
+ * Create new slot request.
+ *
+ * @param uploadServiceAddress the XMPP address of the service to request the slot from.
+ * @param filename name of file
+ * @param size size of file in bytes
+ * @param contentType file content type or null
+ * @throws IllegalArgumentException if size is less than or equal to zero
+ */
+ public SlotRequest_V0_2(DomainBareJid uploadServiceAddress, String filename, long size, String contentType) {
+ super(uploadServiceAddress, filename, size, contentType, NAMESPACE);
+ }
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/Slot_V0_2.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/Slot_V0_2.java
new file mode 100644
index 000000000..20645ac1a
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/Slot_V0_2.java
@@ -0,0 +1,31 @@
+/**
+ *
+ * Copyright 2017 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.httpfileupload.element;
+
+import java.net.URL;
+
+import org.jivesoftware.smackx.httpfileupload.HttpFileUploadManager;
+
+public class Slot_V0_2 extends Slot {
+
+ public static final String NAMESPACE = HttpFileUploadManager.NAMESPACE_0_2;
+
+ public Slot_V0_2(URL putUrl, URL getUrl) {
+ super(putUrl, getUrl, null, NAMESPACE);
+ }
+
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/package-info.java
new file mode 100644
index 000000000..d20c3658b
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/element/package-info.java
@@ -0,0 +1,24 @@
+/**
+ *
+ * Copyright © 2017 Grigory Fedorov
+ *
+ * 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.
+ */
+
+/**
+ * IQ stanzas and extensions for XEP-0363: HTTP File Upload.
+ *
+ * @author Grigory Fedorov
+ * @see XEP-0363: HTTP File Upload
+ */
+package org.jivesoftware.smackx.httpfileupload.element;
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/package-info.java
new file mode 100644
index 000000000..d61996ae9
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/package-info.java
@@ -0,0 +1,24 @@
+/**
+ *
+ * Copyright © 2017 Grigory Fedorov
+ *
+ * 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-0363: HTTP File Upload.
+ *
+ * @author Grigory Fedorov
+ * @see XEP-0363: HTTP File Upload
+ */
+package org.jivesoftware.smackx.httpfileupload;
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/provider/FileTooLargeErrorProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/provider/FileTooLargeErrorProvider.java
new file mode 100644
index 000000000..77ea7fda4
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/provider/FileTooLargeErrorProvider.java
@@ -0,0 +1,66 @@
+/**
+ *
+ * Copyright © 2017 Grigory Fedorov
+ *
+ * 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.httpfileupload.provider;
+
+import org.jivesoftware.smack.provider.ExtensionElementProvider;
+import org.jivesoftware.smackx.httpfileupload.element.FileTooLargeError;
+import org.jivesoftware.smackx.httpfileupload.element.FileTooLargeError_V0_2;
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Provider for File Too Large error extension.
+ *
+ * @author Grigory Fedorov
+ * @see XEP-0363: HTTP File Upload
+ */
+public class FileTooLargeErrorProvider extends ExtensionElementProvider {
+
+ @Override
+ public FileTooLargeError parse(XmlPullParser parser, int initialDepth) throws Exception {
+ final String namespace = parser.getNamespace();
+ Long maxFileSize = null;
+
+ outerloop: while(true) {
+ int event = parser.next();
+
+ switch (event) {
+ case XmlPullParser.START_TAG:
+ String name = parser.getName();
+ switch(name) {
+ case "max-file-size":
+ maxFileSize = Long.valueOf(parser.nextText());
+ break;
+ }
+ break;
+ case XmlPullParser.END_TAG:
+ if (parser.getDepth() == initialDepth) {
+ break outerloop;
+ }
+ break;
+ }
+ }
+
+ switch (namespace) {
+ case FileTooLargeError.NAMESPACE:
+ return new FileTooLargeError(maxFileSize);
+ case FileTooLargeError_V0_2.NAMESPACE:
+ return new FileTooLargeError_V0_2(maxFileSize);
+ default:
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/provider/SlotProvider.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/provider/SlotProvider.java
new file mode 100644
index 000000000..bd85ac5c2
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/provider/SlotProvider.java
@@ -0,0 +1,87 @@
+/**
+ *
+ * Copyright © 2017 Grigory Fedorov
+ *
+ * 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.httpfileupload.provider;
+
+import org.jivesoftware.smack.SmackException;
+import org.jivesoftware.smack.provider.IQProvider;
+import org.jivesoftware.smack.util.ParserUtils;
+import org.jivesoftware.smackx.httpfileupload.element.Slot;
+import org.jivesoftware.smackx.httpfileupload.element.Slot_V0_2;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Provider for Slot.
+ *
+ * @author Grigory Fedorov
+ * @see XEP-0363: HTTP File Upload
+ */
+public class SlotProvider extends IQProvider {
+
+ @Override
+ public Slot parse(XmlPullParser parser, int initialDepth) throws XmlPullParserException, IOException, SmackException {
+ final String namespace = parser.getNamespace();
+ URL putUrl = null;
+ URL getUrl = null;
+ Map headers = null;
+
+ outerloop: while (true) {
+ int event = parser.next();
+
+ switch (event) {
+ case XmlPullParser.START_TAG:
+ String name = parser.getName();
+ switch(name) {
+ case "put":
+ putUrl = new URL(parser.nextText());
+ break;
+ case "get":
+ getUrl = new URL(parser.nextText());
+ break;
+ case "header":
+ String headerName = ParserUtils.getRequiredAttribute(parser, "name");
+ String headerValue = ParserUtils.getRequiredNextText(parser);
+ if (headers == null) {
+ headers = new HashMap<>();
+ }
+ headers.put(headerName, headerValue);
+ break;
+ }
+ break;
+ case XmlPullParser.END_TAG:
+ if (parser.getDepth() == initialDepth) {
+ break outerloop;
+ }
+ break;
+ }
+ }
+
+ switch (namespace) {
+ case Slot.NAMESPACE:
+ return new Slot(putUrl, getUrl, headers);
+ case Slot_V0_2.NAMESPACE:
+ return new Slot_V0_2(putUrl, getUrl);
+ default:
+ throw new AssertionError();
+ }
+ }
+}
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/provider/package-info.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/provider/package-info.java
new file mode 100644
index 000000000..3894df689
--- /dev/null
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/provider/package-info.java
@@ -0,0 +1,24 @@
+/**
+ *
+ * Copyright © 2017 Grigory Fedorov
+ *
+ * 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.
+ */
+
+/**
+ * Providers for XEP-0363: HTTP File Upload.
+ *
+ * @author Grigory Fedorov
+ * @see XEP-0363: HTTP File Upload
+ */
+package org.jivesoftware.smackx.httpfileupload.provider;
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java
index 18cc9ac9e..1a07554a4 100644
--- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/MamManager.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright © 2016 Florian Schmaus and Fernando Ramirez
+ * Copyright © 2016-2017 Florian Schmaus, Fernando Ramirez
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@ package org.jivesoftware.smackx.mam;
import java.util.ArrayList;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -25,10 +26,10 @@ import java.util.WeakHashMap;
import org.jivesoftware.smack.ConnectionCreationListener;
import org.jivesoftware.smack.Manager;
-import org.jivesoftware.smack.StanzaCollector;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.SmackException.NotLoggedInException;
+import org.jivesoftware.smack.StanzaCollector;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPConnectionRegistry;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
@@ -47,6 +48,8 @@ import org.jivesoftware.smackx.mam.filter.MamResultFilter;
import org.jivesoftware.smackx.rsm.packet.RSMSet;
import org.jivesoftware.smackx.xdata.FormField;
import org.jivesoftware.smackx.xdata.packet.DataForm;
+import org.jxmpp.jid.EntityBareJid;
+import org.jxmpp.jid.EntityFullJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.util.XmppDateTime;
@@ -70,7 +73,7 @@ public final class MamManager extends Manager {
});
}
- private static final Map INSTANCES = new WeakHashMap<>();
+ private static final Map> INSTANCES = new WeakHashMap<>();
/**
* Get the singleton instance of MamManager.
@@ -78,19 +81,29 @@ public final class MamManager extends Manager {
* @param connection
* @return the instance of MamManager
*/
- public static synchronized MamManager getInstanceFor(XMPPConnection connection) {
- MamManager mamManager = INSTANCES.get(connection);
+ public static MamManager getInstanceFor(XMPPConnection connection) {
+ return getInstanceFor(connection, null);
+ }
- if (mamManager == null) {
- mamManager = new MamManager(connection);
- INSTANCES.put(connection, mamManager);
+ public static synchronized MamManager getInstanceFor(XMPPConnection connection, Jid archiveAddress) {
+ Map managers = INSTANCES.get(connection);
+ if (managers == null) {
+ managers = new HashMap<>();
+ INSTANCES.put(connection, managers);
+ }
+ MamManager mamManager = managers.get(archiveAddress);
+ if (mamManager == null) {
+ mamManager = new MamManager(connection, archiveAddress);
+ managers.put(archiveAddress, mamManager);
}
-
return mamManager;
}
- private MamManager(XMPPConnection connection) {
+ private final Jid archiveAddress;
+
+ private MamManager(XMPPConnection connection, Jid archiveAddress) {
super(connection);
+ this.archiveAddress = archiveAddress;
}
/**
@@ -106,7 +119,7 @@ public final class MamManager extends Manager {
*/
public MamQueryResult queryArchive(Integer max) throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException, NotLoggedInException {
- return queryArchive(max, null, null, null, null);
+ return queryArchive(null, max, null, null, null, null);
}
/**
@@ -122,7 +135,7 @@ public final class MamManager extends Manager {
*/
public MamQueryResult queryArchive(Jid withJid) throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException, NotLoggedInException {
- return queryArchive(null, null, null, withJid, null);
+ return queryArchive(null, null, null, null, withJid, null);
}
/**
@@ -142,7 +155,7 @@ public final class MamManager extends Manager {
*/
public MamQueryResult queryArchive(Date start, Date end) throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException, NotLoggedInException {
- return queryArchive(null, start, end, null, null);
+ return queryArchive(null, null, start, end, null, null);
}
/**
@@ -158,7 +171,7 @@ public final class MamManager extends Manager {
*/
public MamQueryResult queryArchive(List additionalFields) throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException, NotLoggedInException {
- return queryArchive(null, null, null, null, additionalFields);
+ return queryArchive(null, null, null, null, null, additionalFields);
}
/**
@@ -175,7 +188,7 @@ public final class MamManager extends Manager {
*/
public MamQueryResult queryArchiveWithStartDate(Date start) throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException, NotLoggedInException {
- return queryArchive(null, start, null, null, null);
+ return queryArchive(null, null, start, null, null, null);
}
/**
@@ -192,9 +205,10 @@ public final class MamManager extends Manager {
*/
public MamQueryResult queryArchiveWithEndDate(Date end) throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException, NotLoggedInException {
- return queryArchive(null, null, end, null, null);
+ return queryArchive(null, null, null, end, null, null);
}
+
/**
* Query archive applying filters: max count, start date, end date, from/to
* JID and with additional fields.
@@ -214,6 +228,32 @@ public final class MamManager extends Manager {
public MamQueryResult queryArchive(Integer max, Date start, Date end, Jid withJid, List additionalFields)
throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException,
NotLoggedInException {
+ return queryArchive(null, max, start, end, withJid, additionalFields);
+ }
+
+
+ /**
+ * Query an message archive like a MUC archive or a pubsub node archive, addressed by an archiveAddress, applying
+ * filters: max count, start date, end date, from/to JID and with additional fields. When archiveAddress is null the
+ * default, the server will be requested.
+ *
+ * @param node The Pubsub node name, can be null
+ * @param max
+ * @param start
+ * @param end
+ * @param withJid
+ * @param additionalFields
+ * @return the MAM query result
+ * @throws NoResponseException
+ * @throws XMPPErrorException
+ * @throws NotConnectedException
+ * @throws InterruptedException
+ * @throws NotLoggedInException
+ */
+ public MamQueryResult queryArchive(String node, Integer max, Date start, Date end, Jid withJid,
+ List additionalFields)
+ throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException,
+ NotLoggedInException {
DataForm dataForm = null;
String queryId = UUID.randomUUID().toString();
@@ -225,8 +265,9 @@ public final class MamManager extends Manager {
addAdditionalFields(additionalFields, dataForm);
}
- MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, dataForm);
+ MamQueryIQ mamQueryIQ = new MamQueryIQ(queryId, node, dataForm);
mamQueryIQ.setType(IQ.Type.set);
+ mamQueryIQ.setTo(archiveAddress);
addResultsLimit(max, mamQueryIQ);
return queryArchive(mamQueryIQ);
@@ -290,9 +331,31 @@ public final class MamManager extends Manager {
* @throws NotLoggedInException
*/
public MamQueryResult page(DataForm dataForm, RSMSet rsmSet) throws NoResponseException, XMPPErrorException,
+ NotConnectedException, InterruptedException, NotLoggedInException {
+
+ return page(null, dataForm, rsmSet);
+
+ }
+
+ /**
+ * Returns a page of the archive.
+ *
+ * @param node The Pubsub node name, can be null
+ * @param dataForm
+ * @param rsmSet
+ * @return the MAM query result
+ * @throws NoResponseException
+ * @throws XMPPErrorException
+ * @throws NotConnectedException
+ * @throws InterruptedException
+ * @throws NotLoggedInException
+ */
+ public MamQueryResult page(String node, DataForm dataForm, RSMSet rsmSet)
+ throws NoResponseException, XMPPErrorException,
NotConnectedException, InterruptedException, NotLoggedInException {
- MamQueryIQ mamQueryIQ = new MamQueryIQ(UUID.randomUUID().toString(), dataForm);
+ MamQueryIQ mamQueryIQ = new MamQueryIQ(UUID.randomUUID().toString(), node, dataForm);
mamQueryIQ.setType(IQ.Type.set);
+ mamQueryIQ.setTo(archiveAddress);
mamQueryIQ.addExtension(rsmSet);
return queryArchive(mamQueryIQ);
}
@@ -315,7 +378,7 @@ public final class MamManager extends Manager {
XMPPErrorException, NotConnectedException, InterruptedException, NotLoggedInException {
RSMSet previousResultRsmSet = mamQueryResult.mamFin.getRSMSet();
RSMSet requestRsmSet = new RSMSet(count, previousResultRsmSet.getLast(), RSMSet.PageDirection.after);
- return page(mamQueryResult.form, requestRsmSet);
+ return page(mamQueryResult, requestRsmSet);
}
/**
@@ -336,14 +399,24 @@ public final class MamManager extends Manager {
XMPPErrorException, NotConnectedException, InterruptedException, NotLoggedInException {
RSMSet previousResultRsmSet = mamQueryResult.mamFin.getRSMSet();
RSMSet requestRsmSet = new RSMSet(count, previousResultRsmSet.getFirst(), RSMSet.PageDirection.before);
- return page(mamQueryResult.form, requestRsmSet);
+ return page(mamQueryResult, requestRsmSet);
+ }
+
+ private MamQueryResult page(MamQueryResult mamQueryResult, RSMSet requestRsmSet) throws NoResponseException,
+ XMPPErrorException, NotConnectedException, NotLoggedInException, InterruptedException {
+ ensureMamQueryResultMatchesThisManager(mamQueryResult);
+
+ return page(mamQueryResult.node, mamQueryResult.form, requestRsmSet);
}
/**
* Obtain page before the first message saved (specific chat).
+ *
+ * Note that the messageUid is the XEP-0313 UID and not> the stanza ID of the message.
+ *
*
* @param chatJid
- * @param firstMessageId
+ * @param messageUid the UID of the message of which messages before should be received.
* @param max
* @return the MAM query result
* @throws XMPPErrorException
@@ -352,19 +425,22 @@ public final class MamManager extends Manager {
* @throws InterruptedException
* @throws NoResponseException
*/
- public MamQueryResult pageBefore(Jid chatJid, String firstMessageId, int max) throws XMPPErrorException,
+ public MamQueryResult pageBefore(Jid chatJid, String messageUid, int max) throws XMPPErrorException,
NotLoggedInException, NotConnectedException, InterruptedException, NoResponseException {
- RSMSet rsmSet = new RSMSet(null, firstMessageId, -1, -1, null, max, null, -1);
+ RSMSet rsmSet = new RSMSet(null, messageUid, -1, -1, null, max, null, -1);
DataForm dataForm = getNewMamForm();
addWithJid(chatJid, dataForm);
- return page(dataForm, rsmSet);
+ return page(null, dataForm, rsmSet);
}
/**
* Obtain page after the last message saved (specific chat).
+ *
+ * Note that the messageUid is the XEP-0313 UID and not> the stanza ID of the message.
+ *
*
* @param chatJid
- * @param lastMessageId
+ * @param messageUid the UID of the message of which messages after should be received.
* @param max
* @return the MAM query result
* @throws XMPPErrorException
@@ -373,12 +449,12 @@ public final class MamManager extends Manager {
* @throws InterruptedException
* @throws NoResponseException
*/
- public MamQueryResult pageAfter(Jid chatJid, String lastMessageId, int max) throws XMPPErrorException,
+ public MamQueryResult pageAfter(Jid chatJid, String messageUid, int max) throws XMPPErrorException,
NotLoggedInException, NotConnectedException, InterruptedException, NoResponseException {
- RSMSet rsmSet = new RSMSet(lastMessageId, null, -1, -1, null, max, null, -1);
+ RSMSet rsmSet = new RSMSet(messageUid, null, -1, -1, null, max, null, -1);
DataForm dataForm = getNewMamForm();
addWithJid(chatJid, dataForm);
- return page(dataForm, rsmSet);
+ return page(null, dataForm, rsmSet);
}
/**
@@ -409,9 +485,27 @@ public final class MamManager extends Manager {
* @throws NotLoggedInException
*/
public List retrieveFormFields() throws NoResponseException, XMPPErrorException, NotConnectedException,
+ InterruptedException, NotLoggedInException {
+ return retrieveFormFields(null);
+ }
+
+ /**
+ * Get the form fields supported by the server.
+ *
+ * @param node The Pubsub node name, can be null
+ * @return the list of form fields.
+ * @throws NoResponseException
+ * @throws XMPPErrorException
+ * @throws NotConnectedException
+ * @throws InterruptedException
+ * @throws NotLoggedInException
+ */
+ public List retrieveFormFields(String node)
+ throws NoResponseException, XMPPErrorException, NotConnectedException,
InterruptedException, NotLoggedInException {
String queryId = UUID.randomUUID().toString();
- MamQueryIQ mamQueryIq = new MamQueryIQ(queryId);
+ MamQueryIQ mamQueryIq = new MamQueryIQ(queryId, node, null);
+ mamQueryIq.setTo(archiveAddress);
MamQueryIQ mamResponseQueryIq = connection().createStanzaCollectorAndSend(mamQueryIq).nextResultOrThrow();
@@ -445,7 +539,7 @@ public final class MamManager extends Manager {
forwardedMessages.add(mamResultExtension.getForwarded());
}
- return new MamQueryResult(forwardedMessages, mamFinIQ, DataForm.from(mamQueryIq));
+ return new MamQueryResult(forwardedMessages, mamFinIQ, mamQueryIq.getNode(), DataForm.from(mamQueryIq));
}
/**
@@ -455,15 +549,41 @@ public final class MamManager extends Manager {
public final static class MamQueryResult {
public final List forwardedMessages;
public final MamFinIQ mamFin;
+ private final String node;
private final DataForm form;
- private MamQueryResult(List forwardedMessages, MamFinIQ mamFin, DataForm form) {
+ private MamQueryResult(List forwardedMessages, MamFinIQ mamFin, String node, DataForm form) {
this.forwardedMessages = forwardedMessages;
this.mamFin = mamFin;
+ this.node = node;
this.form = form;
}
}
+ private void ensureMamQueryResultMatchesThisManager(MamQueryResult mamQueryResult) {
+ EntityFullJid localAddress = connection().getUser();
+ EntityBareJid localBareAddress = null;
+ if (localAddress != null) {
+ localBareAddress = localAddress.asEntityBareJid();
+ }
+ boolean isLocalUserArchive = archiveAddress == null || archiveAddress.equals(localBareAddress);
+
+ Jid finIqFrom = mamQueryResult.mamFin.getFrom();
+
+ if (finIqFrom != null) {
+ if (finIqFrom.equals(archiveAddress) || (isLocalUserArchive && finIqFrom.equals(localBareAddress))) {
+ return;
+ }
+ throw new IllegalArgumentException("The given MamQueryResult is from the MAM archive '" + finIqFrom
+ + "' whereas this MamManager is responsible for '" + archiveAddress + '\'');
+ }
+ else if (!isLocalUserArchive) {
+ throw new IllegalArgumentException(
+ "The given MamQueryResult is from the local entity (user) MAM archive, whereas this MamManager is responsible for '"
+ + archiveAddress + '\'');
+ }
+ }
+
/**
* Returns true if Message Archive Management is supported by the server.
*
diff --git a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java
index 9a0c19045..3773985ba 100644
--- a/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java
+++ b/smack-experimental/src/main/java/org/jivesoftware/smackx/mam/element/MamQueryIQ.java
@@ -108,6 +108,15 @@ public class MamQueryIQ extends IQ {
return queryId;
}
+ /**
+ * Get the Node name.
+ *
+ * @return the node
+ */
+ public String getNode() {
+ return node;
+ }
+
/**
* Get the data form.
*
diff --git a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers
index 55b89911d..20ed6cd25 100644
--- a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers
+++ b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.providers
@@ -232,5 +232,27 @@
urn:xmpp:chat-markers:0
org.jivesoftware.smackx.chat_markers.provider.AcknowledgedProvider
-
+
+
+
+ slot
+ urn:xmpp:http:upload:0
+ org.jivesoftware.smackx.httpfileupload.provider.SlotProvider
+
+
+ file-too-large
+ urn:xmpp:http:upload:0
+ org.jivesoftware.smackx.httpfileupload.provider.FileTooLargeErrorProvider
+
+
+ slot
+ urn:xmpp:http:upload
+ org.jivesoftware.smackx.httpfileupload.provider.SlotProvider
+
+
+ file-too-large
+ urn:xmpp:http:upload
+ org.jivesoftware.smackx.httpfileupload.provider.FileTooLargeErrorProvider
+
+
diff --git a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.xml b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.xml
index c621a0d43..05b819a27 100644
--- a/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.xml
+++ b/smack-experimental/src/main/resources/org.jivesoftware.smack.experimental/experimental.xml
@@ -4,5 +4,6 @@
org.jivesoftware.smackx.iot.data.IoTDataManager
org.jivesoftware.smackx.iot.discovery.IoTDiscoveryManager
org.jivesoftware.smackx.iot.provisioning.IoTProvisioningManager
+ org.jivesoftware.smackx.httpfileupload.HttpFileUploadManager
diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/FileTooLargeErrorCreateTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/FileTooLargeErrorCreateTest.java
new file mode 100644
index 000000000..d2abf74f6
--- /dev/null
+++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/FileTooLargeErrorCreateTest.java
@@ -0,0 +1,39 @@
+/**
+ *
+ * Copyright © 2017 Grigory Fedorov
+ *
+ * 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.httpfileupload;
+
+
+import org.jivesoftware.smackx.httpfileupload.element.FileTooLargeError;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class FileTooLargeErrorCreateTest {
+ String fileTooLargeErrorExtensionExample
+ = ""
+ + "20000"
+ + "";
+
+ @Test
+ public void checkFileTooLargeErrorExtensionCreation() {
+ FileTooLargeError fileTooLargeError = new FileTooLargeError(20000);
+
+ Assert.assertEquals(20000, fileTooLargeError.getMaxFileSize());
+ Assert.assertEquals(fileTooLargeErrorExtensionExample, fileTooLargeError.toXML().toString());
+
+ }
+
+}
diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotCreateTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotCreateTest.java
new file mode 100644
index 000000000..ebb5170cd
--- /dev/null
+++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotCreateTest.java
@@ -0,0 +1,46 @@
+/**
+ *
+ * Copyright © 2017 Grigory Fedorov
+ *
+ * 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.httpfileupload;
+
+
+import org.jivesoftware.smackx.httpfileupload.element.Slot;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public class SlotCreateTest {
+ String testSlot
+ = ""
+ + "https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png"
+ + "https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png"
+ + "";
+
+ @Test
+ public void checkSlotRequestCreation() throws MalformedURLException {
+ Slot slot = new Slot(new URL("https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png"),
+ new URL("https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png"));
+
+ Assert.assertEquals(new URL("https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png"),
+ slot.getPutUrl());
+ Assert.assertEquals(new URL("https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png"),
+ slot.getGetUrl());
+
+ Assert.assertEquals(testSlot, slot.getChildElementXML().toString());
+ }
+}
diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotRequestCreateTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotRequestCreateTest.java
new file mode 100644
index 000000000..43c6fe80d
--- /dev/null
+++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/SlotRequestCreateTest.java
@@ -0,0 +1,72 @@
+/**
+ *
+ * Copyright © 2017 Grigory Fedorov
+ *
+ * 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.httpfileupload;
+
+import org.jivesoftware.smackx.httpfileupload.element.SlotRequest;
+import org.junit.Assert;
+import org.junit.Test;
+import org.jxmpp.jid.JidTestUtil;
+import org.jxmpp.stringprep.XmppStringprepException;
+
+
+public class SlotRequestCreateTest {
+
+ String testRequest
+ = ""
+ + "my_juliet.png"
+ + "23456"
+ + "image/jpeg"
+ + "";
+
+ String testRequestWithoutContentType
+ = ""
+ + "my_romeo.png"
+ + "52523"
+ + "";
+
+ @Test
+ public void checkSlotRequestCreation() throws XmppStringprepException {
+ SlotRequest slotRequest = new SlotRequest(JidTestUtil.DOMAIN_BARE_JID_1, "my_juliet.png", 23456, "image/jpeg");
+
+ Assert.assertEquals("my_juliet.png", slotRequest.getFilename());
+ Assert.assertEquals(23456, slotRequest.getSize());
+ Assert.assertEquals("image/jpeg", slotRequest.getContentType());
+
+ Assert.assertEquals(testRequest, slotRequest.getChildElementXML().toString());
+ }
+
+ @Test
+ public void checkSlotRequestCreationWithoutContentType() throws XmppStringprepException {
+ SlotRequest slotRequest = new SlotRequest(JidTestUtil.DOMAIN_BARE_JID_1, "my_romeo.png", 52523);
+
+ Assert.assertEquals("my_romeo.png", slotRequest.getFilename());
+ Assert.assertEquals(52523, slotRequest.getSize());
+ Assert.assertEquals(null, slotRequest.getContentType());
+
+ Assert.assertEquals(testRequestWithoutContentType, slotRequest.getChildElementXML().toString());
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void checkSlotRequestCreationNegativeSize() {
+ new SlotRequest(JidTestUtil.DOMAIN_BARE_JID_1, "my_juliet.png", -23456, "image/jpeg");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void checkSlotRequestCreationZeroSize() {
+ new SlotRequest(JidTestUtil.DOMAIN_BARE_JID_1, "my_juliet.png", 0, "image/jpeg");
+ }
+}
diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/FileTooLargeErrorProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/FileTooLargeErrorProviderTest.java
new file mode 100644
index 000000000..564b142d6
--- /dev/null
+++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/FileTooLargeErrorProviderTest.java
@@ -0,0 +1,58 @@
+/**
+ *
+ * Copyright © 2017 Grigory Fedorov
+ *
+ * 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.httpfileupload.provider;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.jivesoftware.smackx.httpfileupload.element.FileTooLargeError;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class FileTooLargeErrorProviderTest {
+
+ /**
+ * Example 7. Alternative response by the upload service if the file size was too large
+ * @see XEP-0363: HTTP File Upload 5. Error conditions
+ */
+ String slotErrorFileToLarge
+ = ""
+ + ""
+ + "my_juliet.png"
+ + "23456"
+ + ""
+ + ""
+ + ""
+ + "File too large. The maximum file size is 20000 bytes"
+ + ""
+ + "20000"
+ + ""
+ + ""
+ + "";
+
+ @Test
+ public void checkSlotErrorFileToLarge() throws Exception {
+ IQ fileTooLargeErrorIQ = PacketParserUtils.parseStanza(slotErrorFileToLarge);
+
+ Assert.assertEquals(IQ.Type.error, fileTooLargeErrorIQ.getType());
+
+ FileTooLargeError fileTooLargeError = FileTooLargeError.from(fileTooLargeErrorIQ);
+ Assert.assertEquals(20000, fileTooLargeError.getMaxFileSize());
+ }
+}
diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/SlotProviderTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/SlotProviderTest.java
new file mode 100644
index 000000000..4cef3c550
--- /dev/null
+++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/httpfileupload/provider/SlotProviderTest.java
@@ -0,0 +1,55 @@
+/**
+ *
+ * Copyright © 2017 Grigory Fedorov
+ *
+ * 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.httpfileupload.provider;
+
+import org.jivesoftware.smack.packet.IQ;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.jivesoftware.smackx.httpfileupload.element.Slot;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.net.URL;
+
+
+public class SlotProviderTest {
+
+ /**
+ * Example 6. The upload service responds with a slot
+ * @see XEP-0363: HTTP File Upload 4. Requesting a slot
+ */
+ String slotExample
+ = ""
+ + ""
+ + "https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png"
+ + "https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png"
+ + ""
+ + "";
+
+ @Test
+ public void checkSlotProvider() throws Exception {
+ Slot slot = PacketParserUtils.parseStanza(slotExample);
+
+ Assert.assertEquals(IQ.Type.result, slot.getType());
+ Assert.assertEquals(new URL("https://upload.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png"),
+ slot.getPutUrl());
+ Assert.assertEquals(new URL("https://download.montague.tld/4a771ac1-f0b2-4a4a-9700-f2a26fa2bb67/my_juliet.png"),
+ slot.getGetUrl());
+ }
+}
diff --git a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamTest.java b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamTest.java
index 6d7c7c46e..3327f8384 100644
--- a/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamTest.java
+++ b/smack-experimental/src/test/java/org/jivesoftware/smackx/mam/MamTest.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2016 Fernando Ramirez
+ * Copyright 2016-2017 Fernando Ramirez
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,17 +16,16 @@
*/
package org.jivesoftware.smackx.mam;
-import static org.mockito.Mockito.mock;
-
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import org.jivesoftware.smack.DummyConnection;
import org.jivesoftware.smack.XMPPConnection;
-import org.jivesoftware.smackx.ExperimentalInitializerTest;
+import org.jivesoftware.smackx.InitExtensions;
import org.jivesoftware.smackx.xdata.packet.DataForm;
import org.junit.Before;
-public class MamTest extends ExperimentalInitializerTest {
+public class MamTest extends InitExtensions {
protected XMPPConnection connection;
protected String queryId;
@@ -35,7 +34,7 @@ public class MamTest extends ExperimentalInitializerTest {
@Before
public void setup() {
// mock connection
- connection = mock(XMPPConnection.class);
+ connection = new DummyConnection();
// test query id
queryId = "testid";
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java
index 3d240756e..1bc1de725 100644
--- a/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/muc/MultiUserChat.java
@@ -43,7 +43,10 @@ import org.jivesoftware.smack.filter.FromMatchesFilter;
import org.jivesoftware.smack.filter.MessageTypeFilter;
import org.jivesoftware.smack.filter.MessageWithSubjectFilter;
import org.jivesoftware.smack.filter.NotFilter;
+import org.jivesoftware.smack.filter.OrFilter;
+import org.jivesoftware.smack.filter.PresenceTypeFilter;
import org.jivesoftware.smack.filter.StanzaFilter;
+import org.jivesoftware.smack.filter.StanzaIdFilter;
import org.jivesoftware.smack.filter.StanzaExtensionFilter;
import org.jivesoftware.smack.filter.StanzaTypeFilter;
import org.jivesoftware.smack.filter.ToMatchesFilter;
@@ -327,9 +330,17 @@ public class MultiUserChat {
messageCollector = connection.createStanzaCollector(fromRoomGroupchatFilter);
// Wait for a presence packet back from the server.
- // Use a bare JID filter, since the room may rewrite the nickname.
- StanzaFilter responseFilter = new AndFilter(FromMatchesFilter.createBare(getRoom()), new StanzaTypeFilter(
- Presence.class), MUCUserStatusCodeFilter.STATUS_110_PRESENCE_TO_SELF);
+ // @formatter:off
+ StanzaFilter responseFilter = new AndFilter(StanzaTypeFilter.PRESENCE,
+ new OrFilter(
+ // We use a bare JID filter for positive responses, since the MUC service/room may rewrite the nickname.
+ new AndFilter(FromMatchesFilter.createBare(getRoom()), MUCUserStatusCodeFilter.STATUS_110_PRESENCE_TO_SELF),
+ // In case there is an error reply, we match on an error presence with the same stanza id and from the full
+ // JID we send the join presence to.
+ new AndFilter(FromMatchesFilter.createFull(joinPresence.getTo()), new StanzaIdFilter(joinPresence), PresenceTypeFilter.ERROR)
+ )
+ );
+ // @formatter:on
Presence presence;
try {
presence = connection.createStanzaCollectorAndSend(responseFilter, joinPresence).nextResultOrThrow(conf.getTimeout());
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java
index 11f9dcd0f..39b77e81d 100644
--- a/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/ping/PingManager.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2012-2015 Florian Schmaus
+ * Copyright 2012-2017 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,11 +16,10 @@
*/
package org.jivesoftware.smackx.ping;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
@@ -105,8 +104,7 @@ public final class PingManager extends Manager {
defaultPingInterval = interval;
}
- private final Set pingFailedListeners = Collections
- .synchronizedSet(new HashSet());
+ private final Set pingFailedListeners = new CopyOnWriteArraySet<>();
private final ScheduledExecutorService executorService;
diff --git a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java
index e7e6dbce2..286c968b7 100644
--- a/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java
+++ b/smack-im/src/main/java/org/jivesoftware/smack/roster/Roster.java
@@ -66,6 +66,7 @@ import org.jivesoftware.smack.roster.rosterstore.RosterStore;
import org.jivesoftware.smack.util.Objects;
import org.jxmpp.jid.BareJid;
import org.jxmpp.jid.EntityBareJid;
+import org.jxmpp.jid.EntityFullJid;
import org.jxmpp.jid.Jid;
import org.jxmpp.jid.FullJid;
import org.jxmpp.jid.impl.JidCreate;
@@ -1674,9 +1675,16 @@ public final class Roster extends Manager {
final XMPPConnection connection = connection();
RosterPacket rosterPacket = (RosterPacket) iqRequest;
+ EntityFullJid localAddress = connection.getUser();
+ if (localAddress == null) {
+ LOGGER.warning("Ignoring roster push " + iqRequest + " while " + connection
+ + " has no bound resource. This may be a server bug.");
+ return null;
+ }
+
// Roster push (RFC 6121, 2.1.6)
// A roster push with a non-empty from not matching our address MUST be ignored
- EntityBareJid jid = connection.getUser().asEntityBareJid();
+ EntityBareJid jid = localAddress.asEntityBareJid();
Jid from = rosterPacket.getFrom();
if (from != null && !from.equals(jid)) {
LOGGER.warning("Ignoring roster push with a non matching 'from' ourJid='" + jid + "' from='" + from
diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java
index 98c7e6400..e23f2c3ed 100644
--- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java
+++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntTest.java
@@ -16,10 +16,16 @@
*/
package org.igniterealtime.smack.inttest;
+import java.io.File;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
import java.util.Random;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
+import javax.net.ssl.HttpsURLConnection;
+
import org.jivesoftware.smack.StanzaCollector;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
@@ -37,9 +43,12 @@ public abstract class AbstractSmackIntTest {
protected final long timeout;
- protected AbstractSmackIntTest(String testRunId, long timeout) {
+ protected final Configuration sinttestConfiguration;
+
+ protected AbstractSmackIntTest(String testRunId, Configuration configuration) {
this.testRunId = testRunId;
- this.timeout = timeout;
+ this.sinttestConfiguration = configuration;
+ this.timeout = configuration.replyTimeout;
}
protected void performActionAndWaitUntilStanzaReceived(Runnable action, XMPPConnection connection, StanzaFilter filter)
@@ -71,4 +80,19 @@ public abstract class AbstractSmackIntTest {
protected interface Condition {
boolean evaluate() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException;
}
+
+ protected File createNewTempFile() throws IOException {
+ File file = File.createTempFile("smack-integration-test-" + testRunId + "-temp-file", null);
+ file.deleteOnExit();
+ return file;
+ }
+
+ protected HttpURLConnection getHttpUrlConnectionFor(URL url) throws IOException {
+ HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
+ if (sinttestConfiguration.tlsContext != null && urlConnection instanceof HttpsURLConnection) {
+ HttpsURLConnection httpsUrlConnection = (HttpsURLConnection) urlConnection;
+ httpsUrlConnection.setSSLSocketFactory(sinttestConfiguration.tlsContext.getSocketFactory());
+ }
+ return urlConnection;
+ }
}
diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java
index e6cac85ee..91ccba353 100644
--- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackIntegrationTest.java
@@ -41,7 +41,7 @@ public abstract class AbstractSmackIntegrationTest extends AbstractSmackIntTest
protected final XMPPConnection connection;
public AbstractSmackIntegrationTest(SmackIntegrationTestEnvironment environment) {
- super(environment.testRunId, environment.configuration.replyTimeout);
+ super(environment.testRunId, environment.configuration);
this.connection = this.conOne = environment.conOne;
this.conTwo = environment.conTwo;
this.conThree = environment.conThree;
diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackLowLevelIntegrationTest.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackLowLevelIntegrationTest.java
index 81ddb7b4b..6d1bcd7a2 100644
--- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackLowLevelIntegrationTest.java
+++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/AbstractSmackLowLevelIntegrationTest.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2015-2016 Florian Schmaus
+ * Copyright 2015-2017 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,14 +19,10 @@ package org.igniterealtime.smack.inttest;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
-import javax.net.ssl.SSLContext;
-
import org.jivesoftware.smack.tcp.XMPPTCPConnection;
import org.jivesoftware.smack.tcp.XMPPTCPConnectionConfiguration;
import org.jxmpp.jid.DomainBareJid;
-import eu.geekplace.javapinning.java7.Java7Pinning;
-
public abstract class AbstractSmackLowLevelIntegrationTest extends AbstractSmackIntTest {
private final SmackIntegrationTestEnvironment environment;
@@ -39,7 +35,7 @@ public abstract class AbstractSmackLowLevelIntegrationTest extends AbstractSmack
protected final DomainBareJid service;
public AbstractSmackLowLevelIntegrationTest(SmackIntegrationTestEnvironment environment) {
- super(environment.testRunId, environment.configuration.replyTimeout);
+ super(environment.testRunId, environment.configuration);
this.environment = environment;
this.configuration = environment.configuration;
this.service = configuration.service;
@@ -47,9 +43,8 @@ public abstract class AbstractSmackLowLevelIntegrationTest extends AbstractSmack
public final XMPPTCPConnectionConfiguration.Builder getConnectionConfiguration() throws KeyManagementException, NoSuchAlgorithmException {
XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder();
- if (configuration.serviceTlsPin != null) {
- SSLContext sc = Java7Pinning.forPin(configuration.serviceTlsPin);
- builder.setCustomSSLContext(sc);
+ if (configuration.tlsContext != null) {
+ builder.setCustomSSLContext(configuration.tlsContext);
}
builder.setSecurityMode(configuration.securityMode);
builder.setXmppDomain(service);
diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java
index 814b1b4f2..523fc65d4 100644
--- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java
+++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/Configuration.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2015-2016 Florian Schmaus
+ * Copyright 2015-2017 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,8 @@ package org.igniterealtime.smack.inttest;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -26,6 +28,8 @@ import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
+import javax.net.ssl.SSLContext;
+
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.StringUtils;
@@ -33,6 +37,8 @@ import org.jxmpp.jid.DomainBareJid;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
+import eu.geekplace.javapinning.java7.Java7Pinning;
+
public final class Configuration {
public enum AccountRegistration {
@@ -45,6 +51,8 @@ public final class Configuration {
public final String serviceTlsPin;
+ public final SSLContext tlsContext;
+
public final SecurityMode securityMode;
public final int replyTimeout;
@@ -78,10 +86,16 @@ public final class Configuration {
private Configuration(DomainBareJid service, String serviceTlsPin, SecurityMode securityMode, int replyTimeout,
boolean debug, String accountOneUsername, String accountOnePassword, String accountTwoUsername,
String accountTwoPassword, String accountThreeUsername, String accountThreePassword, Set enabledTests, Set disabledTests,
- Set testPackages, String adminAccountUsername, String adminAccountPassword) {
+ Set testPackages, String adminAccountUsername, String adminAccountPassword)
+ throws KeyManagementException, NoSuchAlgorithmException {
this.service = Objects.requireNonNull(service,
"'service' must be set. Either via 'properties' files or via system property 'sinttest.service'.");
this.serviceTlsPin = serviceTlsPin;
+ if (serviceTlsPin != null) {
+ tlsContext = Java7Pinning.forPin(serviceTlsPin);
+ } else {
+ tlsContext = null;
+ }
this.securityMode = securityMode;
if (replyTimeout > 0) {
this.replyTimeout = replyTimeout;
@@ -257,7 +271,7 @@ public final class Configuration {
return this;
}
- public Configuration build() {
+ public Configuration build() throws KeyManagementException, NoSuchAlgorithmException {
return new Configuration(service, serviceTlsPin, securityMode, replyTimeout, debug, accountOneUsername,
accountOnePassword, accountTwoUsername, accountTwoPassword, accountThreeUsername, accountThreePassword, enabledTests, disabledTests,
testPackages, adminAccountUsername, adminAccountPassword);
@@ -266,7 +280,8 @@ public final class Configuration {
private static final String SINTTEST = "sinttest.";
- public static Configuration newConfiguration() throws IOException {
+ public static Configuration newConfiguration()
+ throws IOException, KeyManagementException, NoSuchAlgorithmException {
Properties properties = new Properties();
File propertiesFile = findPropertiesFile();
diff --git a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java
index 35edeac47..c23c5a56e 100644
--- a/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java
+++ b/smack-integration-test/src/main/java/org/igniterealtime/smack/inttest/SmackIntegrationTestFramework.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2015-2016 Florian Schmaus
+ * Copyright 2015-2017 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,8 +43,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.net.ssl.SSLContext;
-
import org.igniterealtime.smack.inttest.IntTestUtil.UsernameAndPassword;
import org.jivesoftware.smack.ConnectionConfiguration.SecurityMode;
import org.jivesoftware.smack.SmackConfiguration;
@@ -64,8 +62,6 @@ import org.reflections.scanners.MethodParameterScanner;
import org.reflections.scanners.SubTypesScanner;
import org.reflections.scanners.TypeAnnotationsScanner;
-import eu.geekplace.javapinning.java7.Java7Pinning;
-
public class SmackIntegrationTestFramework {
private static final Logger LOGGER = Logger.getLogger(SmackIntegrationTestFramework.class.getName());
@@ -553,9 +549,8 @@ public class SmackIntegrationTestFramework {
.setResource(middlefix + '-' + testRunResult.testRunId)
.setSecurityMode(config.securityMode);
// @formatter:on
- if (StringUtils.isNotEmpty(config.serviceTlsPin)) {
- SSLContext sc = Java7Pinning.forPin(config.serviceTlsPin);
- builder.setCustomSSLContext(sc);
+ if (config.tlsContext != null) {
+ builder.setCustomSSLContext(config.tlsContext);
}
XMPPTCPConnection connection = new XMPPTCPConnection(builder.build());
connection.connect();
@@ -581,9 +576,8 @@ public class SmackIntegrationTestFramework {
SmackException, IOException, XMPPException {
Configuration config = environment.configuration;
XMPPTCPConnectionConfiguration.Builder builder = XMPPTCPConnectionConfiguration.builder();
- if (config.serviceTlsPin != null) {
- SSLContext sc = Java7Pinning.forPin(config.serviceTlsPin);
- builder.setCustomSSLContext(sc);
+ if (config.tlsContext != null) {
+ builder.setCustomSSLContext(config.tlsContext);
}
builder.setSecurityMode(config.securityMode);
builder.setXmppDomain(config.service);
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/filetransfer/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/filetransfer/package-info.java
deleted file mode 100644
index 8d52af604..000000000
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/filetransfer/package-info.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- *
- * Copyright 2015 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.
- */
-
-/**
- * TODO describe me.
- */
-package org.jivesoftware.smackx.filetransfer;
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/filetransfer/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/filetransfer/package-info.java
new file mode 120000
index 000000000..538c3207c
--- /dev/null
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/filetransfer/package-info.java
@@ -0,0 +1 @@
+../../../../../../../../smack-extensions/src/main/java/org/jivesoftware/smackx/filetransfer/package-info.java
\ No newline at end of file
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadIntegrationTest.java
new file mode 100644
index 000000000..c846aad07
--- /dev/null
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/HttpFileUploadIntegrationTest.java
@@ -0,0 +1,105 @@
+/**
+ *
+ * Copyright 2017 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.httpfileupload;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import org.igniterealtime.smack.inttest.AbstractSmackIntegrationTest;
+import org.igniterealtime.smack.inttest.SmackIntegrationTest;
+import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
+import org.igniterealtime.smack.inttest.TestNotPossibleException;
+import org.jivesoftware.smack.SmackException;
+import org.jivesoftware.smack.SmackException.NoResponseException;
+import org.jivesoftware.smack.SmackException.NotConnectedException;
+import org.jivesoftware.smack.XMPPException.XMPPErrorException;
+
+public class HttpFileUploadIntegrationTest extends AbstractSmackIntegrationTest {
+
+ private static final int FILE_SIZE = 1024*128;
+
+ private final HttpFileUploadManager hfumOne;
+
+ public HttpFileUploadIntegrationTest(SmackIntegrationTestEnvironment environment) throws XMPPErrorException,
+ NotConnectedException, NoResponseException, InterruptedException, TestNotPossibleException {
+ super(environment);
+ hfumOne = HttpFileUploadManager.getInstanceFor(conOne);
+ if (!hfumOne.discoverUploadService()) {
+ throw new TestNotPossibleException(
+ "HttpFileUploadManager was unable to discover a HTTP File Upload service");
+ }
+ UploadService uploadService = hfumOne.getDefaultUploadService();
+ if (!uploadService.acceptsFileOfSize(FILE_SIZE)) {
+ throw new TestNotPossibleException("The upload service at " + uploadService.getAddress()
+ + " does not accept files of size " + FILE_SIZE
+ + ". It only accepts files with a maximum size of " + uploadService.getMaxFileSize());
+ }
+ hfumOne.setTlsContext(environment.configuration.tlsContext);
+ }
+
+ @SmackIntegrationTest
+ public void httpFileUploadTest() throws FileNotFoundException, IOException, XMPPErrorException, InterruptedException, SmackException {
+ final int fileSize = FILE_SIZE;
+ File file = createNewTempFile();
+ FileOutputStream fos = new FileOutputStream(file.getCanonicalPath());
+ byte[] upBytes;
+ try {
+ upBytes = new byte[fileSize];
+ INSECURE_RANDOM.nextBytes(upBytes);
+ fos.write(upBytes);
+ }
+ finally {
+ fos.close();
+ }
+
+ URL getUrl = hfumOne.uploadFile(file, new UploadProgressListener() {
+ @Override
+ public void onUploadProgress(long uploadedBytes, long totalBytes) {
+ double progress = uploadedBytes / totalBytes;
+ LOGGER.fine("HTTP File Upload progress " + progress + "% (" + uploadedBytes + '/' + totalBytes + ')');
+ }
+ });
+
+ HttpURLConnection urlConnection = getHttpUrlConnectionFor(getUrl);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream(fileSize);
+ byte[] buffer = new byte[4096];
+ int n;
+ try {
+ InputStream is = new BufferedInputStream(urlConnection.getInputStream());
+ while ((n = is.read(buffer)) != -1) {
+ baos.write(buffer, 0, n);
+ }
+ }
+ finally {
+ urlConnection.disconnect();
+ }
+
+ byte[] downBytes = baos.toByteArray();
+
+ assertArrayEquals(upBytes, downBytes);
+ }
+}
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/package-info.java
new file mode 120000
index 000000000..1bde5bbc6
--- /dev/null
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/httpfileupload/package-info.java
@@ -0,0 +1 @@
+../../../../../../../../smack-experimental/src/main/java/org/jivesoftware/smackx/httpfileupload/package-info.java
\ No newline at end of file
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/package-info.java
deleted file mode 100644
index 6e800e994..000000000
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/package-info.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- *
- * Copyright 2015 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.
- */
-
-/**
- * TODO describe me.
- */
-package org.jivesoftware.smackx.iot;
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/package-info.java
new file mode 120000
index 000000000..3c51eae31
--- /dev/null
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iot/package-info.java
@@ -0,0 +1 @@
+../../../../../../../../smack-experimental/src/main/java/org/jivesoftware/smackx/iot/package-info.java
\ No newline at end of file
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iqversion/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iqversion/package-info.java
deleted file mode 100644
index a864122f0..000000000
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iqversion/package-info.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- *
- * Copyright 2015 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.
- */
-
-/**
- * TODO describe me.
- */
-package org.jivesoftware.smackx.iqversion;
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/iqversion/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iqversion/package-info.java
new file mode 120000
index 000000000..f1ac066f1
--- /dev/null
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/iqversion/package-info.java
@@ -0,0 +1 @@
+../../../../../../../../smack-extensions/src/main/java/org/jivesoftware/smackx/iqversion/package-info.java
\ No newline at end of file
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/package-info.java
deleted file mode 100644
index 0ecde2c27..000000000
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/package-info.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- *
- * Copyright 2015 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.
- */
-
-/**
- * TODO describe me.
- */
-package org.jivesoftware.smackx.muc;
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/package-info.java
new file mode 120000
index 000000000..d2e3650fc
--- /dev/null
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/muc/package-info.java
@@ -0,0 +1 @@
+../../../../../../../../smack-extensions/src/main/java/org/jivesoftware/smackx/muc/package-info.java
\ No newline at end of file
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/package-info.java
deleted file mode 100644
index 01e4ee051..000000000
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/package-info.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- *
- * Copyright 2015 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.
- */
-
-/**
- * TODO describe me.
- */
-package org.jivesoftware.smackx;
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/package-info.java
new file mode 120000
index 000000000..f0646658f
--- /dev/null
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/package-info.java
@@ -0,0 +1 @@
+../../../../../../../smack-extensions/src/main/java/org/jivesoftware/smackx/package-info.java
\ No newline at end of file
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ping/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ping/package-info.java
deleted file mode 100644
index 92e6a80e4..000000000
--- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ping/package-info.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- *
- * Copyright 2015 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.
- */
-
-/**
- * TODO describe me.
- */
-package org.jivesoftware.smackx.ping;
diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/ping/package-info.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ping/package-info.java
new file mode 120000
index 000000000..edea7d8fd
--- /dev/null
+++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/ping/package-info.java
@@ -0,0 +1 @@
+../../../../../../../../smack-extensions/src/main/java/org/jivesoftware/smackx/ping/package-info.java
\ No newline at end of file
diff --git a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestUnitTestUtil.java b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestUnitTestUtil.java
index 7032c008e..ba02dd4f6 100644
--- a/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestUnitTestUtil.java
+++ b/smack-integration-test/src/test/java/org/igniterealtime/smack/inttest/SmackIntegrationTestUnitTestUtil.java
@@ -1,6 +1,6 @@
/**
*
- * Copyright 2015 Florian Schmaus
+ * Copyright 2015-2017 Florian Schmaus
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,11 +16,16 @@
*/
package org.igniterealtime.smack.inttest;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+
import org.jxmpp.jid.JidTestUtil;
public class SmackIntegrationTestUnitTestUtil {
- public static DummySmackIntegrationTestFramework getFrameworkForUnitTest(Class extends AbstractSmackIntTest> unitTest) {
+ public static DummySmackIntegrationTestFramework getFrameworkForUnitTest(
+ Class extends AbstractSmackIntTest> unitTest)
+ throws KeyManagementException, NoSuchAlgorithmException {
// @formatter:off
Configuration configuration = Configuration.builder()
.setService(JidTestUtil.DOMAIN_BARE_JID_1)